2016-03-01 15:47:10 +00:00
//-----------------------------------------------------------------------------
2017-04-17 11:33:19 +00:00
// Copyright 1993-1996 id Software
// Copyright 1999-2016 Randy Heit
// Copyright 2002-2016 Christoph Oelckers
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// This program 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 3 of the License, or
// (at your option) any later version.
2016-03-01 15:47:10 +00:00
//
2017-04-17 11:33:19 +00:00
// This program is distributed in the hope that it will be useful,
2016-03-01 15:47:10 +00:00
// but WITHOUT ANY WARRANTY; without even the implied warranty of
2017-04-17 11:33:19 +00:00
// 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, see http://www.gnu.org/licenses/
2016-03-01 15:47:10 +00:00
//
//
// DESCRIPTION:
// DOOM main program (D_DoomMain) and game loop (D_DoomLoop),
// plus functions to determine game mode (shareware, registered),
// parse command line parameters, configure game parameters (turbo),
// and call the startup functions.
//
//-----------------------------------------------------------------------------
// HEADER FILES ------------------------------------------------------------
2018-06-18 11:39:23 +00:00
# ifdef _WIN32
# include <direct.h>
# endif
2016-03-01 15:47:10 +00:00
# ifdef HAVE_FPU_CONTROL
# include <fpu_control.h>
# endif
# if defined(__unix__) || defined(__APPLE__)
# include <unistd.h>
# endif
# include <math.h>
# include <assert.h>
2020-04-11 10:56:55 +00:00
# include "engineerrors.h"
2016-03-01 15:47:10 +00:00
2017-11-12 08:06:40 +00:00
# include "i_time.h"
2016-03-01 15:47:10 +00:00
# include "d_gui.h"
# include "m_random.h"
# include "doomdef.h"
# include "doomstat.h"
# include "gstrings.h"
2020-04-11 11:36:23 +00:00
# include "filesystem.h"
2016-03-01 15:47:10 +00:00
# include "s_sound.h"
# include "v_video.h"
# include "intermission/intermission.h"
# include "f_wipe.h"
# include "m_argv.h"
# include "m_misc.h"
# include "menu/menu.h"
# include "c_console.h"
# include "c_dispatch.h"
# include "i_sound.h"
# include "i_video.h"
# include "g_game.h"
# include "hu_stuff.h"
# include "wi_stuff.h"
# include "st_stuff.h"
# include "am_map.h"
# include "p_setup.h"
# include "r_utility.h"
# include "r_sky.h"
# include "d_main.h"
# include "d_dehacked.h"
# include "cmdlib.h"
# include "v_text.h"
# include "gi.h"
2018-05-03 19:27:45 +00:00
# include "a_dynlight.h"
2016-03-01 15:47:10 +00:00
# include "gameconfigfile.h"
# include "sbar.h"
# include "decallib.h"
# include "version.h"
# include "st_start.h"
# include "teaminfo.h"
# include "hardware.h"
# include "sbarinfo.h"
# include "d_net.h"
# include "d_event.h"
# include "d_netinf.h"
# include "m_cheat.h"
# include "m_joy.h"
# include "po_man.h"
# include "p_local.h"
# include "autosegs.h"
# include "fragglescript/t_fs.h"
2017-01-08 17:45:30 +00:00
# include "g_levellocals.h"
2017-01-22 00:33:53 +00:00
# include "events.h"
2017-04-12 23:12:04 +00:00
# include "vm.h"
# include "types.h"
2019-01-31 18:38:04 +00:00
# include "i_system.h"
2019-01-31 01:05:16 +00:00
# include "g_cvars.h"
2017-06-04 00:00:53 +00:00
# include "r_data/r_vanillatrans.h"
2019-08-23 15:15:19 +00:00
# include "s_music.h"
2019-10-07 18:28:55 +00:00
# include "swrenderer/r_swcolormaps.h"
2020-04-11 10:59:55 +00:00
# include "findfile.h"
2020-04-11 11:26:42 +00:00
# include "md5.h"
2020-04-11 16:00:10 +00:00
# include "c_buttons.h"
2020-04-11 16:09:51 +00:00
# include "d_buttons.h"
2020-04-11 16:22:21 +00:00
# include "i_interface.h"
2020-04-11 16:54:54 +00:00
# include "animations.h"
2020-04-11 17:00:07 +00:00
# include "texturemanager.h"
# include "formats/multipatchtexture.h"
2020-04-11 17:25:32 +00:00
# include "scriptutil.h"
2020-04-11 17:46:57 +00:00
# include "v_palette.h"
2020-04-19 17:52:03 +00:00
# include "texturemanager.h"
2020-04-25 15:58:26 +00:00
# include "hwrenderer/scene/hw_drawinfo.h"
2016-03-01 15:47:10 +00:00
2020-04-12 08:03:01 +00:00
# ifdef __unix__
# include "i_system.h" // for SHARE_DIR
# endif // __unix__
2016-03-01 15:47:10 +00:00
EXTERN_CVAR ( Bool , hud_althud )
2018-06-24 07:32:46 +00:00
EXTERN_CVAR ( Int , vr_mode )
2019-11-26 12:46:18 +00:00
EXTERN_CVAR ( Bool , cl_customizeinvulmap )
2020-04-25 19:52:21 +00:00
EXTERN_CVAR ( Bool , log_vgafont )
EXTERN_CVAR ( Bool , dlg_vgafont )
2016-03-01 15:47:10 +00:00
void DrawHUD ( ) ;
2018-02-13 22:06:59 +00:00
void D_DoAnonStats ( ) ;
2019-10-01 13:24:17 +00:00
void I_DetectOS ( ) ;
2020-04-11 17:18:47 +00:00
void UpdateGenericUI ( bool cvar ) ;
2018-02-13 22:06:59 +00:00
2016-03-01 15:47:10 +00:00
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
2017-10-31 23:47:56 +00:00
extern void I_SetWindowTitle ( const char * caption ) ;
2016-03-01 15:47:10 +00:00
extern void ReadStatistics ( ) ;
extern void M_SetDefaultMode ( ) ;
extern void G_NewInit ( ) ;
extern void SetupPlayerClasses ( ) ;
2017-04-18 14:54:06 +00:00
void DeinitMenus ( ) ;
2019-10-07 18:28:55 +00:00
void CloseNetwork ( ) ;
void P_Shutdown ( ) ;
void M_SaveDefaultsFinal ( ) ;
void R_Shutdown ( ) ;
void I_ShutdownInput ( ) ;
2016-03-01 15:47:10 +00:00
const FIWADInfo * D_FindIWAD ( TArray < FString > & wadfiles , const char * iwad , const char * basewad ) ;
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
2019-10-06 22:55:14 +00:00
bool D_CheckNetGame ( ) ;
2016-03-01 15:47:10 +00:00
void D_ProcessEvents ( ) ;
void G_BuildTiccmd ( ticcmd_t * cmd ) ;
void D_DoAdvanceDemo ( ) ;
void D_LoadWadSettings ( ) ;
2017-03-12 15:56:00 +00:00
void ParseGLDefs ( ) ;
2019-05-19 08:28:07 +00:00
void DrawFullscreenSubtitle ( const char * text ) ;
2019-09-30 22:30:44 +00:00
void D_Cleanup ( ) ;
2019-10-07 18:28:55 +00:00
void FreeSBarInfoScript ( ) ;
2019-12-25 14:52:57 +00:00
void I_UpdateWindowTitle ( ) ;
2020-04-11 16:23:15 +00:00
void S_ParseMusInfo ( ) ;
2016-03-01 15:47:10 +00:00
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
void D_DoomLoop ( ) ;
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
EXTERN_CVAR ( Float , turbo )
EXTERN_CVAR ( Bool , freelook )
EXTERN_CVAR ( Float , m_pitch )
EXTERN_CVAR ( Float , m_yaw )
EXTERN_CVAR ( Bool , invertmouse )
EXTERN_CVAR ( Bool , lookstrafe )
EXTERN_CVAR ( Int , screenblocks )
EXTERN_CVAR ( Bool , sv_cheats )
EXTERN_CVAR ( Bool , sv_unlimited_pickup )
2019-11-03 08:51:22 +00:00
EXTERN_CVAR ( Bool , r_drawplayersprites )
EXTERN_CVAR ( Bool , show_messages )
2016-03-01 15:47:10 +00:00
extern bool setmodeneeded ;
extern bool demorecording ;
extern bool M_DemoNoPlay ; // [RH] if true, then skip any demos in the loop
extern bool insave ;
2018-04-02 10:28:20 +00:00
extern TDeletingArray < FLightDefaults * > LightDefaults ;
2016-03-01 15:47:10 +00:00
2020-04-19 11:04:29 +00:00
const char * iwad_folders [ 13 ] = { " flats/ " , " textures/ " , " hires/ " , " sprites/ " , " voxels/ " , " colormaps/ " , " acs/ " , " maps/ " , " voices/ " , " patches/ " , " graphics/ " , " sounds/ " , " music/ " } ;
const char * iwad_reserved [ 12 ] = { " mapinfo " , " zmapinfo " , " gameinfo " , " sndinfo " , " sbarinfo " , " menudef " , " gldefs " , " animdefs " , " decorate " , " zscript " , " iwadinfo " " maps/ " } ;
2016-03-01 15:47:10 +00:00
2020-03-18 12:58:10 +00:00
CUSTOM_CVAR ( Float , i_timescale , 1.0f , CVAR_NOINITCALL )
{
if ( netgame )
{
Printf ( " Time scale cannot be changed in net games. \n " ) ;
self = 1.0f ;
}
else if ( self > = 0.05f )
{
I_FreezeTime ( true ) ;
TimeScale = self ;
I_FreezeTime ( false ) ;
}
else
{
Printf ( " Time scale must be at least 0.05! \n " ) ;
}
}
2016-03-01 15:47:10 +00:00
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CUSTOM_CVAR ( Int , fraglimit , 0 , CVAR_SERVERINFO )
{
// Check for the fraglimit being hit because the fraglimit is being
// lowered below somebody's current frag count.
if ( deathmatch & & self > 0 )
{
for ( int i = 0 ; i < MAXPLAYERS ; + + i )
{
if ( playeringame [ i ] & & self < = D_GetFragCount ( & players [ i ] ) )
{
Printf ( " %s \n " , GStrings ( " TXT_FRAGLIMIT " ) ) ;
2019-02-01 23:24:43 +00:00
primaryLevel - > ExitLevel ( 0 , false ) ;
2016-03-01 15:47:10 +00:00
break ;
}
}
}
}
CVAR ( Float , timelimit , 0.f , CVAR_SERVERINFO ) ;
CVAR ( Int , wipetype , 1 , CVAR_ARCHIVE ) ;
CVAR ( Int , snd_drawoutput , 0 , 0 ) ;
CUSTOM_CVAR ( String , vid_cursor , " None " , CVAR_ARCHIVE | CVAR_NOINITCALL )
{
bool res = false ;
2018-12-07 01:43:27 +00:00
2016-03-01 15:47:10 +00:00
if ( ! stricmp ( self , " None " ) & & gameinfo . CursorPic . IsNotEmpty ( ) )
{
2020-04-15 17:49:06 +00:00
res = I_SetCursor ( TexMan . GetGameTextureByName ( gameinfo . CursorPic ) ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2020-04-15 17:49:06 +00:00
res = I_SetCursor ( TexMan . GetGameTextureByName ( self ) ) ;
2016-03-01 15:47:10 +00:00
}
if ( ! res )
{
2020-04-15 17:49:06 +00:00
I_SetCursor ( TexMan . GetGameTextureByName ( " cursor " ) ) ;
2016-03-01 15:47:10 +00:00
}
}
2016-12-21 10:11:29 +00:00
// Controlled by startup dialog
CVAR ( Bool , disableautoload , false , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
CVAR ( Bool , autoloadbrightmaps , false , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
CVAR ( Bool , autoloadlights , false , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
2017-07-29 20:26:16 +00:00
CVAR ( Bool , r_debug_disable_vis_filter , false , 0 )
2016-12-21 04:03:06 +00:00
2019-11-03 08:51:22 +00:00
bool hud_toggled = false ;
2016-03-08 12:07:21 +00:00
bool wantToRestart ;
2016-03-01 15:47:10 +00:00
bool DrawFSHUD ; // [RH] Draw fullscreen HUD?
TArray < FString > allwads ;
bool devparm ; // started game with -devparm
const char * D_DrawIcon ; // [RH] Patch name of icon to draw on next refresh
int NoWipe ; // [RH] Allow wipe? (Needs to be set each time)
bool singletics = false ; // debug flag to cancel adaptiveness
FString startmap ;
bool autostart ;
bool advancedemo ;
FILE * debugfile ;
FILE * hashfile ;
event_t events [ MAXEVENTS ] ;
int eventhead ;
int eventtail ;
gamestate_t wipegamestate = GS_DEMOSCREEN ; // can be -1 to force a wipe
bool PageBlank ;
2020-04-13 23:01:29 +00:00
FGameTexture * Advisory ;
2018-07-15 20:51:25 +00:00
FTextureID Page ;
2019-05-19 08:28:07 +00:00
const char * Subtitle ;
2016-03-01 15:47:10 +00:00
bool nospriterename ;
2020-04-22 17:57:14 +00:00
FStartupInfo GameStartupInfo ;
2016-03-01 15:47:10 +00:00
FString lastIWAD ;
int restart = 0 ;
2018-05-04 08:24:37 +00:00
bool AppActive = true ;
2016-03-01 15:47:10 +00:00
cycle_t FrameCycles ;
2017-07-29 15:03:40 +00:00
// [SP] Store the capabilities of the renderer in a global variable, to prevent excessive per-frame processing
uint32_t r_renderercaps = 0 ;
2016-03-01 15:47:10 +00:00
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static int demosequence ;
static int pagetic ;
// CODE --------------------------------------------------------------------
2020-04-11 11:48:55 +00:00
void D_GrabCVarDefaults ( )
{
int lump , lastlump = 0 ;
int lumpversion , gamelastrunversion ;
gamelastrunversion = atoi ( LASTRUNVERSION ) ;
while ( ( lump = fileSystem . FindLump ( " DEFCVARS " , & lastlump ) ) ! = - 1 )
{
// don't parse from wads
if ( lastlump > fileSystem . GetLastEntry ( fileSystem . GetMaxIwadNum ( ) ) )
I_FatalError ( " Cannot load DEFCVARS from a wadfile! \n " ) ;
FScanner sc ( lump ) ;
sc . MustGetString ( ) ;
if ( ! sc . Compare ( " version " ) )
sc . ScriptError ( " Must declare version for defcvars! (currently: %i) " , gamelastrunversion ) ;
sc . MustGetNumber ( ) ;
lumpversion = sc . Number ;
if ( lumpversion > gamelastrunversion )
sc . ScriptError ( " Unsupported version %i (%i supported) " , lumpversion , gamelastrunversion ) ;
if ( lumpversion < 219 )
sc . ScriptError ( " Version must be at least 219 (current version %i) " , gamelastrunversion ) ;
FBaseCVar * var ;
while ( sc . GetString ( ) )
{
if ( sc . Compare ( " set " ) )
{
sc . MustGetString ( ) ;
}
var = FindCVar ( sc . String , NULL ) ;
if ( var ! = NULL )
{
if ( var - > GetFlags ( ) & CVAR_ARCHIVE )
{
UCVarValue val ;
sc . MustGetString ( ) ;
val . String = const_cast < char * > ( sc . String ) ;
var - > SetGenericRepDefault ( val , CVAR_String ) ;
}
else
{
sc . ScriptError ( " Cannot set cvar default for non-config cvar '%s' " , sc . String ) ;
}
}
else
{
sc . ScriptError ( " Unknown cvar '%s' " , sc . String ) ;
}
}
}
}
2019-11-03 08:51:22 +00:00
//==========================================================================
//
// D_ToggleHud
//
// Turns off 2D drawing temporarily.
//
//==========================================================================
void D_ToggleHud ( )
{
static int saved_screenblocks ;
static bool saved_drawplayersprite , saved_showmessages ;
2020-01-04 10:59:26 +00:00
if ( ( hud_toggled = ! hud_toggled ) )
2019-11-03 08:51:22 +00:00
{
saved_screenblocks = screenblocks ;
saved_drawplayersprite = r_drawplayersprites ;
saved_showmessages = show_messages ;
screenblocks = 12 ;
r_drawplayersprites = false ;
show_messages = false ;
C_HideConsole ( ) ;
M_ClearMenus ( ) ;
}
else
{
screenblocks = saved_screenblocks ;
r_drawplayersprites = saved_drawplayersprite ;
show_messages = saved_showmessages ;
}
}
CCMD ( togglehud )
{
D_ToggleHud ( ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// D_ProcessEvents
//
// Send all the events of the given timestamp down the responder chain.
// Events are asynchronous inputs generally generated by the game user.
// Events can be discarded if no responder claims them
//
//==========================================================================
void D_ProcessEvents ( void )
{
event_t * ev ;
for ( ; eventtail ! = eventhead ; eventtail = ( eventtail + 1 ) & ( MAXEVENTS - 1 ) )
{
ev = & events [ eventtail ] ;
if ( ev - > type = = EV_None )
continue ;
if ( ev - > type = = EV_DeviceChange )
UpdateJoystickMenu ( I_UpdateDeviceList ( ) ) ;
if ( C_Responder ( ev ) )
continue ; // console ate the event
if ( M_Responder ( ev ) )
continue ; // menu ate the event
2017-02-03 10:28:40 +00:00
// check events
2019-02-02 15:43:11 +00:00
if ( ev - > type ! = EV_Mouse & & primaryLevel - > localEventManager - > Responder ( ev ) ) // [ZZ] ZScript ate the event // update 07.03.17: mouse events are handled directly
2017-02-03 10:28:40 +00:00
continue ;
2016-03-01 15:47:10 +00:00
G_Responder ( ev ) ;
}
}
//==========================================================================
//
// D_PostEvent
//
// Called by the I/O functions when input is detected.
//
//==========================================================================
void D_PostEvent ( const event_t * ev )
{
// Do not post duplicate consecutive EV_DeviceChange events.
if ( ev - > type = = EV_DeviceChange & & events [ eventhead ] . type = = EV_DeviceChange )
{
return ;
}
events [ eventhead ] = * ev ;
2019-02-02 15:43:11 +00:00
if ( ev - > type = = EV_Mouse & & menuactive = = MENU_Off & & ConsoleState ! = c_down & & ConsoleState ! = c_falling & & ! primaryLevel - > localEventManager - > Responder ( ev ) & & ! paused )
2016-03-01 15:47:10 +00:00
{
2020-04-11 16:09:51 +00:00
if ( buttonMap . ButtonDown ( Button_Mlook ) | | freelook )
2016-03-01 15:47:10 +00:00
{
int look = int ( ev - > y * m_pitch * mouse_sensitivity * 16.0 ) ;
if ( invertmouse )
look = - look ;
2017-10-11 15:22:22 +00:00
G_AddViewPitch ( look , true ) ;
2016-03-01 15:47:10 +00:00
events [ eventhead ] . y = 0 ;
}
2020-04-11 16:09:51 +00:00
if ( ! buttonMap . ButtonDown ( Button_Strafe ) & & ! lookstrafe )
2016-03-01 15:47:10 +00:00
{
2017-10-11 15:22:22 +00:00
G_AddViewAngle ( int ( ev - > x * m_yaw * mouse_sensitivity * 8.0 ) , true ) ;
2016-03-01 15:47:10 +00:00
events [ eventhead ] . x = 0 ;
}
if ( ( events [ eventhead ] . x | events [ eventhead ] . y ) = = 0 )
{
return ;
}
}
eventhead = ( eventhead + 1 ) & ( MAXEVENTS - 1 ) ;
}
//==========================================================================
//
// D_RemoveNextCharEvent
//
// Removes the next EV_GUI_Char event in the input queue. Used by the menu,
// since it (generally) consumes EV_GUI_KeyDown events and not EV_GUI_Char
// events, and it needs to ensure that there is no left over input when it's
// done. If there are multiple EV_GUI_KeyDowns before the EV_GUI_Char, then
// there are dead chars involved, so those should be removed, too. We do
// this by changing the message type to EV_None rather than by actually
// removing the event from the queue.
//
//==========================================================================
void D_RemoveNextCharEvent ( )
{
assert ( events [ eventtail ] . type = = EV_GUI_Event & & events [ eventtail ] . subtype = = EV_GUI_KeyDown ) ;
for ( int evnum = eventtail ; evnum ! = eventhead ; evnum = ( evnum + 1 ) & ( MAXEVENTS - 1 ) )
{
event_t * ev = & events [ evnum ] ;
if ( ev - > type ! = EV_GUI_Event )
break ;
if ( ev - > subtype = = EV_GUI_KeyDown | | ev - > subtype = = EV_GUI_Char )
{
ev - > type = EV_None ;
if ( ev - > subtype = = EV_GUI_Char )
break ;
}
else
{
break ;
}
}
}
2019-01-29 00:09:02 +00:00
//==========================================================================
//
// Render wrapper.
// This function contains all the needed setup and cleanup for starting a render job.
//
//==========================================================================
void D_Render ( std : : function < void ( ) > action , bool interpolate )
{
for ( auto Level : AllLevels ( ) )
{
// Check for the presence of dynamic lights at the start of the frame once.
if ( ( gl_lights & & vid_rendermode = = 4 ) | | ( r_dynlights & & vid_rendermode ! = 4 ) )
{
2019-02-01 21:02:16 +00:00
Level - > HasDynamicLights = ! ! Level - > lights ;
2019-01-29 00:09:02 +00:00
}
else Level - > HasDynamicLights = false ; // lights are off so effectively we have none.
if ( interpolate ) Level - > interpolator . DoInterpolations ( I_GetTimeFrac ( ) ) ;
P_FindParticleSubsectors ( Level ) ;
PO_LinkToSubsectors ( Level ) ;
}
action ( ) ;
if ( interpolate ) for ( auto Level : AllLevels ( ) )
{
Level - > interpolator . RestoreInterpolations ( ) ;
}
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// CVAR dmflags
//
//==========================================================================
2019-02-24 08:57:46 +00:00
CUSTOM_CVAR ( Int , dmflags , 0 , CVAR_SERVERINFO | CVAR_NOINITCALL )
2016-03-01 15:47:10 +00:00
{
// In case DF_NO_FREELOOK was changed, reinitialize the sky
// map. (If no freelook, then no need to stretch the sky.)
2019-01-29 03:44:44 +00:00
R_InitSkyMap ( ) ;
2016-03-01 15:47:10 +00:00
if ( self & DF_NO_FREELOOK )
{
Net_WriteByte ( DEM_CENTERVIEW ) ;
}
// If nofov is set, force everybody to the arbitrator's FOV.
if ( ( self & DF_NO_FOV ) & & consoleplayer = = Net_Arbitrator )
{
2017-08-31 01:53:37 +00:00
float fov ;
2016-03-01 15:47:10 +00:00
Net_WriteByte ( DEM_FOV ) ;
// If the game is started with DF_NO_FOV set, the arbitrator's
// DesiredFOV will not be set when this callback is run, so
// be sure not to transmit a 0 FOV.
2017-08-31 00:20:25 +00:00
fov = players [ consoleplayer ] . DesiredFOV ;
2016-03-01 15:47:10 +00:00
if ( fov = = 0 )
{
fov = 90 ;
}
2017-08-31 00:20:25 +00:00
Net_WriteFloat ( fov ) ;
2016-03-01 15:47:10 +00:00
}
}
CVAR ( Flag , sv_nohealth , dmflags , DF_NO_HEALTH ) ;
CVAR ( Flag , sv_noitems , dmflags , DF_NO_ITEMS ) ;
CVAR ( Flag , sv_weaponstay , dmflags , DF_WEAPONS_STAY ) ;
CVAR ( Flag , sv_falldamage , dmflags , DF_FORCE_FALLINGHX ) ;
CVAR ( Flag , sv_oldfalldamage , dmflags , DF_FORCE_FALLINGZD ) ;
CVAR ( Flag , sv_samelevel , dmflags , DF_SAME_LEVEL ) ;
CVAR ( Flag , sv_spawnfarthest , dmflags , DF_SPAWN_FARTHEST ) ;
CVAR ( Flag , sv_forcerespawn , dmflags , DF_FORCE_RESPAWN ) ;
CVAR ( Flag , sv_noarmor , dmflags , DF_NO_ARMOR ) ;
CVAR ( Flag , sv_noexit , dmflags , DF_NO_EXIT ) ;
CVAR ( Flag , sv_infiniteammo , dmflags , DF_INFINITE_AMMO ) ;
CVAR ( Flag , sv_nomonsters , dmflags , DF_NO_MONSTERS ) ;
CVAR ( Flag , sv_monsterrespawn , dmflags , DF_MONSTERS_RESPAWN ) ;
CVAR ( Flag , sv_itemrespawn , dmflags , DF_ITEMS_RESPAWN ) ;
CVAR ( Flag , sv_fastmonsters , dmflags , DF_FAST_MONSTERS ) ;
CVAR ( Flag , sv_nojump , dmflags , DF_NO_JUMP ) ;
CVAR ( Flag , sv_allowjump , dmflags , DF_YES_JUMP ) ;
CVAR ( Flag , sv_nofreelook , dmflags , DF_NO_FREELOOK ) ;
CVAR ( Flag , sv_allowfreelook , dmflags , DF_YES_FREELOOK ) ;
CVAR ( Flag , sv_nofov , dmflags , DF_NO_FOV ) ;
CVAR ( Flag , sv_noweaponspawn , dmflags , DF_NO_COOP_WEAPON_SPAWN ) ;
CVAR ( Flag , sv_nocrouch , dmflags , DF_NO_CROUCH ) ;
CVAR ( Flag , sv_allowcrouch , dmflags , DF_YES_CROUCH ) ;
CVAR ( Flag , sv_cooploseinventory , dmflags , DF_COOP_LOSE_INVENTORY ) ;
CVAR ( Flag , sv_cooplosekeys , dmflags , DF_COOP_LOSE_KEYS ) ;
CVAR ( Flag , sv_cooploseweapons , dmflags , DF_COOP_LOSE_WEAPONS ) ;
CVAR ( Flag , sv_cooplosearmor , dmflags , DF_COOP_LOSE_ARMOR ) ;
CVAR ( Flag , sv_cooplosepowerups , dmflags , DF_COOP_LOSE_POWERUPS ) ;
CVAR ( Flag , sv_cooploseammo , dmflags , DF_COOP_LOSE_AMMO ) ;
CVAR ( Flag , sv_coophalveammo , dmflags , DF_COOP_HALVE_AMMO ) ;
// Some (hopefully cleaner) interface to these settings.
CVAR ( Mask , sv_crouch , dmflags , DF_NO_CROUCH | DF_YES_CROUCH ) ;
CVAR ( Mask , sv_jump , dmflags , DF_NO_JUMP | DF_YES_JUMP ) ;
CVAR ( Mask , sv_fallingdamage , dmflags , DF_FORCE_FALLINGHX | DF_FORCE_FALLINGZD ) ;
CVAR ( Mask , sv_freelook , dmflags , DF_NO_FREELOOK | DF_YES_FREELOOK ) ;
//==========================================================================
//
// CVAR dmflags2
//
// [RH] From Skull Tag. Some of these were already done as separate cvars
// (such as bfgaiming), but I collected them here like Skull Tag does.
//
//==========================================================================
2019-02-24 08:57:46 +00:00
CUSTOM_CVAR ( Int , dmflags2 , 0 , CVAR_SERVERINFO | CVAR_NOINITCALL )
2016-03-01 15:47:10 +00:00
{
// Stop the automap if we aren't allowed to use it.
if ( ( self & DF2_NO_AUTOMAP ) & & automapactive )
AM_Stop ( ) ;
for ( int i = 0 ; i < MAXPLAYERS ; i + + )
{
player_t * p = & players [ i ] ;
if ( ! playeringame [ i ] )
continue ;
// Revert our view to our own eyes if spying someone else.
if ( self & DF2_DISALLOW_SPYING )
{
// The player isn't looking through its own eyes, so make it.
if ( p - > camera ! = p - > mo )
{
p - > camera = p - > mo ;
S_UpdateSounds ( p - > camera ) ;
2017-03-22 16:29:13 +00:00
StatusBar - > AttachToPlayer ( p ) ;
2016-03-01 15:47:10 +00:00
if ( demoplayback | | multiplayer )
StatusBar - > ShowPlayerName ( ) ;
}
}
// Come out of chasecam mode if we're not allowed to use chasecam.
if ( ! ( dmflags2 & DF2_CHASECAM ) & & CheckCheatmode ( false ) )
{
// Take us out of chasecam mode only.
if ( p - > cheats & CF_CHASECAM )
cht_DoCheat ( p , CHT_CHASECAM ) ;
}
}
}
CVAR ( Flag , sv_weapondrop , dmflags2 , DF2_YES_WEAPONDROP ) ;
CVAR ( Flag , sv_noteamswitch , dmflags2 , DF2_NO_TEAM_SWITCH ) ;
CVAR ( Flag , sv_doubleammo , dmflags2 , DF2_YES_DOUBLEAMMO ) ;
CVAR ( Flag , sv_degeneration , dmflags2 , DF2_YES_DEGENERATION ) ;
CVAR ( Flag , sv_nobfgaim , dmflags2 , DF2_NO_FREEAIMBFG ) ;
CVAR ( Flag , sv_barrelrespawn , dmflags2 , DF2_BARRELS_RESPAWN ) ;
CVAR ( Flag , sv_keepfrags , dmflags2 , DF2_YES_KEEPFRAGS ) ;
CVAR ( Flag , sv_norespawn , dmflags2 , DF2_NO_RESPAWN ) ;
CVAR ( Flag , sv_losefrag , dmflags2 , DF2_YES_LOSEFRAG ) ;
CVAR ( Flag , sv_respawnprotect , dmflags2 , DF2_YES_RESPAWN_INVUL ) ;
CVAR ( Flag , sv_samespawnspot , dmflags2 , DF2_SAME_SPAWN_SPOT ) ;
CVAR ( Flag , sv_infiniteinventory , dmflags2 , DF2_INFINITE_INVENTORY ) ;
CVAR ( Flag , sv_killallmonsters , dmflags2 , DF2_KILL_MONSTERS ) ;
CVAR ( Flag , sv_noautomap , dmflags2 , DF2_NO_AUTOMAP ) ;
CVAR ( Flag , sv_noautomapallies , dmflags2 , DF2_NO_AUTOMAP_ALLIES ) ;
CVAR ( Flag , sv_disallowspying , dmflags2 , DF2_DISALLOW_SPYING ) ;
CVAR ( Flag , sv_chasecam , dmflags2 , DF2_CHASECAM ) ;
CVAR ( Flag , sv_disallowsuicide , dmflags2 , DF2_NOSUICIDE ) ;
CVAR ( Flag , sv_noautoaim , dmflags2 , DF2_NOAUTOAIM ) ;
CVAR ( Flag , sv_dontcheckammo , dmflags2 , DF2_DONTCHECKAMMO ) ;
CVAR ( Flag , sv_killbossmonst , dmflags2 , DF2_KILLBOSSMONST ) ;
CVAR ( Flag , sv_nocountendmonst , dmflags2 , DF2_NOCOUNTENDMONST ) ;
CVAR ( Flag , sv_respawnsuper , dmflags2 , DF2_RESPAWN_SUPER ) ;
//==========================================================================
//
// CVAR compatflags
//
//==========================================================================
EXTERN_CVAR ( Int , compatmode )
2019-02-24 08:57:46 +00:00
CUSTOM_CVAR ( Int , compatflags , 0 , CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_NOINITCALL )
2016-03-01 15:47:10 +00:00
{
2019-01-29 18:28:22 +00:00
for ( auto Level : AllLevels ( ) )
2016-03-01 15:47:10 +00:00
{
2019-03-23 14:28:10 +00:00
Level - > ApplyCompatibility ( ) ;
2016-03-01 15:47:10 +00:00
}
}
2019-02-24 08:57:46 +00:00
CUSTOM_CVAR ( Int , compatflags2 , 0 , CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_NOINITCALL )
2016-03-01 15:47:10 +00:00
{
2019-01-29 18:28:22 +00:00
for ( auto Level : AllLevels ( ) )
{
2019-03-23 14:28:10 +00:00
Level - > ApplyCompatibility2 ( ) ;
2019-01-29 18:28:22 +00:00
Level - > SetCompatLineOnSide ( true ) ;
}
2016-03-01 15:47:10 +00:00
}
CUSTOM_CVAR ( Int , compatmode , 0 , CVAR_ARCHIVE | CVAR_NOINITCALL )
{
int v , w = 0 ;
switch ( self )
{
default :
case 0 :
v = 0 ;
break ;
case 1 : // Doom2.exe compatible with a few relaxed settings
2019-03-24 08:38:26 +00:00
v = COMPATF_SHORTTEX | COMPATF_STAIRINDEX | COMPATF_USEBLOCKING | COMPATF_NODOORLIGHT | COMPATF_SPRITESORT |
COMPATF_TRACE | COMPATF_MISSILECLIP | COMPATF_SOUNDTARGET | COMPATF_DEHHEALTH | COMPATF_CROSSDROPOFF |
COMPATF_LIGHT | COMPATF_MASKEDMIDTEX ;
w = COMPATF2_FLOORMOVE | COMPATF2_EXPLODE1 ;
2016-03-01 15:47:10 +00:00
break ;
case 2 : // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set)
2019-03-24 08:38:26 +00:00
v = COMPATF_SHORTTEX | COMPATF_STAIRINDEX | COMPATF_USEBLOCKING | COMPATF_NODOORLIGHT | COMPATF_SPRITESORT |
COMPATF_TRACE | COMPATF_MISSILECLIP | COMPATF_SOUNDTARGET | COMPATF_NO_PASSMOBJ | COMPATF_LIMITPAIN |
COMPATF_DEHHEALTH | COMPATF_INVISIBILITY | COMPATF_CROSSDROPOFF | COMPATF_CORPSEGIBS | COMPATF_HITSCAN |
COMPATF_WALLRUN | COMPATF_NOTOSSDROPS | COMPATF_LIGHT | COMPATF_MASKEDMIDTEX ;
w = COMPATF2_BADANGLES | COMPATF2_FLOORMOVE | COMPATF2_POINTONLINE | COMPATF2_EXPLODE2 ;
2016-03-01 15:47:10 +00:00
break ;
case 3 : // Boom compat mode
2016-06-21 08:45:17 +00:00
v = COMPATF_TRACE | COMPATF_SOUNDTARGET | COMPATF_BOOMSCROLL | COMPATF_MISSILECLIP | COMPATF_MASKEDMIDTEX ;
2019-03-24 08:38:26 +00:00
w = COMPATF2_EXPLODE1 ;
2016-03-01 15:47:10 +00:00
break ;
case 4 : // Old ZDoom compat mode
v = COMPATF_SOUNDTARGET | COMPATF_LIGHT ;
2019-02-11 14:26:11 +00:00
w = COMPATF2_MULTIEXIT | COMPATF2_TELEPORT | COMPATF2_PUSHWINDOW | COMPATF2_CHECKSWITCHRANGE ;
2016-03-01 15:47:10 +00:00
break ;
case 5 : // MBF compat mode
2019-03-24 08:38:26 +00:00
v = COMPATF_TRACE | COMPATF_SOUNDTARGET | COMPATF_BOOMSCROLL | COMPATF_MISSILECLIP | COMPATF_MUSHROOM |
COMPATF_MBFMONSTERMOVE | COMPATF_NOBLOCKFRIENDS | COMPATF_MASKEDMIDTEX ;
w = COMPATF2_EXPLODE1 ;
2016-03-01 15:47:10 +00:00
break ;
case 6 : // Boom with some added settings to reenable some 'broken' behavior
2019-03-24 08:38:26 +00:00
v = COMPATF_TRACE | COMPATF_SOUNDTARGET | COMPATF_BOOMSCROLL | COMPATF_MISSILECLIP | COMPATF_NO_PASSMOBJ |
COMPATF_INVISIBILITY | COMPATF_CORPSEGIBS | COMPATF_HITSCAN | COMPATF_WALLRUN | COMPATF_NOTOSSDROPS | COMPATF_MASKEDMIDTEX ;
w = COMPATF2_POINTONLINE | COMPATF2_EXPLODE2 ;
2016-03-01 15:47:10 +00:00
break ;
2020-04-08 14:20:13 +00:00
case 7 : // Stricter MBF compatibility
v = COMPATF_CORPSEGIBS | COMPATF_NOBLOCKFRIENDS | COMPATF_MBFMONSTERMOVE | COMPATF_INVISIBILITY |
COMPATF_NOTOSSDROPS | COMPATF_MUSHROOM | COMPATF_NO_PASSMOBJ | COMPATF_BOOMSCROLL | COMPATF_WALLRUN |
COMPATF_TRACE | COMPATF_HITSCAN | COMPATF_MISSILECLIP | COMPATF_MASKEDMIDTEX | COMPATF_SOUNDTARGET ;
w = COMPATF2_POINTONLINE | COMPATF2_EXPLODE1 | COMPATF2_EXPLODE2 ;
break ;
2016-03-01 15:47:10 +00:00
}
compatflags = v ;
compatflags2 = w ;
}
CVAR ( Flag , compat_shortTex , compatflags , COMPATF_SHORTTEX ) ;
CVAR ( Flag , compat_stairs , compatflags , COMPATF_STAIRINDEX ) ;
CVAR ( Flag , compat_limitpain , compatflags , COMPATF_LIMITPAIN ) ;
CVAR ( Flag , compat_silentpickup , compatflags , COMPATF_SILENTPICKUP ) ;
CVAR ( Flag , compat_nopassover , compatflags , COMPATF_NO_PASSMOBJ ) ;
CVAR ( Flag , compat_soundslots , compatflags , COMPATF_MAGICSILENCE ) ;
CVAR ( Flag , compat_wallrun , compatflags , COMPATF_WALLRUN ) ;
CVAR ( Flag , compat_notossdrops , compatflags , COMPATF_NOTOSSDROPS ) ;
CVAR ( Flag , compat_useblocking , compatflags , COMPATF_USEBLOCKING ) ;
CVAR ( Flag , compat_nodoorlight , compatflags , COMPATF_NODOORLIGHT ) ;
CVAR ( Flag , compat_ravenscroll , compatflags , COMPATF_RAVENSCROLL ) ;
CVAR ( Flag , compat_soundtarget , compatflags , COMPATF_SOUNDTARGET ) ;
CVAR ( Flag , compat_dehhealth , compatflags , COMPATF_DEHHEALTH ) ;
CVAR ( Flag , compat_trace , compatflags , COMPATF_TRACE ) ;
CVAR ( Flag , compat_dropoff , compatflags , COMPATF_DROPOFF ) ;
CVAR ( Flag , compat_boomscroll , compatflags , COMPATF_BOOMSCROLL ) ;
CVAR ( Flag , compat_invisibility , compatflags , COMPATF_INVISIBILITY ) ;
CVAR ( Flag , compat_silentinstantfloors , compatflags , COMPATF_SILENT_INSTANT_FLOORS ) ;
CVAR ( Flag , compat_sectorsounds , compatflags , COMPATF_SECTORSOUNDS ) ;
CVAR ( Flag , compat_missileclip , compatflags , COMPATF_MISSILECLIP ) ;
CVAR ( Flag , compat_crossdropoff , compatflags , COMPATF_CROSSDROPOFF ) ;
CVAR ( Flag , compat_anybossdeath , compatflags , COMPATF_ANYBOSSDEATH ) ;
CVAR ( Flag , compat_minotaur , compatflags , COMPATF_MINOTAUR ) ;
CVAR ( Flag , compat_mushroom , compatflags , COMPATF_MUSHROOM ) ;
CVAR ( Flag , compat_mbfmonstermove , compatflags , COMPATF_MBFMONSTERMOVE ) ;
CVAR ( Flag , compat_corpsegibs , compatflags , COMPATF_CORPSEGIBS ) ;
CVAR ( Flag , compat_noblockfriends , compatflags , COMPATF_NOBLOCKFRIENDS ) ;
CVAR ( Flag , compat_spritesort , compatflags , COMPATF_SPRITESORT ) ;
CVAR ( Flag , compat_hitscan , compatflags , COMPATF_HITSCAN ) ;
CVAR ( Flag , compat_light , compatflags , COMPATF_LIGHT ) ;
CVAR ( Flag , compat_polyobj , compatflags , COMPATF_POLYOBJ ) ;
CVAR ( Flag , compat_maskedmidtex , compatflags , COMPATF_MASKEDMIDTEX ) ;
CVAR ( Flag , compat_badangles , compatflags2 , COMPATF2_BADANGLES ) ;
CVAR ( Flag , compat_floormove , compatflags2 , COMPATF2_FLOORMOVE ) ;
CVAR ( Flag , compat_soundcutoff , compatflags2 , COMPATF2_SOUNDCUTOFF ) ;
CVAR ( Flag , compat_pointonline , compatflags2 , COMPATF2_POINTONLINE ) ;
CVAR ( Flag , compat_multiexit , compatflags2 , COMPATF2_MULTIEXIT ) ;
2016-08-07 20:03:36 +00:00
CVAR ( Flag , compat_teleport , compatflags2 , COMPATF2_TELEPORT ) ;
2016-08-09 18:15:13 +00:00
CVAR ( Flag , compat_pushwindow , compatflags2 , COMPATF2_PUSHWINDOW ) ;
2019-02-11 14:26:11 +00:00
CVAR ( Flag , compat_checkswitchrange , compatflags2 , COMPATF2_CHECKSWITCHRANGE ) ;
2019-03-24 08:38:26 +00:00
CVAR ( Flag , compat_explode1 , compatflags2 , COMPATF2_EXPLODE1 ) ;
CVAR ( Flag , compat_explode2 , compatflags2 , COMPATF2_EXPLODE2 ) ;
2019-05-09 08:20:47 +00:00
CVAR ( Flag , compat_railing , compatflags2 , COMPATF2_RAILING ) ;
2016-03-01 15:47:10 +00:00
2018-05-04 08:24:37 +00:00
CVAR ( Bool , vid_activeinbackground , false , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2020-04-27 20:24:41 +00:00
EXTERN_CVAR ( Bool , r_drawvoxels )
EXTERN_CVAR ( Int , gl_tonemap )
static uint32_t GetCaps ( )
{
ActorRenderFeatureFlags FlagSet ;
if ( ! V_IsHardwareRenderer ( ) )
{
FlagSet = RFF_UNCLIPPEDTEX ;
if ( V_IsTrueColor ( ) )
FlagSet | = RFF_TRUECOLOR ;
else
FlagSet | = RFF_COLORMAP ;
}
else
{
// describe our basic feature set
FlagSet = RFF_FLATSPRITES | RFF_MODELS | RFF_SLOPE3DFLOORS |
RFF_TILTPITCH | RFF_ROLLSPRITES | RFF_POLYGONAL | RFF_MATSHADER | RFF_POSTSHADER | RFF_BRIGHTMAP ;
if ( gl_tonemap ! = 5 ) // not running palette tonemap shader
FlagSet | = RFF_TRUECOLOR ;
}
if ( r_drawvoxels )
FlagSet | = RFF_VOXELS ;
return ( uint32_t ) FlagSet ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// D_Display
//
// Draw current display, possibly wiping it from the previous
//
//==========================================================================
void D_Display ( )
{
2020-04-15 22:21:13 +00:00
FGameTexture * wipe = nullptr ;
2018-08-28 13:11:35 +00:00
int wipe_type ;
2018-05-05 09:20:37 +00:00
sector_t * viewsec ;
2016-03-01 15:47:10 +00:00
if ( nodrawers | | screen = = NULL )
return ; // for comparative timing / profiling
2018-05-04 08:24:37 +00:00
if ( ! AppActive & & ( screen - > IsFullscreen ( ) | | ! vid_activeinbackground ) )
{
return ;
}
2016-03-01 15:47:10 +00:00
cycle_t cycles ;
cycles . Reset ( ) ;
cycles . Clock ( ) ;
2017-06-04 10:05:40 +00:00
r_UseVanillaTransparency = UseVanillaTransparency ( ) ; // [SP] Cache UseVanillaTransparency() call
2020-04-27 20:24:41 +00:00
r_renderercaps = GetCaps ( ) ; // [SP] Get the current capabilities of the renderer
2017-06-04 10:05:40 +00:00
2016-03-01 15:47:10 +00:00
if ( players [ consoleplayer ] . camera = = NULL )
{
players [ consoleplayer ] . camera = players [ consoleplayer ] . mo ;
}
2018-06-19 09:20:36 +00:00
auto & vp = r_viewpoint ;
2016-03-01 15:47:10 +00:00
if ( viewactive )
{
2017-08-28 11:17:42 +00:00
DAngle fov = 90.f ;
2017-08-28 16:39:03 +00:00
AActor * cam = players [ consoleplayer ] . camera ;
if ( cam )
2017-08-28 11:17:42 +00:00
{
2017-08-28 16:39:03 +00:00
if ( cam - > player )
fov = cam - > player - > FOV ;
else fov = cam - > CameraFOV ;
2017-08-28 11:17:42 +00:00
}
2018-06-19 09:20:36 +00:00
R_SetFOV ( vp , fov ) ;
2016-03-01 15:47:10 +00:00
}
2018-06-17 14:19:14 +00:00
// fullscreen toggle has been requested
2016-03-01 15:47:10 +00:00
if ( setmodeneeded )
{
2018-07-21 19:32:02 +00:00
setmodeneeded = false ;
2020-04-11 11:05:58 +00:00
screen - > ToggleFullscreen ( vid_fullscreen ) ;
2018-06-17 21:33:22 +00:00
V_OutputResized ( screen - > GetWidth ( ) , screen - > GetHeight ( ) ) ;
2018-07-21 19:32:02 +00:00
}
2016-03-01 15:47:10 +00:00
// change the view size if needed
2018-06-17 13:08:41 +00:00
if ( setsizeneeded )
2016-03-01 15:47:10 +00:00
{
2018-06-17 13:08:41 +00:00
if ( StatusBar = = nullptr )
{
viewwidth = SCREENWIDTH ;
viewheight = SCREENHEIGHT ;
setsizeneeded = false ;
}
else
{
2018-06-19 17:40:52 +00:00
R_ExecuteSetViewSize ( vp , r_viewwindow ) ;
}
2016-03-01 15:47:10 +00:00
}
// [RH] Allow temporarily disabling wipes
if ( NoWipe )
{
NoWipe - - ;
2018-08-28 13:11:35 +00:00
wipe = nullptr ;
2016-03-01 15:47:10 +00:00
wipegamestate = gamestate ;
}
2018-06-24 08:47:42 +00:00
// No wipes when in a stereo3D VR mode
2019-06-26 19:26:14 +00:00
else if ( gamestate ! = wipegamestate & & gamestate ! = GS_FULLCONSOLE & & gamestate ! = GS_TITLELEVEL )
{
if ( vr_mode = = 0 | | vid_rendermode ! = 4 )
2016-03-01 15:47:10 +00:00
{
2019-06-26 19:26:14 +00:00
// save the current screen if about to wipe
2020-04-17 23:00:38 +00:00
wipe = MakeGameTexture ( screen - > WipeStartScreen ( ) , nullptr , ETextureType : : SWCanvas ) ;
2016-03-01 15:47:10 +00:00
2019-06-26 19:26:14 +00:00
switch ( wipegamestate )
{
default :
wipe_type = wipetype ;
break ;
2016-03-01 15:47:10 +00:00
2019-06-26 19:26:14 +00:00
case GS_FORCEWIPEFADE :
wipe_type = wipe_Fade ;
break ;
2016-03-01 15:47:10 +00:00
2019-06-26 19:26:14 +00:00
case GS_FORCEWIPEBURN :
wipe_type = wipe_Burn ;
break ;
case GS_FORCEWIPEMELT :
wipe_type = wipe_Melt ;
break ;
}
2016-03-01 15:47:10 +00:00
}
2019-06-26 19:26:14 +00:00
2016-03-01 15:47:10 +00:00
wipegamestate = gamestate ;
}
else
{
2018-08-28 13:11:35 +00:00
wipe = nullptr ;
2016-03-01 15:47:10 +00:00
}
2018-08-28 10:58:57 +00:00
screen - > FrameTime = I_msTimeFS ( ) ;
2020-04-11 16:54:54 +00:00
TexAnim . UpdateAnimations ( screen - > FrameTime ) ;
2018-08-28 10:58:57 +00:00
R_UpdateSky ( screen - > FrameTime ) ;
screen - > BeginFrame ( ) ;
2020-04-11 17:46:57 +00:00
twod - > ClearClipRect ( ) ;
2018-08-28 10:58:57 +00:00
if ( ( gamestate = = GS_LEVEL | | gamestate = = GS_TITLELEVEL ) & & gametic ! = 0 )
2016-03-01 15:47:10 +00:00
{
2018-08-28 10:58:57 +00:00
// [ZZ] execute event hook that we just started the frame
//E_RenderFrame();
//
2019-01-29 00:09:02 +00:00
D_Render ( [ & ] ( )
2019-01-28 17:26:14 +00:00
{
2020-04-25 15:58:26 +00:00
viewsec = RenderView ( & players [ consoleplayer ] ) ;
2019-01-29 00:09:02 +00:00
} , true ) ;
2019-01-28 17:26:14 +00:00
2018-08-28 10:58:57 +00:00
screen - > Begin2D ( ) ;
2019-11-03 08:51:22 +00:00
if ( ! hud_toggled )
2018-08-28 10:58:57 +00:00
{
2020-04-11 17:49:09 +00:00
V_DrawBlend ( viewsec ) ;
2019-11-03 08:51:22 +00:00
if ( automapactive )
{
primaryLevel - > automap - > Drawer ( ( hud_althud & & viewheight = = SCREENHEIGHT ) ? viewheight : StatusBar - > GetTopOfStatusbar ( ) ) ;
}
2018-08-28 10:58:57 +00:00
2019-11-03 08:51:22 +00:00
// for timing the statusbar code.
//cycle_t stb;
//stb.Reset();
//stb.Clock();
if ( ! automapactive | | viewactive )
2016-03-01 15:47:10 +00:00
{
2019-11-03 08:51:22 +00:00
StatusBar - > RefreshViewBorder ( ) ;
2016-03-01 15:47:10 +00:00
}
2019-11-03 08:51:22 +00:00
if ( hud_althud & & viewheight = = SCREENHEIGHT & & screenblocks > 10 )
{
StatusBar - > DrawBottomStuff ( HUD_AltHud ) ;
if ( DrawFSHUD | | automapactive ) StatusBar - > DrawAltHUD ( ) ;
if ( players [ consoleplayer ] . camera & & players [ consoleplayer ] . camera - > player & & ! automapactive )
{
StatusBar - > DrawCrosshair ( ) ;
}
StatusBar - > CallDraw ( HUD_AltHud , vp . TicFrac ) ;
StatusBar - > DrawTopStuff ( HUD_AltHud ) ;
}
else if ( viewheight = = SCREENHEIGHT & & viewactive & & screenblocks > 10 )
{
EHudState state = DrawFSHUD ? HUD_Fullscreen : HUD_None ;
StatusBar - > DrawBottomStuff ( state ) ;
StatusBar - > CallDraw ( state , vp . TicFrac ) ;
StatusBar - > DrawTopStuff ( state ) ;
}
else
{
StatusBar - > DrawBottomStuff ( HUD_StatusBar ) ;
StatusBar - > CallDraw ( HUD_StatusBar , vp . TicFrac ) ;
StatusBar - > DrawTopStuff ( HUD_StatusBar ) ;
}
//stb.Unclock();
//Printf("Stbar = %f\n", stb.TimeMS());
2018-08-28 10:58:57 +00:00
}
}
else
{
screen - > Begin2D ( ) ;
switch ( gamestate )
{
case GS_FULLCONSOLE :
screen - > Begin2D ( ) ;
C_DrawConsole ( ) ;
M_Drawer ( ) ;
screen - > End2DAndUpdate ( ) ;
return ;
case GS_INTERMISSION :
WI_Drawer ( ) ;
break ;
case GS_FINALE :
F_Drawer ( ) ;
break ;
case GS_DEMOSCREEN :
D_PageDrawer ( ) ;
break ;
default :
break ;
2016-03-01 15:47:10 +00:00
}
}
2019-11-03 08:51:22 +00:00
if ( ! hud_toggled )
2016-03-01 15:47:10 +00:00
{
2019-11-03 08:51:22 +00:00
CT_Drawer ( ) ;
// draw pause pic
if ( ( paused | | pauseext ) & & menuactive = = MENU_Off )
2016-03-01 15:47:10 +00:00
{
2020-04-13 23:01:29 +00:00
auto tex = TexMan . GetGameTextureByName ( gameinfo . PauseSign , true ) ;
double x = ( SCREENWIDTH - tex - > GetDisplayWidth ( ) * CleanXfac ) / 2 +
2019-11-03 08:51:22 +00:00
tex - > GetDisplayLeftOffset ( ) * CleanXfac ;
2020-04-11 17:46:57 +00:00
DrawTexture ( twod , tex , x , 4 , DTA_CleanNoMove , true , TAG_DONE ) ;
2019-11-03 08:51:22 +00:00
if ( paused & & multiplayer )
{
FFont * font = generic_ui ? NewSmallFont : SmallFont ;
FString pstring = GStrings ( " TXT_BY " ) ;
pstring . Substitute ( " %s " , players [ paused - 1 ] . userinfo . GetName ( ) ) ;
2020-04-11 17:46:57 +00:00
DrawText ( twod , font , CR_RED ,
2020-04-11 17:48:14 +00:00
( twod - > GetWidth ( ) - font - > StringWidth ( pstring ) * CleanXfac ) / 2 ,
2019-11-03 08:51:22 +00:00
( tex - > GetDisplayHeight ( ) * CleanYfac ) + 4 , pstring , DTA_CleanNoMove , true , TAG_DONE ) ;
}
2016-03-01 15:47:10 +00:00
}
2019-11-03 08:51:22 +00:00
// [RH] Draw icon, if any
if ( D_DrawIcon )
2016-03-01 15:47:10 +00:00
{
2019-11-03 08:51:22 +00:00
FTextureID picnum = TexMan . CheckForTexture ( D_DrawIcon , ETextureType : : MiscPatch ) ;
D_DrawIcon = NULL ;
if ( picnum . isValid ( ) )
{
2020-04-13 23:01:29 +00:00
auto tex = TexMan . GetGameTexture ( picnum ) ;
2020-04-11 17:46:57 +00:00
DrawTexture ( twod , tex , 160 - tex - > GetDisplayWidth ( ) / 2 , 100 - tex - > GetDisplayHeight ( ) / 2 ,
2019-11-03 08:51:22 +00:00
DTA_320x200 , true , TAG_DONE ) ;
}
NoWipe = 10 ;
2016-03-01 15:47:10 +00:00
}
2019-11-03 08:51:22 +00:00
if ( snd_drawoutput )
{
GSnd - > DrawWaveDebug ( snd_drawoutput ) ;
}
2016-03-01 15:47:10 +00:00
}
2019-11-03 08:51:22 +00:00
if ( ! wipe | | NoWipe < 0 | | wipe_type = = wipe_None | | hud_toggled )
2016-03-01 15:47:10 +00:00
{
2018-09-02 19:13:36 +00:00
if ( wipe ! = nullptr ) delete wipe ;
wipe = nullptr ;
2016-03-01 15:47:10 +00:00
NetUpdate ( ) ; // send out any new accumulation
// normal update
2017-02-03 11:29:17 +00:00
// draw ZScript UI stuff
2018-03-27 18:02:44 +00:00
C_DrawConsole ( ) ; // draw console
2016-03-01 15:47:10 +00:00
M_Drawer ( ) ; // menu is drawn even on top of everything
2019-11-03 08:51:22 +00:00
if ( ! hud_toggled )
2020-04-11 17:46:57 +00:00
FStat : : PrintStat ( twod ) ;
2018-08-28 10:58:57 +00:00
screen - > End2DAndUpdate ( ) ;
2016-03-01 15:47:10 +00:00
}
else
{
// wipe update
2017-11-16 01:33:08 +00:00
uint64_t wipestart , nowtime , diff ;
2016-03-01 15:47:10 +00:00
bool done ;
GSnd - > SetSfxPaused ( true , 1 ) ;
I_FreezeTime ( true ) ;
2018-08-28 10:58:57 +00:00
screen - > End2D ( ) ;
2020-04-17 23:00:38 +00:00
auto wipend = MakeGameTexture ( screen - > WipeEndScreen ( ) , nullptr , ETextureType : : SWCanvas ) ;
2018-08-28 18:25:07 +00:00
auto wiper = Wiper : : Create ( wipe_type ) ;
2020-04-15 22:21:13 +00:00
wiper - > SetTextures ( wipe , wipend ) ;
2016-03-01 15:47:10 +00:00
2017-11-12 23:54:32 +00:00
wipestart = I_msTime ( ) ;
2016-03-01 15:47:10 +00:00
NetUpdate ( ) ; // send out any new accumulation
do
{
do
{
I_WaitVBL ( 2 ) ;
2017-11-12 23:54:32 +00:00
nowtime = I_msTime ( ) ;
2016-03-01 15:47:10 +00:00
diff = ( nowtime - wipestart ) * 40 / 1000 ; // Using 35 here feels too slow.
} while ( diff < 1 ) ;
wipestart = nowtime ;
2018-08-28 10:58:57 +00:00
screen - > Begin2D ( ) ;
2018-08-28 20:58:21 +00:00
done = wiper - > Run ( 1 ) ;
2018-03-27 18:02:44 +00:00
C_DrawConsole ( ) ; // console and
2016-03-01 15:47:10 +00:00
M_Drawer ( ) ; // menu are drawn even on top of wipes
2018-08-28 10:58:57 +00:00
screen - > End2DAndUpdate ( ) ;
2016-03-01 15:47:10 +00:00
NetUpdate ( ) ; // [RH] not sure this is needed anymore
} while ( ! done ) ;
2018-08-28 18:25:07 +00:00
delete wiper ;
2016-03-01 15:47:10 +00:00
I_FreezeTime ( false ) ;
GSnd - > SetSfxPaused ( false , 1 ) ;
}
cycles . Unclock ( ) ;
FrameCycles = cycles ;
}
//==========================================================================
//
// D_ErrorCleanup ()
//
// Cleanup after a recoverable error or a restart
//==========================================================================
void D_ErrorCleanup ( )
{
savegamerestore = false ;
2019-02-01 23:24:43 +00:00
primaryLevel - > BotInfo . RemoveAllBots ( primaryLevel , true ) ;
2016-03-01 15:47:10 +00:00
D_QuitNetGame ( ) ;
if ( demorecording | | demoplayback )
G_CheckDemoStatus ( ) ;
Net_ClearBuffers ( ) ;
G_NewInit ( ) ;
2017-08-25 12:36:06 +00:00
M_ClearMenus ( ) ;
2016-03-01 15:47:10 +00:00
singletics = false ;
playeringame [ 0 ] = 1 ;
players [ 0 ] . playerstate = PST_LIVE ;
gameaction = ga_fullconsole ;
if ( gamestate = = GS_DEMOSCREEN )
{
menuactive = MENU_Off ;
}
2017-03-18 20:19:32 +00:00
if ( gamestate = = GS_INTERMISSION ) gamestate = GS_DEMOSCREEN ;
2016-03-01 15:47:10 +00:00
insave = false ;
2019-10-21 12:54:13 +00:00
ClearGlobalVMStack ( ) ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// D_DoomLoop
//
// Manages timing and IO, calls all ?_Responder, ?_Ticker, and ?_Drawer,
// calls I_GetTime, I_StartFrame, and I_StartTic
//
//==========================================================================
void D_DoomLoop ( )
{
int lasttic = 0 ;
// Clamp the timer to TICRATE until the playloop has been entered.
r_NoInterpolate = true ;
2018-07-15 20:51:25 +00:00
Page . SetInvalid ( ) ;
2019-05-19 08:28:07 +00:00
Subtitle = nullptr ;
2018-07-15 20:51:25 +00:00
Advisory = nullptr ;
2016-03-01 15:47:10 +00:00
vid_cursor . Callback ( ) ;
for ( ; ; )
{
try
{
// frame syncronous IO operations
if ( gametic > lasttic )
{
lasttic = gametic ;
I_StartFrame ( ) ;
}
2017-07-20 12:20:50 +00:00
I_SetFrameTime ( ) ;
2016-03-01 15:47:10 +00:00
// process one or more tics
if ( singletics )
{
I_StartTic ( ) ;
D_ProcessEvents ( ) ;
G_BuildTiccmd ( & netcmds [ consoleplayer ] [ maketic % BACKUPTICS ] ) ;
if ( advancedemo )
D_DoAdvanceDemo ( ) ;
C_Ticker ( ) ;
M_Ticker ( ) ;
G_Ticker ( ) ;
// [RH] Use the consoleplayer's camera to update sounds
S_UpdateSounds ( players [ consoleplayer ] . camera ) ; // move positional sounds
gametic + + ;
maketic + + ;
GC : : CheckGC ( ) ;
Net_NewMakeTic ( ) ;
}
else
{
TryRunTics ( ) ; // will run at least one tic
}
// Update display, next frame, with current state.
2017-11-16 03:20:55 +00:00
I_StartTic ( ) ;
2016-03-01 15:47:10 +00:00
D_Display ( ) ;
2019-08-23 15:15:19 +00:00
S_UpdateMusic ( ) ;
2016-03-08 12:07:21 +00:00
if ( wantToRestart )
{
wantToRestart = false ;
return ;
}
2016-03-01 15:47:10 +00:00
}
catch ( CRecoverableError & error )
{
if ( error . GetMessage ( ) )
{
Printf ( PRINT_BOLD , " \n %s \n " , error . GetMessage ( ) ) ;
}
D_ErrorCleanup ( ) ;
}
2016-12-03 11:23:13 +00:00
catch ( CVMAbortException & error )
{
error . MaybePrintMessage ( ) ;
2016-12-07 10:28:40 +00:00
Printf ( " %s " , error . stacktrace . GetChars ( ) ) ;
2016-12-03 11:23:13 +00:00
D_ErrorCleanup ( ) ;
}
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// D_PageTicker
//
//==========================================================================
void D_PageTicker ( void )
{
if ( - - pagetic < 0 )
D_AdvanceDemo ( ) ;
}
//==========================================================================
//
// D_PageDrawer
//
//==========================================================================
void D_PageDrawer ( void )
{
2020-04-11 17:46:57 +00:00
ClearRect ( twod , 0 , 0 , SCREENWIDTH , SCREENHEIGHT , 0 , 0 ) ;
2018-07-15 20:51:25 +00:00
if ( Page . Exists ( ) )
2016-03-01 15:47:10 +00:00
{
2020-04-13 23:01:29 +00:00
DrawTexture ( twod , TexMan . GetGameTexture ( Page , true ) , 0 , 0 ,
2016-03-01 15:47:10 +00:00
DTA_Fullscreen , true ,
DTA_Masked , false ,
DTA_BilinearFilter , true ,
TAG_DONE ) ;
}
2019-05-19 08:28:07 +00:00
if ( Subtitle ! = nullptr )
{
DrawFullscreenSubtitle ( GStrings [ Subtitle ] ) ;
}
if ( Advisory ! = nullptr )
2016-03-01 15:47:10 +00:00
{
2020-04-11 17:46:57 +00:00
DrawTexture ( twod , Advisory , 4 , 160 , DTA_320x200 , true , TAG_DONE ) ;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// D_AdvanceDemo
//
// Called after each demo or intro demosequence finishes
//
//==========================================================================
void D_AdvanceDemo ( void )
{
advancedemo = true ;
}
//==========================================================================
//
// D_DoStrifeAdvanceDemo
//
//==========================================================================
void D_DoStrifeAdvanceDemo ( )
{
static const char * const fullVoices [ 6 ] =
{
" svox/pro1 " , " svox/pro2 " , " svox/pro3 " , " svox/pro4 " , " svox/pro5 " , " svox/pro6 "
} ;
static const char * const teaserVoices [ 6 ] =
{
" svox/voc91 " , " svox/voc92 " , " svox/voc93 " , " svox/voc94 " , " svox/voc95 " , " svox/voc96 "
} ;
const char * const * voices = gameinfo . flags & GI_SHAREWARE ? teaserVoices : fullVoices ;
2019-05-19 08:28:07 +00:00
const char * pagename = nullptr ;
const char * subtitle = nullptr ;
2016-03-01 15:47:10 +00:00
gamestate = GS_DEMOSCREEN ;
PageBlank = false ;
switch ( demosequence )
{
default :
case 0 :
pagetic = 6 * TICRATE ;
pagename = " TITLEPIC " ;
2020-04-11 11:24:34 +00:00
if ( fileSystem . CheckNumForName ( " d_logo " , ns_music ) < 0 )
2016-03-01 15:47:10 +00:00
{ // strife0.wad does not have d_logo
S_StartMusic ( " " ) ;
}
else
{
S_StartMusic ( " d_logo " ) ;
}
C_HideConsole ( ) ;
break ;
case 1 :
// [RH] Strife fades to black and then to the Rogue logo, but
// I think it looks better if it doesn't fade.
pagetic = 10 * TICRATE / 35 ;
pagename = " " ; // PANEL0, but strife0.wad doesn't have it, so don't use it.
PageBlank = true ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , " bishop/active " , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
break ;
case 2 :
pagetic = 4 * TICRATE ;
pagename = " RGELOGO " ;
break ;
case 3 :
pagetic = 7 * TICRATE ;
pagename = " PANEL1 " ;
2019-05-19 08:28:07 +00:00
subtitle = " TXT_SUB_INTRO1 " ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , voices [ 0 ] , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
// The new Strife teaser has D_FMINTR.
// The full retail Strife has D_INTRO.
// And the old Strife teaser has both. (I do not know which one it actually uses, nor do I care.)
S_StartMusic ( gameinfo . flags & GI_TEASER2 ? " d_fmintr " : " d_intro " ) ;
break ;
case 4 :
pagetic = 9 * TICRATE ;
pagename = " PANEL2 " ;
2019-05-19 08:28:07 +00:00
subtitle = " TXT_SUB_INTRO2 " ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , voices [ 1 ] , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
break ;
case 5 :
pagetic = 12 * TICRATE ;
pagename = " PANEL3 " ;
2019-05-19 08:28:07 +00:00
subtitle = " TXT_SUB_INTRO3 " ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , voices [ 2 ] , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
break ;
case 6 :
pagetic = 11 * TICRATE ;
pagename = " PANEL4 " ;
2019-05-19 08:28:07 +00:00
subtitle = " TXT_SUB_INTRO4 " ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , voices [ 3 ] , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
break ;
case 7 :
pagetic = 10 * TICRATE ;
pagename = " PANEL5 " ;
2019-05-19 08:28:07 +00:00
subtitle = " TXT_SUB_INTRO5 " ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , voices [ 4 ] , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
break ;
case 8 :
pagetic = 16 * TICRATE ;
pagename = " PANEL6 " ;
2019-05-19 08:28:07 +00:00
subtitle = " TXT_SUB_INTRO6 " ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_VOICE , CHANF_UI , voices [ 5 ] , 1 , ATTN_NORM ) ;
2016-03-01 15:47:10 +00:00
break ;
case 9 :
pagetic = 6 * TICRATE ;
pagename = " vellogo " ;
wipegamestate = GS_FORCEWIPEFADE ;
break ;
case 10 :
pagetic = 12 * TICRATE ;
pagename = " CREDIT " ;
wipegamestate = GS_FORCEWIPEFADE ;
break ;
}
if ( demosequence + + > 10 )
demosequence = 0 ;
if ( demosequence = = 9 & & ! ( gameinfo . flags & GI_SHAREWARE ) )
demosequence = 10 ;
2019-05-19 08:28:07 +00:00
if ( pagename ! = nullptr )
{
Page = TexMan . CheckForTexture ( pagename , ETextureType : : MiscPatch ) ;
Subtitle = subtitle ;
}
else
{
Subtitle = nullptr ;
}
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// D_DoAdvanceDemo
//
//==========================================================================
void D_DoAdvanceDemo ( void )
{
static char demoname [ 8 ] = " DEMO1 " ;
static int democount = 0 ;
static int pagecount ;
FString pagename ;
advancedemo = false ;
if ( gameaction ! = ga_nothing )
{
return ;
}
players [ consoleplayer ] . playerstate = PST_LIVE ; // not reborn
usergame = false ; // no save / end game here
paused = 0 ;
// [RH] If you want something more dynamic for your title, create a map
// and name it TITLEMAP. That map will be loaded and used as the title.
if ( P_CheckMapData ( " TITLEMAP " ) )
{
G_InitNew ( " TITLEMAP " , true ) ;
return ;
}
if ( gameinfo . gametype = = GAME_Strife )
{
D_DoStrifeAdvanceDemo ( ) ;
return ;
}
switch ( demosequence )
{
case 3 :
if ( gameinfo . advisoryTime )
{
2020-04-13 23:01:29 +00:00
Advisory = TexMan . GetGameTextureByName ( " ADVISOR " ) ;
2016-03-01 15:47:10 +00:00
demosequence = 1 ;
pagetic = ( int ) ( gameinfo . advisoryTime * TICRATE ) ;
break ;
}
// fall through to case 1 if no advisory notice
case 1 :
Advisory = NULL ;
if ( ! M_DemoNoPlay )
{
democount + + ;
mysnprintf ( demoname + 4 , countof ( demoname ) - 4 , " %d " , democount ) ;
2020-04-11 11:24:34 +00:00
if ( fileSystem . CheckNumForName ( demoname ) < 0 )
2016-03-01 15:47:10 +00:00
{
demosequence = 0 ;
democount = 0 ;
// falls through to case 0 below
}
else
{
2019-03-14 23:16:08 +00:00
singledemo = false ;
2016-03-01 15:47:10 +00:00
G_DeferedPlayDemo ( demoname ) ;
demosequence = 2 ;
break ;
}
}
default :
case 0 :
gamestate = GS_DEMOSCREEN ;
pagename = gameinfo . TitlePage ;
pagetic = ( int ) ( gameinfo . titleTime * TICRATE ) ;
S_ChangeMusic ( gameinfo . titleMusic , gameinfo . titleOrder , false ) ;
demosequence = 3 ;
pagecount = 0 ;
C_HideConsole ( ) ;
break ;
case 2 :
pagetic = ( int ) ( gameinfo . pageTime * TICRATE ) ;
gamestate = GS_DEMOSCREEN ;
if ( gameinfo . creditPages . Size ( ) > 0 )
{
2020-04-11 10:58:38 +00:00
pagename = gameinfo . creditPages [ pagecount ] . GetChars ( ) ;
2016-03-01 15:47:10 +00:00
pagecount = ( pagecount + 1 ) % gameinfo . creditPages . Size ( ) ;
}
demosequence = 1 ;
break ;
}
2018-07-15 20:51:25 +00:00
2016-03-01 15:47:10 +00:00
if ( pagename . IsNotEmpty ( ) )
{
2018-07-15 20:51:25 +00:00
Page = TexMan . CheckForTexture ( pagename , ETextureType : : MiscPatch ) ;
2016-03-01 15:47:10 +00:00
}
}
//==========================================================================
//
// D_StartTitle
//
//==========================================================================
void D_StartTitle ( void )
{
gameaction = ga_nothing ;
demosequence = - 1 ;
D_AdvanceDemo ( ) ;
}
//==========================================================================
//
// Cmd_Endgame
//
// [RH] Quit the current game and go to fullscreen console
//
//==========================================================================
CCMD ( endgame )
{
if ( ! netgame )
{
gameaction = ga_fullconsole ;
demosequence = - 1 ;
G_CheckDemoStatus ( ) ;
}
}
//==========================================================================
//
// ParseCVarInfo
//
//==========================================================================
void ParseCVarInfo ( )
{
int lump , lastlump = 0 ;
bool addedcvars = false ;
2020-04-11 11:24:34 +00:00
while ( ( lump = fileSystem . FindLump ( " CVARINFO " , & lastlump ) ) ! = - 1 )
2016-03-01 15:47:10 +00:00
{
FScanner sc ( lump ) ;
sc . SetCMode ( true ) ;
while ( sc . GetToken ( ) )
{
FString cvarname ;
char * cvardefault = NULL ;
ECVarType cvartype = CVAR_Dummy ;
int cvarflags = CVAR_MOD | CVAR_ARCHIVE ;
FBaseCVar * cvar ;
// Check for flag tokens.
while ( sc . TokenType = = TK_Identifier )
{
if ( stricmp ( sc . String , " server " ) = = 0 )
{
cvarflags | = CVAR_SERVERINFO ;
}
else if ( stricmp ( sc . String , " user " ) = = 0 )
{
cvarflags | = CVAR_USERINFO ;
}
else if ( stricmp ( sc . String , " noarchive " ) = = 0 )
{
cvarflags & = ~ CVAR_ARCHIVE ;
}
2017-01-31 09:05:29 +00:00
else if ( stricmp ( sc . String , " cheat " ) = = 0 )
{
cvarflags | = CVAR_CHEAT ;
}
2017-11-11 14:37:41 +00:00
else if ( stricmp ( sc . String , " latch " ) = = 0 )
{
cvarflags | = CVAR_LATCH ;
}
2020-01-05 16:27:50 +00:00
else if ( stricmp ( sc . String , " nosave " ) = = 0 )
{
2020-01-16 15:48:18 +00:00
cvarflags | = CVAR_CONFIG_ONLY ;
2020-01-05 16:27:50 +00:00
}
2016-03-01 15:47:10 +00:00
else
{
sc . ScriptError ( " Unknown cvar attribute '%s' " , sc . String ) ;
}
sc . MustGetAnyToken ( ) ;
}
2020-01-16 15:48:18 +00:00
// Possibility of defining a cvar as 'server nosave' or 'user nosave' is kept for
// compatibility reasons.
if ( cvarflags & CVAR_CONFIG_ONLY )
{
cvarflags & = ~ CVAR_SERVERINFO ;
cvarflags & = ~ CVAR_USERINFO ;
}
2016-03-01 15:47:10 +00:00
// Do some sanity checks.
2020-01-16 15:48:18 +00:00
// No need to check server-nosave and user-nosave combinations because they
// are made impossible right above.
if ( ( cvarflags & ( CVAR_SERVERINFO | CVAR_USERINFO | CVAR_CONFIG_ONLY ) ) = = 0 | |
2016-03-01 15:47:10 +00:00
( cvarflags & ( CVAR_SERVERINFO | CVAR_USERINFO ) ) = = ( CVAR_SERVERINFO | CVAR_USERINFO ) )
{
2020-01-16 15:48:18 +00:00
sc . ScriptError ( " One of 'server', 'user', or 'nosave' must be specified " ) ;
2016-03-01 15:47:10 +00:00
}
// The next token must be the cvar type.
if ( sc . TokenType = = TK_Bool )
{
cvartype = CVAR_Bool ;
}
else if ( sc . TokenType = = TK_Int )
{
cvartype = CVAR_Int ;
}
else if ( sc . TokenType = = TK_Float )
{
cvartype = CVAR_Float ;
}
else if ( sc . TokenType = = TK_Color )
{
cvartype = CVAR_Color ;
}
else if ( sc . TokenType = = TK_String )
{
cvartype = CVAR_String ;
}
else
{
sc . ScriptError ( " Bad cvar type '%s' " , sc . String ) ;
}
// The next token must be the cvar name.
sc . MustGetToken ( TK_Identifier ) ;
if ( FindCVar ( sc . String , NULL ) ! = NULL )
{
sc . ScriptError ( " cvar '%s' already exists " , sc . String ) ;
}
cvarname = sc . String ;
// A default value is optional and signalled by a '=' token.
if ( sc . CheckToken ( ' = ' ) )
{
switch ( cvartype )
{
case CVAR_Bool :
if ( ! sc . CheckToken ( TK_True ) & & ! sc . CheckToken ( TK_False ) )
{
sc . ScriptError ( " Expected true or false " ) ;
}
cvardefault = sc . String ;
break ;
case CVAR_Int :
sc . MustGetNumber ( ) ;
cvardefault = sc . String ;
break ;
case CVAR_Float :
sc . MustGetFloat ( ) ;
cvardefault = sc . String ;
break ;
default :
sc . MustGetString ( ) ;
cvardefault = sc . String ;
break ;
}
}
// Now create the cvar.
cvar = C_CreateCVar ( cvarname , cvartype , cvarflags ) ;
if ( cvardefault ! = NULL )
{
UCVarValue val ;
val . String = cvardefault ;
cvar - > SetGenericRepDefault ( val , CVAR_String ) ;
}
// To be like C and ACS, require a semicolon after everything.
sc . MustGetToken ( ' ; ' ) ;
addedcvars = true ;
}
}
// Only load mod cvars from the config if we defined some, so we don't
// clutter up the cvar space when not playing mods with custom cvars.
if ( addedcvars )
{
GameConfig - > DoModSetup ( gameinfo . ConfigName ) ;
}
2018-02-22 06:21:19 +00:00
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// ConsiderPatches
//
// Tries to add any deh/bex patches from the command line.
//
//==========================================================================
bool ConsiderPatches ( const char * arg )
{
int i , argc ;
FString * args ;
const char * f ;
argc = Args - > CheckParmList ( arg , & args ) ;
for ( i = 0 ; i < argc ; + + i )
{
2020-04-11 16:08:20 +00:00
if ( ( f = BaseFileSearch ( args [ i ] , " .deh " , false , GameConfig ) ) | |
( f = BaseFileSearch ( args [ i ] , " .bex " , false , GameConfig ) ) )
2016-03-01 15:47:10 +00:00
{
D_LoadDehFile ( f ) ;
}
}
return argc > 0 ;
}
//==========================================================================
//
// D_MultiExec
//
//==========================================================================
2017-04-14 11:31:58 +00:00
FExecList * D_MultiExec ( FArgs * list , FExecList * exec )
2016-03-01 15:47:10 +00:00
{
for ( int i = 0 ; i < list - > NumArgs ( ) ; + + i )
{
exec = C_ParseExecFile ( list - > GetArg ( i ) , exec ) ;
}
return exec ;
}
static void GetCmdLineFiles ( TArray < FString > & wadfiles )
{
FString * args ;
int i , argc ;
argc = Args - > CheckParmList ( " -file " , & args ) ;
for ( i = 0 ; i < argc ; + + i )
{
2020-04-11 16:08:20 +00:00
D_AddWildFile ( wadfiles , args [ i ] , " .wad " , GameConfig ) ;
2016-03-01 15:47:10 +00:00
}
}
static void CopyFiles ( TArray < FString > & to , TArray < FString > & from )
{
unsigned int ndx = to . Reserve ( from . Size ( ) ) ;
for ( unsigned i = 0 ; i < from . Size ( ) ; i + + )
{
to [ ndx + i ] = from [ i ] ;
}
}
static FString ParseGameInfo ( TArray < FString > & pwads , const char * fn , const char * data , int size )
{
FScanner sc ;
FString iwad ;
int pos = 0 ;
const char * lastSlash = strrchr ( fn , ' / ' ) ;
sc . OpenMem ( " GAMEINFO " , data , size ) ;
while ( sc . GetToken ( ) )
{
sc . TokenMustBe ( TK_Identifier ) ;
FString nextKey = sc . String ;
sc . MustGetToken ( ' = ' ) ;
if ( ! nextKey . CompareNoCase ( " IWAD " ) )
{
sc . MustGetString ( ) ;
iwad = sc . String ;
}
else if ( ! nextKey . CompareNoCase ( " LOAD " ) )
{
do
{
sc . MustGetString ( ) ;
// Try looking for the wad in the same directory as the .wad
// before looking for it in the current directory.
FString checkpath ;
if ( lastSlash ! = NULL )
{
checkpath = FString ( fn , ( lastSlash - fn ) + 1 ) ;
checkpath + = sc . String ;
}
else
{
checkpath = sc . String ;
}
if ( ! FileExists ( checkpath ) )
{
2020-04-11 16:08:20 +00:00
pos + = D_AddFile ( pwads , sc . String , true , pos , GameConfig ) ;
2016-03-01 15:47:10 +00:00
}
else
{
2020-04-11 16:08:20 +00:00
pos + = D_AddFile ( pwads , checkpath , true , pos , GameConfig ) ;
2016-03-01 15:47:10 +00:00
}
}
while ( sc . CheckToken ( ' , ' ) ) ;
}
else if ( ! nextKey . CompareNoCase ( " NOSPRITERENAME " ) )
{
sc . MustGetString ( ) ;
nospriterename = sc . Compare ( " true " ) ;
}
else if ( ! nextKey . CompareNoCase ( " STARTUPTITLE " ) )
{
sc . MustGetString ( ) ;
2020-04-22 17:57:14 +00:00
GameStartupInfo . Name = sc . String ;
2016-03-01 15:47:10 +00:00
}
else if ( ! nextKey . CompareNoCase ( " STARTUPCOLORS " ) )
{
sc . MustGetString ( ) ;
2020-04-22 17:57:14 +00:00
GameStartupInfo . FgColor = V_GetColor ( NULL , sc ) ;
2016-03-01 15:47:10 +00:00
sc . MustGetStringName ( " , " ) ;
sc . MustGetString ( ) ;
2020-04-22 17:57:14 +00:00
GameStartupInfo . BkColor = V_GetColor ( NULL , sc ) ;
2016-03-01 15:47:10 +00:00
}
else if ( ! nextKey . CompareNoCase ( " STARTUPTYPE " ) )
{
sc . MustGetString ( ) ;
FString sttype = sc . String ;
if ( ! sttype . CompareNoCase ( " DOOM " ) )
2020-04-22 17:57:14 +00:00
GameStartupInfo . Type = FStartupInfo : : DoomStartup ;
2016-03-01 15:47:10 +00:00
else if ( ! sttype . CompareNoCase ( " HERETIC " ) )
2020-04-22 17:57:14 +00:00
GameStartupInfo . Type = FStartupInfo : : HereticStartup ;
2016-03-01 15:47:10 +00:00
else if ( ! sttype . CompareNoCase ( " HEXEN " ) )
2020-04-22 17:57:14 +00:00
GameStartupInfo . Type = FStartupInfo : : HexenStartup ;
2016-03-01 15:47:10 +00:00
else if ( ! sttype . CompareNoCase ( " STRIFE " ) )
2020-04-22 17:57:14 +00:00
GameStartupInfo . Type = FStartupInfo : : StrifeStartup ;
else GameStartupInfo . Type = FStartupInfo : : DefaultStartup ;
2016-03-01 15:47:10 +00:00
}
else if ( ! nextKey . CompareNoCase ( " STARTUPSONG " ) )
{
sc . MustGetString ( ) ;
2020-04-22 17:57:14 +00:00
GameStartupInfo . Song = sc . String ;
2016-03-01 15:47:10 +00:00
}
2018-12-19 00:37:48 +00:00
else if ( ! nextKey . CompareNoCase ( " LOADLIGHTS " ) )
{
sc . MustGetNumber ( ) ;
2020-04-22 17:57:14 +00:00
GameStartupInfo . LoadLights = ! ! sc . Number ;
2018-12-19 00:37:48 +00:00
}
else if ( ! nextKey . CompareNoCase ( " LOADBRIGHTMAPS " ) )
{
sc . MustGetNumber ( ) ;
2020-04-22 17:57:14 +00:00
GameStartupInfo . LoadBrightmaps = ! ! sc . Number ;
2018-12-19 00:37:48 +00:00
}
2016-03-01 15:47:10 +00:00
else
{
// Silently ignore unknown properties
do
{
sc . MustGetAnyToken ( ) ;
}
while ( sc . CheckToken ( ' , ' ) ) ;
}
}
return iwad ;
}
static FString CheckGameInfo ( TArray < FString > & pwads )
{
2020-04-11 11:24:34 +00:00
FileSystem check ;
2016-03-01 15:47:10 +00:00
2020-04-19 11:04:29 +00:00
LumpFilterInfo lfi ;
for ( auto p : iwad_folders ) lfi . reservedFolders . Push ( p ) ;
for ( auto p : iwad_reserved ) lfi . requiredPrefixes . Push ( p ) ;
2020-04-11 11:14:52 +00:00
// Open the entire list as a temporary file system and look for a GAMEINFO lump. The last one will automatically win.
2020-04-19 11:04:29 +00:00
check . InitMultipleFiles ( pwads , true , & lfi ) ;
2020-04-11 11:27:19 +00:00
if ( check . GetNumEntries ( ) > 0 )
2020-04-11 11:14:52 +00:00
{
int num = check . CheckNumForName ( " GAMEINFO " ) ;
if ( num > = 0 )
2016-03-01 15:47:10 +00:00
{
2020-04-11 11:14:52 +00:00
// Found one!
2020-04-11 11:27:19 +00:00
auto data = check . GetFileData ( num ) ;
auto wadname = check . GetResourceFileName ( check . GetFileContainer ( num ) ) ;
2020-04-11 11:14:52 +00:00
return ParseGameInfo ( pwads , wadname , ( const char * ) data . Data ( ) , data . Size ( ) ) ;
2016-03-01 15:47:10 +00:00
}
}
return " " ;
}
//==========================================================================
//
// Checks the IWAD for MAP01 and if found sets GI_MAPxx
//
//==========================================================================
static void SetMapxxFlag ( )
{
2020-04-11 11:24:34 +00:00
int lump_name = fileSystem . CheckNumForName ( " MAP01 " , ns_global , fileSystem . GetIwadNum ( ) ) ;
int lump_wad = fileSystem . CheckNumForFullName ( " maps/map01.wad " , fileSystem . GetIwadNum ( ) ) ;
int lump_map = fileSystem . CheckNumForFullName ( " maps/map01.map " , fileSystem . GetIwadNum ( ) ) ;
2016-03-01 15:47:10 +00:00
if ( lump_name > = 0 | | lump_wad > = 0 | | lump_map > = 0 ) gameinfo . flags | = GI_MAPxx ;
}
//==========================================================================
//
// Initialize
//
//==========================================================================
static void D_DoomInit ( )
{
// Set the FPU precision to 53 significant bits. This is the default
// for Visual C++, but not for GCC, so some slight math variances
// might crop up if we leave it alone.
2016-03-09 04:00:16 +00:00
# if defined(_FPU_GETCW) && defined(_FPU_EXTENDED) && defined(_FPU_DOUBLE)
2016-03-01 15:47:10 +00:00
{
int cw ;
_FPU_GETCW ( cw ) ;
cw = ( cw & ~ _FPU_EXTENDED ) | _FPU_DOUBLE ;
_FPU_SETCW ( cw ) ;
}
# elif defined(_PC_53)
// On the x64 architecture, changing the floating point precision is not supported.
# ifndef _WIN64
int cfp = _control87 ( _PC_53 , _MCW_PC ) ;
# endif
# endif
// Check response files before coalescing file parameters.
M_FindResponseFile ( ) ;
// Combine different file parameters with their pre-switch bits.
Args - > CollectFiles ( " -deh " , " .deh " ) ;
Args - > CollectFiles ( " -bex " , " .bex " ) ;
Args - > CollectFiles ( " -exec " , " .cfg " ) ;
Args - > CollectFiles ( " -playdemo " , " .lmp " ) ;
Args - > CollectFiles ( " -file " , NULL ) ; // anything left goes after -file
gamestate = GS_STARTUP ;
const char * v = Args - > CheckValue ( " -rngseed " ) ;
if ( v )
{
rngseed = staticrngseed = atoi ( v ) ;
use_staticrng = true ;
if ( ! batchrun ) Printf ( " D_DoomInit: Static RNGseed %d set. \n " , rngseed ) ;
}
else
{
rngseed = I_MakeRNGSeed ( ) ;
use_staticrng = false ;
}
2017-07-15 07:34:07 +00:00
srand ( rngseed ) ;
2016-03-01 15:47:10 +00:00
FRandom : : StaticClearRandom ( ) ;
if ( ! batchrun ) Printf ( " M_LoadDefaults: Load system defaults. \n " ) ;
M_LoadDefaults ( ) ; // load before initing other systems
}
//==========================================================================
//
// AddAutoloadFiles
//
//==========================================================================
static void AddAutoloadFiles ( const char * autoname )
{
LumpFilterIWAD . Format ( " %s. " , autoname ) ; // The '.' is appened to simplify parsing the string
2016-12-21 10:11:29 +00:00
// [SP] Dialog reaction - load lights.pk3 and brightmaps.pk3 based on user choices
if ( ! ( gameinfo . flags & GI_SHAREWARE ) )
{
2020-04-22 17:57:14 +00:00
if ( GameStartupInfo . LoadLights = = 1 | | ( GameStartupInfo . LoadLights ! = 0 & & autoloadlights ) )
2016-12-21 10:11:29 +00:00
{
2020-04-11 16:08:20 +00:00
const char * lightswad = BaseFileSearch ( " lights.pk3 " , NULL , false , GameConfig ) ;
2016-12-21 10:11:29 +00:00
if ( lightswad )
2020-04-11 16:08:20 +00:00
D_AddFile ( allwads , lightswad , true , - 1 , GameConfig ) ;
2016-12-21 10:11:29 +00:00
}
2020-04-22 17:57:14 +00:00
if ( GameStartupInfo . LoadBrightmaps = = 1 | | ( GameStartupInfo . LoadBrightmaps ! = 0 & & autoloadbrightmaps ) )
2016-12-21 10:11:29 +00:00
{
2020-04-11 16:08:20 +00:00
const char * bmwad = BaseFileSearch ( " brightmaps.pk3 " , NULL , false , GameConfig ) ;
2016-12-21 10:11:29 +00:00
if ( bmwad )
2020-04-11 16:08:20 +00:00
D_AddFile ( allwads , bmwad , true , - 1 , GameConfig ) ;
2016-12-21 10:11:29 +00:00
}
}
2016-12-21 04:03:06 +00:00
if ( ! ( gameinfo . flags & GI_SHAREWARE ) & & ! Args - > CheckParm ( " -noautoload " ) & & ! disableautoload )
2016-03-01 15:47:10 +00:00
{
FString file ;
// [RH] zvox.wad - A wad I had intended to be automatically generated
// from Q2's pak0.pak so the female and cyborg player could have
// voices. I never got around to writing the utility to do it, though.
// And I probably never will now. But I know at least one person uses
// it for something else, so this gets to stay here.
2020-04-11 16:08:20 +00:00
const char * wad = BaseFileSearch ( " zvox.wad " , NULL , false , GameConfig ) ;
2016-03-01 15:47:10 +00:00
if ( wad )
2020-04-11 16:08:20 +00:00
D_AddFile ( allwads , wad , true , - 1 , GameConfig ) ;
2016-03-01 15:47:10 +00:00
// [RH] Add any .wad files in the skins directory
# ifdef __unix__
file = SHARE_DIR ;
# else
file = progdir ;
# endif
file + = " skins " ;
2020-04-11 16:08:20 +00:00
D_AddDirectory ( allwads , file , " *.wad " , GameConfig ) ;
2016-03-01 15:47:10 +00:00
# ifdef __unix__
2017-12-10 16:27:36 +00:00
file = NicePath ( " $HOME/ " GAME_DIR " /skins " ) ;
2020-04-11 16:08:20 +00:00
D_AddDirectory ( allwads , file , " *.wad " , GameConfig ) ;
2016-03-01 15:47:10 +00:00
# endif
// Add common (global) wads
2020-04-11 16:08:20 +00:00
D_AddConfigFiles ( allwads , " Global.Autoload " , " *.wad " , GameConfig ) ;
2016-03-01 15:47:10 +00:00
long len ;
int lastpos = - 1 ;
while ( ( len = LumpFilterIWAD . IndexOf ( ' . ' , lastpos + 1 ) ) > 0 )
{
file = LumpFilterIWAD . Left ( len ) + " .Autoload " ;
2020-04-11 16:08:20 +00:00
D_AddConfigFiles ( allwads , file , " *.wad " , GameConfig ) ;
2016-03-01 15:47:10 +00:00
lastpos = len ;
}
}
}
//==========================================================================
//
// CheckCmdLine
//
//==========================================================================
static void CheckCmdLine ( )
{
int flags = dmflags ;
int p ;
const char * v ;
if ( ! batchrun ) Printf ( " Checking cmd-line parameters... \n " ) ;
if ( Args - > CheckParm ( " -nomonsters " ) ) flags | = DF_NO_MONSTERS ;
if ( Args - > CheckParm ( " -respawn " ) ) flags | = DF_MONSTERS_RESPAWN ;
if ( Args - > CheckParm ( " -fast " ) ) flags | = DF_FAST_MONSTERS ;
devparm = ! ! Args - > CheckParm ( " -devparm " ) ;
if ( Args - > CheckParm ( " -altdeath " ) )
{
deathmatch = 1 ;
flags | = DF_ITEMS_RESPAWN ;
}
else if ( Args - > CheckParm ( " -deathmatch " ) )
{
deathmatch = 1 ;
flags | = DF_WEAPONS_STAY | DF_ITEMS_RESPAWN ;
}
dmflags = flags ;
// get skill / episode / map from parms
if ( gameinfo . gametype ! = GAME_Hexen )
{
startmap = ( gameinfo . flags & GI_MAPxx ) ? " MAP01 " : " E1M1 " ;
}
else
{
startmap = " &wt@01 " ;
}
autostart = StoredWarp . IsNotEmpty ( ) ;
const char * val = Args - > CheckValue ( " -skill " ) ;
if ( val )
{
gameskill = val [ 0 ] - ' 1 ' ;
autostart = true ;
}
p = Args - > CheckParm ( " -warp " ) ;
if ( p & & p < Args - > NumArgs ( ) - 1 )
{
int ep , map ;
if ( gameinfo . flags & GI_MAPxx )
{
ep = 1 ;
map = atoi ( Args - > GetArg ( p + 1 ) ) ;
}
else
{
ep = atoi ( Args - > GetArg ( p + 1 ) ) ;
map = p < Args - > NumArgs ( ) - 2 ? atoi ( Args - > GetArg ( p + 2 ) ) : 10 ;
if ( map < 1 | | map > 9 )
{
map = ep ;
ep = 1 ;
}
}
startmap = CalcMapName ( ep , map ) ;
autostart = true ;
}
// [RH] Hack to handle +map. The standard console command line handler
// won't be able to handle it, so we take it out of the command line and set
// it up like -warp.
FString mapvalue = Args - > TakeValue ( " +map " ) ;
if ( mapvalue . IsNotEmpty ( ) )
{
if ( ! P_CheckMapData ( mapvalue ) )
{
Printf ( " Can't find map %s \n " , mapvalue . GetChars ( ) ) ;
}
else
{
startmap = mapvalue ;
autostart = true ;
}
}
if ( devparm )
{
Printf ( " %s " , GStrings ( " D_DEVSTR " ) ) ;
}
// turbo option // [RH] (now a cvar)
v = Args - > CheckValue ( " -turbo " ) ;
if ( v ! = NULL )
{
double amt = atof ( v ) ;
Printf ( " turbo scale: %.0f%% \n " , amt ) ;
turbo = ( float ) amt ;
}
v = Args - > CheckValue ( " -timer " ) ;
if ( v )
{
double time = strtod ( v , NULL ) ;
Printf ( " Levels will end after %g minute%s. \n " , time , time > 1 ? " s " : " " ) ;
timelimit = ( float ) time ;
}
v = Args - > CheckValue ( " -avg " ) ;
if ( v )
{
Printf ( " Austin Virtual Gaming: Levels will end after 20 minutes \n " ) ;
timelimit = 20.f ;
}
//
// Build status bar line!
//
if ( deathmatch )
StartScreen - > AppendStatusLine ( " DeathMatch... " ) ;
if ( dmflags & DF_NO_MONSTERS )
StartScreen - > AppendStatusLine ( " No Monsters... " ) ;
if ( dmflags & DF_MONSTERS_RESPAWN )
StartScreen - > AppendStatusLine ( " Respawning... " ) ;
if ( autostart )
{
FString temp ;
temp . Format ( " Warp to map %s, Skill %d " , startmap . GetChars ( ) , gameskill + 1 ) ;
StartScreen - > AppendStatusLine ( temp ) ;
}
}
2019-10-06 22:37:56 +00:00
static void NewFailure ( )
{
I_FatalError ( " Failed to allocate memory from system heap " ) ;
}
2020-04-11 11:26:42 +00:00
//==========================================================================
//
// RenameSprites
//
// Renames sprites in IWADs so that unique actors can have unique sprites,
// making it possible to import any actor from any game into any other
// game without jumping through hoops to resolve duplicate sprite names.
// You just need to know what the sprite's new name is.
//
//==========================================================================
static void RenameSprites ( FileSystem & fileSystem , const TArray < FString > & deletelumps )
{
bool renameAll ;
bool MNTRZfound = false ;
const char * altbigfont = gameinfo . gametype = = GAME_Strife ? " SBIGFONT " : ( gameinfo . gametype & GAME_Raven ) ? " HBIGFONT " : " DBIGFONT " ;
static const uint32_t HereticRenames [ ] =
{ MAKE_ID ( ' H ' , ' E ' , ' A ' , ' D ' ) , MAKE_ID ( ' L ' , ' I ' , ' C ' , ' H ' ) , // Ironlich
} ;
static const uint32_t HexenRenames [ ] =
{ MAKE_ID ( ' B ' , ' A ' , ' R ' , ' L ' ) , MAKE_ID ( ' Z ' , ' B ' , ' A ' , ' R ' ) , // ZBarrel
MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 1 ' ) , MAKE_ID ( ' A ' , ' R ' , ' _ ' , ' 1 ' ) , // MeshArmor
MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 2 ' ) , MAKE_ID ( ' A ' , ' R ' , ' _ ' , ' 2 ' ) , // FalconShield
MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 3 ' ) , MAKE_ID ( ' A ' , ' R ' , ' _ ' , ' 3 ' ) , // PlatinumHelm
MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 4 ' ) , MAKE_ID ( ' A ' , ' R ' , ' _ ' , ' 4 ' ) , // AmuletOfWarding
MAKE_ID ( ' S ' , ' U ' , ' I ' , ' T ' ) , MAKE_ID ( ' Z ' , ' S ' , ' U ' , ' I ' ) , // ZSuitOfArmor and ZArmorChunk
MAKE_ID ( ' T ' , ' R ' , ' E ' , ' 1 ' ) , MAKE_ID ( ' Z ' , ' T ' , ' R ' , ' E ' ) , // ZTree and ZTreeDead
MAKE_ID ( ' T ' , ' R ' , ' E ' , ' 2 ' ) , MAKE_ID ( ' T ' , ' R ' , ' E ' , ' S ' ) , // ZTreeSwamp150
MAKE_ID ( ' C ' , ' A ' , ' N ' , ' D ' ) , MAKE_ID ( ' B ' , ' C ' , ' A ' , ' N ' ) , // ZBlueCandle
MAKE_ID ( ' R ' , ' O ' , ' C ' , ' K ' ) , MAKE_ID ( ' R ' , ' O ' , ' K ' , ' K ' ) , // rocks and dirt in a_debris.cpp
MAKE_ID ( ' W ' , ' A ' , ' T ' , ' R ' ) , MAKE_ID ( ' H ' , ' W ' , ' A ' , ' T ' ) , // Strife also has WATR
MAKE_ID ( ' G ' , ' I ' , ' B ' , ' S ' ) , MAKE_ID ( ' P ' , ' O ' , ' L ' , ' 5 ' ) , // RealGibs
MAKE_ID ( ' E ' , ' G ' , ' G ' , ' M ' ) , MAKE_ID ( ' P ' , ' R ' , ' K ' , ' M ' ) , // PorkFX
MAKE_ID ( ' I ' , ' N ' , ' V ' , ' U ' ) , MAKE_ID ( ' D ' , ' E ' , ' F ' , ' N ' ) , // Icon of the Defender
} ;
static const uint32_t StrifeRenames [ ] =
{ MAKE_ID ( ' M ' , ' I ' , ' S ' , ' L ' ) , MAKE_ID ( ' S ' , ' M ' , ' I ' , ' S ' ) , // lots of places
MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 1 ' ) , MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 3 ' ) , // MetalArmor
MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 2 ' ) , MAKE_ID ( ' A ' , ' R ' , ' M ' , ' 4 ' ) , // LeatherArmor
MAKE_ID ( ' P ' , ' M ' , ' A ' , ' P ' ) , MAKE_ID ( ' S ' , ' M ' , ' A ' , ' P ' ) , // StrifeMap
MAKE_ID ( ' T ' , ' L ' , ' M ' , ' P ' ) , MAKE_ID ( ' T ' , ' E ' , ' C ' , ' H ' ) , // TechLampSilver and TechLampBrass
MAKE_ID ( ' T ' , ' R ' , ' E ' , ' 1 ' ) , MAKE_ID ( ' T ' , ' R ' , ' E ' , ' T ' ) , // TreeStub
MAKE_ID ( ' B ' , ' A ' , ' R ' , ' 1 ' ) , MAKE_ID ( ' B ' , ' A ' , ' R ' , ' C ' ) , // BarricadeColumn
MAKE_ID ( ' S ' , ' H ' , ' T ' , ' 2 ' ) , MAKE_ID ( ' M ' , ' P ' , ' U ' , ' F ' ) , // MaulerPuff
MAKE_ID ( ' B ' , ' A ' , ' R ' , ' L ' ) , MAKE_ID ( ' B ' , ' B ' , ' A ' , ' R ' ) , // StrifeBurningBarrel
MAKE_ID ( ' T ' , ' R ' , ' C ' , ' H ' ) , MAKE_ID ( ' T ' , ' R ' , ' H ' , ' L ' ) , // SmallTorchLit
MAKE_ID ( ' S ' , ' H ' , ' R ' , ' D ' ) , MAKE_ID ( ' S ' , ' H ' , ' A ' , ' R ' ) , // glass shards
MAKE_ID ( ' B ' , ' L ' , ' S ' , ' T ' ) , MAKE_ID ( ' M ' , ' A ' , ' U ' , ' L ' ) , // Mauler
MAKE_ID ( ' L ' , ' O ' , ' G ' , ' G ' ) , MAKE_ID ( ' L ' , ' O ' , ' G ' , ' W ' ) , // StickInWater
MAKE_ID ( ' V ' , ' A ' , ' S ' , ' E ' ) , MAKE_ID ( ' V ' , ' A ' , ' Z ' , ' E ' ) , // Pot and Pitcher
MAKE_ID ( ' C ' , ' N ' , ' D ' , ' L ' ) , MAKE_ID ( ' K ' , ' N ' , ' D ' , ' L ' ) , // Candle
MAKE_ID ( ' P ' , ' O ' , ' T ' , ' 1 ' ) , MAKE_ID ( ' M ' , ' P ' , ' O ' , ' T ' ) , // MetalPot
MAKE_ID ( ' S ' , ' P ' , ' I ' , ' D ' ) , MAKE_ID ( ' S ' , ' T ' , ' L ' , ' K ' ) , // Stalker
} ;
const uint32_t * renames ;
int numrenames ;
switch ( gameinfo . gametype )
{
case GAME_Doom :
default :
// Doom's sprites don't get renamed.
renames = nullptr ;
numrenames = 0 ;
break ;
case GAME_Heretic :
renames = HereticRenames ;
numrenames = sizeof ( HereticRenames ) / 8 ;
break ;
case GAME_Hexen :
renames = HexenRenames ;
numrenames = sizeof ( HexenRenames ) / 8 ;
break ;
case GAME_Strife :
renames = StrifeRenames ;
numrenames = sizeof ( StrifeRenames ) / 8 ;
break ;
}
2020-04-11 11:27:19 +00:00
unsigned NumFiles = fileSystem . GetNumEntries ( ) ;
2020-04-11 11:26:42 +00:00
for ( uint32_t i = 0 ; i < NumFiles ; i + + )
{
// check for full Minotaur animations. If this is not found
// some frames need to be renamed.
2020-04-11 11:27:19 +00:00
if ( fileSystem . GetFileNamespace ( i ) = = ns_sprites )
2020-04-11 11:26:42 +00:00
{
auto & shortName = fileSystem . GetShortName ( i ) ;
if ( shortName . dword = = MAKE_ID ( ' M ' , ' N ' , ' T ' , ' R ' ) & & shortName . String [ 4 ] = = ' Z ' )
{
MNTRZfound = true ;
break ;
}
}
}
renameAll = ! ! Args - > CheckParm ( " -oldsprites " ) | | nospriterename ;
for ( uint32_t i = 0 ; i < NumFiles ; i + + )
{
auto & shortName = fileSystem . GetShortName ( i ) ;
2020-04-11 11:27:19 +00:00
if ( fileSystem . GetFileNamespace ( i ) = = ns_sprites )
2020-04-11 11:26:42 +00:00
{
// Only sprites in the IWAD normally get renamed
2020-04-11 11:27:19 +00:00
if ( renameAll | | fileSystem . GetFileContainer ( i ) = = fileSystem . GetIwadNum ( ) )
2020-04-11 11:26:42 +00:00
{
for ( int j = 0 ; j < numrenames ; + + j )
{
if ( shortName . dword = = renames [ j * 2 ] )
{
shortName . dword = renames [ j * 2 + 1 ] ;
}
}
if ( gameinfo . gametype = = GAME_Hexen )
{
2020-04-11 11:27:19 +00:00
if ( fileSystem . CheckFileName ( i , " ARTIINVU " ) )
2020-04-11 11:26:42 +00:00
{
shortName . String [ 4 ] = ' D ' ; shortName . String [ 5 ] = ' E ' ;
shortName . String [ 6 ] = ' F ' ; shortName . String [ 7 ] = ' N ' ;
}
}
}
if ( ! MNTRZfound )
{
if ( shortName . dword = = MAKE_ID ( ' M ' , ' N ' , ' T ' , ' R ' ) )
{
for ( size_t fi : { 4 , 6 } )
{
if ( shortName . String [ fi ] > = ' F ' & & shortName . String [ fi ] < = ' K ' )
{
shortName . String [ fi ] + = ' U ' - ' F ' ;
}
}
}
}
// When not playing Doom rename all BLOD sprites to BLUD so that
// the same blood states can be used everywhere
if ( ! ( gameinfo . gametype & GAME_DoomChex ) )
{
if ( shortName . dword = = MAKE_ID ( ' B ' , ' L ' , ' O ' , ' D ' ) )
{
shortName . dword = MAKE_ID ( ' B ' , ' L ' , ' U ' , ' D ' ) ;
}
}
}
2020-04-11 11:27:19 +00:00
else if ( fileSystem . GetFileNamespace ( i ) = = ns_global )
2020-04-11 11:26:42 +00:00
{
2020-04-11 11:27:19 +00:00
int fn = fileSystem . GetFileContainer ( i ) ;
2020-04-11 11:26:42 +00:00
if ( fn > = fileSystem . GetIwadNum ( ) & & fn < = fileSystem . GetMaxIwadNum ( ) & & deletelumps . Find ( shortName . String ) < deletelumps . Size ( ) )
{
shortName . String [ 0 ] = 0 ; // Lump must be deleted from directory.
}
// Rename the game specific big font lumps so that the font manager does not have to do problematic special checks for them.
else if ( ! strcmp ( shortName . String , altbigfont ) )
{
strcpy ( shortName . String , " BIGFONT " ) ;
}
}
}
}
//==========================================================================
//
// RenameNerve
//
// Renames map headers and map name pictures in nerve.wad so as to load it
// alongside Doom II and offer both episodes without causing conflicts.
// MD5 checksum for NERVE.WAD: 967d5ae23daf45196212ae1b605da3b0 (3,819,855)
// MD5 checksum for Unity version of NERVE.WAD: 4214c47651b63ee2257b1c2490a518c9 (3,821,966)
//
//==========================================================================
void RenameNerve ( FileSystem & fileSystem )
{
if ( gameinfo . gametype ! = GAME_Doom )
return ;
const int numnerveversions = 2 ;
bool found = false ;
uint8_t cksum [ 16 ] ;
static const uint8_t nerve [ numnerveversions ] [ 16 ] = {
{ 0x96 , 0x7d , 0x5a , 0xe2 , 0x3d , 0xaf , 0x45 , 0x19 ,
0x62 , 0x12 , 0xae , 0x1b , 0x60 , 0x5d , 0xa3 , 0xb0 } ,
{ 0x42 , 0x14 , 0xc4 , 0x76 , 0x51 , 0xb6 , 0x3e , 0xe2 ,
0x25 , 0x7b , 0x1c , 0x24 , 0x90 , 0xa5 , 0x18 , 0xc9 }
} ;
size_t nervesize [ numnerveversions ] = { 3819855 , 3821966 } ; // NERVE.WAD's file size
int w = fileSystem . GetIwadNum ( ) ;
while ( + + w < fileSystem . GetNumWads ( ) )
{
auto fr = fileSystem . GetFileReader ( w ) ;
int isizecheck = - 1 ;
if ( fr = = NULL )
{
continue ;
}
for ( int icheck = 0 ; icheck < numnerveversions ; icheck + + )
if ( fr - > GetLength ( ) = = ( long ) nervesize [ icheck ] )
isizecheck = icheck ;
if ( isizecheck = = - 1 )
{
// Skip MD5 computation when there is a
// cheaper way to know this is not the file
continue ;
}
fr - > Seek ( 0 , FileReader : : SeekSet ) ;
MD5Context md5 ;
md5Update ( * fr , md5 , ( unsigned ) fr - > GetLength ( ) ) ;
md5 . Final ( cksum ) ;
if ( memcmp ( nerve [ isizecheck ] , cksum , 16 ) = = 0 )
{
found = true ;
break ;
}
}
if ( ! found )
return ;
2020-04-11 11:27:19 +00:00
for ( int i = fileSystem . GetFirstEntry ( w ) ; i < = fileSystem . GetLastEntry ( w ) ; i + + )
2020-04-11 11:26:42 +00:00
{
auto & shortName = fileSystem . GetShortName ( i ) ;
// Only rename the maps from NERVE.WAD
if ( shortName . dword = = MAKE_ID ( ' C ' , ' W ' , ' I ' , ' L ' ) )
{
shortName . String [ 0 ] = ' N ' ;
}
else if ( shortName . dword = = MAKE_ID ( ' M ' , ' A ' , ' P ' , ' 0 ' ) )
{
shortName . String [ 6 ] = shortName . String [ 4 ] ;
shortName . String [ 5 ] = ' 0 ' ;
shortName . String [ 4 ] = ' L ' ;
shortName . dword = MAKE_ID ( ' L ' , ' E ' , ' V ' , ' E ' ) ;
}
}
}
//==========================================================================
//
// FixMacHexen
//
// Discard all extra lumps in Mac version of Hexen IWAD (demo or full)
// to avoid any issues caused by names of these lumps, including:
// * Wrong height of small font
// * Broken life bar of mage class
//
//==========================================================================
void FixMacHexen ( FileSystem & fileSystem )
{
if ( GAME_Hexen ! = gameinfo . gametype )
{
return ;
}
FileReader * reader = fileSystem . GetFileReader ( fileSystem . GetIwadNum ( ) ) ;
auto iwadSize = reader - > GetLength ( ) ;
static const long DEMO_SIZE = 13596228 ;
static const long BETA_SIZE = 13749984 ;
static const long FULL_SIZE = 21078584 ;
if ( DEMO_SIZE ! = iwadSize
& & BETA_SIZE ! = iwadSize
& & FULL_SIZE ! = iwadSize )
{
return ;
}
reader - > Seek ( 0 , FileReader : : SeekSet ) ;
uint8_t checksum [ 16 ] ;
MD5Context md5 ;
md5Update ( * reader , md5 , ( unsigned ) iwadSize ) ;
md5 . Final ( checksum ) ;
static const uint8_t HEXEN_DEMO_MD5 [ 16 ] =
{
0x92 , 0x5f , 0x9f , 0x50 , 0x00 , 0xe1 , 0x7d , 0xc8 ,
0x4b , 0x0a , 0x6a , 0x3b , 0xed , 0x3a , 0x6f , 0x31
} ;
static const uint8_t HEXEN_BETA_MD5 [ 16 ] =
{
0x2a , 0xf1 , 0xb2 , 0x7c , 0xd1 , 0x1f , 0xb1 , 0x59 ,
0xe6 , 0x08 , 0x47 , 0x2a , 0x1b , 0x53 , 0xe4 , 0x0e
} ;
static const uint8_t HEXEN_FULL_MD5 [ 16 ] =
{
0xb6 , 0x81 , 0x40 , 0xa7 , 0x96 , 0xf6 , 0xfd , 0x7f ,
0x3a , 0x5d , 0x32 , 0x26 , 0xa3 , 0x2b , 0x93 , 0xbe
} ;
const bool isBeta = 0 = = memcmp ( HEXEN_BETA_MD5 , checksum , sizeof checksum ) ;
if ( ! isBeta
& & 0 ! = memcmp ( HEXEN_DEMO_MD5 , checksum , sizeof checksum )
& & 0 ! = memcmp ( HEXEN_FULL_MD5 , checksum , sizeof checksum ) )
{
return ;
}
static const int EXTRA_LUMPS = 299 ;
// Hexen Beta is very similar to Demo but it has MAP41: Maze at the end of the WAD
// So keep this map if it's present but discard all extra lumps
2020-04-11 11:27:19 +00:00
const int lastLump = fileSystem . GetLastEntry ( fileSystem . GetIwadNum ( ) ) - ( isBeta ? 12 : 0 ) ;
assert ( fileSystem . GetFirstEntry ( fileSystem . GetIwadNum ( ) ) + 299 < lastLump ) ;
2020-04-11 11:26:42 +00:00
for ( int i = lastLump - EXTRA_LUMPS + 1 ; i < = lastLump ; + + i )
{
auto & shortName = fileSystem . GetShortName ( i ) ;
shortName . String [ 0 ] = ' \0 ' ;
}
}
2020-04-11 11:27:19 +00:00
static void FindStrifeTeaserVoices ( FileSystem & fileSystem )
{
if ( gameinfo . gametype = = GAME_Strife & & gameinfo . flags & GI_SHAREWARE )
{
unsigned NumFiles = fileSystem . GetNumEntries ( ) ;
for ( uint32_t i = 0 ; i < NumFiles ; i + + )
{
auto & shortName = fileSystem . GetShortName ( i ) ;
if ( fileSystem . GetFileNamespace ( i ) = = ns_global )
{
// Only sprites in the IWAD normally get renamed
if ( fileSystem . GetFileContainer ( i ) = = fileSystem . GetIwadNum ( ) )
{
if ( shortName . String [ 0 ] = = ' V ' & &
shortName . String [ 1 ] = = ' O ' & &
shortName . String [ 2 ] = = ' C ' )
{
int j ;
for ( j = 3 ; j < 8 ; + + j )
{
if ( shortName . String [ j ] ! = 0 & & ! isdigit ( shortName . String [ j ] ) )
break ;
}
if ( j = = 8 )
{
fileSystem . SetFileNamespace ( i , ns_strifevoices ) ;
}
}
}
}
}
}
}
2020-04-11 16:09:51 +00:00
static const char * DoomButtons [ ] =
{
" am_panleft " ,
" user2 " ,
" jump " ,
" right " ,
" zoom " ,
" back " ,
" am_zoomin " ,
" reload " ,
" lookdown " ,
" am_zoomout " ,
" user4 " ,
" attack " ,
" user1 " ,
" klook " ,
" forward " ,
" movedown " ,
" altattack " ,
" moveleft " ,
" moveright " ,
" am_panright " ,
" am_panup " ,
" mlook " ,
" crouch " ,
" left " ,
" lookup " ,
" user3 " ,
" strafe " ,
" am_pandown " ,
" showscores " ,
" speed " ,
" use " ,
" moveup " } ;
CVAR ( Bool , lookspring , true , CVAR_ARCHIVE ) ; // Generate centerview when -mlook encountered?
2020-04-11 16:12:28 +00:00
EXTERN_CVAR ( String , language )
2020-04-11 16:09:51 +00:00
2020-04-11 16:22:21 +00:00
CUSTOM_CVAR ( Int , mouse_capturemode , 1 , CVAR_GLOBALCONFIG | CVAR_ARCHIVE )
{
if ( self < 0 )
{
self = 0 ;
}
else if ( self > 2 )
{
self = 2 ;
}
}
2020-04-11 16:09:51 +00:00
void Mlook_ReleaseHandler ( )
{
if ( lookspring )
{
Net_WriteByte ( DEM_CENTERVIEW ) ;
}
}
2020-04-11 11:26:42 +00:00
2020-04-11 16:12:28 +00:00
int StrTable_GetGender ( )
{
return players [ consoleplayer ] . userinfo . GetGender ( ) ;
}
bool StrTable_ValidFilter ( const char * str )
{
if ( gameinfo . gametype = = GAME_Strife & & ( gameinfo . flags & GI_SHAREWARE ) & & ! stricmp ( str , " strifeteaser " ) ) return true ;
return stricmp ( str , GameNames [ gameinfo . gametype ] ) = = 0 ;
}
2020-04-11 16:22:21 +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 ) ;
}
// [ZZ] check active event handlers that want the UI processing
if ( ! wantCapt & & primaryLevel - > localEventManager - > CheckUiProcessors ( ) )
wantCapt = true ;
return wantCapt ;
}
bool System_WantLeftButton ( )
{
return ( gamestate = = GS_DEMOSCREEN | | gamestate = = GS_TITLELEVEL ) ;
}
bool System_NetGame ( )
{
return netgame ;
}
bool System_WantNativeMouse ( )
{
return primaryLevel - > localEventManager - > CheckRequireMouse ( ) ;
}
static bool System_CaptureModeInGame ( )
{
2020-04-22 17:57:14 +00:00
if ( demoplayback | | paused ) return false ;
2020-04-11 16:22:21 +00:00
switch ( mouse_capturemode )
{
default :
case 0 :
return gamestate = = GS_LEVEL ;
case 1 :
return gamestate = = GS_LEVEL | | gamestate = = GS_INTERMISSION | | gamestate = = GS_FINALE ;
case 2 :
return true ;
}
}
2020-04-23 20:33:43 +00:00
static void System_PlayStartupSound ( const char * sndname )
{
S_Sound ( CHAN_BODY , 0 , sndname , 1 , ATTN_NONE ) ;
}
2020-04-25 19:52:21 +00:00
static bool System_IsSpecialUI ( )
{
return ( generic_ui | | ! ! log_vgafont | | ! ! dlg_vgafont | | ConsoleState ! = c_up | | multiplayer | |
( menuactive = = MENU_On & & CurrentMenu & & ! CurrentMenu - > IsKindOf ( " ConversationMenu " ) ) ) ;
}
2020-04-22 17:57:14 +00:00
//==========================================================================
//
// DoomSpecificInfo
//
// Called by the crash logger to get application-specific information.
//
//==========================================================================
void System_CrashInfo ( char * buffer , size_t bufflen , const char * lfstr )
{
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 , " %sWad %d: %s " , lfstr , i , arg ) ;
}
if ( gamestate ! = GS_LEVEL & & gamestate ! = GS_TITLELEVEL )
{
buffer + = mysnprintf ( buffer , buffend - buffer , " %s%sNot in a level. " , lfstr , lfstr ) ;
}
else
{
buffer + = mysnprintf ( buffer , buffend - buffer , " %s%sCurrent map: %s " , lfstr , lfstr , primaryLevel - > MapName . GetChars ( ) ) ;
if ( ! viewactive )
{
buffer + = mysnprintf ( buffer , buffend - buffer , " %s%sView not active. " , lfstr , lfstr ) ;
}
else
{
auto & vp = r_viewpoint ;
buffer + = mysnprintf ( buffer , buffend - buffer , " %s%sviewx = %f " , lfstr , lfstr , vp . Pos . X ) ;
buffer + = mysnprintf ( buffer , buffend - buffer , " %sviewy = %f " , lfstr , vp . Pos . Y ) ;
buffer + = mysnprintf ( buffer , buffend - buffer , " %sviewz = %f " , lfstr , vp . Pos . Z ) ;
2020-04-22 18:42:13 +00:00
buffer + = mysnprintf ( buffer , buffend - buffer , " %sviewangle = %f " , lfstr , vp . Angles . Yaw . Degrees ) ;
2020-04-22 17:57:14 +00:00
}
}
buffer + = mysnprintf ( buffer , buffend - buffer , " %s " , lfstr ) ;
* buffer = 0 ;
}
2020-04-11 16:22:21 +00:00
2020-04-11 17:00:07 +00:00
static void PatchTextures ( )
{
// The Hexen scripts use BLANK as a blank texture, even though it's really not.
// I guess the Doom renderer must have clipped away the line at the bottom of
// the texture so it wasn't visible. Change its use type to a blank null texture to really make it blank.
if ( gameinfo . gametype = = GAME_Hexen )
{
FTextureID tex = TexMan . CheckForTexture ( " BLANK " , ETextureType : : Wall , false ) ;
if ( tex . Exists ( ) )
{
2020-04-14 18:45:26 +00:00
auto texture = TexMan . GetGameTexture ( tex , false ) ;
2020-04-11 17:00:07 +00:00
texture - > SetUseType ( ETextureType : : Null ) ;
}
}
}
//==========================================================================
//
// this gets called during texture creation to fix known problems
//
//==========================================================================
static void CheckForHacks ( BuildInfo & buildinfo )
{
if ( buildinfo . Parts . Size ( ) = = 0 )
{
return ;
}
// Heretic sky textures are marked as only 128 pixels tall,
// even though they are really 200 pixels tall.
if ( gameinfo . gametype = = GAME_Heretic & &
buildinfo . Name . Len ( ) = = 4 & &
buildinfo . Name [ 0 ] = = ' S ' & &
buildinfo . Name [ 1 ] = = ' K ' & &
buildinfo . Name [ 2 ] = = ' Y ' & &
buildinfo . Name [ 3 ] > = ' 1 ' & &
buildinfo . Name [ 3 ] < = ' 3 ' & &
2020-04-17 19:32:09 +00:00
buildinfo . Height = = 128 & &
buildinfo . Parts . Size ( ) = = 1 )
{
// This must alter the size of both the texture image and the game texture.
2020-04-19 17:52:03 +00:00
buildinfo . Height = buildinfo . Parts [ 0 ] . TexImage - > GetImage ( ) - > GetHeight ( ) ;
2020-04-11 17:00:07 +00:00
return ;
}
// The Doom E1 sky has its patch's y offset at -8 instead of 0.
if ( gameinfo . gametype = = GAME_Doom & &
! ( gameinfo . flags & GI_MAPxx ) & &
buildinfo . Name . Len ( ) = = 4 & &
buildinfo . Parts . Size ( ) = = 1 & &
buildinfo . Height = = 128 & &
buildinfo . Parts [ 0 ] . OriginY = = - 8 & &
buildinfo . Name [ 0 ] = = ' S ' & &
buildinfo . Name [ 1 ] = = ' K ' & &
buildinfo . Name [ 2 ] = = ' Y ' & &
buildinfo . Name [ 3 ] = = ' 1 ' )
{
buildinfo . Parts [ 0 ] . OriginY = 0 ;
return ;
}
// BIGDOOR7 in Doom also has patches at y offset -4 instead of 0.
if ( gameinfo . gametype = = GAME_Doom & &
! ( gameinfo . flags & GI_MAPxx ) & &
buildinfo . Name . CompareNoCase ( " BIGDOOR7 " ) = = 0 & &
buildinfo . Parts . Size ( ) = = 2 & &
buildinfo . Height = = 128 & &
buildinfo . Parts [ 0 ] . OriginY = = - 4 & &
buildinfo . Parts [ 1 ] . OriginY = = - 4 )
{
buildinfo . Parts [ 0 ] . OriginY = 0 ;
buildinfo . Parts [ 1 ] . OriginY = 0 ;
return ;
}
}
//==========================================================================
//
2020-04-13 23:23:37 +00:00
//
2020-04-11 17:00:07 +00:00
//
//==========================================================================
2020-04-11 17:21:41 +00:00
static void Doom_CastSpriteIDToString ( FString * a , unsigned int b )
{
* a = ( b > = sprites . Size ( ) ) ? " TNT1 " : sprites [ b ] . name ;
}
2020-04-11 17:00:07 +00:00
2020-04-11 17:24:28 +00:00
extern DThinker * NextToThink ;
static void GC_MarkGameRoots ( )
{
GC : : Mark ( DIntermissionController : : CurrentIntermission ) ;
GC : : Mark ( staticEventManager . FirstEventHandler ) ;
GC : : Mark ( staticEventManager . LastEventHandler ) ;
for ( auto Level : AllLevels ( ) )
Level - > Mark ( ) ;
// Mark players.
for ( int i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( playeringame [ i ] )
players [ i ] . PropagateMark ( ) ;
}
// NextToThink must not be freed while thinkers are ticking.
GC : : Mark ( NextToThink ) ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// D_DoomMain
//
//==========================================================================
2019-10-06 22:55:14 +00:00
static int D_DoomMain_Internal ( void )
2016-03-01 15:47:10 +00:00
{
int p ;
const char * v ;
const char * wad ;
TArray < FString > pwads ;
FString * args ;
int argcount ;
FIWadManager * iwad_man ;
2020-04-11 16:09:51 +00:00
2020-04-11 17:24:28 +00:00
GC : : AddMarkerFunc ( GC_MarkGameRoots ) ;
2020-04-11 17:21:41 +00:00
VM_CastSpriteIDToString = Doom_CastSpriteIDToString ;
2020-04-11 16:09:51 +00:00
// Set up the button list. Mlook and Klook need a bit of extra treatment.
buttonMap . SetButtons ( DoomButtons , countof ( DoomButtons ) ) ;
buttonMap . GetButton ( Button_Mlook ) - > ReleaseHandler = Mlook_ReleaseHandler ;
buttonMap . GetButton ( Button_Mlook ) - > bReleaseLock = true ;
buttonMap . GetButton ( Button_Klook ) - > bReleaseLock = true ;
2020-04-11 16:12:28 +00:00
static StringtableCallbacks stblcb =
{
StrTable_ValidFilter ,
StrTable_GetGender
} ;
GStrings . SetCallbacks ( & stblcb ) ;
2020-04-11 16:22:21 +00:00
static SystemCallbacks syscb =
{
System_WantGuiCapture ,
System_WantLeftButton ,
System_NetGame ,
System_WantNativeMouse ,
System_CaptureModeInGame ,
2020-04-22 17:57:14 +00:00
System_CrashInfo ,
2020-04-25 19:52:21 +00:00
System_PlayStartupSound ,
System_IsSpecialUI ,
2020-04-11 16:22:21 +00:00
} ;
sysCallbacks = & syscb ;
2019-10-06 22:37:56 +00:00
std : : set_new_handler ( NewFailure ) ;
2016-03-01 15:47:10 +00:00
const char * batchout = Args - > CheckValue ( " -errorlog " ) ;
2019-09-30 23:00:37 +00:00
C_InitConsole ( 80 * 8 , 25 * 8 , false ) ;
2019-09-30 23:02:49 +00:00
I_DetectOS ( ) ;
2016-03-01 15:47:10 +00:00
// +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 " ) ;
if ( logfile . IsNotEmpty ( ) )
{
execLogfile ( logfile ) ;
}
else if ( batchout ! = NULL & & * batchout ! = 0 )
{
batchrun = true ;
2020-04-11 16:26:58 +00:00
nosound = true ;
2016-03-01 15:47:10 +00:00
execLogfile ( batchout , true ) ;
Printf ( " Command line: " ) ;
for ( int i = 0 ; i < Args - > NumArgs ( ) ; i + + )
{
Printf ( " %s " , Args - > GetArg ( i ) ) ;
}
Printf ( " \n " ) ;
}
if ( Args - > CheckParm ( " -hashfiles " ) )
{
const char * filename = " fileinfo.txt " ;
Printf ( " Hashing loaded content to: %s \n " , filename ) ;
hashfile = fopen ( filename , " w " ) ;
if ( hashfile )
{
fprintf ( hashfile , " %s version %s (%s) \n " , GAMENAME , GetVersionString ( ) , GetGitHash ( ) ) ;
# ifdef __VERSION__
fprintf ( hashfile , " Compiler version: %s \n " , __VERSION__ ) ;
# endif
fprintf ( hashfile , " Command line: " ) ;
for ( int i = 0 ; i < Args - > NumArgs ( ) ; + + i )
{
fprintf ( hashfile , " %s " , Args - > GetArg ( i ) ) ;
}
fprintf ( hashfile , " \n " ) ;
}
}
2017-08-19 18:13:12 +00:00
if ( ! batchrun ) Printf ( PRINT_LOG , " %s version %s \n " , GAMENAME , GetVersionString ( ) ) ;
2016-03-01 15:47:10 +00:00
D_DoomInit ( ) ;
2018-03-14 08:20:46 +00:00
extern void D_ConfirmSendStats ( ) ;
D_ConfirmSendStats ( ) ;
2016-03-01 15:47:10 +00:00
// [RH] Make sure zdoom.pk3 is always loaded,
// as it contains magic stuff we need.
2020-04-11 16:08:20 +00:00
wad = BaseFileSearch ( BASEWAD , NULL , true , GameConfig ) ;
2016-03-01 15:47:10 +00:00
if ( wad = = NULL )
{
I_FatalError ( " Cannot find " BASEWAD ) ;
}
FString basewad = wad ;
2020-04-11 16:08:20 +00:00
FString optionalwad = BaseFileSearch ( OPTIONALWAD , NULL , true , GameConfig ) ;
2017-09-09 10:36:20 +00:00
2019-07-16 18:22:15 +00:00
iwad_man = new FIWadManager ( basewad , optionalwad ) ;
2016-03-01 15:47:10 +00:00
// Now that we have the IWADINFO, initialize the autoload ini sections.
GameConfig - > DoAutoloadSetup ( iwad_man ) ;
// reinit from here
2020-04-11 11:48:55 +00:00
ConsoleCallbacks cb = {
D_UserInfoChanged ,
D_SendServerInfoChange ,
D_SendServerFlagChange ,
G_GetUserCVar ,
[ ] ( ) { return gamestate ! = GS_FULLCONSOLE & & gamestate ! = GS_STARTUP ; }
} ;
C_InstallHandlers ( & cb ) ;
2016-03-01 15:47:10 +00:00
do
{
PClass : : StaticInit ( ) ;
PType : : StaticInit ( ) ;
if ( restart )
{
C_InitConsole ( SCREENWIDTH , SCREENHEIGHT , false ) ;
}
nospriterename = false ;
// Load zdoom.pk3 alone so that we can get access to the internal gameinfos before
// the IWAD is known.
GetCmdLineFiles ( pwads ) ;
FString iwad = CheckGameInfo ( pwads ) ;
// The IWAD selection dialogue does not show in fullscreen so if the
// restart is initiated without a defined IWAD assume for now that it's not going to change.
if ( iwad . IsEmpty ( ) ) iwad = lastIWAD ;
if ( iwad_man = = NULL )
{
2019-07-16 18:22:15 +00:00
iwad_man = new FIWadManager ( basewad , optionalwad ) ;
2016-03-01 15:47:10 +00:00
}
2017-09-09 10:36:20 +00:00
const FIWADInfo * iwad_info = iwad_man - > FindIWAD ( allwads , iwad , basewad , optionalwad ) ;
2019-10-06 23:24:51 +00:00
if ( ! iwad_info ) return 0 ; // user exited the selection popup via cancel button.
2016-03-01 15:47:10 +00:00
gameinfo . gametype = iwad_info - > gametype ;
gameinfo . flags = iwad_info - > flags ;
2019-11-13 11:58:27 +00:00
gameinfo . nokeyboardcheats = iwad_info - > nokeyboardcheats ;
2016-03-01 15:47:10 +00:00
gameinfo . ConfigName = iwad_info - > Configname ;
lastIWAD = iwad ;
2020-04-22 22:02:34 +00:00
endoomName = gameinfo . Endoom ;
2016-03-01 15:47:10 +00:00
if ( ( gameinfo . flags & GI_SHAREWARE ) & & pwads . Size ( ) > 0 )
{
I_FatalError ( " You cannot -file with the shareware version. Register! " ) ;
}
FBaseCVar : : DisableCallbacks ( ) ;
GameConfig - > DoGameSetup ( gameinfo . ConfigName ) ;
AddAutoloadFiles ( iwad_info - > Autoname ) ;
// Process automatically executed files
FExecList * exec ;
2017-04-14 11:31:58 +00:00
FArgs * execFiles = new FArgs ;
2016-03-01 15:47:10 +00:00
GameConfig - > AddAutoexec ( execFiles , gameinfo . ConfigName ) ;
exec = D_MultiExec ( execFiles , NULL ) ;
2017-04-14 11:31:58 +00:00
delete execFiles ;
2016-03-01 15:47:10 +00:00
// Process .cfg files at the start of the command line.
execFiles = Args - > GatherFiles ( " -exec " ) ;
exec = D_MultiExec ( execFiles , exec ) ;
2017-04-14 11:31:58 +00:00
delete execFiles ;
2016-03-01 15:47:10 +00:00
// [RH] process all + commands on the command line
exec = C_ParseCmdLineParams ( exec ) ;
CopyFiles ( allwads , pwads ) ;
if ( exec ! = NULL )
{
2020-04-11 16:08:20 +00:00
exec - > AddPullins ( allwads , GameConfig ) ;
2016-03-01 15:47:10 +00:00
}
// Since this function will never leave we must delete this array here manually.
pwads . Clear ( ) ;
pwads . ShrinkToFit ( ) ;
if ( hashfile )
{
Printf ( " Notice: File hashing is incredibly verbose. Expect loading files to take much longer than usual. \n " ) ;
}
if ( ! batchrun ) Printf ( " W_Init: Init WADfiles. \n " ) ;
2020-04-11 11:21:18 +00:00
LumpFilterInfo lfi ;
lfi . dotFilter = LumpFilterIWAD ;
static const struct { int match ; const char * name ; } blanket [ ] =
{
{ GAME_Raven , " game-Raven " } ,
{ GAME_DoomStrifeChex , " game-DoomStrifeChex " } ,
{ GAME_DoomChex , " game-DoomChex " } ,
{ GAME_Any , NULL }
} ;
for ( auto & inf : blanket )
{
if ( gameinfo . gametype & inf . match ) lfi . gameTypeFilter . Push ( inf . name ) ;
}
lfi . gameTypeFilter . Push ( FStringf ( " game-%s " , GameTypeName ( ) ) ) ;
2020-04-19 11:04:29 +00:00
for ( auto p : iwad_folders ) lfi . reservedFolders . Push ( p ) ;
for ( auto p : iwad_reserved ) lfi . requiredPrefixes . Push ( p ) ;
2020-04-11 11:22:28 +00:00
2020-04-11 11:26:42 +00:00
lfi . postprocessFunc = [ & ] ( )
{
RenameNerve ( fileSystem ) ;
RenameSprites ( fileSystem , iwad_info - > DeleteLumps ) ;
FixMacHexen ( fileSystem ) ;
2020-04-11 11:27:19 +00:00
FindStrifeTeaserVoices ( fileSystem ) ;
2020-04-11 11:26:42 +00:00
} ;
fileSystem . InitMultipleFiles ( allwads , false , & lfi ) ;
2016-03-01 15:47:10 +00:00
allwads . Clear ( ) ;
allwads . ShrinkToFit ( ) ;
SetMapxxFlag ( ) ;
2020-04-11 11:48:55 +00:00
D_GrabCVarDefaults ( ) ; //parse DEFCVARS
2019-11-27 23:27:59 +00:00
2016-03-01 15:47:10 +00:00
GameConfig - > DoKeySetup ( gameinfo . ConfigName ) ;
// Now that wads are loaded, define mod-specific cvars.
ParseCVarInfo ( ) ;
// Actually exec command line commands and exec files.
if ( exec ! = NULL )
{
exec - > ExecCommands ( ) ;
delete exec ;
exec = NULL ;
}
// [RH] Initialize localizable strings.
2020-04-11 16:12:28 +00:00
GStrings . LoadStrings ( language ) ;
2016-03-01 15:47:10 +00:00
V_InitFontColors ( ) ;
// [RH] Moved these up here so that we can do most of our
// startup output in a fullscreen console.
CT_Init ( ) ;
if ( ! restart )
{
if ( ! batchrun ) Printf ( " I_Init: Setting up machine state. \n " ) ;
2020-04-11 11:05:12 +00:00
CheckCPUID ( & CPU ) ;
CalculateCPUSpeed ( ) ;
auto ci = DumpCPUInfo ( & CPU ) ;
Printf ( " %s " , ci . GetChars ( ) ) ;
2016-03-01 15:47:10 +00:00
}
2019-09-30 22:21:51 +00:00
// [RH] Initialize palette management
InitPalette ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! batchrun ) Printf ( " V_Init: allocate screen. \n " ) ;
2019-09-30 22:21:51 +00:00
if ( ! restart )
{
V_InitScreenSize ( ) ;
}
if ( ! restart )
{
// This allocates a dummy framebuffer as a stand-in until V_Init2 is called.
V_InitScreen ( ) ;
}
if ( restart )
{
// Update screen palette when restarting
screen - > UpdatePalette ( ) ;
}
2016-03-01 15:47:10 +00:00
// Base systems have been inited; enable cvar callbacks
FBaseCVar : : EnableCallbacks ( ) ;
if ( ! batchrun ) Printf ( " S_Init: Setting up sound. \n " ) ;
S_Init ( ) ;
if ( ! batchrun ) Printf ( " ST_Init: Init startup screen. \n " ) ;
if ( ! restart )
{
2020-04-22 22:02:34 +00:00
if ( GameStartupInfo . Type = = FStartupInfo : : DefaultStartup )
{
switch ( gameinfo . gametype )
{
case GAME_Hexen :
GameStartupInfo . Type = FStartupInfo : : HexenStartup ;
break ;
case GAME_Heretic :
GameStartupInfo . Type = FStartupInfo : : HereticStartup ;
break ;
case GAME_Strife :
GameStartupInfo . Type = FStartupInfo : : StrifeStartup ;
break ;
default :
break ;
}
}
2016-03-01 15:47:10 +00:00
StartScreen = FStartupScreen : : CreateInstance ( TexMan . GuesstimateNumTextures ( ) + 5 ) ;
}
else
{
StartScreen = new FStartupScreen ( 0 ) ;
}
CheckCmdLine ( ) ;
// [RH] Load sound environments
S_ParseReverbDef ( ) ;
// [RH] Parse any SNDINFO lumps
if ( ! batchrun ) Printf ( " S_InitData: Load sound definitions. \n " ) ;
S_InitData ( ) ;
// [RH] Parse through all loaded mapinfo lumps
if ( ! batchrun ) Printf ( " G_ParseMapInfo: Load map definitions. \n " ) ;
G_ParseMapInfo ( iwad_info - > MapInfo ) ;
ReadStatistics ( ) ;
// MUSINFO must be parsed after MAPINFO
S_ParseMusInfo ( ) ;
if ( ! batchrun ) Printf ( " Texman.Init: Init texture manager. \n " ) ;
2020-04-16 22:37:05 +00:00
UpdateUpscaleMask ( ) ;
2020-04-11 17:00:07 +00:00
SpriteFrames . Clear ( ) ;
TexMan . Init ( [ ] ( ) { StartScreen - > Progress ( ) ; } , CheckForHacks ) ;
PatchTextures ( ) ;
2020-04-11 16:54:54 +00:00
TexAnim . Init ( ) ;
2016-03-01 15:47:10 +00:00
C_InitConback ( ) ;
2017-02-05 12:14:22 +00:00
StartScreen - > Progress ( ) ;
V_InitFonts ( ) ;
2020-04-11 17:18:47 +00:00
UpdateGenericUI ( false ) ;
2017-02-05 12:14:22 +00:00
2016-03-01 15:47:10 +00:00
// [CW] Parse any TEAMINFO lumps.
if ( ! batchrun ) Printf ( " ParseTeamInfo: Load team definitions. \n " ) ;
TeamLibrary . ParseTeamInfo ( ) ;
2016-10-02 11:35:25 +00:00
R_ParseTrnslate ( ) ;
2016-03-01 15:47:10 +00:00
PClassActor : : StaticInit ( ) ;
// [GRB] Initialize player class list
SetupPlayerClasses ( ) ;
// [RH] Load custom key and weapon settings from WADs
D_LoadWadSettings ( ) ;
// [GRB] Check if someone used clearplayerclasses but not addplayerclass
if ( PlayerClasses . Size ( ) = = 0 )
{
I_FatalError ( " No player classes defined " ) ;
}
StartScreen - > Progress ( ) ;
2017-03-12 15:56:00 +00:00
ParseGLDefs ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! batchrun ) Printf ( " R_Init: Init %s refresh subsystem. \n " , gameinfo . ConfigName . GetChars ( ) ) ;
StartScreen - > LoadingStatus ( " Loading graphics " , 0x3f ) ;
R_Init ( ) ;
if ( ! batchrun ) Printf ( " DecalLibrary: Load decals. \n " ) ;
DecalLibrary . ReadAllDecals ( ) ;
2017-04-04 09:20:12 +00:00
// Load embedded Dehacked patches
D_LoadDehLumps ( FromIWAD ) ;
2016-03-01 15:47:10 +00:00
// [RH] Add any .deh and .bex files on the command line.
// If there are none, try adding any in the config file.
// Note that the command line overrides defaults from the config.
if ( ( ConsiderPatches ( " -deh " ) | ConsiderPatches ( " -bex " ) ) = = 0 & &
gameinfo . gametype = = GAME_Doom & & GameConfig - > SetSection ( " Doom.DefaultDehacked " ) )
{
const char * key ;
const char * value ;
while ( GameConfig - > NextInSection ( key , value ) )
{
if ( stricmp ( key , " Path " ) = = 0 & & FileExists ( value ) )
{
if ( ! batchrun ) Printf ( " Applying patch %s \n " , value ) ;
D_LoadDehFile ( value ) ;
}
}
}
// Load embedded Dehacked patches
2017-04-04 09:20:12 +00:00
D_LoadDehLumps ( FromPWADs ) ;
2016-03-01 15:47:10 +00:00
// Create replacements for dehacked pickups
FinishDehPatch ( ) ;
2017-02-11 20:28:48 +00:00
if ( ! batchrun ) Printf ( " M_Init: Init menus. \n " ) ;
M_Init ( ) ;
2017-01-24 10:57:42 +00:00
// clean up the compiler symbols which are not needed any longer.
RemoveUnusedSymbols ( ) ;
2016-03-01 15:47:10 +00:00
InitActorNumsFromMapinfo ( ) ;
InitSpawnablesFromMapinfo ( ) ;
PClassActor : : StaticSetActorNums ( ) ;
//Added by MC:
2019-02-01 23:24:43 +00:00
primaryLevel - > BotInfo . getspawned . Clear ( ) ;
2016-03-01 15:47:10 +00:00
argcount = Args - > CheckParmList ( " -bots " , & args ) ;
for ( p = 0 ; p < argcount ; + + p )
{
2019-02-01 23:24:43 +00:00
primaryLevel - > BotInfo . getspawned . Push ( args [ p ] ) ;
2016-03-01 15:47:10 +00:00
}
2019-02-01 23:24:43 +00:00
primaryLevel - > BotInfo . spawn_tries = 0 ;
primaryLevel - > BotInfo . wanted_botnum = primaryLevel - > BotInfo . getspawned . Size ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! batchrun ) Printf ( " P_Init: Init Playloop state. \n " ) ;
StartScreen - > LoadingStatus ( " Init game engine " , 0x3f ) ;
AM_StaticInit ( ) ;
P_Init ( ) ;
P_SetupWeapons_ntohton ( ) ;
2017-03-26 20:04:58 +00:00
//SBarInfo support. Note that the first SBARINFO lump contains the mugshot definition so it even needs to be read when a regular status bar is being used.
SBarInfo : : Load ( ) ;
2016-03-01 15:47:10 +00:00
if ( ! batchrun )
{
// [RH] User-configurable startup strings. Because BOOM does.
static const char * startupString [ 5 ] = {
" STARTUP1 " , " STARTUP2 " , " STARTUP3 " , " STARTUP4 " , " STARTUP5 "
} ;
for ( p = 0 ; p < 5 ; + + p )
{
2019-03-02 11:06:24 +00:00
// At this point we cannot use the player's gender info yet so force 'male' here.
const char * str = GStrings . GetString ( startupString [ p ] , nullptr , 0 ) ;
2016-03-01 15:47:10 +00:00
if ( str ! = NULL & & str [ 0 ] ! = ' \0 ' )
{
Printf ( " %s \n " , str ) ;
}
}
}
if ( ! restart )
{
if ( ! batchrun ) Printf ( " D_CheckNetGame: Checking network game status. \n " ) ;
StartScreen - > LoadingStatus ( " Checking network game status. " , 0x3f ) ;
2019-10-06 22:55:14 +00:00
if ( ! D_CheckNetGame ( ) )
{
return 0 ;
}
2016-03-01 15:47:10 +00:00
}
2017-06-04 00:00:53 +00:00
// [SP] Force vanilla transparency auto-detection to re-detect our game lumps now
UpdateVanillaTransparency ( ) ;
2016-03-01 15:47:10 +00:00
// [RH] Lock any cvars that should be locked now that we're
// about to begin the game.
FBaseCVar : : EnableNoSet ( ) ;
delete iwad_man ; // now we won't need this anymore
iwad_man = NULL ;
// [RH] Run any saved commands from the command line or autoexec.cfg now.
gamestate = GS_FULLCONSOLE ;
Net_NewMakeTic ( ) ;
2019-01-27 09:23:47 +00:00
C_RunDelayedCommands ( ) ;
2016-03-01 15:47:10 +00:00
gamestate = GS_STARTUP ;
2019-11-26 12:46:18 +00:00
// enable custom invulnerability map here
if ( cl_customizeinvulmap )
2020-01-18 15:44:58 +00:00
R_UpdateInvulnerabilityColormap ( ) ;
2019-11-26 12:46:18 +00:00
2016-03-01 15:47:10 +00:00
if ( ! restart )
{
// start the apropriate game based on parms
v = Args - > CheckValue ( " -record " ) ;
if ( v )
{
G_RecordDemo ( v ) ;
autostart = true ;
}
delete StartScreen ;
StartScreen = NULL ;
2019-12-16 22:52:39 +00:00
S_Sound ( CHAN_BODY , 0 , " misc/startupdone " , 1 , ATTN_NONE ) ;
2016-03-01 15:47:10 +00:00
if ( Args - > CheckParm ( " -norun " ) | | batchrun )
{
2019-10-06 22:55:14 +00:00
return 1337 ; // special exit
2016-03-01 15:47:10 +00:00
}
V_Init2 ( ) ;
2020-04-11 17:46:57 +00:00
twod - > fullscreenautoaspect = gameinfo . fullscreenautoaspect ;
2016-03-01 15:47:10 +00:00
UpdateJoystickMenu ( NULL ) ;
2019-03-01 05:19:09 +00:00
UpdateVRModes ( ) ;
2016-03-01 15:47:10 +00:00
v = Args - > CheckValue ( " -loadgame " ) ;
if ( v )
{
FString file ( v ) ;
FixPathSeperator ( file ) ;
2016-09-21 15:37:56 +00:00
DefaultExtension ( file , " . " SAVEGAME_EXT ) ;
2016-03-01 15:47:10 +00:00
G_LoadGame ( file ) ;
}
v = Args - > CheckValue ( " -playdemo " ) ;
if ( v ! = NULL )
{
singledemo = true ; // quit after one demo
G_DeferedPlayDemo ( v ) ;
D_DoomLoop ( ) ; // never returns
}
2016-12-03 14:44:46 +00:00
else
2016-03-01 15:47:10 +00:00
{
2016-12-03 14:44:46 +00:00
v = Args - > CheckValue ( " -timedemo " ) ;
if ( v )
2016-03-01 15:47:10 +00:00
{
2016-12-03 14:44:46 +00:00
G_TimeDemo ( v ) ;
D_DoomLoop ( ) ; // never returns
}
else
{
if ( gameaction ! = ga_loadgame & & gameaction ! = ga_loadgamehidecon )
2016-03-01 15:47:10 +00:00
{
2016-12-03 14:44:46 +00:00
if ( autostart | | netgame )
{
// Do not do any screenwipes when autostarting a game.
if ( ! Args - > CheckParm ( " -warpwipe " ) )
{
NoWipe = TICRATE ;
}
CheckWarpTransMap ( startmap , true ) ;
if ( demorecording )
G_BeginRecording ( startmap ) ;
G_InitNew ( startmap , false ) ;
if ( StoredWarp . IsNotEmpty ( ) )
{
2019-01-27 09:23:47 +00:00
AddCommandString ( StoredWarp ) ;
2019-02-14 21:22:15 +00:00
StoredWarp = " " ;
2016-12-03 14:44:46 +00:00
}
}
else
{
D_StartTitle ( ) ; // start up intro loop
}
2016-03-01 15:47:10 +00:00
}
2016-12-03 14:44:46 +00:00
else if ( demorecording )
2016-03-01 15:47:10 +00:00
{
2016-12-03 14:44:46 +00:00
G_BeginRecording ( NULL ) ;
2016-03-01 15:47:10 +00:00
}
}
}
}
else
{
// These calls from inside V_Init2 are still necessary
C_NewModeAdjust ( ) ;
D_StartTitle ( ) ; // start up intro loop
setmodeneeded = false ; // This may be set to true here, but isn't needed for a restart
}
2018-03-05 04:49:01 +00:00
D_DoAnonStats ( ) ;
2019-12-25 14:52:57 +00:00
I_UpdateWindowTitle ( ) ;
2017-10-31 23:47:56 +00:00
2016-03-08 12:07:21 +00:00
D_DoomLoop ( ) ; // this only returns if a 'restart' CCMD is given.
//
// Clean up after a restart
//
2016-03-01 15:47:10 +00:00
2019-09-30 22:30:44 +00:00
D_Cleanup ( ) ;
2018-05-27 13:15:05 +00:00
gamestate = GS_STARTUP ;
2016-03-01 15:47:10 +00:00
}
while ( 1 ) ;
}
2020-04-22 17:57:14 +00:00
int GameMain ( )
2019-10-06 22:37:56 +00:00
{
int ret = 0 ;
2020-03-18 12:58:10 +00:00
GameTicRate = TICRATE ;
2019-10-06 22:37:56 +00:00
try
{
2019-10-06 22:55:14 +00:00
ret = D_DoomMain_Internal ( ) ;
2019-10-06 22:37:56 +00:00
}
2019-10-06 23:24:51 +00:00
catch ( const CExitEvent & exit ) // This is a regular exit initiated from deeply nested code.
{
ret = exit . Reason ( ) ;
}
catch ( const std : : exception & error )
2019-10-06 22:37:56 +00:00
{
I_ShowFatalError ( error . what ( ) ) ;
ret = - 1 ;
}
// Unless something really bad happened, the game should only exit through this single point in the code.
// No more 'exit', please.
2019-10-07 18:28:55 +00:00
D_Cleanup ( ) ;
CloseNetwork ( ) ;
GC : : FinalGC = true ;
GC : : FullGC ( ) ;
GC : : DelSoftRootHead ( ) ; // the soft root head will not be collected by a GC so we have to do it explicitly
C_DeinitConsole ( ) ;
R_DeinitColormaps ( ) ;
R_Shutdown ( ) ;
I_ShutdownGraphics ( ) ;
I_ShutdownInput ( ) ;
M_SaveDefaultsFinal ( ) ;
DeleteStartupScreen ( ) ;
delete Args ;
Args = nullptr ;
2019-10-06 22:37:56 +00:00
return ret ;
}
2019-09-30 22:30:44 +00:00
//==========================================================================
//
// clean up the resources
//
//==========================================================================
void D_Cleanup ( )
{
2019-10-07 18:28:55 +00:00
if ( demorecording )
{
G_CheckDemoStatus ( ) ;
}
2019-09-30 22:30:44 +00:00
// Music and sound should be stopped first
S_StopMusic ( true ) ;
2019-10-07 18:28:55 +00:00
S_ClearSoundData ( ) ;
S_UnloadReverbDef ( ) ;
G_ClearMapinfo ( ) ;
2019-09-30 22:30:44 +00:00
M_ClearMenus ( ) ; // close menu if open
F_EndFinale ( ) ; // If an intermission is active, end it now
AM_ClearColorsets ( ) ;
2019-10-07 18:28:55 +00:00
DeinitSWColorMaps ( ) ;
FreeSBarInfoScript ( ) ;
2019-09-30 22:30:44 +00:00
// clean up game state
ST_Clear ( ) ;
D_ErrorCleanup ( ) ;
2019-10-07 18:28:55 +00:00
P_Shutdown ( ) ;
2019-09-30 22:30:44 +00:00
M_SaveDefaults ( NULL ) ; // save config before the restart
// delete all data that cannot be left until reinitialization
2020-04-25 15:58:26 +00:00
CleanSWDrawer ( ) ;
2019-09-30 22:30:44 +00:00
V_ClearFonts ( ) ; // must clear global font pointers
ColorSets . Clear ( ) ;
PainFlashes . Clear ( ) ;
gameinfo . ~ gameinfo_t ( ) ;
new ( & gameinfo ) gameinfo_t ; // Reset gameinfo
S_Shutdown ( ) ; // free all channels and delete playlist
C_ClearAliases ( ) ; // CCMDs won't be reinitialized so these need to be deleted here
DestroyCVarsFlagged ( CVAR_MOD ) ; // Delete any cvar left by mods
DeinitMenus ( ) ;
LightDefaults . DeleteAndClear ( ) ; // this can leak heap memory if it isn't cleared.
2020-04-11 16:54:54 +00:00
TexAnim . DeleteAll ( ) ;
2020-04-18 21:56:22 +00:00
TexMan . DeleteAll ( ) ;
2019-09-30 22:30:44 +00:00
2020-04-22 17:57:14 +00:00
// delete GameStartupInfo data
GameStartupInfo . Name = " " ;
GameStartupInfo . BkColor = GameStartupInfo . FgColor = GameStartupInfo . Type = 0 ;
GameStartupInfo . LoadLights = GameStartupInfo . LoadBrightmaps = - 1 ;
2019-09-30 22:30:44 +00:00
GC : : FullGC ( ) ; // clean up before taking down the object list.
// Delete the reference to the VM functions here which were deleted and will be recreated after the restart.
FAutoSegIterator probe ( ARegHead , ARegTail ) ;
while ( * + + probe ! = NULL )
{
AFuncDesc * afunc = ( AFuncDesc * ) * probe ;
* ( afunc - > VMPointer ) = NULL ;
}
GC : : DelSoftRootHead ( ) ;
2020-04-11 17:25:32 +00:00
for ( auto & p : players )
{
p . PendingWeapon = nullptr ;
}
PClassActor : : AllActorClasses . Clear ( ) ;
ScriptUtil : : Clear ( ) ;
2019-09-30 22:30:44 +00:00
PClass : : StaticShutdown ( ) ;
GC : : FullGC ( ) ; // perform one final garbage collection after shutdown
assert ( GC : : Root = = nullptr ) ;
restart + + ;
PClass : : bShutdown = false ;
PClass : : bVMOperational = false ;
}
2016-03-01 15:47:10 +00:00
//==========================================================================
//
// restart the game
//
//==========================================================================
2018-01-07 13:03:49 +00:00
UNSAFE_CCMD ( restart )
2016-03-01 15:47:10 +00:00
{
// remove command line args that would get in the way during restart
Args - > RemoveArgs ( " -iwad " ) ;
Args - > RemoveArgs ( " -deh " ) ;
Args - > RemoveArgs ( " -bex " ) ;
Args - > RemoveArgs ( " -playdemo " ) ;
Args - > RemoveArgs ( " -file " ) ;
Args - > RemoveArgs ( " -altdeath " ) ;
Args - > RemoveArgs ( " -deathmatch " ) ;
Args - > RemoveArgs ( " -skill " ) ;
Args - > RemoveArgs ( " -savedir " ) ;
Args - > RemoveArgs ( " -xlat " ) ;
Args - > RemoveArgs ( " -oldsprites " ) ;
if ( argv . argc ( ) > 1 )
{
2016-03-08 12:07:21 +00:00
for ( int i = 1 ; i < argv . argc ( ) ; i + + )
2016-03-01 15:47:10 +00:00
{
Args - > AppendArg ( argv [ i ] ) ;
}
}
2016-03-08 12:07:21 +00:00
wantToRestart = true ;
2016-03-01 15:47:10 +00:00
}
//==========================================================================
//
// FStartupScreen Constructor
//
//==========================================================================
FStartupScreen : : FStartupScreen ( int max_progress )
{
MaxPos = max_progress ;
CurPos = 0 ;
NotchPos = 0 ;
}
//==========================================================================
//
// FStartupScreen Destructor
//
//==========================================================================
FStartupScreen : : ~ FStartupScreen ( )
{
}
//==========================================================================
//
// FStartupScreen :: LoadingStatus
//
// Used by Heretic for the Loading Status "window."
//
//==========================================================================
void FStartupScreen : : LoadingStatus ( const char * message , int colors )
{
}
//==========================================================================
//
// FStartupScreen :: AppendStatusLine
//
// Used by Heretic for the "status line" at the bottom of the screen.
//
//==========================================================================
void FStartupScreen : : AppendStatusLine ( const char * status )
{
}
2019-10-07 18:28:55 +00:00
//===========================================================================
//
// DeleteStartupScreen
//
// Makes sure the startup screen has been deleted before quitting.
//
//===========================================================================
void DeleteStartupScreen ( )
{
if ( StartScreen ! = nullptr )
{
delete StartScreen ;
StartScreen = nullptr ;
}
}
2016-03-01 15:47:10 +00:00
void FStartupScreen : : Progress ( void ) { }
void FStartupScreen : : NetInit ( char const * , int ) { }
void FStartupScreen : : NetProgress ( int ) { }
void FStartupScreen : : NetMessage ( char const * , . . . ) { }
void FStartupScreen : : NetDone ( void ) { }
bool FStartupScreen : : NetLoop ( bool ( * ) ( void * ) , void * ) { return false ; }
2017-02-14 18:34:01 +00:00
DEFINE_FIELD_X ( InputEventData , event_t , type )
DEFINE_FIELD_X ( InputEventData , event_t , subtype )
DEFINE_FIELD_X ( InputEventData , event_t , data1 )
DEFINE_FIELD_X ( InputEventData , event_t , data2 )
DEFINE_FIELD_X ( InputEventData , event_t , data3 )
DEFINE_FIELD_X ( InputEventData , event_t , x )
DEFINE_FIELD_X ( InputEventData , event_t , y )
2017-10-31 23:47:56 +00:00
2019-12-25 14:52:57 +00:00
CUSTOM_CVAR ( Int , I_FriendlyWindowTitle , 1 , CVAR_GLOBALCONFIG | CVAR_ARCHIVE | CVAR_NOINITCALL )
{
I_UpdateWindowTitle ( ) ;
}
2017-10-31 23:47:56 +00:00
2019-12-25 14:52:57 +00:00
void I_UpdateWindowTitle ( )
2017-10-31 23:47:56 +00:00
{
2019-12-25 14:52:57 +00:00
switch ( I_FriendlyWindowTitle )
{
case 1 :
if ( level . LevelName & & level . LevelName . GetChars ( ) [ 0 ] )
{
FString titlestr ;
2020-04-22 17:57:14 +00:00
titlestr . Format ( " %s - %s " , level . LevelName . GetChars ( ) , GameStartupInfo . Name . GetChars ( ) ) ;
2019-12-25 14:52:57 +00:00
I_SetWindowTitle ( titlestr . GetChars ( ) ) ;
break ;
}
case 2 :
2020-04-22 17:57:14 +00:00
I_SetWindowTitle ( GameStartupInfo . Name . GetChars ( ) ) ;
2019-12-25 14:52:57 +00:00
break ;
default :
2017-10-31 23:47:56 +00:00
I_SetWindowTitle ( NULL ) ;
2019-12-25 14:52:57 +00:00
}
2018-01-07 13:03:49 +00:00
}