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 "control.h"
# include "keyboard.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 16:50:08 +00:00
# include "_control.h"
# include "control.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-05 19:07:16 +00:00
# ifndef NETCODE_DISABLE
# include "enet.h"
# endif
2019-10-25 22:32:49 +00:00
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-10-26 17:24:46 +00:00
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 " ) ;
}
auto v = Args - > CheckValue ( " -port " ) ;
if ( v ) netPort = strtol ( v , nullptr , 0 ) ;
netServerMode = Args - > CheckParm ( " -server " ) ;
netServerAddress = Args - > CheckValue ( " -connect " ) ;
netPassword = Args - > CheckValue ( " -password " ) ;
v = Args - > CheckValue ( " -addon " ) ;
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-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
}
2019-12-12 23:19:34 +00:00
namespace Powerslave
{
: : 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
}
2019-12-12 23:19:34 +00:00
else if ( flags & GAMEFLAG_PSEXHUMED )
{
gi = Powerslave : : 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-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 ) ;
# ifndef NETCODE_DISABLE
gHaveNetworking = ! enet_initialize ( ) ;
if ( ! gHaveNetworking )
initprintf ( " An error occurred while initializing ENet. \n " ) ;
# endif
int r ;
try
{
r = CONFIG_Init ( ) ;
}
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-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-10-26 17:24:46 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-31 23:32:56 +00:00
int CONFIG_Init ( )
2019-10-26 11:16:32 +00:00
{
2019-10-28 21:19:50 +00:00
SetClipshapes ( ) ;
2019-10-26 11:16:32 +00:00
// This must be done before initializing any data, so doing it late in the startup process won't work.
2019-11-01 18:25:42 +00:00
if ( CONTROL_Startup ( controltype_keyboardandmouse , BGetTime , 120 ) )
2019-10-26 11:16:32 +00:00
{
2019-10-31 23:32:56 +00:00
return 1 ;
2019-10-26 11:16:32 +00:00
}
2019-11-02 13:25:35 +00:00
2019-10-28 21:19:50 +00:00
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-10-26 11:16:32 +00:00
CONTROL_ClearAssignments ( ) ;
2019-10-26 21:45:55 +00:00
CONFIG_InitMouseAndController ( ) ;
CONFIG_SetGameControllerDefaultsStandard ( ) ;
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-05 19:07:16 +00:00
V_InitFonts ( ) ;
2019-11-09 21:22:51 +00:00
buttonMap . SetGameAliases ( ) ;
2019-11-11 00:01:18 +00:00
Mus_Init ( ) ;
2019-11-14 20:07:43 +00:00
InitStatistics ( ) ;
2019-11-02 19:13:00 +00:00
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 ;
2019-11-24 09:03:19 +00:00
try
{
sc . Open ( " demolition/combatmacros.txt " ) ;
for ( auto s : CombatMacros )
{
sc . MustGetToken ( TK_StringConst ) ;
if ( strlen ( * s ) = = 0 )
* s = sc . String ;
}
}
catch ( const std : : runtime_error & )
2019-10-27 07:14:58 +00:00
{
2019-11-24 09:03:19 +00:00
// We do not want this to error out. Just ignore if it fails.
2019-10-27 07:14:58 +00:00
}
}
2019-10-26 19:50:49 +00:00
//==========================================================================
//
//
//
//==========================================================================
2019-10-27 12:40:24 +00:00
static FString CONFIG_GetMD4EntryName ( uint8_t const * const md4 )
{
return FStringf ( " MD4_%08x%08x%08x%08x " ,
B_BIG32 ( B_UNBUF32 ( & md4 [ 0 ] ) ) , B_BIG32 ( B_UNBUF32 ( & md4 [ 4 ] ) ) ,
B_BIG32 ( B_UNBUF32 ( & md4 [ 8 ] ) ) , B_BIG32 ( B_UNBUF32 ( & md4 [ 12 ] ) ) ) ;
}
int32_t CONFIG_GetMapBestTime ( char const * const mapname , uint8_t const * const mapmd4 )
{
auto m = CONFIG_GetMD4EntryName ( mapmd4 ) ;
if ( GameConfig - > SetSection ( " MapTimes " ) )
{
auto s = GameConfig - > GetValueForKey ( m ) ;
if ( s ) ( int ) strtoull ( s , nullptr , 0 ) ;
}
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 19:50:49 +00:00
int32_t MouseDigitalFunctions [ MAXMOUSEAXES ] [ 2 ] ;
int32_t MouseAnalogueAxes [ MAXMOUSEAXES ] ;
int32_t JoystickFunctions [ MAXJOYBUTTONSANDHATS ] [ 2 ] ;
int32_t JoystickDigitalFunctions [ MAXJOYAXES ] [ 2 ] ;
int32_t JoystickAnalogueAxes [ MAXJOYAXES ] ;
int32_t JoystickAnalogueScale [ MAXJOYAXES ] ;
int32_t JoystickAnalogueDead [ MAXJOYAXES ] ;
int32_t JoystickAnalogueSaturate [ MAXJOYAXES ] ;
int32_t JoystickAnalogueInvert [ MAXJOYAXES ] ;
2019-10-26 21:45:55 +00:00
static const char * mouseanalogdefaults [ MAXMOUSEAXES ] =
{
" analog_turning " ,
" analog_moving " ,
} ;
static const char * mousedigitaldefaults [ MAXMOUSEDIGITAL ] =
{
} ;
static const char * joystickdefaults [ MAXJOYBUTTONSANDHATS ] =
{
" Fire " ,
" Strafe " ,
" Run " ,
" Open " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" Aim_Down " ,
" Look_Right " ,
" Aim_Up " ,
" Look_Left " ,
} ;
static const char * joystickclickeddefaults [ MAXJOYBUTTONSANDHATS ] =
{
" " ,
" Inventory " ,
" Jump " ,
" Crouch " ,
} ;
static const char * joystickanalogdefaults [ MAXJOYAXES ] =
{
" analog_turning " ,
" analog_moving " ,
" analog_strafing " ,
} ;
static const char * joystickdigitaldefaults [ MAXJOYDIGITAL ] =
{
" " ,
" " ,
" " ,
" " ,
" " ,
" " ,
" Run " ,
} ;
2019-10-26 19:50:49 +00:00
//==========================================================================
//
//
//
//==========================================================================
int32_t CONFIG_AnalogNameToNum ( const char * func )
{
if ( ! func )
return - 1 ;
if ( ! Bstrcasecmp ( func , " analog_turning " ) )
{
return analog_turning ;
}
if ( ! Bstrcasecmp ( func , " analog_strafing " ) )
{
return analog_strafing ;
}
if ( ! Bstrcasecmp ( func , " analog_moving " ) )
{
return analog_moving ;
}
if ( ! Bstrcasecmp ( func , " analog_lookingupanddown " ) )
{
return analog_lookingupanddown ;
}
return - 1 ;
}
//==========================================================================
//
//
//
//==========================================================================
const char * CONFIG_AnalogNumToName ( int32_t func )
{
switch ( func )
{
case analog_turning :
return " analog_turning " ;
case analog_strafing :
return " analog_strafing " ;
case analog_moving :
return " analog_moving " ;
case analog_lookingupanddown :
return " analog_lookingupanddown " ;
}
return NULL ;
}
2019-10-26 21:45:55 +00:00
void CONFIG_SetupMouse ( void )
{
2019-10-28 06:05:32 +00:00
CONTROL_MouseEnabled = ( in_mouse & & CONTROL_MousePresent ) ;
2019-10-26 21:45:55 +00:00
}
void CONFIG_SetupJoystick ( void )
{
const char * val ;
FString section = currentGame + " .ControllerSettings " ;
if ( ! GameConfig - > SetSection ( section ) ) return ;
for ( int i = 0 ; i < MAXJOYBUTTONSANDHATS ; i + + )
{
section . Format ( " ControllerButton%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
2019-11-04 22:01:50 +00:00
JoystickFunctions [ i ] [ 0 ] = buttonMap . FindButtonIndex ( val ) ;
2019-10-26 21:45:55 +00:00
section . Format ( " ControllerButtonClicked%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
2019-11-04 22:01:50 +00:00
JoystickFunctions [ i ] [ 1 ] = buttonMap . FindButtonIndex ( val ) ;
2019-10-26 21:45:55 +00:00
}
// map over the axes
for ( int i = 0 ; i < MAXJOYAXES ; i + + )
{
section . Format ( " ControllerAnalogAxes%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
JoystickAnalogueAxes [ i ] = CONFIG_AnalogNameToNum ( val ) ;
section . Format ( " ControllerDigitalAxes%d_0 " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
2019-11-04 22:01:50 +00:00
JoystickDigitalFunctions [ i ] [ 0 ] = buttonMap . FindButtonIndex ( val ) ;
2019-10-26 21:45:55 +00:00
section . Format ( " ControllerDigitalAxes%d_1 " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
2019-11-04 22:01:50 +00:00
JoystickDigitalFunctions [ i ] [ 1 ] = buttonMap . FindButtonIndex ( val ) ;
2019-10-26 21:45:55 +00:00
section . Format ( " ControllerAnalogScale%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
JoystickAnalogueScale [ i ] = ( int32_t ) strtoull ( val , nullptr , 0 ) ;
section . Format ( " ControllerAnalogInvert%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
JoystickAnalogueInvert [ i ] = ( int32_t ) strtoull ( val , nullptr , 0 ) ;
section . Format ( " ControllerAnalogDead%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
JoystickAnalogueDead [ i ] = ( int32_t ) strtoull ( val , nullptr , 0 ) ;
section . Format ( " ControllerAnalogSaturate%d " , i ) ;
val = GameConfig - > GetValueForKey ( section ) ;
if ( val )
JoystickAnalogueSaturate [ i ] = ( int32_t ) strtoull ( val , nullptr , 0 ) ;
}
for ( int i = 0 ; i < MAXJOYAXES ; i + + )
{
CONTROL_MapAnalogAxis ( i , JoystickAnalogueAxes [ i ] , controldevice_joystick ) ;
CONTROL_MapDigitalAxis ( i , JoystickDigitalFunctions [ i ] [ 0 ] , 0 , controldevice_joystick ) ;
CONTROL_MapDigitalAxis ( i , JoystickDigitalFunctions [ i ] [ 1 ] , 1 , controldevice_joystick ) ;
CONTROL_SetAnalogAxisScale ( i , JoystickAnalogueScale [ i ] , controldevice_joystick ) ;
CONTROL_SetAnalogAxisInvert ( i , JoystickAnalogueInvert [ i ] , controldevice_joystick ) ;
}
2019-10-28 06:05:32 +00:00
CONTROL_JoystickEnabled = ( in_joystick & & CONTROL_JoyPresent ) ;
// JBF 20040215: evil and nasty place to do this, but joysticks are evil and nasty too
for ( int i = 0 ; i < joystick . numAxes ; i + + )
joySetDeadZone ( i , JoystickAnalogueDead [ i ] , JoystickAnalogueSaturate [ i ] ) ;
2019-10-26 21:45:55 +00:00
}
static void CONFIG_SetJoystickButtonFunction ( int i , int j , int function )
{
JoystickFunctions [ i ] [ j ] = function ;
2019-11-10 14:15:14 +00:00
//CONTROL_MapButton(function, i, j, controldevice_joystick);
2019-10-26 21:45:55 +00:00
}
static void CONFIG_SetJoystickAnalogAxisScale ( int i , int scale )
{
JoystickAnalogueScale [ i ] = scale ;
CONTROL_SetAnalogAxisScale ( i , scale , controldevice_joystick ) ;
}
static void CONFIG_SetJoystickAnalogAxisInvert ( int i , int invert )
{
JoystickAnalogueInvert [ i ] = invert ;
CONTROL_SetAnalogAxisInvert ( i , invert , controldevice_joystick ) ;
}
static void CONFIG_SetJoystickAnalogAxisDeadSaturate ( int i , int dead , int saturate )
{
JoystickAnalogueDead [ i ] = dead ;
JoystickAnalogueSaturate [ i ] = saturate ;
joySetDeadZone ( i , dead , saturate ) ;
}
static void CONFIG_SetJoystickDigitalAxisFunction ( int i , int j , int function )
{
JoystickDigitalFunctions [ i ] [ j ] = function ;
CONTROL_MapDigitalAxis ( i , function , j , controldevice_joystick ) ;
}
static void CONFIG_SetJoystickAnalogAxisFunction ( int i , int function )
{
JoystickAnalogueAxes [ i ] = function ;
CONTROL_MapAnalogAxis ( i , function , controldevice_joystick ) ;
}
struct GameControllerButtonSetting
{
GameControllerButton button ;
int function ;
void apply ( ) const
{
CONFIG_SetJoystickButtonFunction ( button , 0 , function ) ;
}
} ;
struct GameControllerAnalogAxisSetting
{
GameControllerAxis axis ;
int function ;
void apply ( ) const
{
CONFIG_SetJoystickAnalogAxisFunction ( axis , function ) ;
}
} ;
struct GameControllerDigitalAxisSetting
{
GameControllerAxis axis ;
int polarity ;
int function ;
void apply ( ) const
{
CONFIG_SetJoystickDigitalAxisFunction ( axis , polarity , function ) ;
}
} ;
void CONFIG_SetGameControllerDefaultsClear ( )
{
for ( int i = 0 ; i < MAXJOYBUTTONSANDHATS ; i + + )
{
CONFIG_SetJoystickButtonFunction ( i , 0 , - 1 ) ;
CONFIG_SetJoystickButtonFunction ( i , 1 , - 1 ) ;
}
for ( int i = 0 ; i < MAXJOYAXES ; i + + )
{
CONFIG_SetJoystickAnalogAxisScale ( i , DEFAULTJOYSTICKANALOGUESCALE ) ;
CONFIG_SetJoystickAnalogAxisInvert ( i , 0 ) ;
CONFIG_SetJoystickAnalogAxisDeadSaturate ( i , DEFAULTJOYSTICKANALOGUEDEAD , DEFAULTJOYSTICKANALOGUESATURATE ) ;
CONFIG_SetJoystickDigitalAxisFunction ( i , 0 , - 1 ) ;
CONFIG_SetJoystickDigitalAxisFunction ( i , 1 , - 1 ) ;
CONFIG_SetJoystickAnalogAxisFunction ( i , - 1 ) ;
}
}
static void CONFIG_SetGameControllerAxesModern ( )
{
static GameControllerAnalogAxisSetting const analogAxes [ ] =
{
{ GAMECONTROLLER_AXIS_LEFTX , analog_strafing } ,
{ GAMECONTROLLER_AXIS_LEFTY , analog_moving } ,
{ GAMECONTROLLER_AXIS_RIGHTX , analog_turning } ,
{ GAMECONTROLLER_AXIS_RIGHTY , analog_lookingupanddown } ,
} ;
CONFIG_SetJoystickAnalogAxisScale ( GAMECONTROLLER_AXIS_RIGHTX , 32768 + 16384 ) ;
CONFIG_SetJoystickAnalogAxisScale ( GAMECONTROLLER_AXIS_RIGHTY , 32768 + 16384 ) ;
for ( auto const & analogAxis : analogAxes )
analogAxis . apply ( ) ;
}
void CONFIG_SetGameControllerDefaultsStandard ( )
{
CONFIG_SetGameControllerDefaultsClear ( ) ;
CONFIG_SetGameControllerAxesModern ( ) ;
static GameControllerButtonSetting const buttons [ ] =
{
{ GAMECONTROLLER_BUTTON_A , gamefunc_Jump } ,
{ GAMECONTROLLER_BUTTON_B , gamefunc_Toggle_Crouch } ,
{ GAMECONTROLLER_BUTTON_BACK , gamefunc_Map } ,
{ GAMECONTROLLER_BUTTON_LEFTSTICK , gamefunc_Run } ,
{ GAMECONTROLLER_BUTTON_RIGHTSTICK , gamefunc_Quick_Kick } ,
{ GAMECONTROLLER_BUTTON_LEFTSHOULDER , gamefunc_Crouch } ,
{ GAMECONTROLLER_BUTTON_RIGHTSHOULDER , gamefunc_Jump } ,
{ GAMECONTROLLER_BUTTON_DPAD_UP , gamefunc_Previous_Weapon } ,
{ GAMECONTROLLER_BUTTON_DPAD_DOWN , gamefunc_Next_Weapon } ,
} ;
static GameControllerButtonSetting const buttonsDuke [ ] =
{
{ GAMECONTROLLER_BUTTON_X , gamefunc_Open } ,
{ GAMECONTROLLER_BUTTON_Y , gamefunc_Inventory } ,
{ GAMECONTROLLER_BUTTON_DPAD_LEFT , gamefunc_Inventory_Left } ,
{ GAMECONTROLLER_BUTTON_DPAD_RIGHT , gamefunc_Inventory_Right } ,
} ;
static GameControllerButtonSetting const buttonsFury [ ] =
{
{ GAMECONTROLLER_BUTTON_X , gamefunc_Steroids } , // Reload
{ GAMECONTROLLER_BUTTON_Y , gamefunc_Open } ,
{ GAMECONTROLLER_BUTTON_DPAD_LEFT , gamefunc_MedKit } ,
{ GAMECONTROLLER_BUTTON_DPAD_RIGHT , gamefunc_NightVision } , // Radar
} ;
static GameControllerDigitalAxisSetting const digitalAxes [ ] =
{
{ GAMECONTROLLER_AXIS_TRIGGERLEFT , 1 , gamefunc_Alt_Fire } ,
{ GAMECONTROLLER_AXIS_TRIGGERRIGHT , 1 , gamefunc_Fire } ,
} ;
for ( auto const & button : buttons )
button . apply ( ) ;
/*
if ( FURY )
{
for ( auto const & button : buttonsFury )
button . apply ( ) ;
}
else
*/
{
for ( auto const & button : buttonsDuke )
button . apply ( ) ;
}
for ( auto const & digitalAxis : digitalAxes )
digitalAxis . apply ( ) ;
}
void CONFIG_SetGameControllerDefaultsPro ( )
{
CONFIG_SetGameControllerDefaultsClear ( ) ;
CONFIG_SetGameControllerAxesModern ( ) ;
static GameControllerButtonSetting const buttons [ ] =
{
{ GAMECONTROLLER_BUTTON_A , gamefunc_Open } ,
{ GAMECONTROLLER_BUTTON_B , gamefunc_Third_Person_View } ,
{ GAMECONTROLLER_BUTTON_Y , gamefunc_Quick_Kick } ,
{ GAMECONTROLLER_BUTTON_BACK , gamefunc_Map } ,
{ GAMECONTROLLER_BUTTON_LEFTSTICK , gamefunc_Run } ,
{ GAMECONTROLLER_BUTTON_RIGHTSTICK , gamefunc_Crouch } ,
{ GAMECONTROLLER_BUTTON_DPAD_UP , gamefunc_Previous_Weapon } ,
{ GAMECONTROLLER_BUTTON_DPAD_DOWN , gamefunc_Next_Weapon } ,
} ;
static GameControllerButtonSetting const buttonsDuke [ ] =
{
{ GAMECONTROLLER_BUTTON_X , gamefunc_Inventory } ,
{ GAMECONTROLLER_BUTTON_LEFTSHOULDER , gamefunc_Previous_Weapon } ,
{ GAMECONTROLLER_BUTTON_RIGHTSHOULDER , gamefunc_Next_Weapon } ,
{ GAMECONTROLLER_BUTTON_DPAD_LEFT , gamefunc_Inventory_Left } ,
{ GAMECONTROLLER_BUTTON_DPAD_RIGHT , gamefunc_Inventory_Right } ,
} ;
static GameControllerButtonSetting const buttonsFury [ ] =
{
{ GAMECONTROLLER_BUTTON_X , gamefunc_Steroids } , // Reload
{ GAMECONTROLLER_BUTTON_LEFTSHOULDER , gamefunc_Crouch } ,
{ GAMECONTROLLER_BUTTON_RIGHTSHOULDER , gamefunc_Alt_Fire } ,
{ GAMECONTROLLER_BUTTON_DPAD_LEFT , gamefunc_MedKit } ,
{ GAMECONTROLLER_BUTTON_DPAD_RIGHT , gamefunc_NightVision } , // Radar
} ;
static GameControllerDigitalAxisSetting const digitalAxes [ ] =
{
{ GAMECONTROLLER_AXIS_TRIGGERLEFT , 1 , gamefunc_Jump } ,
{ GAMECONTROLLER_AXIS_TRIGGERRIGHT , 1 , gamefunc_Fire } ,
} ;
for ( auto const & button : buttons )
button . apply ( ) ;
#if 0 // ouch...
if ( FURY )
{
for ( auto const & button : buttonsFury )
button . apply ( ) ;
}
else
# endif
{
for ( auto const & button : buttonsDuke )
button . apply ( ) ;
}
for ( auto const & digitalAxis : digitalAxes )
digitalAxis . apply ( ) ;
}
2019-11-03 19:24:50 +00:00
FString CONFIG_GetGameFuncOnKeyboard ( int gameFunc )
2019-10-26 21:45:55 +00:00
{
2019-11-04 22:01:50 +00:00
auto binding = buttonMap . GetButtonAlias ( gameFunc ) ;
2019-11-03 19:24:50 +00:00
auto keys = Bindings . GetKeysForCommand ( binding ) ;
for ( auto key : keys )
{
if ( key < KEY_FIRSTMOUSEBUTTON )
{
auto scan = KB_ScanCodeToString ( key ) ;
if ( scan ) return scan ;
}
}
return " " ;
2019-10-26 21:45:55 +00:00
}
2019-11-03 19:24:50 +00:00
FString CONFIG_GetGameFuncOnMouse ( int gameFunc )
2019-10-26 21:45:55 +00:00
{
2019-11-04 22:01:50 +00:00
auto binding = buttonMap . GetButtonAlias ( gameFunc ) ;
2019-11-03 19:24:50 +00:00
auto keys = Bindings . GetKeysForCommand ( binding ) ;
for ( auto key : keys )
{
2019-11-03 23:53:55 +00:00
if ( ( key > = KEY_FIRSTMOUSEBUTTON & & key < KEY_FIRSTJOYBUTTON ) | | ( key > = KEY_MWHEELUP & & key < = KEY_MWHEELLEFT ) )
2019-11-03 19:24:50 +00:00
{
auto scan = KB_ScanCodeToString ( key ) ;
if ( scan ) return scan ;
2019-10-26 21:45:55 +00:00
2019-11-03 19:24:50 +00:00
}
}
2019-10-26 21:45:55 +00:00
return " " ;
}
char const * CONFIG_GetGameFuncOnJoystick ( int gameFunc )
{
2019-11-04 22:01:50 +00:00
auto binding = buttonMap . GetButtonAlias ( gameFunc ) ;
2019-11-03 19:24:50 +00:00
auto keys = Bindings . GetKeysForCommand ( binding ) ;
for ( auto key : keys )
{
2019-11-03 23:53:55 +00:00
if ( key > = KEY_FIRSTJOYBUTTON & & ! ( key > = KEY_MWHEELUP & & key < = KEY_MWHEELLEFT ) )
2019-11-03 19:24:50 +00:00
{
auto scan = KB_ScanCodeToString ( key ) ;
if ( scan ) return scan ;
}
}
2019-10-28 16:50:08 +00:00
return " " ;
}
// FIXME: Consider the mouse as well!
FString CONFIG_GetBoundKeyForLastInput ( int gameFunc )
{
if ( CONTROL_LastSeenInput = = LastSeenInput : : Joystick )
{
2019-11-03 19:24:50 +00:00
FString name = CONFIG_GetGameFuncOnJoystick ( gameFunc ) ;
if ( name . IsNotEmpty ( ) )
2019-10-28 16:50:08 +00:00
{
2019-11-03 19:24:50 +00:00
return name ;
2019-10-28 16:50:08 +00:00
}
2019-11-03 19:24:50 +00:00
}
2019-10-28 16:50:08 +00:00
2019-11-03 19:24:50 +00:00
FString name = CONFIG_GetGameFuncOnKeyboard ( gameFunc ) ;
if ( name . IsNotEmpty ( ) )
{
return name ;
2019-10-28 16:50:08 +00:00
}
2019-11-03 19:24:50 +00:00
name = CONFIG_GetGameFuncOnMouse ( gameFunc ) ;
if ( name . IsNotEmpty ( ) )
2019-10-28 16:50:08 +00:00
{
2019-11-03 19:24:50 +00:00
return name ;
}
2019-10-28 16:50:08 +00:00
2019-11-03 19:24:50 +00:00
name = CONFIG_GetGameFuncOnJoystick ( gameFunc ) ;
if ( name . IsNotEmpty ( ) )
{
return name ;
2019-10-28 16:50:08 +00:00
}
2019-10-28 21:19:50 +00:00
return " UNBOUND " ;
2019-10-28 16:50:08 +00:00
}
2019-10-26 21:45:55 +00:00
void CONFIG_InitMouseAndController ( )
{
memset ( MouseDigitalFunctions , - 1 , sizeof ( MouseDigitalFunctions ) ) ;
memset ( JoystickFunctions , - 1 , sizeof ( JoystickFunctions ) ) ;
memset ( JoystickDigitalFunctions , - 1 , sizeof ( JoystickDigitalFunctions ) ) ;
for ( int i = 0 ; i < MAXMOUSEAXES ; i + + )
{
2019-11-04 22:01:50 +00:00
MouseDigitalFunctions [ i ] [ 0 ] = buttonMap . FindButtonIndex ( mousedigitaldefaults [ i * 2 ] ) ;
MouseDigitalFunctions [ i ] [ 1 ] = buttonMap . FindButtonIndex ( mousedigitaldefaults [ i * 2 + 1 ] ) ;
2019-10-26 21:45:55 +00:00
CONTROL_MapDigitalAxis ( i , MouseDigitalFunctions [ i ] [ 0 ] , 0 , controldevice_mouse ) ;
CONTROL_MapDigitalAxis ( i , MouseDigitalFunctions [ i ] [ 1 ] , 1 , controldevice_mouse ) ;
MouseAnalogueAxes [ i ] = CONFIG_AnalogNameToNum ( mouseanalogdefaults [ i ] ) ;
CONTROL_MapAnalogAxis ( i , MouseAnalogueAxes [ i ] , controldevice_mouse ) ;
}
CONFIG_SetupMouse ( ) ;
CONFIG_SetupJoystick ( ) ;
2019-11-03 23:53:55 +00:00
inputState . ClearKeysDown ( ) ;
inputState . keyFlushChars ( ) ;
inputState . keyFlushScans ( ) ;
2019-10-26 21:45:55 +00:00
}
2019-10-28 16:50:08 +00:00
2019-10-26 21:45:55 +00:00
void CONFIG_PutNumber ( const char * key , int number )
{
FStringf str ( " %d " , number ) ;
GameConfig - > SetValueForKey ( key , str ) ;
}
void CONFIG_WriteControllerSettings ( )
{
FString buf ;
if ( in_joystick )
{
FString section = currentGame + " .ControllerSettings " ;
GameConfig - > SetSection ( section ) ;
for ( int dummy = 0 ; dummy < MAXJOYBUTTONSANDHATS ; dummy + + )
{
2019-11-04 22:01:50 +00:00
if ( buttonMap . GetButtonName ( JoystickFunctions [ dummy ] [ 0 ] ) )
2019-10-26 21:45:55 +00:00
{
buf . Format ( " ControllerButton%d " , dummy ) ;
2019-11-04 22:01:50 +00:00
GameConfig - > SetValueForKey ( buf , buttonMap . GetButtonName ( JoystickFunctions [ dummy ] [ 0 ] ) ) ;
2019-10-26 21:45:55 +00:00
}
2019-11-04 22:01:50 +00:00
if ( buttonMap . GetButtonName ( JoystickFunctions [ dummy ] [ 1 ] ) )
2019-10-26 21:45:55 +00:00
{
buf . Format ( " ControllerButtonClicked%d " , dummy ) ;
2019-11-04 22:01:50 +00:00
GameConfig - > SetValueForKey ( buf , buttonMap . GetButtonName ( JoystickFunctions [ dummy ] [ 1 ] ) ) ;
2019-10-26 21:45:55 +00:00
}
}
for ( int dummy = 0 ; dummy < MAXJOYAXES ; dummy + + )
{
if ( CONFIG_AnalogNumToName ( JoystickAnalogueAxes [ dummy ] ) )
{
buf . Format ( " ControllerAnalogAxes%d " , dummy ) ;
GameConfig - > SetValueForKey ( buf , CONFIG_AnalogNumToName ( JoystickAnalogueAxes [ dummy ] ) ) ;
}
2019-11-04 22:01:50 +00:00
if ( buttonMap . GetButtonName ( JoystickDigitalFunctions [ dummy ] [ 0 ] ) )
2019-10-26 21:45:55 +00:00
{
buf . Format ( " ControllerDigitalAxes%d_0 " , dummy ) ;
2019-11-04 22:01:50 +00:00
GameConfig - > SetValueForKey ( buf , buttonMap . GetButtonName ( JoystickDigitalFunctions [ dummy ] [ 0 ] ) ) ;
2019-10-26 21:45:55 +00:00
}
2019-11-04 22:01:50 +00:00
if ( buttonMap . GetButtonName ( JoystickDigitalFunctions [ dummy ] [ 1 ] ) )
2019-10-26 21:45:55 +00:00
{
buf . Format ( " ControllerDigitalAxes%d_1 " , dummy ) ;
2019-11-04 22:01:50 +00:00
GameConfig - > SetValueForKey ( buf , buttonMap . GetButtonName ( JoystickDigitalFunctions [ dummy ] [ 1 ] ) ) ;
2019-10-26 21:45:55 +00:00
}
buf . Format ( " ControllerAnalogScale%d " , dummy ) ;
CONFIG_PutNumber ( buf , JoystickAnalogueScale [ dummy ] ) ;
buf . Format ( " ControllerAnalogInvert%d " , dummy ) ;
CONFIG_PutNumber ( buf , JoystickAnalogueInvert [ dummy ] ) ;
buf . Format ( " ControllerAnalogDead%d " , dummy ) ;
CONFIG_PutNumber ( buf , JoystickAnalogueDead [ dummy ] ) ;
buf . Format ( " ControllerAnalogSaturate%d " , dummy ) ;
CONFIG_PutNumber ( buf , JoystickAnalogueSaturate [ dummy ] ) ;
}
}
}