2019-12-14 19:15:15 +00:00
//-------------------------------------------------------------------------
/*
Copyright ( C ) 2019 Christoph Oelckers
This is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
//-------------------------------------------------------------------------
2020-01-12 13:54:43 +00:00
# include <stdexcept>
2019-10-25 22:32:49 +00:00
# include "gamecontrol.h"
# include "tarray.h"
# include "zstring.h"
# include "name.h"
# include "sc_man.h"
2019-10-26 11:16:32 +00:00
# include "c_cvars.h"
2019-10-26 17:24:46 +00:00
# include "gameconfigfile.h"
2019-10-26 21:45:55 +00:00
# include "gamecvars.h"
2019-10-26 11:16:32 +00:00
# include "build.h"
2019-10-28 05:47:49 +00:00
# include "inputstate.h"
2019-10-28 21:19:50 +00:00
# include "m_argv.h"
# include "rts.h"
2019-11-01 18:25:42 +00:00
# include "printf.h"
2019-11-03 19:24:50 +00:00
# include "c_bind.h"
2019-11-05 19:07:16 +00:00
# include "v_font.h"
# include "c_console.h"
# include "c_dispatch.h"
# include "i_specialpaths.h"
2020-04-12 06:07:48 +00:00
# include "raze_music.h"
2019-11-14 20:07:43 +00:00
# include "statistics.h"
2019-11-21 21:31:46 +00:00
# include "menu.h"
2019-11-23 11:38:38 +00:00
# include "gstrings.h"
2019-12-08 23:33:14 +00:00
# include "quotemgr.h"
2019-12-09 23:01:45 +00:00
# include "mapinfo.h"
2020-04-12 06:09:38 +00:00
# include "raze_sound.h"
2019-12-23 18:37:40 +00:00
# include "i_system.h"
# include "inputstate.h"
# include "v_video.h"
# include "st_start.h"
2019-12-24 11:59:26 +00:00
# include "s_music.h"
2019-12-24 17:53:29 +00:00
# include "i_video.h"
2020-01-11 16:05:25 +00:00
# include "v_text.h"
# include "resourcefile.h"
2019-12-28 11:59:19 +00:00
# include "c_dispatch.h"
2019-12-24 17:53:29 +00:00
# include "glbackend/glbackend.h"
2020-04-11 21:50:43 +00:00
# include "engineerrors.h"
2020-03-29 13:22:07 +00:00
# include "mmulti.h"
2020-04-11 22:04:02 +00:00
# include "gamestate.h"
2020-04-11 22:11:50 +00:00
# include "gstrings.h"
2020-05-24 21:13:08 +00:00
# include "texturemanager.h"
2020-04-23 19:18:40 +00:00
# include "i_interface.h"
# include "x86.h"
# include "startupinfo.h"
2020-06-14 17:20:04 +00:00
# include "mapinfo.h"
# include "menustate.h"
2020-07-19 10:48:31 +00:00
# include "screenjob.h"
2020-08-16 00:55:50 +00:00
# include "statusbar.h"
2020-08-25 16:51:56 +00:00
# include "uiinput.h"
2020-08-30 08:42:44 +00:00
# include "d_net.h"
2020-09-06 18:49:43 +00:00
# include "automap.h"
2020-04-23 19:18:40 +00:00
CVAR ( Bool , autoloadlights , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
CVAR ( Bool , autoloadbrightmaps , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2020-04-11 22:11:50 +00:00
CUSTOM_CVAR ( String , language , " auto " , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
{
GStrings . UpdateLanguage ( self ) ;
}
2020-03-29 13:22:07 +00:00
2020-04-23 19:18:40 +00:00
CUSTOM_CVAR ( Int , mouse_capturemode , 1 , CVAR_GLOBALCONFIG | CVAR_ARCHIVE )
{
if ( self < 0 )
{
self = 0 ;
}
else if ( self > 2 )
{
self = 2 ;
}
}
2020-03-29 13:22:07 +00:00
// The last remains of sdlayer.cpp
GameInterface * gi ;
int myconnectindex , numplayers ;
int connecthead , connectpoint2 [ MAXMULTIPLAYERS ] ;
auto vsnprintfptr = vsnprintf ; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries.
2020-08-25 23:41:23 +00:00
int lastTic ;
2020-03-29 13:22:07 +00:00
2020-08-30 10:02:32 +00:00
extern bool pauseext ;
2020-08-24 17:31:43 +00:00
2020-08-29 19:20:10 +00:00
cycle_t thinktime , actortime , gameupdatetime , drawtime ;
2020-06-23 19:12:15 +00:00
2020-04-11 22:04:02 +00:00
gamestate_t gamestate = GS_STARTUP ;
2020-09-03 21:10:28 +00:00
gameaction_t gameaction = ga_nothing ;
// gameaction state
MapRecord * g_nextmap ;
int g_nextskill ;
2020-04-11 22:04:02 +00:00
2020-04-11 21:54:33 +00:00
FILE * hashfile ;
2020-04-23 19:18:40 +00:00
FStartupInfo GameStartupInfo ;
2019-12-24 17:53:29 +00:00
FMemArena dump ; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown.
2019-10-27 23:24:09 +00:00
InputState inputState ;
2019-10-31 22:25:21 +00:00
int ShowStartupWindow ( TArray < GrpEntry > & ) ;
2020-01-11 16:05:25 +00:00
FString GetGameFronUserFiles ( ) ;
2019-11-01 18:25:42 +00:00
void InitFileSystem ( TArray < GrpEntry > & ) ;
2020-01-11 16:05:25 +00:00
void I_SetWindowTitle ( const char * caption ) ;
2020-04-12 06:07:48 +00:00
void S_ParseSndInfo ( ) ;
2020-05-31 08:53:11 +00:00
void I_DetectOS ( void ) ;
2020-06-14 17:20:04 +00:00
void LoadScripts ( ) ;
2020-08-30 08:42:44 +00:00
void MainLoop ( ) ;
2020-05-31 08:53:11 +00:00
2020-04-12 06:07:48 +00:00
2020-08-30 10:49:21 +00:00
bool AppActive = true ;
2019-10-27 23:24:09 +00:00
2019-10-31 23:32:56 +00:00
FString currentGame ;
2019-10-28 22:46:15 +00:00
FString LumpFilter ;
2019-11-28 00:02:45 +00:00
TMap < FName , int32_t > NameToTileIndex ; // for assigning names to tiles. The menu accesses this list. By default it gets everything from the dynamic tile map in Duke Nukem and Redneck Rampage.
// Todo: Add additional definition file for the other games or textures not in that list so that the menu does not have to rely on indices.
2019-10-26 11:16:32 +00:00
2019-12-24 14:28:00 +00:00
CVAR ( Bool , queryiwad , true , CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) ;
CVAR ( String , defaultiwad , " " , CVAR_ARCHIVE | CVAR_GLOBALCONFIG ) ;
CVAR ( Bool , disableautoload , false , CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG )
//CVAR(Bool, autoloadbrightmaps, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) // hopefully this is an option for later
//CVAR(Bool, autoloadlights, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG)
2019-10-26 11:16:32 +00:00
2020-01-22 12:53:26 +00:00
extern int hud_size_max ;
2020-05-28 23:22:45 +00:00
int paused ;
bool pausedWithKey ;
2020-04-11 22:11:50 +00:00
CUSTOM_CVAR ( Int , cl_gender , 0 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
{
if ( self < 0 | | self > 3 ) self = 0 ;
}
int StrTable_GetGender ( )
{
return cl_gender ;
}
bool validFilter ( const char * str ) ;
static StringtableCallbacks stblcb =
{
validFilter ,
StrTable_GetGender
} ;
2020-05-17 06:51:49 +00:00
extern int chatmodeon ;
2020-04-11 22:11:50 +00:00
2020-04-23 19:18:40 +00:00
bool System_WantGuiCapture ( )
{
bool wantCapt ;
if ( menuactive = = MENU_Off )
{
wantCapt = ConsoleState = = c_down | | ConsoleState = = c_falling | | chatmodeon ;
}
else
{
wantCapt = ( menuactive = = MENU_On | | menuactive = = MENU_OnNoPause ) ;
}
return wantCapt ;
}
bool System_WantLeftButton ( )
{
2020-08-10 22:46:27 +00:00
return false ; // (gamestate == GS_MENUSCREEN || gamestate == GS_TITLELEVEL);
2020-04-23 19:18:40 +00:00
}
bool System_NetGame ( )
{
return false ; // fixme later. For now there is no netgame support.
}
bool System_WantNativeMouse ( )
{
return false ;
}
static bool System_CaptureModeInGame ( )
{
return true ;
}
2020-01-11 16:05:25 +00:00
2020-05-30 22:01:00 +00:00
static bool System_DisableTextureFilter ( )
{
return hw_useindexedcolortextures ;
}
2020-04-28 20:55:37 +00:00
static IntRect System_GetSceneRect ( )
{
// Special handling so the view with a visible status bar displays properly
int height = windowxy2 . y - windowxy1 . y + 1 , width = windowxy2 . x - windowxy1 . x + 1 ;
IntRect mSceneViewport ;
mSceneViewport . left = windowxy1 . x ;
mSceneViewport . top = windowxy1 . y ;
mSceneViewport . width = width ;
mSceneViewport . height = height ;
return mSceneViewport ;
}
2020-01-08 00:00:57 +00:00
//==========================================================================
//
2020-04-23 19:18:40 +00:00
// DoomSpecificInfo
2020-01-08 00:00:57 +00:00
//
2020-04-23 19:18:40 +00:00
// Called by the crash logger to get application-specific information.
2020-01-08 00:00:57 +00:00
//
//==========================================================================
2020-04-23 19:18:40 +00:00
void System_CrashInfo ( char * buffer , size_t bufflen , const char * lfstr )
2020-01-08 00:00:57 +00:00
{
2020-04-23 19:18:40 +00:00
const char * arg ;
char * const buffend = buffer + bufflen - 2 ; // -2 for CRLF at end
int i ;
buffer + = mysnprintf ( buffer , buffend - buffer , GAMENAME " version %s (%s) " , GetVersionString ( ) , GetGitHash ( ) ) ;
buffer + = snprintf ( buffer , buffend - buffer , " %sCommand line: " , lfstr ) ;
for ( i = 0 ; i < Args - > NumArgs ( ) ; + + i )
{
buffer + = snprintf ( buffer , buffend - buffer , " %s " , Args - > GetArg ( i ) ) ;
}
for ( i = 0 ; ( arg = fileSystem . GetResourceFileName ( i ) ) ! = NULL ; + + i )
{
buffer + = mysnprintf ( buffer , buffend - buffer , " %sFile %d: %s " , lfstr , i , arg ) ;
}
buffer + = mysnprintf ( buffer , buffend - buffer , " %s " , lfstr ) ;
* buffer = 0 ;
2020-01-08 00:00:57 +00:00
}
2020-04-23 19:18:40 +00:00
2020-01-08 00:00:57 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-28 21:19:50 +00:00
UserConfig userConfig ;
void UserConfig : : ProcessOptions ( )
{
// -help etc are omitted
// -cfg / -setupfile refer to Build style config which are not supported.
if ( Args - > CheckParm ( " -cfg " ) | | Args - > CheckParm ( " -setupfile " ) )
{
2020-04-11 21:45:45 +00:00
Printf ( " Build-format config files not supported and will be ignored \n " ) ;
2019-10-28 21:19:50 +00:00
}
2019-12-11 22:41:05 +00:00
auto v = Args - > CheckValue ( " -addon " ) ;
2019-10-28 21:19:50 +00:00
if ( v )
{
auto val = strtol ( v , nullptr , 0 ) ;
static const char * const addons [ ] = { " DUKE3D.GRP " , " DUKEDC.GRP " , " NWINTER.GRP " , " VACATION.GRP " } ;
2020-02-09 08:48:55 +00:00
if ( val > = 0 & & val < 4 ) gamegrp = addons [ val ] ;
2020-04-11 21:45:45 +00:00
else Printf ( " %s: Unknown Addon \n " , v ) ;
2019-10-28 21:19:50 +00:00
}
else if ( Args - > CheckParm ( " -nam " ) )
{
gamegrp = " NAM.GRP " ;
}
else if ( Args - > CheckParm ( " -napalm " ) )
{
gamegrp = " NAPALM.GRP " ;
}
else if ( Args - > CheckParm ( " -ww2gi " ) )
{
gamegrp = " WW2GI.GRP " ;
}
2019-12-11 22:41:05 +00:00
// Set up all needed content for these two mod which feature a very messy distribution.
// As an alternative they can be zipped up - the launcher will be able to detect and set up such versions automatically.
else if ( Args - > CheckParm ( " -route66 " ) )
{
gamegrp = " REDNECK.GRP " ;
DefaultCon = " GAME66.CON " ;
const char * argv [ ] = { " tilesa66.art " , " tilesb66.art " } ;
AddArt . reset ( new FArgs ( 2 , argv ) ) ;
2020-01-10 20:36:46 +00:00
toBeDeleted . Push ( " turd66.anm*turdmov.anm " ) ;
toBeDeleted . Push ( " turd66.voc*turdmov.voc " ) ;
toBeDeleted . Push ( " end66.anm*rr_outro.anm " ) ;
toBeDeleted . Push ( " end66.voc*rr_outro.voc " ) ;
2019-12-11 22:41:05 +00:00
}
else if ( Args - > CheckParm ( " -cryptic " ) )
{
gamegrp = " BLOOD.RFF " ;
DefaultCon = " CRYPTIC.INI " ;
const char * argv [ ] = { " cpart07.ar_ " , " cpart15.ar_ " } ;
AddArt . reset ( new FArgs ( 2 , argv ) ) ;
}
2019-10-28 21:19:50 +00:00
2019-11-01 18:25:42 +00:00
v = Args - > CheckValue ( " -gamegrp " ) ;
2019-10-28 21:19:50 +00:00
if ( v )
{
gamegrp = v ;
}
else
{
// This is to enable the use of Doom launchers. that are limited to -iwad for specifying the game's main resource.
v = Args - > CheckValue ( " -iwad " ) ;
if ( v )
{
gamegrp = v ;
}
}
Args - > CollectFiles ( " -rts " , " .rts " ) ;
auto rts = Args - > CheckValue ( " -rts " ) ;
if ( rts ) RTS_Init ( rts ) ;
Args - > CollectFiles ( " -map " , " .map " ) ;
CommandMap = Args - > CheckValue ( " -map " ) ;
static const char * defs [ ] = { " -def " , " -h " , nullptr } ;
Args - > CollectFiles ( " -def " , defs , " .def " ) ;
DefaultDef = Args - > CheckValue ( " -def " ) ;
2020-01-10 20:36:46 +00:00
if ( DefaultCon . IsEmpty ( ) )
{
static const char * cons [ ] = { " -con " , " -x " , nullptr } ;
Args - > CollectFiles ( " -con " , cons , " .con " ) ;
DefaultCon = Args - > CheckValue ( " -con " ) ;
if ( DefaultCon . IsEmpty ( ) ) DefaultCon = Args - > CheckValue ( " -ini " ) ;
}
2019-10-28 21:19:50 +00:00
static const char * demos [ ] = { " -playback " , " -d " , " -demo " , nullptr } ;
Args - > CollectFiles ( " -demo " , demos , " .dmo " ) ;
CommandDemo = Args - > CheckValue ( " -demo " ) ;
static const char * names [ ] = { " -pname " , " -name " , nullptr } ;
Args - > CollectFiles ( " -name " , names , " .--- " ) ; // this shouldn't collect any file names at all so use a nonsense extension
CommandName = Args - > CheckValue ( " -name " ) ;
2020-08-22 18:14:00 +00:00
static const char * nomos [ ] = { " -nomonsters " , " -nodudes " , " -nocreatures " , nullptr } ;
2019-10-28 21:19:50 +00:00
Args - > CollectFiles ( " -nomonsters " , nomos , " .--- " ) ; // this shouldn't collect any file names at all so use a nonsense extension
nomonsters = Args - > CheckParm ( " -nomonsters " ) ;
static const char * acons [ ] = { " -addcon " , " -mx " , nullptr } ;
Args - > CollectFiles ( " -addcon " , acons , " .con " ) ;
AddCons . reset ( Args - > GatherFiles ( " -addcon " ) ) ;
static const char * adefs [ ] = { " -adddef " , " -mh " , nullptr } ;
Args - > CollectFiles ( " -adddef " , adefs , " .def " ) ;
AddDefs . reset ( Args - > GatherFiles ( " -adddef " ) ) ;
Args - > CollectFiles ( " -art " , " .art " ) ;
AddArt . reset ( Args - > GatherFiles ( " -art " ) ) ;
nologo = Args - > CheckParm ( " -nologo " ) | | Args - > CheckParm ( " -quick " ) ;
2020-04-12 06:07:48 +00:00
nosound = Args - > CheckParm ( " -nosfx " ) | | Args - > CheckParm ( " -nosound " ) ;
2019-12-24 14:28:00 +00:00
if ( Args - > CheckParm ( " -setup " ) ) queryiwad = 1 ;
else if ( Args - > CheckParm ( " -nosetup " ) ) queryiwad = 0 ;
2019-10-28 21:19:50 +00:00
if ( Args - > CheckParm ( " -file " ) )
{
// For file loading there's two modes:
// If -file is given, all content will be processed in order and the legacy options be ignored entirely.
//This allows mixing directories and GRP files in arbitrary order.
Args - > CollectFiles ( " -file " , NULL ) ;
AddFiles . reset ( Args - > GatherFiles ( " -file " ) ) ;
}
else
{
2019-11-01 18:25:42 +00:00
// Trying to emulate Build. This means to treat RFF files as lowest priority, then all GRPs and then all directories.
2019-10-28 21:19:50 +00:00
// This is only for people depending on lauchers. Since the semantics are so crappy it is strongly recommended to
2019-11-01 18:25:42 +00:00
// use -file instead which gives the user full control over the order in which things are added.
2019-10-28 21:19:50 +00:00
// For single mods this is no problem but don't even think about loading more stuff consistently...
static const char * grps [ ] = { " -g " , " -grp " , nullptr } ;
static const char * dirs [ ] = { " -game_dir " , " -j " , nullptr } ;
static const char * rffs [ ] = { " -rff " , " -snd " , nullptr } ;
static const char * twostep [ ] = { " -rff " , " -grp " , nullptr } ;
// Abuse the inner workings to get the files into proper order. This is not 100% accurate but should work fine for everything that doesn't intentionally fuck things up.
Args - > CollectFiles ( " -rff " , rffs , " .rff " ) ;
Args - > CollectFiles ( " -grp " , grps , nullptr ) ;
Args - > CollectFiles ( " -grp " , twostep , nullptr ) ; // The two previous calls have already brought the content in order so collecting it again gives us one list with everything.
AddFilesPre . reset ( Args - > GatherFiles ( " -grp " ) ) ;
Args - > CollectFiles ( " -game_dir " , dirs , nullptr ) ;
AddFiles . reset ( Args - > GatherFiles ( " -game_dir " ) ) ;
}
2019-12-23 19:55:12 +00:00
if ( Args - > CheckParm ( " -showcoords " ) | | Args - > CheckParm ( " -w " ) )
{
C_DoCommand ( " stat coord " ) ;
}
2019-10-28 21:19:50 +00:00
}
2020-07-15 10:34:42 +00:00
//==========================================================================
//
//
//
//==========================================================================
void CheckUserMap ( )
{
if ( userConfig . CommandMap . IsEmpty ( ) ) return ;
FString startupMap = userConfig . CommandMap ;
if ( startupMap . IndexOfAny ( " / \\ " ) < 0 ) startupMap . Insert ( 0 , " / " ) ;
DefaultExtension ( startupMap , " .map " ) ;
startupMap . Substitute ( " \\ " , " / " ) ;
NormalizeFileName ( startupMap ) ;
if ( fileSystem . FileExists ( startupMap ) )
{
Printf ( " Using level: \" %s \" . \n " , startupMap . GetChars ( ) ) ;
}
else
{
Printf ( " Level \" %s \" not found. \n " , startupMap . GetChars ( ) ) ;
startupMap = " " ;
}
userConfig . CommandMap = startupMap ;
}
2019-10-31 22:25:21 +00:00
//==========================================================================
//
//
//
//==========================================================================
2020-06-11 07:22:16 +00:00
namespace Duke3d
{
: : GameInterface * CreateInterface ( ) ;
}
2019-10-31 22:25:21 +00:00
namespace Blood
{
2019-11-03 11:32:58 +00:00
: : GameInterface * CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
namespace ShadowWarrior
{
2019-11-03 11:32:58 +00:00
: : GameInterface * CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
2019-12-12 23:19:34 +00:00
namespace Powerslave
{
: : GameInterface * CreateInterface ( ) ;
}
2019-10-31 22:25:21 +00:00
2020-06-20 08:46:51 +00:00
extern int MinFPSRate ; // this is a bit messy.
2020-02-12 19:25:59 +00:00
2019-10-31 22:25:21 +00:00
void CheckFrontend ( int flags )
{
if ( flags & GAMEFLAG_BLOOD )
{
2019-11-03 11:32:58 +00:00
gi = Blood : : CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
else if ( flags & GAMEFLAG_SW )
{
2020-06-20 08:46:51 +00:00
MinFPSRate = 40 ;
2019-11-03 11:32:58 +00:00
gi = ShadowWarrior : : CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
2019-12-12 23:19:34 +00:00
else if ( flags & GAMEFLAG_PSEXHUMED )
{
gi = Powerslave : : CreateInterface ( ) ;
}
2020-02-12 19:25:59 +00:00
else
{
2020-07-14 11:09:34 +00:00
gi = Duke3d : : CreateInterface ( ) ;
2020-02-12 19:25:59 +00:00
}
2019-10-31 22:25:21 +00:00
}
2019-12-14 16:15:17 +00:00
void I_StartupJoysticks ( ) ;
void I_ShutdownInput ( ) ;
int RunGame ( ) ;
2019-10-31 22:25:21 +00:00
2019-11-05 19:07:16 +00:00
int GameMain ( )
{
int r ;
2020-04-23 19:18:40 +00:00
static SystemCallbacks syscb =
{
System_WantGuiCapture ,
System_WantLeftButton ,
System_NetGame ,
System_WantNativeMouse ,
System_CaptureModeInGame ,
2020-05-30 22:01:00 +00:00
nullptr ,
nullptr ,
nullptr ,
System_DisableTextureFilter ,
2020-04-28 20:55:37 +00:00
nullptr ,
System_GetSceneRect ,
2020-04-23 19:18:40 +00:00
} ;
sysCallbacks = & syscb ;
2019-11-05 19:07:16 +00:00
try
{
2019-12-14 16:15:17 +00:00
r = RunGame ( ) ;
2019-11-05 19:07:16 +00:00
}
2020-04-11 21:50:43 +00:00
catch ( const CExitEvent & exit )
2019-11-05 19:07:16 +00:00
{
// Just let the rest of the function execute.
r = exit . Reason ( ) ;
}
2019-12-24 15:30:33 +00:00
catch ( const std : : exception & err )
{
// shut down critical systems before showing a message box.
I_ShowFatalError ( err . what ( ) ) ;
r = - 1 ;
}
2020-07-19 10:48:31 +00:00
DeleteScreenJob ( ) ;
2019-12-25 10:26:19 +00:00
M_ClearMenus ( true ) ;
if ( gi )
{
gi - > FreeGameData ( ) ; // Must be done before taking down any subsystems.
}
2019-12-24 15:30:33 +00:00
S_StopMusic ( true ) ;
if ( soundEngine ) delete soundEngine ;
2019-12-24 17:53:29 +00:00
soundEngine = nullptr ;
I_CloseSound ( ) ;
2019-12-24 15:30:33 +00:00
I_ShutdownInput ( ) ;
2019-11-10 18:42:26 +00:00
G_SaveConfig ( ) ;
2019-12-24 15:09:43 +00:00
C_DeinitConsole ( ) ;
2019-12-24 15:30:33 +00:00
V_ClearFonts ( ) ;
2019-12-25 07:57:58 +00:00
vox_deinit ( ) ;
2020-05-24 21:26:47 +00:00
TexMan . DeleteAll ( ) ;
TileFiles . CloseAll ( ) ; // delete the texture data before shutting down graphics.
2019-12-24 17:53:29 +00:00
GLInterface . Deinit ( ) ;
I_ShutdownGraphics ( ) ;
2019-12-24 19:06:55 +00:00
M_DeinitMenus ( ) ;
2019-12-25 10:26:19 +00:00
engineUnInit ( ) ;
2019-12-24 19:06:55 +00:00
if ( gi )
{
delete gi ;
gi = nullptr ;
}
2019-12-24 15:30:33 +00:00
DeleteStartupScreen ( ) ;
2020-06-14 17:20:04 +00:00
PClass : : StaticShutdown ( ) ;
2019-12-24 15:09:43 +00:00
if ( Args ) delete Args ;
2019-11-05 19:07:16 +00:00
return r ;
}
2019-10-26 17:24:46 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-11-24 23:02:00 +00:00
void SetDefaultStrings ( )
{
2020-01-09 16:35:38 +00:00
if ( ( g_gameType & GAMEFLAG_DUKE ) & & fileSystem . FindFile ( " E4L1.MAP " ) < 0 )
2020-01-02 23:44:39 +00:00
{
2020-01-09 16:35:38 +00:00
// Pre-Atomic releases do not define this.
2020-01-02 23:44:39 +00:00
gVolumeNames [ 0 ] = " $L.A. Meltdown " ;
gVolumeNames [ 1 ] = " $Lunar Apocalypse " ;
gVolumeNames [ 2 ] = " $Shrapnel City " ;
2020-01-09 16:35:38 +00:00
if ( g_gameType & GAMEFLAG_SHAREWARE ) gVolumeNames [ 3 ] = " $The Birth " ;
2020-01-02 23:44:39 +00:00
gSkillNames [ 0 ] = " $Piece of Cake " ;
gSkillNames [ 1 ] = " $Let's Rock " ;
gSkillNames [ 2 ] = " $Come get Some " ;
gSkillNames [ 3 ] = " $Damn I'm Good " ;
}
2019-12-08 23:33:14 +00:00
// Blood hard codes its skill names, so we have to define them manually.
if ( g_gameType & GAMEFLAG_BLOOD )
2019-11-24 23:02:00 +00:00
{
2019-12-08 23:33:14 +00:00
gSkillNames [ 0 ] = " $STILL KICKING " ;
gSkillNames [ 1 ] = " $PINK ON THE INSIDE " ;
gSkillNames [ 2 ] = " $LIGHTLY BROILED " ;
gSkillNames [ 3 ] = " $WELL DONE " ;
gSkillNames [ 4 ] = " $EXTRA CRISPY " ;
2019-11-24 23:02:00 +00:00
}
2019-12-08 23:33:14 +00:00
//Set a few quotes which are used for common handling of a few status messages
quoteMgr . InitializeQuote ( 23 , " $MESSAGES: ON " ) ;
2019-12-09 01:01:30 +00:00
quoteMgr . InitializeQuote ( 24 , " $MESSAGES: OFF " ) ;
quoteMgr . InitializeQuote ( 83 , " $FOLLOW MODE OFF " ) ;
quoteMgr . InitializeQuote ( 84 , " $FOLLOW MODE ON " ) ;
2019-12-08 23:33:14 +00:00
quoteMgr . InitializeQuote ( 85 , " $AUTORUNOFF " ) ;
quoteMgr . InitializeQuote ( 86 , " $AUTORUNON " ) ;
2019-11-24 23:02:00 +00:00
}
2019-12-08 23:33:14 +00:00
2019-10-26 17:24:46 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-12-23 18:37:40 +00:00
static TArray < GrpEntry > SetupGame ( )
2019-10-26 11:16:32 +00:00
{
2019-10-29 18:53:46 +00:00
// Startup dialog must be presented here so that everything can be set up before reading the keybinds.
2019-10-30 23:41:56 +00:00
auto groups = GrpScan ( ) ;
2019-11-01 18:25:42 +00:00
if ( groups . Size ( ) = = 0 )
{
// Abort if no game data found.
2019-11-03 09:51:47 +00:00
G_SaveConfig ( ) ;
2019-11-01 18:25:42 +00:00
I_Error ( " Unable to find any game data. Please verify your settings. " ) ;
}
decltype ( groups ) usedgroups ;
int groupno = - 1 ;
// If the user has specified a file name, let's see if we know it.
//
2020-01-11 16:05:25 +00:00
FString game = GetGameFronUserFiles ( ) ;
if ( userConfig . gamegrp . IsEmpty ( ) )
{
userConfig . gamegrp = game ;
}
2019-11-19 21:35:52 +00:00
if ( userConfig . gamegrp . Len ( ) )
2019-10-31 23:32:56 +00:00
{
2019-11-19 21:35:52 +00:00
FString gamegrplower = " / " + userConfig . gamegrp . MakeLower ( ) ;
2019-11-01 18:25:42 +00:00
int g = 0 ;
for ( auto & grp : groups )
2019-10-31 23:32:56 +00:00
{
2019-11-19 20:35:35 +00:00
auto grplower = grp . FileName . MakeLower ( ) ;
2019-11-19 21:35:52 +00:00
grplower . Substitute ( " \\ " , " / " ) ;
2019-11-19 20:35:35 +00:00
if ( grplower . LastIndexOf ( gamegrplower ) = = grplower . Len ( ) - gamegrplower . Len ( ) )
2019-10-31 23:32:56 +00:00
{
2019-11-02 12:27:40 +00:00
groupno = g ;
2019-10-31 23:32:56 +00:00
break ;
}
2019-11-01 18:25:42 +00:00
g + + ;
}
}
2019-12-24 14:28:00 +00:00
if ( groupno = = - 1 )
{
int pick = 0 ;
// We got more than one so present the IWAD selection box.
if ( groups . Size ( ) > 1 )
{
// Locate the user's prefered IWAD, if it was found.
if ( defaultiwad [ 0 ] ! = ' \0 ' )
{
for ( unsigned i = 0 ; i < groups . Size ( ) ; + + i )
{
FString & basename = groups [ i ] . FileName ;
if ( stricmp ( basename , defaultiwad ) = = 0 )
{
pick = i ;
break ;
}
}
}
if ( groups . Size ( ) > 1 )
{
TArray < WadStuff > wads ;
for ( auto & found : groups )
{
WadStuff stuff ;
stuff . Name = found . FileInfo . name ;
stuff . Path = ExtractFileBase ( found . FileName ) ;
wads . Push ( stuff ) ;
}
pick = I_PickIWad ( & wads [ 0 ] , ( int ) wads . Size ( ) , queryiwad , pick ) ;
if ( pick > = 0 )
{
// The newly selected IWAD becomes the new default
defaultiwad = groups [ pick ] . FileName ;
}
groupno = pick ;
}
}
2019-12-29 03:49:28 +00:00
else if ( groups . Size ( ) = = 1 )
{
groupno = 0 ;
}
2019-12-24 14:28:00 +00:00
}
2019-11-01 18:25:42 +00:00
2019-12-23 18:37:40 +00:00
if ( groupno = = - 1 ) return TArray < GrpEntry > ( ) ;
auto & group = groups [ groupno ] ;
2019-11-01 18:25:42 +00:00
2020-04-23 19:18:40 +00:00
if ( GameStartupInfo . Name . IsNotEmpty ( ) ) I_SetWindowTitle ( GameStartupInfo . Name ) ;
2020-01-11 16:05:25 +00:00
else I_SetWindowTitle ( group . FileInfo . name ) ;
2019-11-01 18:25:42 +00:00
// Now filter out the data we actually need and delete the rest.
usedgroups . Push ( group ) ;
auto crc = group . FileInfo . dependencyCRC ;
2019-11-02 12:27:40 +00:00
if ( crc ! = 0 ) for ( auto & dep : groups )
2019-11-01 18:25:42 +00:00
{
if ( dep . FileInfo . CRC = = crc )
{
usedgroups . Insert ( 0 , dep ) ; // Order from least dependent to most dependent, which is the loading order of data.
2019-10-31 23:32:56 +00:00
}
}
2019-11-01 18:25:42 +00:00
groups . Reset ( ) ;
FString selectedScript ;
FString selectedDef ;
for ( auto & ugroup : usedgroups )
{
// For CONs the command line has priority, aside from that, the last one wins. For Blood this handles INIs - the rules are the same.
if ( ugroup . FileInfo . scriptname . IsNotEmpty ( ) ) selectedScript = ugroup . FileInfo . scriptname ;
if ( ugroup . FileInfo . defname . IsNotEmpty ( ) ) selectedDef = ugroup . FileInfo . defname ;
// CVAR has priority. This also overwrites the global variable each time. Init here is lazy so this is ok.
2019-12-23 18:37:40 +00:00
if ( ugroup . FileInfo . rtsname . IsNotEmpty ( ) & & * * rtsname = = 0 ) RTS_Init ( ugroup . FileInfo . rtsname ) ;
2019-11-01 18:25:42 +00:00
// For the game filter the last non-empty one wins.
if ( ugroup . FileInfo . gamefilter . IsNotEmpty ( ) ) LumpFilter = ugroup . FileInfo . gamefilter ;
g_gameType | = ugroup . FileInfo . flags ;
}
if ( userConfig . DefaultCon . IsEmpty ( ) ) userConfig . DefaultCon = selectedScript ;
if ( userConfig . DefaultDef . IsEmpty ( ) ) userConfig . DefaultDef = selectedDef ;
// This can only happen with a custom game that does not define any filter.
// In this case take the display name and strip all whitespace and invaliid path characters from it.
if ( LumpFilter . IsEmpty ( ) )
{
LumpFilter = usedgroups . Last ( ) . FileInfo . name ;
LumpFilter . StripChars ( " .:/ \\ <>? \" *| \t \r \n " ) ;
}
2019-10-29 18:53:46 +00:00
2019-10-31 23:32:56 +00:00
currentGame = LumpFilter ;
currentGame . Truncate ( currentGame . IndexOf ( " . " ) ) ;
2019-11-01 18:25:42 +00:00
CheckFrontend ( g_gameType ) ;
2019-12-23 18:37:40 +00:00
return usedgroups ;
}
2020-09-05 19:33:04 +00:00
//==========================================================================
//
// reads a Duke Nukem World Tour translation file.
//
//==========================================================================
static void LoadLanguageWT ( int lumpnum , TArray < uint8_t > & buffer , const char * langid )
{
uint32_t activeMap ;
FScanner sc ;
if ( stricmp ( langid , " default " ) = = 0 ) activeMap = FStringTable : : default_table ;
else activeMap = MAKE_ID ( tolower ( langid [ 0 ] ) , tolower ( langid [ 1 ] ) , tolower ( langid [ 2 ] ) , 0 ) ;
// Notes about the text files:
// The English text has a few lines where a period follows a quoted string, they are skipped over by the first check in the loop.
// The Russian text file contains a line starting with two quotation marks.
// Unfortunately we need to get across that because important stuff comes afterward.
// The Japanese text also has a fatal error, but it comes after all the texts we actually need so it is easier to handle by just aborting the parse.
if ( ! stricmp ( langid , " ru " ) )
{
for ( unsigned i = 1 ; i < buffer . Size ( ) ; i + + )
{
if ( buffer [ i ] = = ' " ' & & buffer [ i - 1 ] = = ' " ' )
{
buffer . Delete ( i ) ;
break ;
}
}
}
sc . OpenMem ( fileSystem . GetFileFullName ( lumpnum ) , buffer ) ;
while ( sc . GetString ( ) )
{
if ( sc . String [ 0 ] ! = ' # ' ) sc . MustGetString ( ) ; // there's a few fucked up texts in the files.
if ( sc . String [ 0 ] ! = ' # ' ) return ;
FName strName ( sc . String ) ;
sc . MustGetString ( ) ;
GStrings . InsertString ( lumpnum , activeMap , strName , sc . String ) ;
}
}
//==========================================================================
//
//
//
//==========================================================================
void InitLanguages ( )
{
GStrings . LoadStrings ( language ) ;
if ( g_gameType & GAMEFLAG_DUKE ) // these are for World Tour but at this point we do not know yet, do just look for them anyway
{
static const struct wt_lang
{
const char * filename ;
const char * langid ;
} langfiles [ ] = {
{ " locale/english/strings.txt " , " default " } ,
{ " locale/brazilian/brazilian.txt " , " pt " } ,
{ " locale/french/french.txt " , " fr " } ,
{ " locale/german/german.txt " , " de " } ,
{ " locale/italian/italian.txt " , " it " } ,
{ " locale/japanese/japanese.txt " , " jp " } ,
{ " locale/russian/russian.txt " , " ru " } ,
{ " locale/spanish/spanish.txt " , " es " } } ;
for ( const auto & li : langfiles )
{
auto buffer = fileSystem . LoadFile ( li . filename , 1 ) ;
if ( buffer . Size ( ) > 0 ) LoadLanguageWT ( fileSystem . FindFile ( li . filename ) , buffer , li . langid ) ;
}
}
}
2019-12-23 18:37:40 +00:00
//==========================================================================
//
//
//
//==========================================================================
int RunGame ( )
{
// Set up the console before anything else so that it can receive text.
C_InitConsole ( 1024 , 768 , true ) ;
// +logfile gets checked too late to catch the full startup log in the logfile so do some extra check for it here.
FString logfile = Args - > TakeValue ( " +logfile " ) ;
// As long as this engine is still in prerelease mode let's always write a log file.
2019-12-26 13:04:53 +00:00
if ( logfile . IsEmpty ( ) ) logfile . Format ( " %s " GAMENAMELOWERCASE " .log " , M_GetDocumentsPath ( ) . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
if ( logfile . IsNotEmpty ( ) )
{
execLogfile ( logfile ) ;
}
I_DetectOS ( ) ;
userConfig . ProcessOptions ( ) ;
G_LoadConfig ( ) ;
auto usedgroups = SetupGame ( ) ;
2019-10-28 21:19:50 +00:00
2019-11-02 19:13:00 +00:00
InitFileSystem ( usedgroups ) ;
2019-12-23 18:37:40 +00:00
if ( usedgroups . Size ( ) = = 0 ) return 0 ;
2020-01-22 15:14:01 +00:00
// Handle CVARs with game specific defaults here.
2019-12-26 12:04:29 +00:00
if ( g_gameType & GAMEFLAG_BLOOD )
{
2020-01-22 12:53:26 +00:00
mus_redbook . SetGenericRepDefault ( false , CVAR_Bool ) ; // Blood should default to CD Audio off - all other games must default to on.
2020-08-24 22:01:33 +00:00
am_showlabel . SetGenericRepDefault ( true , CVAR_Bool ) ;
2019-12-26 12:04:29 +00:00
}
2020-01-22 14:21:07 +00:00
if ( g_gameType & GAMEFLAG_SW )
{
2020-02-12 21:46:18 +00:00
cl_weaponswitch . SetGenericRepDefault ( 1 , CVAR_Int ) ;
if ( cl_weaponswitch > 1 ) cl_weaponswitch = 1 ;
2020-01-22 14:21:07 +00:00
}
2020-08-23 22:25:42 +00:00
if ( g_gameType & ( GAMEFLAG_BLOOD | GAMEFLAG_RR ) )
{
am_nameontop . SetGenericRepDefault ( true , CVAR_Bool ) ; // Blood and RR show the map name on the top of the screen by default.
}
2019-12-26 12:04:29 +00:00
2019-12-23 18:37:40 +00:00
G_ReadConfig ( currentGame ) ;
V_InitFontColors ( ) ;
2020-09-05 19:33:04 +00:00
InitLanguages ( ) ;
2019-12-23 18:37:40 +00:00
2020-04-23 19:18:40 +00:00
CheckCPUID ( & CPU ) ;
CalculateCPUSpeed ( ) ;
auto ci = DumpCPUInfo ( & CPU ) ;
Printf ( " %s " , ci . GetChars ( ) ) ;
2019-12-23 18:37:40 +00:00
V_InitScreenSize ( ) ;
V_InitScreen ( ) ;
StartScreen = FStartupScreen : : CreateInstance ( 100 ) ;
2019-12-11 22:41:05 +00:00
TArray < FString > addArt ;
for ( auto & grp : usedgroups )
{
for ( auto & art : grp . FileInfo . loadart )
{
addArt . Push ( art ) ;
}
}
2019-12-17 19:08:59 +00:00
if ( userConfig . AddArt ) for ( auto & art : * userConfig . AddArt )
{
addArt . Push ( art ) ;
}
2019-12-11 22:41:05 +00:00
TileFiles . AddArt ( addArt ) ;
2019-11-02 19:13:00 +00:00
2019-12-23 18:37:40 +00:00
inputState . ClearAllInput ( ) ;
2019-11-04 22:01:50 +00:00
2019-11-02 19:13:00 +00:00
if ( ! GameConfig - > IsInitialized ( ) )
{
CONFIG_ReadCombatMacros ( ) ;
}
if ( userConfig . CommandName . IsNotEmpty ( ) )
{
playername = userConfig . CommandName ;
}
2020-08-23 15:47:05 +00:00
GameTicRate = 30 ;
2020-07-15 10:34:42 +00:00
CheckUserMap ( ) ;
2020-04-05 20:51:53 +00:00
GPalette . Init ( MAXPALOOKUPS + 2 ) ; // one slot for each translation, plus a separate one for the base palettes and the internal one
2020-05-24 21:13:08 +00:00
TexMan . Init ( [ ] ( ) { } , [ ] ( BuildInfo & ) { } ) ;
2019-11-05 19:07:16 +00:00
V_InitFonts ( ) ;
2020-05-24 21:26:47 +00:00
TileFiles . Init ( ) ;
2019-12-26 13:04:53 +00:00
sfx_empty = fileSystem . FindFile ( " engine/dsempty.lmp " ) ; // this must be done outside the sound code because it's initialized late.
2020-04-12 06:07:48 +00:00
I_InitSound ( ) ;
Mus_InitMusic ( ) ;
S_ParseSndInfo ( ) ;
2019-11-14 20:07:43 +00:00
InitStatistics ( ) ;
2020-06-14 17:20:04 +00:00
LoadScripts ( ) ;
2019-11-24 23:02:00 +00:00
SetDefaultStrings ( ) ;
2019-12-28 11:59:19 +00:00
if ( Args - > CheckParm ( " -sounddebug " ) )
C_DoCommand ( " stat sounddebug " ) ;
2019-12-23 18:37:40 +00:00
if ( enginePreInit ( ) )
{
2020-08-23 15:47:05 +00:00
I_FatalError ( " There was a problem initializing the Build engine: %s \n " , engineerrstr ) ;
2019-12-23 18:37:40 +00:00
}
2020-01-23 18:14:10 +00:00
auto exec = C_ParseCmdLineParams ( nullptr ) ;
if ( exec ) exec - > ExecCommands ( ) ;
2020-08-26 22:25:59 +00:00
SetupGameButtons ( ) ;
2020-08-23 15:47:05 +00:00
gi - > app_init ( ) ;
2020-09-08 20:37:21 +00:00
M_Init ( ) ;
2020-09-13 18:15:46 +00:00
if ( ! ( paletteloaded & PALETTE_MAIN ) )
I_FatalError ( " No palette found. " ) ;
V_LoadTranslations ( ) ; // loading the translations must be delayed until the palettes have been fully set up.
lookups . postLoadTables ( ) ;
TileFiles . PostLoadSetup ( ) ;
2020-08-30 10:49:21 +00:00
videoInit ( ) ;
2020-08-30 08:42:44 +00:00
2020-09-02 20:55:57 +00:00
D_CheckNetGame ( ) ;
MainLoop ( ) ;
2020-09-02 21:03:48 +00:00
return 0 ; // this is never reached. MainLoop only exits via exception.
2019-10-25 22:32:49 +00:00
}
2020-08-23 15:47:05 +00:00
//---------------------------------------------------------------------------
//
// The one and only main loop in the entire engine. Yay!
//
//---------------------------------------------------------------------------
2020-08-25 17:42:11 +00:00
void TickSubsystems ( )
{
// run these on an independent timer until we got something working for the games.
static const uint64_t tickInterval = 1'000'000'000 / 30 ;
static uint64_t nexttick = 0 ;
auto nowtick = I_nsTime ( ) ;
if ( nexttick = = 0 ) nexttick = nowtick ;
int cnt = 0 ;
while ( nexttick < = nowtick & & cnt < 5 )
{
nexttick + = tickInterval ;
C_Ticker ( ) ;
M_Ticker ( ) ;
2020-08-25 19:16:37 +00:00
C_RunDelayedCommands ( ) ;
2020-08-25 17:42:11 +00:00
cnt + + ;
}
// If this took too long the engine was most likely suspended so recalibrate the timer.
// Perfect precision is not needed here.
if ( cnt = = 5 ) nexttick = nowtick + tickInterval ;
}
2020-08-30 17:59:46 +00:00
void updatePauseStatus ( )
2020-08-26 14:47:30 +00:00
{
2020-08-27 20:38:52 +00:00
// This must go through the network in multiplayer games.
2020-08-26 14:47:30 +00:00
if ( M_Active ( ) | | System_WantGuiCapture ( ) )
{
paused = 1 ;
}
else if ( ! M_Active ( ) | | ! System_WantGuiCapture ( ) )
{
if ( ! pausedWithKey )
{
paused = 0 ;
}
if ( sendPause )
{
sendPause = false ;
paused = pausedWithKey ? 0 : 2 ;
pausedWithKey = ! ! paused ;
}
}
paused ? S_PauseSound ( ! pausedWithKey , ! paused ) : S_ResumeSound ( paused ) ;
}
2020-07-14 15:35:19 +00:00
//==========================================================================
//
//
//
//==========================================================================
void PolymostProcessVoxels ( void ) ;
void videoInit ( )
{
lookups . postLoadLookups ( ) ;
V_Init2 ( ) ;
videoSetGameMode ( vid_fullscreen , screen - > GetWidth ( ) , screen - > GetHeight ( ) , 32 , 1 ) ;
PolymostProcessVoxels ( ) ;
GLInterface . Init ( screen - > GetWidth ( ) ) ;
2020-09-12 19:23:14 +00:00
screen - > BeginFrame ( ) ;
2020-07-14 15:35:19 +00:00
screen - > SetTextureFilterMode ( ) ;
2020-08-16 00:55:50 +00:00
setViewport ( hud_size ) ;
2020-07-14 15:35:19 +00:00
}
2019-12-24 18:59:14 +00:00
void G_FatalEngineError ( void )
{
2020-01-07 00:11:19 +00:00
I_FatalError ( " There was a problem initializing the engine: %s \n \n The application will now close. " , engineerrstr ) ;
2019-12-24 18:59:14 +00:00
}
2019-10-27 07:14:58 +00:00
//==========================================================================
//
//
//
//==========================================================================
CVAR ( String , combatmacro0 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro1 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro2 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro3 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro4 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro5 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro6 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro7 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro8 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
CVAR ( String , combatmacro9 , " " , CVAR_ARCHIVE | CVAR_USERINFO )
FStringCVar * const CombatMacros [ ] = { & combatmacro0 , & combatmacro1 , & combatmacro2 , & combatmacro3 , & combatmacro4 , & combatmacro5 , & combatmacro6 , & combatmacro7 , & combatmacro8 , & combatmacro9 } ;
void CONFIG_ReadCombatMacros ( )
{
FScanner sc ;
2019-11-24 09:03:19 +00:00
try
{
2019-12-26 13:46:14 +00:00
sc . Open ( " engine/combatmacros.txt " ) ;
2019-11-24 09:03:19 +00:00
for ( auto s : CombatMacros )
{
sc . MustGetToken ( TK_StringConst ) ;
2020-07-27 16:12:24 +00:00
UCVarValue val ;
val . String = sc . String ;
s - > SetGenericRepDefault ( val , CVAR_String ) ;
2019-11-24 09:03:19 +00:00
}
}
2020-04-11 21:50:43 +00:00
catch ( const CRecoverableError & )
2019-10-27 07:14:58 +00:00
{
2019-11-24 09:03:19 +00:00
// We do not want this to error out. Just ignore if it fails.
2019-10-27 07:14:58 +00:00
}
}
2019-10-26 19:50:49 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-26 21:45:55 +00:00
2019-12-15 12:34:00 +00:00
CCMD ( snd_reset )
2019-10-26 21:45:55 +00:00
{
2019-12-15 12:34:00 +00:00
Mus_Stop ( ) ;
2019-12-15 16:16:11 +00:00
if ( soundEngine ) soundEngine - > Reset ( ) ;
2019-12-26 12:04:29 +00:00
Mus_ResumeSaved ( ) ;
2019-10-26 21:45:55 +00:00
}
2020-05-28 23:22:45 +00:00
//==========================================================================
//
// S_PauseSound
//
// Stop music and sound effects, during game PAUSE.
//
//==========================================================================
void S_PauseSound ( bool notmusic , bool notsfx )
{
if ( ! notmusic )
{
S_PauseMusic ( ) ;
}
if ( ! notsfx )
{
soundEngine - > SetPaused ( true ) ;
GSnd - > SetSfxPaused ( true , 0 ) ;
}
}
//==========================================================================
//
// S_ResumeSound
//
// Resume music and sound effects, after game PAUSE.
//
//==========================================================================
void S_ResumeSound ( bool notsfx )
{
S_ResumeMusic ( ) ;
if ( ! notsfx )
{
soundEngine - > SetPaused ( false ) ;
GSnd - > SetSfxPaused ( false , 0 ) ;
}
}
2019-12-22 19:55:47 +00:00
//==========================================================================
//
// S_SetSoundPaused
//
// Called with state non-zero when the app is active, zero when it isn't.
//
//==========================================================================
void S_SetSoundPaused ( int state )
{
if ( state )
{
if ( paused = = 0 )
{
S_ResumeSound ( true ) ;
if ( GSnd ! = nullptr )
{
GSnd - > SetInactive ( SoundRenderer : : INACTIVE_Active ) ;
}
}
}
else
{
if ( paused = = 0 )
{
S_PauseSound ( false , true ) ;
if ( GSnd ! = nullptr )
{
2020-05-28 23:22:45 +00:00
GSnd - > SetInactive ( SoundRenderer : : INACTIVE_Complete ) ;
2019-12-22 19:55:47 +00:00
}
}
}
2020-08-30 10:49:21 +00:00
#if 0
2020-08-30 08:42:44 +00:00
if ( ! netgame
#if 0 //def _DEBUG
& & ! demoplayback
# endif
)
{
pauseext = ! state ;
}
2020-08-30 10:49:21 +00:00
# endif
2019-12-22 19:55:47 +00:00
}
2020-01-12 13:54:43 +00:00
2020-02-05 11:57:17 +00:00
FString G_GetDemoPath ( )
{
FString path = M_GetDemoPath ( ) ;
path < < LumpFilter < < ' / ' ;
CreatePath ( path ) ;
return path ;
}
2020-03-08 12:54:00 +00:00
CCMD ( printinterface )
{
Printf ( " Current interface is %s \n " , gi - > Name ( ) ) ;
}
2020-04-11 22:04:02 +00:00
CCMD ( togglemsg )
{
FBaseCVar * var , * prev ;
UCVarValue val ;
if ( argv . argc ( ) > 1 )
{
if ( ( var = FindCVar ( argv [ 1 ] , & prev ) ) )
{
var - > MarkUnsafe ( ) ;
val = var - > GetGenericRep ( CVAR_Bool ) ;
val . Bool = ! val . Bool ;
var - > SetGenericRep ( val , CVAR_Bool ) ;
const char * statestr = argv . argc ( ) < = 2 ? " * " : argv [ 2 ] ;
if ( * statestr = = ' * ' )
{
2020-06-30 20:53:15 +00:00
Printf ( PRINT_MEDIUM | PRINT_NOTIFY , " \" %s \" = \" %s \" \n " , var - > GetName ( ) , val . Bool ? " true " : " false " ) ;
2020-04-11 22:04:02 +00:00
}
else
{
int state = ( int ) strtoll ( argv [ 2 ] , nullptr , 0 ) ;
if ( state ! = 0 )
{
// Order of Duke's quote string varies, some have on first, some off, so use the sign of the parameter to decide.
// Positive means Off/On, negative means On/Off
int quote = state > 0 ? state + val . Bool : - ( state + val . Bool ) ;
auto text = quoteMgr . GetQuote ( quote ) ;
2020-06-30 20:53:15 +00:00
if ( text ) Printf ( PRINT_MEDIUM | PRINT_NOTIFY , " %s \n " , text ) ;
2020-04-11 22:04:02 +00:00
}
}
}
}
}
2020-06-14 16:57:55 +00:00
bool OkForLocalization ( FTextureID texnum , const char * substitute )
{
return false ;
}
2020-08-24 18:34:18 +00:00
// Mainly a dummy.
CCMD ( taunt )
{
if ( argv . argc ( ) > 2 )
{
int taunt = atoi ( argv [ 1 ] ) ;
int mode = atoi ( argv [ 2 ] ) ;
// In a ZDoom-style protocol this should be sent:
// Net_WriteByte(DEM_TAUNT);
// Net_WriteByte(taunt);
// Net_WriteByte(mode);
if ( mode = = 1 )
{
// todo:
//gi->PlayTaunt(taunt);
// Duke:
// startrts(taunt, 1)
// Blood:
// sndStartSample(4400 + taunt, 128, 1, 0);
// SW:
// PlaySoundRTS(taunt);
// Exhumed does not implement RTS, should be like Duke
//
}
Printf ( PRINT_NOTIFY , " %s " , * * CombatMacros [ taunt - 1 ] ) ;
}
}
2020-08-30 10:02:32 +00:00
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-09-03 21:10:28 +00:00
void GameInterface : : FreeLevelData ( )
{
// Make sure that there is no more level to toy around with.
initspritelists ( ) ;
numsectors = numwalls = 0 ;
currentLevel = nullptr ;
}
2020-09-04 22:58:25 +00:00
2020-09-05 11:57:26 +00:00
//---------------------------------------------------------------------------
//
// Load crosshair definitions
//
//---------------------------------------------------------------------------
FGameTexture * CrosshairImage ;
int CrosshairNum ;
CVAR ( Int , crosshair , 0 , CVAR_ARCHIVE )
CVAR ( Color , crosshaircolor , 0xff0000 , CVAR_ARCHIVE ) ;
CVAR ( Int , crosshairhealth , 2 , CVAR_ARCHIVE ) ;
void ST_LoadCrosshair ( int num , bool alwaysload )
{
2020-09-05 19:33:04 +00:00
char name [ 16 ] ;
2020-09-05 11:57:26 +00:00
if ( ! alwaysload & & CrosshairNum = = num & & CrosshairImage ! = NULL )
{ // No change.
return ;
}
if ( num = = 0 )
{
CrosshairNum = 0 ;
CrosshairImage = NULL ;
return ;
}
if ( num < 0 )
{
num = - num ;
}
mysnprintf ( name , countof ( name ) , " XHAIRB%d " , num ) ;
FTextureID texid = TexMan . CheckForTexture ( name , ETextureType : : MiscPatch , FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ShortNameOnly ) ;
if ( ! texid . isValid ( ) )
{
mysnprintf ( name , countof ( name ) , " XHAIRB1 " ) ;
texid = TexMan . CheckForTexture ( name , ETextureType : : MiscPatch , FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ShortNameOnly ) ;
if ( ! texid . isValid ( ) )
{
texid = TexMan . CheckForTexture ( " XHAIRS1 " , ETextureType : : MiscPatch , FTextureManager : : TEXMAN_TryAny | FTextureManager : : TEXMAN_ShortNameOnly ) ;
}
}
CrosshairNum = num ;
CrosshairImage = TexMan . GetGameTexture ( texid ) ;
}
//---------------------------------------------------------------------------
//
// DrawCrosshair
//
//---------------------------------------------------------------------------
void DrawGenericCrosshair ( int num , int phealth , double xdelta )
{
uint32_t color ;
double size ;
int w , h ;
ST_LoadCrosshair ( num , false ) ;
// Don't draw the crosshair if there is none
if ( CrosshairImage = = NULL )
{
return ;
}
float crosshairscale = cl_crosshairscale * 0.005 ;
if ( crosshairscale > 0.0f )
{
size = twod - > GetHeight ( ) * crosshairscale / 200. ;
}
else
{
size = 1. ;
}
w = int ( CrosshairImage - > GetDisplayWidth ( ) * size ) ;
h = int ( CrosshairImage - > GetDisplayHeight ( ) * size ) ;
if ( crosshairhealth = = 1 )
{
// "Standard" crosshair health (green-red)
int health = phealth ;
if ( health > = 85 )
{
color = 0x00ff00 ;
}
else
{
int red , green ;
health - = 25 ;
if ( health < 0 )
{
health = 0 ;
}
if ( health < 30 )
{
red = 255 ;
green = health * 255 / 30 ;
}
else
{
red = ( 60 - health ) * 255 / 30 ;
green = 255 ;
}
color = ( red < < 16 ) | ( green < < 8 ) ;
}
}
else if ( crosshairhealth = = 2 )
{
// "Enhanced" crosshair health (blue-green-yellow-red)
int health = clamp ( phealth , 0 , 200 ) ;
float rr , gg , bb ;
float saturation = health < 150 ? 1.f : 1.f - ( health - 150 ) / 100.f ;
HSVtoRGB ( & rr , & gg , & bb , health * 1.2f , saturation , 1 ) ;
int red = int ( rr * 255 ) ;
int green = int ( gg * 255 ) ;
int blue = int ( bb * 255 ) ;
color = ( red < < 16 ) | ( green < < 8 ) | blue ;
}
else
{
color = crosshaircolor ;
}
DrawTexture ( twod , CrosshairImage ,
( windowxy1 . x + windowxy2 . x ) / 2 + xdelta * ( windowxy2 . y - windowxy1 . y ) / 240. ,
( windowxy1 . y + windowxy2 . y ) / 2 ,
DTA_DestWidth , w ,
DTA_DestHeight , h ,
DTA_AlphaChannel , true ,
DTA_FillColor , color & 0xFFFFFF ,
TAG_DONE ) ;
}
2020-09-21 21:29:52 +00:00
void DrawCrosshair ( int deftile , int health , double xdelta , double ydelta , double scale , PalEntry color )
2020-09-05 11:57:26 +00:00
{
int type = - 1 ;
if ( automapMode = = am_off & & cl_crosshair )
{
if ( deftile < MAXTILES & & crosshair = = 0 )
{
auto tile = tileGetTexture ( deftile ) ;
if ( tile )
{
double crosshair_scale = cl_crosshairscale * .01 * scale ;
2020-09-21 21:29:52 +00:00
DrawTexture ( twod , tile , 160 + xdelta , 100 + ydelta , DTA_Color , color ,
2020-09-05 11:57:26 +00:00
DTA_FullscreenScale , FSMode_Fit320x200 , DTA_ScaleX , crosshair_scale , DTA_ScaleY , crosshair_scale , DTA_CenterOffsetRel , true ,
DTA_ViewportX , windowxy1 . x , DTA_ViewportY , windowxy1 . y , DTA_ViewportWidth , windowxy2 . x - windowxy1 . x + 1 , DTA_ViewportHeight , windowxy2 . y - windowxy1 . y + 1 , TAG_DONE ) ;
return ;
}
}
DrawGenericCrosshair ( crosshair = = 0 ? 2 : * crosshair , health , xdelta ) ;
}
2020-09-16 14:42:44 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void LoadDefinitions ( )
{
loaddefinitionsfile ( " engine/engine.def " ) ; // Internal stuff that is required.
const char * defsfile = G_DefFile ( ) ;
cycle_t deftimer ;
deftimer . Reset ( ) ;
deftimer . Clock ( ) ;
if ( ! loaddefinitionsfile ( defsfile , true ) )
{
deftimer . Unclock ( ) ;
Printf ( PRINT_NONOTIFY , " Definitions file \" %s \" loaded in %.3f ms. \n " , defsfile , deftimer . TimeMS ( ) ) ;
}
userConfig . AddDefs . reset ( ) ;
// load the widescreen replacements last so that they do not clobber the CRC for the original items so that mod-side replacement are picked up.
if ( fileSystem . FindFile ( " engine/widescreen.def " ) > = 0 & & ! Args - > CheckParm ( " -nowidescreen " ) )
{
loaddefinitionsfile ( " engine/widescreen.def " ) ;
}
}
2020-09-16 11:46:03 +00:00
//---------------------------------------------------------------------------
//
// code fron gameexec/conrun
//
//---------------------------------------------------------------------------
int getincangle ( int a , int na )
{
a & = 2047 ;
na & = 2047 ;
if ( abs ( a - na ) < 1024 )
return ( na - a ) ;
else
{
if ( na > 1024 ) na - = 2048 ;
if ( a > 1024 ) a - = 2048 ;
na - = 2048 ;
a - = 2048 ;
return ( na - a ) ;
}
}
fixed_t getincangleq16 ( fixed_t a , fixed_t na )
{
a & = 0x7FFFFFF ;
na & = 0x7FFFFFF ;
if ( abs ( a - na ) < IntToFixed ( 1024 ) )
return ( na - a ) ;
else
{
if ( na > IntToFixed ( 1024 ) ) na - = IntToFixed ( 2048 ) ;
if ( a > IntToFixed ( 1024 ) ) a - = IntToFixed ( 2048 ) ;
na - = IntToFixed ( 2048 ) ;
a - = IntToFixed ( 2048 ) ;
return ( na - a ) ;
}
}
2020-09-20 10:01:05 +00:00
2020-09-24 12:32:37 +00:00
//---------------------------------------------------------------------------
//
// Player's movement function, called from game's ticker or from gi->GetInput() as required.
//
//---------------------------------------------------------------------------
2020-09-25 12:26:50 +00:00
void processMovement ( InputPacket * currInput , InputPacket * inputBuffer , ControlInfo * const hidInput , double const scaleAdjust , short const drink_amt , bool const allowstrafe , double const turnscale )
2020-09-24 12:32:37 +00:00
{
// set up variables
int const running = ! ! ( inputBuffer - > actions & SB_RUN ) ;
int const keymove = gi - > playerKeyMove ( ) < < running ;
int const mousevelscale = g_gameType & GAMEFLAG_BLOOD ? 32 : 4 ;
2020-09-24 14:57:48 +00:00
int const cntrlvelscale = g_gameType & GAMEFLAG_PSEXHUMED ? 8 : 1 ;
2020-09-24 12:32:37 +00:00
// process mouse and initial controller input.
if ( buttonMap . ButtonDown ( gamefunc_Strafe ) & & allowstrafe )
2020-09-25 02:03:56 +00:00
currInput - > svel - = xs_CRoundToInt ( ( hidInput - > mousex * mousevelscale ) + ( scaleAdjust * ( hidInput - > dyaw / 60 ) * keymove * cntrlvelscale ) ) ;
2020-09-24 12:32:37 +00:00
else
currInput - > q16avel + = FloatToFixed ( hidInput - > mousex + ( scaleAdjust * hidInput - > dyaw ) ) ;
2020-09-25 12:17:21 +00:00
if ( ! ( inputBuffer - > actions & SB_AIMMODE ) )
2020-09-24 12:32:37 +00:00
currInput - > q16horz - = FloatToFixed ( hidInput - > mousey ) ;
else
currInput - > fvel - = xs_CRoundToInt ( hidInput - > mousey * mousevelscale * 2 ) ;
if ( in_mouseflip )
currInput - > q16horz = - currInput - > q16horz ;
// process remaining controller input.
currInput - > q16horz - = FloatToFixed ( scaleAdjust * hidInput - > dpitch ) ;
2020-09-25 12:17:21 +00:00
currInput - > svel - = xs_CRoundToInt ( scaleAdjust * hidInput - > dx * keymove * cntrlvelscale ) ;
currInput - > fvel - = xs_CRoundToInt ( scaleAdjust * hidInput - > dz * keymove * cntrlvelscale ) ;
2020-09-24 12:32:37 +00:00
// process keyboard turning keys.
if ( buttonMap . ButtonDown ( gamefunc_Strafe ) & & allowstrafe )
{
if ( abs ( inputBuffer - > svel ) < keymove )
{
if ( buttonMap . ButtonDown ( gamefunc_Turn_Left ) )
currInput - > svel + = keymove ;
if ( buttonMap . ButtonDown ( gamefunc_Turn_Right ) )
currInput - > svel - = keymove ;
}
}
else
{
static double turnheldtime ;
int const turnheldamt = 120 / GameTicRate ;
double const turboturntime = 590. / GameTicRate ;
2020-09-25 13:07:38 +00:00
double turnamount = ( ( running ? 43375. / 27. : 867.5 ) / GameTicRate ) * turnscale ;
double preambleturn = turnamount / ( 347. / 92. ) ;
2020-09-24 12:32:37 +00:00
// allow Exhumed to use its legacy values given the drastic difference from the other games.
if ( ( g_gameType & GAMEFLAG_PSEXHUMED ) & & cl_exhumedoldturn )
{
turnamount = running ? 12 : 8 ;
preambleturn = turnamount ;
}
if ( buttonMap . ButtonDown ( gamefunc_Turn_Left ) | | ( buttonMap . ButtonDown ( gamefunc_Strafe_Left ) & & ! allowstrafe ) )
{
turnheldtime + = scaleAdjust * turnheldamt ;
currInput - > q16avel - = FloatToFixed ( scaleAdjust * ( turnheldtime > = turboturntime ? turnamount : preambleturn ) ) ;
}
else if ( buttonMap . ButtonDown ( gamefunc_Turn_Right ) | | ( buttonMap . ButtonDown ( gamefunc_Strafe_Right ) & & ! allowstrafe ) )
{
turnheldtime + = scaleAdjust * turnheldamt ;
currInput - > q16avel + = FloatToFixed ( scaleAdjust * ( turnheldtime > = turboturntime ? turnamount : preambleturn ) ) ;
}
else
{
turnheldtime = 0 ;
}
}
// process keyboard forward/side velocity keys.
if ( abs ( inputBuffer - > svel ) < keymove )
{
if ( buttonMap . ButtonDown ( gamefunc_Strafe_Left ) & & allowstrafe )
currInput - > svel + = keymove ;
if ( buttonMap . ButtonDown ( gamefunc_Strafe_Right ) & & allowstrafe )
currInput - > svel - = keymove ;
}
if ( abs ( inputBuffer - > fvel ) < keymove )
{
if ( isRR ( ) & & drink_amt > = 66 & & drink_amt < = 87 )
{
if ( buttonMap . ButtonDown ( gamefunc_Move_Forward ) )
{
currInput - > fvel + = keymove ;
if ( drink_amt & 1 )
currInput - > svel + = keymove ;
else
currInput - > svel - = keymove ;
}
if ( buttonMap . ButtonDown ( gamefunc_Move_Backward ) )
{
currInput - > fvel - = keymove ;
if ( drink_amt & 1 )
currInput - > svel - = keymove ;
else
currInput - > svel + = keymove ;
}
}
else
{
if ( buttonMap . ButtonDown ( gamefunc_Move_Forward ) )
currInput - > fvel + = keymove ;
if ( buttonMap . ButtonDown ( gamefunc_Move_Backward ) )
currInput - > fvel - = keymove ;
}
}
// add collected input to game's local input accumulation packet.
inputBuffer - > fvel = clamp ( inputBuffer - > fvel + currInput - > fvel , - keymove , keymove ) ;
inputBuffer - > svel = clamp ( inputBuffer - > svel + currInput - > svel , - keymove , keymove ) ;
inputBuffer - > q16avel + = currInput - > q16avel ;
inputBuffer - > q16horz + = currInput - > q16horz ;
}
2020-09-20 10:01:05 +00:00
//---------------------------------------------------------------------------
//
2020-09-21 07:00:07 +00:00
// Player's horizon function, called from game's ticker or from gi->GetInput() as required.
2020-09-20 10:01:05 +00:00
//
//---------------------------------------------------------------------------
void sethorizon ( fixed_t * q16horiz , fixed_t const q16horz , ESyncBits * actions , double const scaleAdjust )
{
// Calculate adjustment as true pitch (Fixed point math really sucks...)
double horizAngle = atan2 ( * q16horiz - IntToFixed ( 100 ) , IntToFixed ( 128 ) ) * ( 512. / pi : : pi ( ) ) ;
if ( q16horz )
{
* actions & = ~ SB_CENTERVIEW ;
horizAngle = clamp ( horizAngle + FixedToFloat ( q16horz ) , - 180 , 180 ) ;
}
// this is the locked type
if ( * actions & ( SB_AIM_UP | SB_AIM_DOWN ) )
{
* actions & = ~ SB_CENTERVIEW ;
double const amount = 250. / GameTicRate ;
if ( * actions & SB_AIM_DOWN )
horizAngle - = scaleAdjust * amount ;
if ( * actions & SB_AIM_UP )
horizAngle + = scaleAdjust * amount ;
}
// this is the unlocked type
if ( * actions & ( SB_LOOK_UP | SB_LOOK_DOWN ) )
{
* actions | = SB_CENTERVIEW ;
double const amount = 500. / GameTicRate ;
if ( * actions & SB_LOOK_DOWN )
horizAngle - = scaleAdjust * amount ;
if ( * actions & SB_LOOK_UP )
horizAngle + = scaleAdjust * amount ;
}
// convert back to Build's horizon
* q16horiz = IntToFixed ( 100 ) + xs_CRoundToInt ( IntToFixed ( 128 ) * tan ( horizAngle * ( pi : : pi ( ) / 512. ) ) ) ;
// return to center if conditions met.
if ( ( * actions & SB_CENTERVIEW ) & & ! ( * actions & ( SB_LOOK_UP | SB_LOOK_DOWN ) ) )
{
if ( * q16horiz < FloatToFixed ( 99.75 ) | | * q16horiz > FloatToFixed ( 100.25 ) )
{
// move *q16horiz back to 100
* q16horiz + = xs_CRoundToInt ( scaleAdjust * ( ( ( 1000. / GameTicRate ) * FRACUNIT ) - ( * q16horiz * ( 10. / GameTicRate ) ) ) ) ;
}
else
{
// not looking anymore because *q16horiz is back at 100
* q16horiz = IntToFixed ( 100 ) ;
* actions & = ~ SB_CENTERVIEW ;
}
}
// clamp before returning
* q16horiz = clamp ( * q16horiz , gi - > playerHorizMin ( ) , gi - > playerHorizMax ( ) ) ;
}
2020-09-21 07:00:07 +00:00
//---------------------------------------------------------------------------
//
// Player's angle function, called from game's ticker or from gi->GetInput() as required.
//
//---------------------------------------------------------------------------
2020-09-21 05:51:33 +00:00
void applylook ( fixed_t * q16ang , fixed_t * q16look_ang , fixed_t * q16rotscrnang , fixed_t * spin , fixed_t const q16avel , ESyncBits * actions , double const scaleAdjust , bool const crouching )
2020-09-21 07:00:07 +00:00
{
2020-09-21 05:51:33 +00:00
// return q16rotscrnang to 0 and set to 0 if less than a quarter of a FRACUNIT (16384)
* q16rotscrnang - = xs_CRoundToInt ( scaleAdjust * ( * q16rotscrnang * ( 15. / GameTicRate ) ) ) ;
if ( abs ( * q16rotscrnang ) < ( FRACUNIT > > 2 ) ) * q16rotscrnang = 0 ;
2020-09-21 07:00:07 +00:00
2020-09-21 05:51:33 +00:00
// return q16look_ang to 0 and set to 0 if less than a quarter of a FRACUNIT (16384)
* q16look_ang - = xs_CRoundToInt ( scaleAdjust * ( * q16look_ang * ( 7.5 / GameTicRate ) ) ) ;
if ( abs ( * q16look_ang ) < ( FRACUNIT > > 2 ) ) * q16look_ang = 0 ;
2020-09-21 07:00:07 +00:00
2020-09-21 05:51:33 +00:00
if ( * actions & SB_LOOK_LEFT )
{
// start looking left
* q16look_ang - = FloatToFixed ( scaleAdjust * ( 4560. / GameTicRate ) ) ;
* q16rotscrnang + = FloatToFixed ( scaleAdjust * ( 720. / GameTicRate ) ) ;
}
2020-09-21 07:00:07 +00:00
2020-09-21 05:51:33 +00:00
if ( * actions & SB_LOOK_RIGHT )
{
// start looking right
* q16look_ang + = FloatToFixed ( scaleAdjust * ( 4560. / GameTicRate ) ) ;
* q16rotscrnang - = FloatToFixed ( scaleAdjust * ( 720. / GameTicRate ) ) ;
}
2020-09-21 07:00:07 +00:00
2020-09-21 05:51:33 +00:00
if ( * actions & SB_TURNAROUND )
{
if ( * spin = = 0 )
2020-09-21 07:00:07 +00:00
{
2020-09-21 05:51:33 +00:00
// currently not spinning, so start a spin
* spin = IntToFixed ( - 1024 ) ;
2020-09-21 07:00:07 +00:00
}
2020-09-21 05:51:33 +00:00
* actions & = ~ SB_TURNAROUND ;
}
2020-09-21 07:00:07 +00:00
2020-09-21 05:51:33 +00:00
if ( * spin < 0 )
{
// return spin to 0
fixed_t add = FloatToFixed ( scaleAdjust * ( ( ! crouching ? 3840. : 1920. ) / GameTicRate ) ) ;
* spin + = add ;
if ( * spin > 0 )
2020-09-21 07:00:07 +00:00
{
2020-09-21 05:51:33 +00:00
// Don't overshoot our target. With variable factor this is possible.
add - = * spin ;
* spin = 0 ;
2020-09-21 07:00:07 +00:00
}
2020-09-21 05:51:33 +00:00
* q16ang + = add ;
}
2020-09-21 07:00:07 +00:00
2020-09-21 05:51:33 +00:00
if ( q16avel )
{
// add player's input
* q16ang = ( * q16ang + q16avel ) & 0x7FFFFFF ;
2020-09-21 07:00:07 +00:00
}
}
2020-09-21 03:41:16 +00:00
//---------------------------------------------------------------------------
//
// Player's ticrate helper functions.
//
//---------------------------------------------------------------------------
void playerAddAngle ( fixed_t * q16ang , double * helper , double adjustment )
{
if ( ! cl_syncinput )
{
* helper + = adjustment ;
}
else
{
* q16ang = ( * q16ang + FloatToFixed ( adjustment ) ) & 0x7FFFFFF ;
}
}
void playerSetAngle ( fixed_t * q16ang , fixed_t * helper , double adjustment )
{
if ( ! cl_syncinput )
{
// Add slight offset if adjustment is coming in as absolute 0.
2020-09-21 05:51:33 +00:00
if ( adjustment = = 0 ) adjustment + = ( 1. / ( FRACUNIT > > 1 ) ) ;
2020-09-21 03:41:16 +00:00
* helper = * q16ang + getincangleq16 ( * q16ang , FloatToFixed ( adjustment ) ) ;
}
else
{
* q16ang = FloatToFixed ( adjustment ) ;
}
}
void playerAddHoriz ( fixed_t * q16horiz , double * helper , double adjustment )
{
if ( ! cl_syncinput )
{
* helper + = adjustment ;
}
else
{
* q16horiz + = FloatToFixed ( adjustment ) ;
}
}
void playerSetHoriz ( fixed_t * q16horiz , fixed_t * helper , double adjustment )
{
if ( ! cl_syncinput )
{
// Add slight offset if adjustment is coming in as absolute 0.
2020-09-21 05:51:33 +00:00
if ( adjustment = = 0 ) adjustment + = ( 1. / ( FRACUNIT > > 1 ) ) ;
2020-09-21 03:41:16 +00:00
* helper = FloatToFixed ( adjustment ) ;
}
else
{
* q16horiz = FloatToFixed ( adjustment ) ;
}
}
//---------------------------------------------------------------------------
//
// Player's ticrate helper processor.
//
//---------------------------------------------------------------------------
void playerProcessHelpers ( fixed_t * q16ang , double * angAdjust , fixed_t * angTarget , fixed_t * q16horiz , double * horizAdjust , fixed_t * horizTarget , double const scaleAdjust )
{
// Process angle amendments from the game's ticker.
if ( * angTarget )
{
fixed_t angDelta = getincangleq16 ( * q16ang , * angTarget ) ;
* q16ang = ( * q16ang + xs_CRoundToInt ( scaleAdjust * angDelta ) ) ;
if ( abs ( * q16ang - * angTarget ) < FRACUNIT )
{
* q16ang = * angTarget ;
* angTarget = 0 ;
}
}
else if ( * angAdjust )
{
* q16ang = ( * q16ang + FloatToFixed ( scaleAdjust * * angAdjust ) ) & 0x7FFFFFF ;
}
// Process horizon amendments from the game's ticker.
if ( * horizTarget )
{
fixed_t horizDelta = * horizTarget - * q16horiz ;
* q16horiz + = xs_CRoundToInt ( scaleAdjust * horizDelta ) ;
if ( abs ( * q16horiz - * horizTarget ) < FRACUNIT )
{
* q16horiz = * horizTarget ;
* horizTarget = 0 ;
}
}
else if ( * horizAdjust )
{
* q16horiz + = FloatToFixed ( scaleAdjust * * horizAdjust ) ;
}
}