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 .
*/
//-------------------------------------------------------------------------
2019-11-01 18:25:42 +00:00
# include <filesystem>
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"
2019-11-11 00:01:18 +00:00
# include "z_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"
2019-11-05 19:07:16 +00:00
# ifndef NETCODE_DISABLE
# include "enet.h"
# endif
2019-10-25 22:32:49 +00:00
2019-12-10 21:22:59 +00:00
MapRecord mapList [ 512 ] ; // Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts.
MapRecord * currentLevel ; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
MapRecord * lastLevel ; // Same here, for the last level.
MapRecord userMapRecord ; // stand-in for the user map.
2019-12-09 23:01:45 +00:00
2019-12-02 01:07:32 +00:00
void C_CON_SetAliases ( ) ;
2019-10-27 23:24:09 +00:00
InputState inputState ;
2019-10-28 21:19:50 +00:00
void SetClipshapes ( ) ;
2019-10-31 22:25:21 +00:00
int ShowStartupWindow ( TArray < GrpEntry > & ) ;
2019-11-01 18:25:42 +00:00
void InitFileSystem ( TArray < GrpEntry > & ) ;
2019-11-05 19:07:16 +00:00
bool gHaveNetworking ;
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
CVAR ( Int , cl_defaultconfiguration , 2 , CVAR_ARCHIVE | CVAR_GLOBALCONFIG )
2019-10-25 22:32:49 +00:00
2019-10-27 07:14:58 +00:00
2019-10-26 11:16:32 +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 " ) )
{
initprintf ( " Build-format config files not supported and will be ignored \n " ) ;
}
2019-12-11 22:41:05 +00:00
#if 0 // MP disabled pending evaluation
2019-10-28 21:19:50 +00:00
auto v = Args - > CheckValue ( " -port " ) ;
if ( v ) netPort = strtol ( v , nullptr , 0 ) ;
netServerMode = Args - > CheckParm ( " -server " ) ;
netServerAddress = Args - > CheckValue ( " -connect " ) ;
netPassword = Args - > CheckValue ( " -password " ) ;
2019-12-11 22:41:05 +00:00
# endif
2019-10-28 21:19:50 +00:00
2019-12-11 22:41:05 +00:00
auto v = Args - > CheckValue ( " -addon " ) ;
2019-10-28 21:19:50 +00:00
if ( v )
{
auto val = strtol ( v , nullptr , 0 ) ;
static const char * const addons [ ] = { " DUKE3D.GRP " , " DUKEDC.GRP " , " NWINTER.GRP " , " VACATION.GRP " } ;
if ( val > 0 & & val < 4 ) gamegrp = addons [ val ] ;
else initprintf ( " %s: Unknown Addon \n " , v ) ;
}
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 ) ) ;
}
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 " ) ;
static const char * cons [ ] = { " -con " , " -x " , nullptr } ;
Args - > CollectFiles ( " -con " , cons , " .con " ) ;
DefaultCon = Args - > CheckValue ( " -con " ) ;
static const char * demos [ ] = { " -playback " , " -d " , " -demo " , nullptr } ;
Args - > CollectFiles ( " -demo " , demos , " .dmo " ) ;
CommandDemo = Args - > CheckValue ( " -demo " ) ;
static const char * names [ ] = { " -pname " , " -name " , nullptr } ;
Args - > CollectFiles ( " -name " , names , " .--- " ) ; // this shouldn't collect any file names at all so use a nonsense extension
CommandName = Args - > CheckValue ( " -name " ) ;
static const char * nomos [ ] = { " -nomonsters " , " -nodudes " , nullptr } ;
Args - > CollectFiles ( " -nomonsters " , nomos , " .--- " ) ; // this shouldn't collect any file names at all so use a nonsense extension
nomonsters = Args - > CheckParm ( " -nomonsters " ) ;
static const char * acons [ ] = { " -addcon " , " -mx " , nullptr } ;
Args - > CollectFiles ( " -addcon " , acons , " .con " ) ;
AddCons . reset ( Args - > GatherFiles ( " -addcon " ) ) ;
static const char * adefs [ ] = { " -adddef " , " -mh " , nullptr } ;
Args - > CollectFiles ( " -adddef " , adefs , " .def " ) ;
AddDefs . reset ( Args - > GatherFiles ( " -adddef " ) ) ;
Args - > CollectFiles ( " -art " , " .art " ) ;
AddArt . reset ( Args - > GatherFiles ( " -art " ) ) ;
CommandIni = Args - > CheckValue ( " -ini " ) ;
nologo = Args - > CheckParm ( " -nologo " ) | | Args - > CheckParm ( " -quick " ) ;
nomusic = Args - > CheckParm ( " -nomusic " ) ;
nosound = Args - > CheckParm ( " -nosfx " ) ;
if ( Args - > CheckParm ( " -nosound " ) ) nomusic = nosound = true ;
if ( Args - > CheckParm ( " -setup " ) ) setupstate = 1 ;
else if ( Args - > CheckParm ( " -nosetup " ) ) setupstate = 0 ;
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-10-31 22:25:21 +00:00
//==========================================================================
//
//
//
//==========================================================================
namespace Duke
{
2019-11-03 11:32:58 +00:00
: : GameInterface * CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
namespace Redneck
{
2019-11-03 11:32:58 +00:00
: : 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
}
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_RR )
{
2019-11-03 11:32:58 +00:00
gi = Redneck : : CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
else if ( flags & GAMEFLAG_SW )
{
2019-11-03 11:32:58 +00:00
gi = ShadowWarrior : : CreateInterface ( ) ;
2019-10-31 22:25:21 +00:00
}
else
{
2019-11-03 11:32:58 +00:00
gi = Duke : : CreateInterface ( ) ;
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 ( )
{
// Set up the console before anything else so that it can receive text.
C_InitConsole ( 1024 , 768 , true ) ;
2019-11-11 00:01:18 +00:00
FStringf logpath ( " logfile %sdemolition.log " , M_GetDocumentsPath ( ) . GetChars ( ) ) ;
2019-11-05 19:07:16 +00:00
C_DoCommand ( logpath ) ;
2019-12-14 16:15:17 +00:00
I_StartupJoysticks ( ) ;
2019-12-14 19:15:15 +00:00
mouseInit ( ) ;
2019-11-05 19:07:16 +00:00
# ifndef NETCODE_DISABLE
gHaveNetworking = ! enet_initialize ( ) ;
if ( ! gHaveNetworking )
initprintf ( " An error occurred while initializing ENet. \n " ) ;
# endif
int r ;
try
{
2019-12-14 16:15:17 +00:00
r = RunGame ( ) ;
2019-11-05 19:07:16 +00:00
}
catch ( const std : : runtime_error & err )
{
wm_msgbox ( " Error " , " %s " , err . what ( ) ) ;
return 3 ;
}
catch ( const ExitEvent & exit )
{
// Just let the rest of the function execute.
r = exit . Reason ( ) ;
}
2019-12-14 18:21:49 +00:00
I_ShutdownInput ( ) ;
2019-11-10 18:42:26 +00:00
G_SaveConfig ( ) ;
2019-11-05 19:07:16 +00:00
# ifndef NETCODE_DISABLE
if ( gHaveNetworking ) enet_deinitialize ( ) ;
# endif
return r ;
}
2019-11-24 23:02:00 +00:00
//==========================================================================
//
2019-12-08 23:33:14 +00:00
//
2019-11-24 23:02:00 +00:00
//
//==========================================================================
2019-12-08 23:33:14 +00:00
# define LOCALIZED_STRING(s) "$" s
2019-11-24 23:02:00 +00:00
void SetDefaultStrings ( )
{
2019-12-08 23:33:14 +00:00
// Blood hard codes its skill names, so we have to define them manually.
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-14 16:15:17 +00:00
int RunGame ( )
2019-10-26 11:16:32 +00:00
{
2019-10-28 21:19:50 +00:00
SetClipshapes ( ) ;
userConfig . ProcessOptions ( ) ;
2019-10-29 18:53:46 +00:00
G_LoadConfig ( ) ;
2019-10-31 22:25:21 +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.
//
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 + + ;
}
}
if ( groupno = = - 1 | | userConfig . setupstate = = 1 )
groupno = ShowStartupWindow ( groups ) ;
if ( groupno = = - 1 ) return 0 ;
auto & group = groups [ groupno ] ;
// 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.
if ( ugroup . FileInfo . rtsname . IsNotEmpty ( ) & & * * rtsname = = 0 ) RTS_Init ( ugroup . FileInfo . rtsname ) ;
// 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-10-28 21:19:50 +00:00
2019-11-02 19:13:00 +00:00
InitFileSystem ( usedgroups ) ;
2019-12-11 22:41:05 +00:00
TArray < FString > addArt ;
for ( auto & grp : usedgroups )
{
for ( auto & art : grp . FileInfo . loadart )
{
addArt . Push ( art ) ;
}
}
TileFiles . AddArt ( addArt ) ;
2019-11-02 19:13:00 +00:00
2019-10-26 21:45:55 +00:00
CONFIG_InitMouseAndController ( ) ;
2019-11-02 19:13:00 +00:00
CONFIG_SetDefaultKeys ( cl_defaultconfiguration = = 1 ? " demolition/origbinds.txt " : cl_defaultconfiguration = = 2 ? " demolition/leftbinds.txt " : " demolition/defbinds.txt " ) ;
2019-11-04 22:01:50 +00:00
2019-11-02 19:13:00 +00:00
G_ReadConfig ( currentGame ) ;
if ( ! GameConfig - > IsInitialized ( ) )
{
CONFIG_ReadCombatMacros ( ) ;
}
if ( userConfig . CommandName . IsNotEmpty ( ) )
{
playername = userConfig . CommandName ;
}
2019-11-23 11:38:38 +00:00
GStrings . LoadStrings ( ) ;
2019-11-05 19:07:16 +00:00
V_InitFonts ( ) ;
2019-12-02 01:07:32 +00:00
C_CON_SetAliases ( ) ;
2019-11-11 00:01:18 +00:00
Mus_Init ( ) ;
2019-11-14 20:07:43 +00:00
InitStatistics ( ) ;
2019-11-21 21:31:46 +00:00
M_Init ( ) ;
2019-11-24 23:02:00 +00:00
SetDefaultStrings ( ) ;
2019-12-10 21:22:59 +00:00
if ( g_gameType & GAMEFLAG_RR ) InitRREndMap ( ) ; // this needs to be done better later
2019-10-31 23:32:56 +00:00
return gi - > app_main ( ) ;
2019-10-25 22:32:49 +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 ;
sc . Open ( " demolition/combatmacros.txt " ) ;
for ( auto s : CombatMacros )
{
sc . MustGetToken ( TK_StringConst ) ;
if ( strlen ( * s ) = = 0 )
* s = sc . String ;
}
}
2019-10-26 19:50:49 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-27 12:40:24 +00:00
static FString CONFIG_GetMD4EntryName ( uint8_t const * const md4 )
{
return FStringf ( " MD4_%08x%08x%08x%08x " ,
B_BIG32 ( B_UNBUF32 ( & md4 [ 0 ] ) ) , B_BIG32 ( B_UNBUF32 ( & md4 [ 4 ] ) ) ,
B_BIG32 ( B_UNBUF32 ( & md4 [ 8 ] ) ) , B_BIG32 ( B_UNBUF32 ( & md4 [ 12 ] ) ) ) ;
}
int32_t CONFIG_GetMapBestTime ( char const * const mapname , uint8_t const * const mapmd4 )
{
auto m = CONFIG_GetMD4EntryName ( mapmd4 ) ;
if ( GameConfig - > SetSection ( " MapTimes " ) )
{
auto s = GameConfig - > GetValueForKey ( m ) ;
2019-12-02 01:07:32 +00:00
if ( s ) return ( int ) strtoull ( s , nullptr , 0 ) ;
2019-10-27 12:40:24 +00:00
}
return - 1 ;
}
int CONFIG_SetMapBestTime ( uint8_t const * const mapmd4 , int32_t tm )
{
FStringf t ( " %d " , tm ) ;
auto m = CONFIG_GetMD4EntryName ( mapmd4 ) ;
if ( GameConfig - > SetSection ( " MapTimes " ) )
{
GameConfig - > SetValueForKey ( m , t ) ;
}
return 0 ;
}
2019-10-26 21:45:55 +00:00
void CONFIG_InitMouseAndController ( )
{
2019-11-03 23:53:55 +00:00
inputState . ClearKeysDown ( ) ;
inputState . keyFlushChars ( ) ;
inputState . keyFlushScans ( ) ;
2019-10-26 21:45:55 +00:00
}