2022-02-27 07:53:44 -05:00
# include <STDInclude.hpp>
2021-01-02 01:49:45 -05:00
2021-09-11 10:34:04 -04:00
namespace Components
2021-08-21 18:04:30 -04:00
{
2022-05-03 11:59:51 -04:00
Game : : ButtonToCodeMap_t Gamepad : : buttonList [ ]
{
{ Game : : GPAD_X , Game : : K_BUTTON_X } ,
{ Game : : GPAD_A , Game : : K_BUTTON_A } ,
{ Game : : GPAD_B , Game : : K_BUTTON_B } ,
{ Game : : GPAD_Y , Game : : K_BUTTON_Y } ,
{ Game : : GPAD_L_TRIG , Game : : K_BUTTON_LTRIG } ,
{ Game : : GPAD_R_TRIG , Game : : K_BUTTON_RTRIG } ,
{ Game : : GPAD_L_SHLDR , Game : : K_BUTTON_LSHLDR } ,
{ Game : : GPAD_R_SHLDR , Game : : K_BUTTON_RSHLDR } ,
{ Game : : GPAD_START , Game : : K_BUTTON_START } ,
{ Game : : GPAD_BACK , Game : : K_BUTTON_BACK } ,
{ Game : : GPAD_L3 , Game : : K_BUTTON_LSTICK } ,
{ Game : : GPAD_R3 , Game : : K_BUTTON_RSTICK } ,
{ Game : : GPAD_UP , Game : : K_DPAD_UP } ,
{ Game : : GPAD_DOWN , Game : : K_DPAD_DOWN } ,
{ Game : : GPAD_LEFT , Game : : K_DPAD_LEFT } ,
{ Game : : GPAD_RIGHT , Game : : K_DPAD_RIGHT }
} ;
Game : : StickToCodeMap_t Gamepad : : analogStickList [ 4 ]
{
{ Game : : GPAD_LX , Game : : K_APAD_RIGHT , Game : : K_APAD_LEFT } ,
{ Game : : GPAD_LY , Game : : K_APAD_UP , Game : : K_APAD_DOWN } ,
{ Game : : GPAD_RX , Game : : K_APAD_RIGHT , Game : : K_APAD_LEFT } ,
{ Game : : GPAD_RY , Game : : K_APAD_UP , Game : : K_APAD_DOWN } ,
} ;
Game : : GamePadStick Gamepad : : stickForAxis [ Game : : GPAD_PHYSAXIS_COUNT ]
{
Game : : GPAD_RX ,
Game : : GPAD_RY ,
Game : : GPAD_LX ,
Game : : GPAD_LY ,
Game : : GPAD_INVALID ,
Game : : GPAD_INVALID
} ;
Game : : GamepadPhysicalAxis Gamepad : : axisSameStick [ Game : : GPAD_PHYSAXIS_COUNT ]
{
Game : : GPAD_PHYSAXIS_RSTICK_Y ,
Game : : GPAD_PHYSAXIS_RSTICK_X ,
Game : : GPAD_PHYSAXIS_LSTICK_Y ,
Game : : GPAD_PHYSAXIS_LSTICK_X ,
Game : : GPAD_PHYSAXIS_NONE ,
Game : : GPAD_PHYSAXIS_NONE
} ;
const char * Gamepad : : physicalAxisNames [ Game : : GPAD_PHYSAXIS_COUNT ]
{
" A_RSTICK_X " ,
" A_RSTICK_Y " ,
" A_LSTICK_X " ,
" A_LSTICK_Y " ,
" A_RTRIGGER " ,
" A_LTRIGGER "
} ;
const char * Gamepad : : virtualAxisNames [ Game : : GPAD_VIRTAXIS_COUNT ]
{
" VA_SIDE " ,
" VA_FORWARD " ,
" VA_UP " ,
" VA_YAW " ,
" VA_PITCH " ,
" VA_ATTACK "
} ;
const char * Gamepad : : gamePadMappingTypeNames [ Game : : GPAD_MAP_COUNT ]
{
" MAP_LINEAR " ,
" MAP_SQUARED "
} ;
Game : : keyNum_t Gamepad : : menuScrollButtonList [ ]
{
Game : : K_APAD_UP ,
Game : : K_APAD_DOWN ,
Game : : K_APAD_LEFT ,
Game : : K_APAD_RIGHT ,
Game : : K_DPAD_UP ,
Game : : K_DPAD_DOWN ,
Game : : K_DPAD_LEFT ,
Game : : K_DPAD_RIGHT
} ;
Game : : keyname_t Gamepad : : extendedKeyNames [ ]
{
{ " BUTTON_A " , Game : : K_BUTTON_A } ,
{ " BUTTON_B " , Game : : K_BUTTON_B } ,
{ " BUTTON_X " , Game : : K_BUTTON_X } ,
{ " BUTTON_Y " , Game : : K_BUTTON_Y } ,
{ " BUTTON_LSHLDR " , Game : : K_BUTTON_LSHLDR } ,
{ " BUTTON_RSHLDR " , Game : : K_BUTTON_RSHLDR } ,
{ " BUTTON_START " , Game : : K_BUTTON_START } ,
{ " BUTTON_BACK " , Game : : K_BUTTON_BACK } ,
{ " BUTTON_LSTICK " , Game : : K_BUTTON_LSTICK } ,
{ " BUTTON_RSTICK " , Game : : K_BUTTON_RSTICK } ,
{ " BUTTON_LTRIG " , Game : : K_BUTTON_LTRIG } ,
{ " BUTTON_RTRIG " , Game : : K_BUTTON_RTRIG } ,
{ " DPAD_UP " , Game : : K_DPAD_UP } ,
{ " DPAD_DOWN " , Game : : K_DPAD_DOWN } ,
{ " DPAD_LEFT " , Game : : K_DPAD_LEFT } ,
{ " DPAD_RIGHT " , Game : : K_DPAD_RIGHT } ,
} ;
Game : : keyname_t Gamepad : : extendedLocalizedKeyNamesXenon [ ]
{
// Material text icons pattern: 0x01 width height material_name_len
{ " ^ \x01 \x32 \x32 \x08 " " button_a " , Game : : K_BUTTON_A } ,
{ " ^ \x01 \x32 \x32 \x08 " " button_b " , Game : : K_BUTTON_B } ,
{ " ^ \x01 \x32 \x32 \x08 " " button_x " , Game : : K_BUTTON_X } ,
{ " ^ \x01 \x32 \x32 \x08 " " button_y " , Game : : K_BUTTON_Y } ,
{ " ^ \x01 \x32 \x32 \x0D " " button_lshldr " , Game : : K_BUTTON_LSHLDR } ,
{ " ^ \x01 \x32 \x32 \x0D " " button_rshldr " , Game : : K_BUTTON_RSHLDR } ,
{ " ^ \x01 \x32 \x32 \x0C " " button_start " , Game : : K_BUTTON_START } ,
{ " ^ \x01 \x32 \x32 \x0B " " button_back " , Game : : K_BUTTON_BACK } ,
{ " ^ \x01 \x48 \x32 \x0D " " button_lstick " , Game : : K_BUTTON_LSTICK } ,
{ " ^ \x01 \x48 \x32 \x0D " " button_rstick " , Game : : K_BUTTON_RSTICK } ,
{ " ^ \x01 \x32 \x32 \x0C " " button_ltrig " , Game : : K_BUTTON_LTRIG } ,
{ " ^ \x01 \x32 \x32 \x0C " " button_rtrig " , Game : : K_BUTTON_RTRIG } ,
{ " ^ \x01 \x32 \x32 \x07 " " dpad_up " , Game : : K_DPAD_UP } ,
{ " ^ \x01 \x32 \x32 \x09 " " dpad_down " , Game : : K_DPAD_DOWN } ,
{ " ^ \x01 \x32 \x32 \x09 " " dpad_left " , Game : : K_DPAD_LEFT } ,
{ " ^ \x01 \x32 \x32 \x0A " " dpad_right " , Game : : K_DPAD_RIGHT } ,
} ;
Game : : keyname_t Gamepad : : extendedLocalizedKeyNamesPs3 [ ]
{
// Material text icons pattern: 0x01 width height material_name_len
{ " ^ \x01 \x32 \x32 \x10 " " button_ps3_cross " , Game : : K_BUTTON_A } ,
{ " ^ \x01 \x32 \x32 \x11 " " button_ps3_circle " , Game : : K_BUTTON_B } ,
{ " ^ \x01 \x32 \x32 \x11 " " button_ps3_square " , Game : : K_BUTTON_X } ,
{ " ^ \x01 \x32 \x32 \x13 " " button_ps3_triangle " , Game : : K_BUTTON_Y } ,
{ " ^ \x01 \x32 \x32 \x0D " " button_ps3_l1 " , Game : : K_BUTTON_LSHLDR } ,
{ " ^ \x01 \x32 \x32 \x0D " " button_ps3_r1 " , Game : : K_BUTTON_RSHLDR } ,
{ " ^ \x01 \x32 \x32 \x10 " " button_ps3_start " , Game : : K_BUTTON_START } ,
{ " ^ \x01 \x32 \x32 \x0F " " button_ps3_back " , Game : : K_BUTTON_BACK } ,
{ " ^ \x01 \x48 \x32 \x0D " " button_ps3_l3 " , Game : : K_BUTTON_LSTICK } ,
{ " ^ \x01 \x48 \x32 \x0D " " button_ps3_r3 " , Game : : K_BUTTON_RSTICK } ,
{ " ^ \x01 \x32 \x32 \x0D " " button_ps3_l2 " , Game : : K_BUTTON_LTRIG } ,
{ " ^ \x01 \x32 \x32 \x0D " " button_ps3_r2 " , Game : : K_BUTTON_RTRIG } ,
{ " ^ \x01 \x32 \x32 \x0B " " dpad_ps3_up " , Game : : K_DPAD_UP } ,
{ " ^ \x01 \x32 \x32 \x0D " " dpad_ps3_down " , Game : : K_DPAD_DOWN } ,
{ " ^ \x01 \x32 \x32 \x0D " " dpad_ps3_left " , Game : : K_DPAD_LEFT } ,
{ " ^ \x01 \x32 \x32 \x0E " " dpad_ps3_right " , Game : : K_DPAD_RIGHT } ,
} ;
Game : : keyname_t Gamepad : : combinedKeyNames [ Game : : KEY_NAME_COUNT + std : : extent_v < decltype ( extendedKeyNames ) > + 1 ] ;
Game : : keyname_t Gamepad : : combinedLocalizedKeyNamesXenon [ Game : : KEY_NAME_COUNT + std : : extent_v < decltype ( extendedLocalizedKeyNamesXenon ) > + 1 ] ;
Game : : keyname_t Gamepad : : combinedLocalizedKeyNamesPs3 [ Game : : KEY_NAME_COUNT + std : : extent_v < decltype ( extendedLocalizedKeyNamesPs3 ) > + 1 ] ;
Gamepad : : ControllerMenuKeyMapping Gamepad : : controllerMenuKeyMappings [ ]
{
{ Game : : K_BUTTON_A , Game : : K_ENTER } ,
{ Game : : K_BUTTON_START , Game : : K_ENTER } ,
{ Game : : K_BUTTON_B , Game : : K_ESCAPE } ,
{ Game : : K_BUTTON_BACK , Game : : K_ESCAPE } ,
{ Game : : K_DPAD_UP , Game : : K_UPARROW } ,
{ Game : : K_APAD_UP , Game : : K_UPARROW } ,
{ Game : : K_DPAD_DOWN , Game : : K_DOWNARROW } ,
{ Game : : K_APAD_DOWN , Game : : K_DOWNARROW } ,
{ Game : : K_DPAD_LEFT , Game : : K_LEFTARROW } ,
{ Game : : K_APAD_LEFT , Game : : K_LEFTARROW } ,
{ Game : : K_DPAD_RIGHT , Game : : K_RIGHTARROW } ,
{ Game : : K_APAD_RIGHT , Game : : K_RIGHTARROW } ,
} ;
Gamepad : : GamePad Gamepad : : gamePads [ Game : : MAX_GAMEPADS ] { } ;
Gamepad : : GamePadGlobals Gamepad : : gamePadGlobals [ Game : : MAX_GAMEPADS ] { { } } ;
int Gamepad : : gamePadBindingsModifiedFlags = 0 ;
Dvar : : Var Gamepad : : gpad_enabled ;
Dvar : : Var Gamepad : : gpad_debug ;
Dvar : : Var Gamepad : : gpad_present ;
Dvar : : Var Gamepad : : gpad_in_use ;
Dvar : : Var Gamepad : : gpad_style ;
Dvar : : Var Gamepad : : gpad_sticksConfig ;
Dvar : : Var Gamepad : : gpad_buttonConfig ;
Dvar : : Var Gamepad : : gpad_menu_scroll_delay_first ;
Dvar : : Var Gamepad : : gpad_menu_scroll_delay_rest ;
Dvar : : Var Gamepad : : gpad_rumble ;
Dvar : : Var Gamepad : : gpad_stick_pressed_hysteresis ;
Dvar : : Var Gamepad : : gpad_stick_pressed ;
Dvar : : Var Gamepad : : gpad_stick_deadzone_max ;
Dvar : : Var Gamepad : : gpad_stick_deadzone_min ;
Dvar : : Var Gamepad : : gpad_button_deadzone ;
Dvar : : Var Gamepad : : gpad_button_rstick_deflect_max ;
Dvar : : Var Gamepad : : gpad_button_lstick_deflect_max ;
Dvar : : Var Gamepad : : gpad_use_hold_time ;
Dvar : : Var Gamepad : : gpad_lockon_enabled ;
Dvar : : Var Gamepad : : gpad_slowdown_enabled ;
Dvar : : Var Gamepad : : input_viewSensitivity ;
Dvar : : Var Gamepad : : input_invertPitch ;
Dvar : : Var Gamepad : : sv_allowAimAssist ;
Dvar : : Var Gamepad : : aim_turnrate_pitch ;
Dvar : : Var Gamepad : : aim_turnrate_pitch_ads ;
Dvar : : Var Gamepad : : aim_turnrate_yaw ;
Dvar : : Var Gamepad : : aim_turnrate_yaw_ads ;
Dvar : : Var Gamepad : : aim_accel_turnrate_enabled ;
Dvar : : Var Gamepad : : aim_accel_turnrate_lerp ;
Dvar : : Var Gamepad : : aim_input_graph_enabled ;
Dvar : : Var Gamepad : : aim_input_graph_index ;
Dvar : : Var Gamepad : : aim_scale_view_axis ;
Dvar : : Var Gamepad : : cl_bypassMouseInput ;
Dvar : : Var Gamepad : : cg_mapLocationSelectionCursorSpeed ;
Dvar : : Var Gamepad : : aim_aimAssistRangeScale ;
Dvar : : Var Gamepad : : aim_slowdown_enabled ;
Dvar : : Var Gamepad : : aim_slowdown_debug ;
Dvar : : Var Gamepad : : aim_slowdown_pitch_scale ;
Dvar : : Var Gamepad : : aim_slowdown_pitch_scale_ads ;
Dvar : : Var Gamepad : : aim_slowdown_yaw_scale ;
Dvar : : Var Gamepad : : aim_slowdown_yaw_scale_ads ;
Dvar : : Var Gamepad : : aim_lockon_enabled ;
Dvar : : Var Gamepad : : aim_lockon_deflection ;
Dvar : : Var Gamepad : : aim_lockon_pitch_strength ;
Dvar : : Var Gamepad : : aim_lockon_strength ;
Gamepad : : GamePadGlobals : : GamePadGlobals ( )
: axes { } ,
nextScrollTime ( 0 )
{
for ( auto & virtualAxis : axes . virtualAxes )
{
virtualAxis . physicalAxis = Game : : GPAD_PHYSAXIS_NONE ;
virtualAxis . mapType = Game : : GPAD_MAP_NONE ;
}
}
__declspec ( naked ) void Gamepad : : MSG_WriteDeltaUsercmdKeyStub ( )
{
__asm
{
// fix stack pointer
add esp , 0 Ch
// put both forward move and rightmove values in the movement button
mov dl , byte ptr [ edi + 1 Ah ] // to_forwardMove
mov dh , byte ptr [ edi + 1 Bh ] // to_rightMove
mov [ esp + 30 h ] , dx // to_buttons
mov dl , byte ptr [ ebp + 1 Ah ] // from_forwardMove
mov dh , byte ptr [ ebp + 1 Bh ] // from_rightMove
mov [ esp + 2 Ch ] , dx // from_buttons
// return back
push 0x60E40E
retn
}
}
void Gamepad : : ApplyMovement ( Game : : msg_t * msg , int key , Game : : usercmd_s * from , Game : : usercmd_s * to )
{
char forward ;
char right ;
if ( Game : : MSG_ReadBit ( msg ) )
{
short movementBits = static_cast < short > ( key ^ Game : : MSG_ReadBits ( msg , 16 ) ) ;
forward = static_cast < char > ( movementBits ) ;
right = static_cast < char > ( movementBits > > 8 ) ;
}
else
{
forward = from - > forwardmove ;
right = from - > rightmove ;
}
to - > forwardmove = forward ;
to - > rightmove = right ;
}
__declspec ( naked ) void Gamepad : : MSG_ReadDeltaUsercmdKeyStub ( )
{
__asm
{
push ebx // to
push ebp // from
push edi // key
push esi // msg
call ApplyMovement
add esp , 10 h
// return back
push 0x4921BF
ret
}
}
__declspec ( naked ) void Gamepad : : MSG_ReadDeltaUsercmdKeyStub2 ( )
{
__asm
{
push ebx // to
push ebp // from
push edi // key
push esi // msg
call ApplyMovement
add esp , 10 h
// return back
push 3
push esi
push 0x492085
ret
}
}
bool Gamepad : : GPad_Check ( const int gamePadIndex , const int portIndex )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
if ( XInputGetCapabilities ( portIndex , XINPUT_FLAG_GAMEPAD , & gamePad . caps ) = = ERROR_SUCCESS )
{
gamePad . enabled = true ;
gamePad . portIndex = portIndex ;
return true ;
}
gamePad . enabled = false ;
return false ;
}
void Gamepad : : GPad_RefreshAll ( )
{
auto currentGamePadNum = 0 ;
for ( auto currentPort = 0 ; currentPort < XUSER_MAX_COUNT & & currentGamePadNum < Game : : MAX_GAMEPADS ; currentPort + + )
{
if ( GPad_Check ( currentGamePadNum , currentPort ) )
currentGamePadNum + + ;
}
}
float Gamepad : : LinearTrack ( const float target , const float current , const float rate , const float deltaTime )
{
const auto err = target - current ;
float step ;
if ( err < = 0.0f )
step = - rate * deltaTime ;
else
step = rate * deltaTime ;
if ( std : : fabs ( err ) < = 0.001f )
return target ;
if ( std : : fabs ( step ) < = std : : fabs ( err ) )
return current + step ;
return target ;
}
bool Gamepad : : AimAssist_DoBoundsIntersectCenterBox ( const float * clipMins , const float * clipMaxs , const float clipHalfWidth , const float clipHalfHeight )
{
return clipHalfWidth > = clipMins [ 0 ] & & clipMaxs [ 0 ] > = - clipHalfWidth
& & clipHalfHeight > = clipMins [ 1 ] & & clipMaxs [ 1 ] > = - clipHalfHeight ;
}
bool Gamepad : : AimAssist_IsPlayerUsingOffhand ( Game : : AimAssistPlayerState * ps )
{
// Check offhand flag
if ( ( ps - > weapFlags & 2 ) = = 0 )
return false ;
// If offhand weapon has no id we are not using one
if ( ! ps - > weapIndex )
return false ;
const auto * weaponDef = Game : : BG_GetWeaponDef ( ps - > weapIndex ) ;
return weaponDef - > offhandClass ! = Game : : OFFHAND_CLASS_NONE ;
}
const Game : : AimScreenTarget * Gamepad : : AimAssist_GetBestTarget ( const Game : : AimAssistGlobals * aaGlob , const float range , const float regionWidth , const float regionHeight )
{
const auto rangeSqr = range * range ;
for ( auto targetIndex = 0 ; targetIndex < aaGlob - > screenTargetCount ; targetIndex + + )
{
const auto * currentTarget = & aaGlob - > screenTargets [ targetIndex ] ;
if ( currentTarget - > distSqr < = rangeSqr & & AimAssist_DoBoundsIntersectCenterBox ( currentTarget - > clipMins , currentTarget - > clipMaxs , regionWidth , regionHeight ) )
{
return currentTarget ;
}
}
return nullptr ;
}
const Game : : AimScreenTarget * Gamepad : : AimAssist_GetTargetFromEntity ( const Game : : AimAssistGlobals * aaGlob , const int entIndex )
{
if ( entIndex = = Game : : AIM_TARGET_INVALID )
return nullptr ;
for ( auto targetIndex = 0 ; targetIndex < aaGlob - > screenTargetCount ; targetIndex + + )
{
const auto * currentTarget = & aaGlob - > screenTargets [ targetIndex ] ;
if ( currentTarget - > entIndex = = entIndex )
return currentTarget ;
}
return nullptr ;
}
const Game : : AimScreenTarget * Gamepad : : AimAssist_GetPrevOrBestTarget ( const Game : : AimAssistGlobals * aaGlob , const float range , const float regionWidth , const float regionHeight ,
const int prevTargetEnt )
{
const auto screenTarget = AimAssist_GetTargetFromEntity ( aaGlob , prevTargetEnt ) ;
if ( screenTarget & & ( range * range ) > screenTarget - > distSqr & & AimAssist_DoBoundsIntersectCenterBox ( screenTarget - > clipMins , screenTarget - > clipMaxs , regionWidth , regionHeight ) )
return screenTarget ;
return AimAssist_GetBestTarget ( aaGlob , range , regionWidth , regionHeight ) ;
}
bool Gamepad : : AimAssist_IsLockonActive ( const int gamePadIndex )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & aaGlob = Game : : aaGlobArray [ gamePadIndex ] ;
if ( ! aim_lockon_enabled . get < bool > ( ) | | ! gpad_lockon_enabled . get < bool > ( ) )
return false ;
if ( AimAssist_IsPlayerUsingOffhand ( & aaGlob . ps ) )
return false ;
if ( aaGlob . autoAimActive | | aaGlob . autoMeleeState = = Game : : AIM_MELEE_STATE_UPDATING )
return false ;
return true ;
}
void Gamepad : : AimAssist_ApplyLockOn ( const Game : : AimInput * input , Game : : AimOutput * output )
{
assert ( input ) ;
assert ( input - > localClientNum < Game : : MAX_GAMEPADS ) ;
auto & aaGlob = Game : : aaGlobArray [ input - > localClientNum ] ;
const auto prevTargetEnt = aaGlob . lockOnTargetEnt ;
aaGlob . lockOnTargetEnt = Game : : AIM_TARGET_INVALID ;
if ( ! AimAssist_IsLockonActive ( input - > localClientNum ) )
return ;
const auto * weaponDef = Game : : BG_GetWeaponDef ( aaGlob . ps . weapIndex ) ;
if ( weaponDef - > requireLockonToFire )
return ;
const auto deflection = aim_lockon_deflection . get < float > ( ) ;
if ( deflection > std : : fabs ( input - > pitchAxis ) & & deflection > std : : fabs ( input - > yawAxis ) & & deflection > std : : fabs ( input - > rightAxis ) )
return ;
if ( ! aaGlob . ps . weapIndex )
return ;
const auto aimAssistRange = AimAssist_Lerp ( weaponDef - > aimAssistRange , weaponDef - > aimAssistRangeAds , aaGlob . adsLerp ) * aim_aimAssistRangeScale . get < float > ( ) ;
const auto screenTarget = AimAssist_GetPrevOrBestTarget ( & aaGlob , aimAssistRange , aaGlob . tweakables . lockOnRegionWidth , aaGlob . tweakables . lockOnRegionHeight , prevTargetEnt ) ;
if ( screenTarget & & screenTarget - > distSqr > 0.0f )
{
aaGlob . lockOnTargetEnt = screenTarget - > entIndex ;
const auto arcLength = std : : sqrt ( screenTarget - > distSqr ) * static_cast < float > ( M_PI ) ;
const auto pitchTurnRate =
( screenTarget - > velocity [ 0 ] * aaGlob . viewAxis [ 2 ] [ 0 ] + screenTarget - > velocity [ 1 ] * aaGlob . viewAxis [ 2 ] [ 1 ] + screenTarget - > velocity [ 2 ] * aaGlob . viewAxis [ 2 ] [ 2 ]
- ( aaGlob . ps . velocity [ 0 ] * aaGlob . viewAxis [ 2 ] [ 0 ] + aaGlob . ps . velocity [ 1 ] * aaGlob . viewAxis [ 2 ] [ 1 ] + aaGlob . ps . velocity [ 2 ] * aaGlob . viewAxis [ 2 ] [ 2 ] ) )
/ arcLength * 180.0f * aim_lockon_pitch_strength . get < float > ( ) ;
const auto yawTurnRate =
( screenTarget - > velocity [ 0 ] * aaGlob . viewAxis [ 1 ] [ 0 ] + screenTarget - > velocity [ 1 ] * aaGlob . viewAxis [ 1 ] [ 1 ] + screenTarget - > velocity [ 2 ] * aaGlob . viewAxis [ 1 ] [ 2 ]
- ( aaGlob . ps . velocity [ 0 ] * aaGlob . viewAxis [ 1 ] [ 0 ] + aaGlob . ps . velocity [ 1 ] * aaGlob . viewAxis [ 1 ] [ 1 ] + aaGlob . ps . velocity [ 2 ] * aaGlob . viewAxis [ 1 ] [ 2 ] ) )
/ arcLength * 180.0f * aim_lockon_strength . get < float > ( ) ;
output - > pitch - = pitchTurnRate * input - > deltaTime ;
output - > yaw + = yawTurnRate * input - > deltaTime ;
}
}
void Gamepad : : AimAssist_CalcAdjustedAxis ( const Game : : AimInput * input , float * pitchAxis , float * yawAxis )
{
assert ( input ) ;
assert ( pitchAxis ) ;
assert ( yawAxis ) ;
const auto graphIndex = aim_input_graph_index . get < int > ( ) ;
if ( aim_input_graph_enabled . get < bool > ( ) & & graphIndex > = 0 & & static_cast < unsigned > ( graphIndex ) < Game : : AIM_ASSIST_GRAPH_COUNT )
{
const auto deflection = std : : sqrt ( input - > pitchAxis * input - > pitchAxis + input - > yawAxis * input - > yawAxis ) ;
float fraction ;
if ( deflection - 1.0f < 0.0f )
fraction = deflection ;
else
fraction = 1.0f ;
if ( 0.0f - deflection > = 0.0f )
fraction = 0.0f ;
const auto graphScale = Game : : GraphFloat_GetValue ( & Game : : aaInputGraph [ graphIndex ] , fraction ) ;
* pitchAxis = input - > pitchAxis * graphScale ;
* yawAxis = input - > yawAxis * graphScale ;
}
else
{
* pitchAxis = input - > pitchAxis ;
* yawAxis = input - > yawAxis ;
}
if ( aim_scale_view_axis . get < bool > ( ) )
{
const auto absPitchAxis = std : : fabs ( * pitchAxis ) ;
const auto absYawAxis = std : : fabs ( * yawAxis ) ;
if ( absPitchAxis < = absYawAxis )
* pitchAxis = ( 1.0f - ( absYawAxis - absPitchAxis ) ) * * pitchAxis ;
else
* yawAxis = ( 1.0f - ( absPitchAxis - absYawAxis ) ) * * yawAxis ;
}
}
bool Gamepad : : AimAssist_IsSlowdownActive ( const Game : : AimAssistPlayerState * ps )
{
if ( ! aim_slowdown_enabled . get < bool > ( ) | | ! gpad_slowdown_enabled . get < bool > ( ) )
return false ;
if ( ! ps - > weapIndex )
return false ;
const auto * weaponDef = Game : : BG_GetWeaponDef ( ps - > weapIndex ) ;
if ( weaponDef - > requireLockonToFire )
return false ;
if ( ps - > linkFlags & Game : : PLF_WEAPONVIEW_ONLY )
return false ;
if ( ps - > weaponState > = Game : : WEAPON_STUNNED_START & & ps - > weaponState < = Game : : WEAPON_STUNNED_END )
return false ;
if ( ps - > eFlags & ( Game : : EF_VEHICLE_ACTIVE | Game : : EF_TURRET_ACTIVE_DUCK | Game : : EF_TURRET_ACTIVE_PRONE ) )
return false ;
if ( ! ps - > hasAmmo )
return false ;
return true ;
}
void Gamepad : : AimAssist_CalcSlowdown ( const Game : : AimInput * input , float * pitchScale , float * yawScale )
{
assert ( input ) ;
assert ( input - > localClientNum < Game : : MAX_GAMEPADS ) ;
auto & aaGlob = Game : : aaGlobArray [ input - > localClientNum ] ;
assert ( pitchScale ) ;
assert ( yawScale ) ;
* pitchScale = 1.0f ;
* yawScale = 1.0f ;
if ( ! AimAssist_IsSlowdownActive ( & aaGlob . ps ) )
return ;
const auto * weaponDef = Game : : BG_GetWeaponDef ( aaGlob . ps . weapIndex ) ;
const auto aimAssistRange = AimAssist_Lerp ( weaponDef - > aimAssistRange , weaponDef - > aimAssistRangeAds , aaGlob . adsLerp ) * aim_aimAssistRangeScale . get < float > ( ) ;
const auto screenTarget = AimAssist_GetBestTarget ( & aaGlob , aimAssistRange , aaGlob . tweakables . slowdownRegionWidth , aaGlob . tweakables . slowdownRegionHeight ) ;
if ( screenTarget )
{
* pitchScale = AimAssist_Lerp ( aim_slowdown_pitch_scale . get < float > ( ) , aim_slowdown_pitch_scale_ads . get < float > ( ) , aaGlob . adsLerp ) ;
* yawScale = AimAssist_Lerp ( aim_slowdown_yaw_scale . get < float > ( ) , aim_slowdown_yaw_scale_ads . get < float > ( ) , aaGlob . adsLerp ) ;
}
if ( AimAssist_IsPlayerUsingOffhand ( & aaGlob . ps ) )
* pitchScale = 1.0f ;
}
float Gamepad : : AimAssist_Lerp ( const float from , const float to , const float fraction )
{
return ( to - from ) * fraction + from ;
}
void Gamepad : : AimAssist_ApplyTurnRates ( const Game : : AimInput * input , Game : : AimOutput * output )
{
assert ( input - > localClientNum < Game : : MAX_GAMEPADS ) ;
auto & aaGlob = Game : : aaGlobArray [ input - > localClientNum ] ;
auto slowdownPitchScale = 0.0f ;
auto slowdownYawScale = 0.0f ;
float adjustedPitchAxis ;
float adjustedYawAxis ;
if ( aaGlob . autoMeleeState = = Game : : AIM_MELEE_STATE_UPDATING )
{
adjustedPitchAxis = 0.0f ;
adjustedYawAxis = 0.0f ;
slowdownPitchScale = 1.0f ;
slowdownYawScale = 1.0f ;
}
else
{
AimAssist_CalcAdjustedAxis ( input , & adjustedPitchAxis , & adjustedYawAxis ) ;
AimAssist_CalcSlowdown ( input , & slowdownPitchScale , & slowdownYawScale ) ;
}
const auto sensitivity = input_viewSensitivity . get < float > ( ) ;
auto pitchTurnRate = AimAssist_Lerp ( aim_turnrate_pitch . get < float > ( ) , aim_turnrate_pitch_ads . get < float > ( ) , aaGlob . adsLerp ) ;
pitchTurnRate = slowdownPitchScale * aaGlob . fovTurnRateScale * sensitivity * pitchTurnRate ;
auto yawTurnRate = AimAssist_Lerp ( aim_turnrate_yaw . get < float > ( ) , aim_turnrate_yaw_ads . get < float > ( ) , aaGlob . adsLerp ) ;
yawTurnRate = slowdownYawScale * aaGlob . fovTurnRateScale * sensitivity * yawTurnRate ;
if ( input - > pitchMax > 0 & & input - > pitchMax < pitchTurnRate )
pitchTurnRate = input - > pitchMax ;
if ( input - > yawMax > 0 & & input - > yawMax < yawTurnRate )
yawTurnRate = input - > yawMax ;
const auto pitchSign = adjustedPitchAxis > = 0.0f ? 1.0f : - 1.0f ;
const auto yawSign = adjustedYawAxis > = 0.0f ? 1.0f : - 1.0f ;
const auto pitchDelta = std : : fabs ( adjustedPitchAxis ) * pitchTurnRate ;
const auto yawDelta = std : : fabs ( adjustedYawAxis ) * yawTurnRate ;
if ( ! aim_accel_turnrate_enabled . get < bool > ( ) )
{
aaGlob . pitchDelta = pitchDelta ;
aaGlob . yawDelta = yawDelta ;
}
else
{
const auto accel = aim_accel_turnrate_lerp . get < float > ( ) * sensitivity ;
if ( pitchDelta < = aaGlob . pitchDelta )
aaGlob . pitchDelta = pitchDelta ;
else
aaGlob . pitchDelta = LinearTrack ( pitchDelta , aaGlob . pitchDelta , accel , input - > deltaTime ) ;
if ( yawDelta < = aaGlob . yawDelta )
aaGlob . yawDelta = yawDelta ;
else
aaGlob . yawDelta = LinearTrack ( yawDelta , aaGlob . yawDelta , accel , input - > deltaTime ) ;
}
output - > pitch + = aaGlob . pitchDelta * input - > deltaTime * pitchSign ;
output - > yaw + = aaGlob . yawDelta * input - > deltaTime * yawSign ;
}
void Gamepad : : AimAssist_UpdateGamePadInput ( const Game : : AimInput * input , Game : : AimOutput * output )
{
assert ( input - > localClientNum < Game : : MAX_GAMEPADS ) ;
auto & aaGlob = Game : : aaGlobArray [ input - > localClientNum ] ;
output - > pitch = input - > pitch ;
output - > yaw = input - > yaw ;
if ( aaGlob . initialized )
{
Game : : AimAssist_UpdateTweakables ( input - > localClientNum ) ;
Game : : AimAssist_UpdateAdsLerp ( input ) ;
AimAssist_ApplyTurnRates ( input , output ) ;
Game : : AimAssist_ApplyAutoMelee ( input , output ) ;
AimAssist_ApplyLockOn ( input , output ) ;
}
aaGlob . prevButtons = input - > buttons ;
}
void Gamepad : : CL_RemoteControlMove_GamePad ( const int localClientNum , Game : : usercmd_s * cmd )
{
// Buttons are already handled by keyboard input handler
const auto up = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_FORWARD ) ;
const auto right = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_SIDE ) ;
const auto yaw = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_YAW ) ;
const auto pitch = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_PITCH ) ;
const auto sensitivity = input_viewSensitivity . get < float > ( ) ;
constexpr auto scale = static_cast < float > ( std : : numeric_limits < char > : : max ( ) ) ;
cmd - > remoteControlAngles [ 0 ] = ClampChar ( cmd - > remoteControlAngles [ 0 ] + static_cast < int > ( std : : floor ( - up * scale * sensitivity ) )
+ static_cast < int > ( std : : floor ( - pitch * scale * sensitivity ) ) ) ;
cmd - > remoteControlAngles [ 1 ] = ClampChar ( cmd - > remoteControlAngles [ 1 ] + static_cast < int > ( std : : floor ( - right * scale * sensitivity ) )
+ static_cast < int > ( std : : floor ( - yaw * scale * sensitivity ) ) ) ;
}
constexpr auto CL_RemoteControlMove = 0x5A6BA0 ;
__declspec ( naked ) void Gamepad : : CL_RemoteControlMove_Stub ( )
{
__asm
{
// Prepare args for our function call
push edi // usercmd
push eax // localClientNum
call CL_RemoteControlMove
// Call our function, the args were already prepared earlier
call CL_RemoteControlMove_GamePad
add esp , 0x8
ret
}
}
bool Gamepad : : CG_HandleLocationSelectionInput_GamePad ( const int localClientNum , Game : : usercmd_s * /*cmd*/ )
{
// Buttons are already handled by keyboard input handler
const auto frameTime = static_cast < float > ( Game : : cgArray [ 0 ] . frametime ) * 0.001f ;
const auto mapAspectRatio = Game : : cgArray [ 0 ] . compassMapWorldSize [ 0 ] / Game : : cgArray [ 0 ] . compassMapWorldSize [ 1 ] ;
const auto selectionRequiresAngle = ( Game : : cgArray [ 0 ] . predictedPlayerState . locationSelectionInfo & 0x80 ) ! = 0 ;
auto up = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_FORWARD ) ;
auto right = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_SIDE ) ;
auto magnitude = up * up + right * right ;
if ( magnitude > 1.0f )
{
magnitude = std : : sqrt ( magnitude ) ;
up / = magnitude ;
right / = magnitude ;
}
Game : : cgArray [ 0 ] . selectedLocation [ 0 ] + = right * cg_mapLocationSelectionCursorSpeed . get < float > ( ) * frameTime ;
Game : : cgArray [ 0 ] . selectedLocation [ 1 ] - = up * mapAspectRatio * cg_mapLocationSelectionCursorSpeed . get < float > ( ) * frameTime ;
if ( selectionRequiresAngle )
{
const auto yawUp = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_PITCH ) ;
const auto yawRight = CL_GamepadAxisValue ( localClientNum , Game : : GPAD_VIRTAXIS_YAW ) ;
if ( std : : fabs ( yawUp ) > 0.0f | | std : : fabs ( yawRight ) > 0.0f )
{
Game : : vec2_t vec
{
yawUp ,
- yawRight
} ;
Game : : cgArray [ 0 ] . selectedLocationAngle = Game : : AngleNormalize360 ( Game : : vectoyaw ( & vec ) ) ;
Game : : cgArray [ 0 ] . selectedAngleLocation [ 0 ] = Game : : cgArray [ 0 ] . selectedLocation [ 0 ] ;
Game : : cgArray [ 0 ] . selectedAngleLocation [ 1 ] = Game : : cgArray [ 0 ] . selectedLocation [ 1 ] ;
}
}
else
{
Game : : cgArray [ 0 ] . selectedAngleLocation [ 0 ] = Game : : cgArray [ 0 ] . selectedLocation [ 0 ] ;
Game : : cgArray [ 0 ] . selectedAngleLocation [ 1 ] = Game : : cgArray [ 0 ] . selectedLocation [ 1 ] ;
}
return true ;
}
constexpr auto CG_HandleLocationSelectionInput = 0x5A67A0 ;
__declspec ( naked ) void Gamepad : : CG_HandleLocationSelectionInput_Stub ( )
{
__asm
{
// Prepare args for our function call
push esi // usercmd
push eax // localClientNum
call CG_HandleLocationSelectionInput
test al , al
jz exit_handling
// Call our function, the args were already prepared earlier
call CG_HandleLocationSelectionInput_GamePad
exit_handling :
add esp , 0x8
ret
}
}
bool Gamepad : : CG_ShouldUpdateViewAngles ( const int localClientNum )
{
return ! Game : : Key_IsKeyCatcherActive ( localClientNum , Game : : KEYCATCH_MASK_ANY ) | | Game : : UI_GetActiveMenu ( localClientNum ) = = Game : : UIMENU_SCOREBOARD ;
}
float Gamepad : : CL_GamepadAxisValue ( const int gamePadIndex , const Game : : GamepadVirtualAxis virtualAxis )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
assert ( virtualAxis > Game : : GPAD_VIRTAXIS_NONE & & virtualAxis < Game : : GPAD_VIRTAXIS_COUNT ) ;
const auto & gamePadGlobal = gamePadGlobals [ gamePadIndex ] ;
const auto & [ physicalAxis , mapType ] = gamePadGlobal . axes . virtualAxes [ virtualAxis ] ;
if ( physicalAxis < = Game : : GPAD_PHYSAXIS_NONE | | physicalAxis > = Game : : GPAD_PHYSAXIS_COUNT )
return 0.0f ;
auto axisDeflection = gamePadGlobal . axes . axesValues [ physicalAxis ] ;
if ( mapType = = Game : : GPAD_MAP_SQUARED )
{
const auto otherAxisSameStick = axisSameStick [ physicalAxis ] ;
float otherAxisDeflection ;
if ( otherAxisSameStick < = Game : : GPAD_PHYSAXIS_NONE | | otherAxisSameStick > = Game : : GPAD_PHYSAXIS_COUNT )
otherAxisDeflection = 0.0f ;
else
otherAxisDeflection = gamePadGlobal . axes . axesValues [ otherAxisSameStick ] ;
axisDeflection = std : : sqrt ( axisDeflection * axisDeflection + otherAxisDeflection * otherAxisDeflection ) * axisDeflection ;
}
return axisDeflection ;
}
char Gamepad : : ClampChar ( const int value )
{
return static_cast < char > ( std : : clamp < int > ( value , std : : numeric_limits < char > : : min ( ) , std : : numeric_limits < char > : : max ( ) ) ) ;
}
void Gamepad : : CL_GamepadMove ( const int gamePadIndex , Game : : usercmd_s * cmd , const float frameTimeBase )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
auto & clientActive = Game : : clients [ gamePadIndex ] ;
if ( ! gpad_enabled . get < bool > ( ) | | ! gamePad . enabled )
return ;
auto pitch = CL_GamepadAxisValue ( gamePadIndex , Game : : GPAD_VIRTAXIS_PITCH ) ;
if ( ! input_invertPitch . get < bool > ( ) )
pitch * = - 1 ;
auto yaw = - CL_GamepadAxisValue ( gamePadIndex , Game : : GPAD_VIRTAXIS_YAW ) ;
auto forward = CL_GamepadAxisValue ( gamePadIndex , Game : : GPAD_VIRTAXIS_FORWARD ) ;
auto side = CL_GamepadAxisValue ( gamePadIndex , Game : : GPAD_VIRTAXIS_SIDE ) ;
// The game implements an attack axis at this location. This axis is unused however so for this patch it was not implemented.
//auto attack = CL_GamepadAxisValue(gamePadIndex, Game::GPAD_VIRTAXIS_ATTACK);
auto moveScale = static_cast < float > ( std : : numeric_limits < char > : : max ( ) ) ;
if ( std : : fabs ( side ) > 0.0f | | std : : fabs ( forward ) > 0.0f )
{
const auto length = std : : fabs ( side ) < = std : : fabs ( forward )
? side / forward
: forward / side ;
moveScale = std : : sqrt ( ( length * length ) + 1.0f ) * moveScale ;
}
const auto forwardMove = static_cast < int > ( std : : floor ( forward * moveScale ) ) ;
const auto rightMove = static_cast < int > ( std : : floor ( side * moveScale ) ) ;
cmd - > rightmove = ClampChar ( cmd - > rightmove + rightMove ) ;
cmd - > forwardmove = ClampChar ( cmd - > forwardmove + forwardMove ) ;
// Swap attack and throw buttons when using controller and akimbo to match "left trigger"="left weapon" and "right trigger"="right weapon"
if ( gamePad . inUse & & clientActive . snap . ps . weapCommon . lastWeaponHand = = Game : : WEAPON_HAND_LEFT )
{
auto oldButtons = cmd - > buttons ;
if ( oldButtons & Game : : CMD_BUTTON_ATTACK )
cmd - > buttons | = Game : : CMD_BUTTON_THROW ;
else
cmd - > buttons & = ~ Game : : CMD_BUTTON_THROW ;
if ( oldButtons & Game : : CMD_BUTTON_THROW )
cmd - > buttons | = Game : : CMD_BUTTON_ATTACK ;
else
cmd - > buttons & = ~ Game : : CMD_BUTTON_ATTACK ;
}
// Check for frozen controls. Flag name should start with PMF_
if ( CG_ShouldUpdateViewAngles ( gamePadIndex ) & & ( clientActive . snap . ps . pm_flags & Game : : PMF_FROZEN ) = = 0 )
{
Game : : AimInput aimInput { } ;
Game : : AimOutput aimOutput { } ;
aimInput . deltaTime = frameTimeBase ;
aimInput . buttons = cmd - > buttons ;
aimInput . localClientNum = gamePadIndex ;
aimInput . deltaTimeScaled = static_cast < float > ( Game : : cls - > frametime ) * 0.001f ;
aimInput . pitch = clientActive . clViewangles [ 0 ] ;
aimInput . pitchAxis = pitch ;
aimInput . pitchMax = clientActive . cgameMaxPitchSpeed ;
aimInput . yaw = clientActive . clViewangles [ 1 ] ;
aimInput . yawAxis = yaw ;
aimInput . yawMax = clientActive . cgameMaxYawSpeed ;
aimInput . forwardAxis = forward ;
aimInput . rightAxis = side ;
AimAssist_UpdateGamePadInput ( & aimInput , & aimOutput ) ;
clientActive . clViewangles [ 0 ] = aimOutput . pitch ;
clientActive . clViewangles [ 1 ] = aimOutput . yaw ;
cmd - > meleeChargeDist = aimOutput . meleeChargeDist ;
cmd - > meleeChargeYaw = aimOutput . meleeChargeYaw ;
}
}
constexpr auto CL_MouseMove = 0x5A6240 ;
__declspec ( naked ) void Gamepad : : CL_MouseMove_Stub ( )
{
__asm
{
// Prepare args for our function call
push [ esp + 0x4 ] // frametime_base
push ebx // cmd
push eax // localClientNum
push [ esp + 0x8 ] // restore frametime_base on the stack
call CL_MouseMove
add esp , 4
// Call our function, the args were already prepared earlier
call CL_GamepadMove
add esp , 0xC
ret
}
}
bool Gamepad : : Gamepad_ShouldUse ( const Game : : gentity_s * playerEnt , const unsigned useTime )
{
// Only apply hold time to +usereload keybind
return ! ( playerEnt - > client - > buttons & Game : : CMD_BUTTON_USE_RELOAD ) | | useTime > = static_cast < unsigned > ( gpad_use_hold_time . get < int > ( ) ) ;
}
__declspec ( naked ) void Gamepad : : Player_UseEntity_Stub ( )
{
__asm
{
// Execute overwritten instructions
cmp eax , [ ecx + 0x10 ]
jl skipUse
// Call our custom check
push eax
pushad
push eax
push edi
call Gamepad_ShouldUse
add esp , 8 h
mov [ esp + 0x20 ] , eax
popad
pop eax
// Skip use if custom check returns false
test al , al
jz skipUse
// perform use
push 0x5FE39B
ret
skipUse :
push 0x5FE3AF
ret
}
}
bool Gamepad : : Key_IsValidGamePadChar ( const int key )
{
return key > = Game : : K_FIRSTGAMEPADBUTTON_RANGE_1 & & key < = Game : : K_LASTGAMEPADBUTTON_RANGE_1
| | key > = Game : : K_FIRSTGAMEPADBUTTON_RANGE_2 & & key < = Game : : K_LASTGAMEPADBUTTON_RANGE_2
| | key > = Game : : K_FIRSTGAMEPADBUTTON_RANGE_3 & & key < = Game : : K_LASTGAMEPADBUTTON_RANGE_3 ;
}
void Gamepad : : CL_GamepadResetMenuScrollTime ( const int gamePadIndex , const int key , const bool down , const unsigned time )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePadGlobal = gamePadGlobals [ gamePadIndex ] ;
if ( ! down )
return ;
const auto scrollDelayFirst = gpad_menu_scroll_delay_first . get < int > ( ) ;
for ( const auto scrollButton : menuScrollButtonList )
{
if ( key = = scrollButton )
{
gamePadGlobal . nextScrollTime = scrollDelayFirst + time ;
return ;
}
}
}
void Gamepad : : CL_GamepadGenerateAPad ( const int gamePadIndex , const Game : : GamepadPhysicalAxis physicalAxis , unsigned time )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
assert ( physicalAxis < Game : : GPAD_PHYSAXIS_COUNT & & physicalAxis > = 0 ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
const auto stick = stickForAxis [ physicalAxis ] ;
const auto stickIndex = stick & Game : : GPAD_VALUE_MASK ;
if ( stick ! = Game : : GPAD_INVALID )
{
assert ( stickIndex < 4 ) ;
const auto & mapping = analogStickList [ stickIndex ] ;
if ( gamePad . stickDown [ stickIndex ] [ Game : : GPAD_STICK_POS ] )
{
const Game : : GamePadButtonEvent event = gamePad . stickDownLast [ stickIndex ] [ Game : : GPAD_STICK_POS ] ? Game : : GPAD_BUTTON_UPDATE : Game : : GPAD_BUTTON_PRESSED ;
CL_GamepadButtonEvent ( gamePadIndex , mapping . posCode , event , time ) ;
}
else if ( gamePad . stickDown [ stickIndex ] [ Game : : GPAD_STICK_NEG ] )
{
const Game : : GamePadButtonEvent event = gamePad . stickDownLast [ stickIndex ] [ Game : : GPAD_STICK_NEG ] ? Game : : GPAD_BUTTON_UPDATE : Game : : GPAD_BUTTON_PRESSED ;
CL_GamepadButtonEvent ( gamePadIndex , mapping . negCode , event , time ) ;
}
else if ( gamePad . stickDownLast [ stickIndex ] [ Game : : GPAD_STICK_POS ] )
{
CL_GamepadButtonEvent ( gamePadIndex , mapping . posCode , Game : : GPAD_BUTTON_RELEASED , time ) ;
}
else if ( gamePad . stickDownLast [ stickIndex ] [ Game : : GPAD_STICK_NEG ] )
{
CL_GamepadButtonEvent ( gamePadIndex , mapping . negCode , Game : : GPAD_BUTTON_RELEASED , time ) ;
}
}
}
void Gamepad : : CL_GamepadEvent ( const int gamePadIndex , const Game : : GamepadPhysicalAxis physicalAxis , const float value , const unsigned time )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
assert ( physicalAxis < Game : : GPAD_PHYSAXIS_COUNT & & physicalAxis > = 0 ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
auto & gamePadGlobal = gamePadGlobals [ gamePadIndex ] ;
gamePadGlobal . axes . axesValues [ physicalAxis ] = value ;
CL_GamepadGenerateAPad ( gamePadIndex , physicalAxis , time ) ;
if ( std : : fabs ( value ) > 0.0f )
{
gamePad . inUse = true ;
gpad_in_use . setRaw ( true ) ;
}
}
void Gamepad : : UI_GamepadKeyEvent ( const int gamePadIndex , const int key , const bool down )
{
for ( const auto & mapping : controllerMenuKeyMappings )
{
if ( mapping . controllerKey = = key )
{
Game : : UI_KeyEvent ( gamePadIndex , mapping . pcKey , down ) ;
return ;
}
}
// No point in sending unmapped controller keystrokes to the key event handler since it doesn't know how to use it anyway
// Game::UI_KeyEvent(gamePadIndex, key, down);
}
bool Gamepad : : Scoreboard_HandleInput ( int gamePadIndex , int key )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & keyState = Game : : playerKeys [ gamePadIndex ] ;
if ( keyState . keys [ key ] . binding & & strcmp ( keyState . keys [ key ] . binding , " togglescores " ) = = 0 )
{
Game : : Cbuf_AddText ( gamePadIndex , " togglescores \n " ) ;
return true ;
}
switch ( key )
{
case Game : : K_DPAD_UP :
Game : : CG_ScrollScoreboardUp ( Game : : cgArray ) ;
return true ;
case Game : : K_DPAD_DOWN :
Game : : CG_ScrollScoreboardDown ( Game : : cgArray ) ;
return true ;
default :
return false ;
}
}
bool Gamepad : : CL_CheckForIgnoreDueToRepeat ( const int gamePadIndex , const int key , const int repeatCount , const unsigned time )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePadGlobal = gamePadGlobals [ gamePadIndex ] ;
if ( Game : : Key_IsKeyCatcherActive ( gamePadIndex , Game : : KEYCATCH_UI ) )
{
const int scrollDelayFirst = gpad_menu_scroll_delay_first . get < int > ( ) ;
const int scrollDelayRest = gpad_menu_scroll_delay_rest . get < int > ( ) ;
for ( const auto menuScrollButton : menuScrollButtonList )
{
if ( key = = menuScrollButton )
{
if ( repeatCount = = 1 )
{
gamePadGlobal . nextScrollTime = time + scrollDelayFirst ;
return false ;
}
if ( time > gamePadGlobal . nextScrollTime )
{
gamePadGlobal . nextScrollTime = time + scrollDelayRest ;
return false ;
}
break ;
}
}
}
return repeatCount > 1 ;
}
void Gamepad : : CL_GamepadButtonEvent ( const int gamePadIndex , const int key , const Game : : GamePadButtonEvent buttonEvent , const unsigned time )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
const auto pressed = buttonEvent = = Game : : GPAD_BUTTON_PRESSED ;
const auto pressedOrUpdated = pressed | | buttonEvent = = Game : : GPAD_BUTTON_UPDATE ;
auto & keyState = Game : : playerKeys [ gamePadIndex ] ;
keyState . keys [ key ] . down = pressedOrUpdated ;
if ( pressedOrUpdated )
{
if ( + + keyState . keys [ key ] . repeats = = 1 )
keyState . anyKeyDown + + ;
}
else if ( buttonEvent = = Game : : GPAD_BUTTON_RELEASED & & keyState . keys [ key ] . repeats > 0 )
{
keyState . keys [ key ] . repeats = 0 ;
if ( - - keyState . anyKeyDown < 0 )
keyState . anyKeyDown = 0 ;
}
if ( pressedOrUpdated & & CL_CheckForIgnoreDueToRepeat ( gamePadIndex , key , keyState . keys [ key ] . repeats , time ) )
return ;
if ( Game : : Key_IsKeyCatcherActive ( gamePadIndex , Game : : KEYCATCH_LOCATION_SELECTION ) & & pressedOrUpdated )
{
if ( key = = Game : : K_BUTTON_B | | keyState . keys [ key ] . binding & & strcmp ( keyState . keys [ key ] . binding , " +actionslot 4 " ) = = 0 )
{
keyState . locSelInputState = Game : : LOC_SEL_INPUT_CANCEL ;
}
else if ( key = = Game : : K_BUTTON_A | | keyState . keys [ key ] . binding & & strcmp ( keyState . keys [ key ] . binding , " +attack " ) = = 0 )
{
keyState . locSelInputState = Game : : LOC_SEL_INPUT_CONFIRM ;
}
return ;
}
const auto activeMenu = Game : : UI_GetActiveMenu ( gamePadIndex ) ;
if ( activeMenu = = Game : : UIMENU_SCOREBOARD )
{
if ( buttonEvent = = Game : : GPAD_BUTTON_PRESSED & & Scoreboard_HandleInput ( gamePadIndex , key ) )
return ;
}
keyState . locSelInputState = Game : : LOC_SEL_INPUT_NONE ;
const auto * keyBinding = keyState . keys [ key ] . binding ;
char cmd [ 1024 ] ;
if ( pressedOrUpdated )
{
if ( Game : : Key_IsKeyCatcherActive ( gamePadIndex , Game : : KEYCATCH_UI ) )
{
UI_GamepadKeyEvent ( gamePadIndex , key , pressedOrUpdated ) ;
return ;
}
if ( keyBinding )
{
if ( keyBinding [ 0 ] = = ' + ' )
{
sprintf_s ( cmd , " %s %i %i \n " , keyBinding , key , time ) ;
Game : : Cbuf_AddText ( gamePadIndex , cmd ) ;
}
else
{
Game : : Cbuf_InsertText ( gamePadIndex , keyBinding ) ;
}
}
}
else
{
if ( keyBinding & & keyBinding [ 0 ] = = ' + ' )
{
sprintf_s ( cmd , " -%s %i %i \n " , & keyBinding [ 1 ] , key , time ) ;
Game : : Cbuf_AddText ( gamePadIndex , cmd ) ;
}
if ( Game : : Key_IsKeyCatcherActive ( gamePadIndex , Game : : KEYCATCH_UI ) )
{
UI_GamepadKeyEvent ( gamePadIndex , key , pressedOrUpdated ) ;
}
}
}
void Gamepad : : CL_GamepadButtonEventForPort ( const int gamePadIndex , const int key , const Game : : GamePadButtonEvent buttonEvent , const unsigned time )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
gamePad . inUse = true ;
gpad_in_use . setRaw ( true ) ;
if ( Game : : Key_IsKeyCatcherActive ( gamePadIndex , Game : : KEYCATCH_UI ) )
CL_GamepadResetMenuScrollTime ( gamePadIndex , key , buttonEvent = = Game : : GPAD_BUTTON_PRESSED , time ) ;
CL_GamepadButtonEvent ( gamePadIndex , key , buttonEvent , time ) ;
}
void Gamepad : : GPad_ConvertStickToFloat ( const short x , const short y , float & outX , float & outY )
{
if ( x = = 0 & & y = = 0 )
{
outX = 0.0f ;
outY = 0.0f ;
return ;
}
Game : : vec2_t stickVec ;
stickVec [ 0 ] = static_cast < float > ( x ) / static_cast < float > ( std : : numeric_limits < short > : : max ( ) ) ;
stickVec [ 1 ] = static_cast < float > ( y ) / static_cast < float > ( std : : numeric_limits < short > : : max ( ) ) ;
const auto deadZoneTotal = gpad_stick_deadzone_min . get < float > ( ) + gpad_stick_deadzone_max . get < float > ( ) ;
auto len = Game : : Vec2Normalize ( stickVec ) ;
if ( gpad_stick_deadzone_min . get < float > ( ) < = len )
{
if ( 1.0f - gpad_stick_deadzone_max . get < float > ( ) > = len )
len = ( len - gpad_stick_deadzone_min . get < float > ( ) ) / ( 1.0f - deadZoneTotal ) ;
else
len = 1.0f ;
}
else
len = 0.0f ;
outX = stickVec [ 0 ] * len ;
outY = stickVec [ 1 ] * len ;
}
float Gamepad : : GPad_GetStick ( const int gamePadIndex , const Game : : GamePadStick stick )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
return gamePad . sticks [ stick ] ;
}
float Gamepad : : GPad_GetButton ( const int gamePadIndex , Game : : GamePadButton button )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
float value = 0.0f ;
if ( button & Game : : GPAD_DIGITAL_MASK )
{
const auto buttonValue = button & Game : : GPAD_VALUE_MASK ;
value = buttonValue & gamePad . digitals ? 1.0f : 0.0f ;
}
else if ( button & Game : : GPAD_ANALOG_MASK )
{
const auto analogIndex = button & Game : : GPAD_VALUE_MASK ;
if ( analogIndex < std : : extent_v < decltype ( gamePad . analogs ) > )
{
value = gamePad . analogs [ analogIndex ] ;
}
}
return value ;
}
bool Gamepad : : GPad_IsButtonPressed ( const int gamePadIndex , Game : : GamePadButton button )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
bool down = false ;
bool lastDown = false ;
if ( button & Game : : GPAD_DIGITAL_MASK )
{
const auto buttonValue = button & Game : : GPAD_VALUE_MASK ;
down = ( buttonValue & gamePad . digitals ) ! = 0 ;
lastDown = ( buttonValue & gamePad . lastDigitals ) ! = 0 ;
}
else if ( button & Game : : GPAD_ANALOG_MASK )
{
const auto analogIndex = button & Game : : GPAD_VALUE_MASK ;
assert ( analogIndex < std : : extent_v < decltype ( gamePad . analogs ) > ) ;
if ( analogIndex < std : : extent_v < decltype ( gamePad . analogs ) > )
{
down = gamePad . analogs [ analogIndex ] > 0.0f ;
lastDown = gamePad . lastAnalogs [ analogIndex ] > 0.0f ;
}
}
return down & & ! lastDown ;
}
bool Gamepad : : GPad_ButtonRequiresUpdates ( const int gamePadIndex , Game : : GamePadButton button )
{
return ( button & Game : : GPAD_ANALOG_MASK | | button & Game : : GPAD_DPAD_MASK ) & & GPad_GetButton ( gamePadIndex , button ) > 0.0f ;
}
bool Gamepad : : GPad_IsButtonReleased ( int gamePadIndex , Game : : GamePadButton button )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
bool down = false ;
bool lastDown = false ;
if ( button & Game : : GPAD_DIGITAL_MASK )
{
const auto buttonValue = button & Game : : GPAD_VALUE_MASK ;
down = ( gamePad . digitals & buttonValue ) ! = 0 ;
lastDown = ( gamePad . lastDigitals & buttonValue ) ! = 0 ;
}
else if ( button & Game : : GPAD_ANALOG_MASK )
{
const auto analogIndex = button & Game : : GPAD_VALUE_MASK ;
assert ( analogIndex < std : : extent_v < decltype ( gamePad . analogs ) > ) ;
if ( analogIndex < std : : extent_v < decltype ( gamePad . analogs ) > )
{
down = gamePad . analogs [ analogIndex ] > 0.0f ;
lastDown = gamePad . lastAnalogs [ analogIndex ] > 0.0f ;
}
}
return ! down & & lastDown ;
}
void Gamepad : : GPad_UpdateSticksDown ( const int gamePadIndex )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
for ( auto stickIndex = 0u ; stickIndex < std : : extent_v < decltype ( GamePad : : sticks ) > ; stickIndex + + )
{
for ( auto dir = 0 ; dir < Game : : GPAD_STICK_DIR_COUNT ; dir + + )
{
gamePad . stickDownLast [ stickIndex ] [ dir ] = gamePad . stickDown [ stickIndex ] [ dir ] ;
auto threshold = gpad_stick_pressed . get < float > ( ) ;
if ( gamePad . stickDownLast [ stickIndex ] [ dir ] )
threshold - = gpad_stick_pressed_hysteresis . get < float > ( ) ;
else
threshold + = gpad_stick_pressed_hysteresis . get < float > ( ) ;
if ( dir = = Game : : GPAD_STICK_POS )
{
gamePad . stickDown [ stickIndex ] [ dir ] = gamePad . sticks [ stickIndex ] > threshold ;
}
else
{
assert ( dir = = Game : : GPAD_STICK_NEG ) ;
gamePad . stickDown [ stickIndex ] [ dir ] = gamePad . sticks [ stickIndex ] < - threshold ;
}
}
}
}
void Gamepad : : GPad_UpdateSticks ( const int gamePadIndex , const XINPUT_GAMEPAD & state )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePad = gamePads [ gamePadIndex ] ;
Game : : vec2_t lVec , rVec ;
GPad_ConvertStickToFloat ( state . sThumbLX , state . sThumbLY , lVec [ 0 ] , lVec [ 1 ] ) ;
GPad_ConvertStickToFloat ( state . sThumbRX , state . sThumbRY , rVec [ 0 ] , rVec [ 1 ] ) ;
gamePad . lastSticks [ 0 ] = gamePad . sticks [ 0 ] ;
gamePad . sticks [ 0 ] = lVec [ 0 ] ;
gamePad . lastSticks [ 1 ] = gamePad . sticks [ 1 ] ;
gamePad . sticks [ 1 ] = lVec [ 1 ] ;
gamePad . lastSticks [ 2 ] = gamePad . sticks [ 2 ] ;
gamePad . sticks [ 2 ] = rVec [ 0 ] ;
gamePad . lastSticks [ 3 ] = gamePad . sticks [ 3 ] ;
gamePad . sticks [ 3 ] = rVec [ 1 ] ;
GPad_UpdateSticksDown ( gamePadIndex ) ;
2021-08-21 09:20:26 -04:00
# ifdef DEBUG
2022-05-03 11:59:51 -04:00
if ( gpad_debug . get < bool > ( ) )
{
Logger : : Print ( " Left: X: %f Y: %f \n " , lVec [ 0 ] , lVec [ 1 ] ) ;
Logger : : Print ( " Right: X: %f Y: %f \n " , rVec [ 0 ] , rVec [ 1 ] ) ;
Logger : : Print ( " Down: %i:%i %i:%i %i:%i %i:%i \n " , gamePad . stickDown [ 0 ] [ Game : : GPAD_STICK_POS ] , gamePad . stickDown [ 0 ] [ Game : : GPAD_STICK_NEG ] ,
gamePad . stickDown [ 1 ] [ Game : : GPAD_STICK_POS ] , gamePad . stickDown [ 1 ] [ Game : : GPAD_STICK_NEG ] ,
gamePad . stickDown [ 2 ] [ Game : : GPAD_STICK_POS ] , gamePad . stickDown [ 2 ] [ Game : : GPAD_STICK_NEG ] ,
gamePad . stickDown [ 3 ] [ Game : : GPAD_STICK_POS ] , gamePad . stickDown [ 3 ] [ Game : : GPAD_STICK_NEG ] ) ;
}
2021-08-21 09:20:26 -04:00
# endif
2022-05-03 11:59:51 -04:00
}
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
void Gamepad : : GPad_UpdateDigitals ( const int gamePadIndex , const XINPUT_GAMEPAD & state )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
auto & gamePad = gamePads [ gamePadIndex ] ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
gamePad . lastDigitals = gamePad . digitals ;
gamePad . digitals = state . wButtons ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
const auto leftDeflect = gpad_button_lstick_deflect_max . get < float > ( ) ;
if ( std : : fabs ( gamePad . sticks [ 0 ] ) > leftDeflect | | std : : fabs ( gamePad . sticks [ 1 ] ) > leftDeflect )
gamePad . digitals & = ~ static_cast < short > ( XINPUT_GAMEPAD_LEFT_THUMB ) ;
const auto rightDeflect = gpad_button_rstick_deflect_max . get < float > ( ) ;
if ( std : : fabs ( gamePad . sticks [ 2 ] ) > leftDeflect | | std : : fabs ( gamePad . sticks [ 3 ] ) > rightDeflect )
gamePad . digitals & = ~ static_cast < short > ( XINPUT_GAMEPAD_RIGHT_THUMB ) ;
2021-08-21 09:16:51 -04:00
# ifdef DEBUG
2022-05-03 11:59:51 -04:00
if ( gpad_debug . get < bool > ( ) )
{
Logger : : Print ( " Buttons: %x \n " , gamePad . digitals ) ;
}
2021-08-21 09:16:51 -04:00
# endif
2022-05-03 11:59:51 -04:00
}
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
void Gamepad : : GPad_UpdateAnalogs ( const int gamePadIndex , const XINPUT_GAMEPAD & state )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
auto & gamePad = gamePads [ gamePadIndex ] ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
const auto buttonDeadZone = gpad_button_deadzone . get < float > ( ) ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
gamePad . lastAnalogs [ 0 ] = gamePad . analogs [ 0 ] ;
gamePad . analogs [ 0 ] = static_cast < float > ( state . bLeftTrigger ) / static_cast < float > ( std : : numeric_limits < unsigned char > : : max ( ) ) ;
if ( gamePad . analogs [ 0 ] < buttonDeadZone )
gamePad . analogs [ 0 ] = 0.0f ;
2021-08-21 09:16:51 -04:00
2022-05-03 11:59:51 -04:00
gamePad . lastAnalogs [ 1 ] = gamePad . analogs [ 1 ] ;
gamePad . analogs [ 1 ] = static_cast < float > ( state . bRightTrigger ) / static_cast < float > ( std : : numeric_limits < unsigned char > : : max ( ) ) ;
if ( gamePad . analogs [ 1 ] < buttonDeadZone )
gamePad . analogs [ 1 ] = 0.0f ;
2021-08-21 09:16:51 -04:00
# ifdef DEBUG
2022-05-03 11:59:51 -04:00
if ( gpad_debug . get < bool > ( ) )
{
Logger : : Print ( " Triggers: %f %f \n " , gamePad . analogs [ 0 ] , gamePad . analogs [ 1 ] ) ;
}
2021-08-21 09:16:51 -04:00
# endif
2022-05-03 11:59:51 -04:00
}
void Gamepad : : GPad_UpdateAll ( )
{
GPad_RefreshAll ( ) ;
for ( auto currentGamePadIndex = 0 ; currentGamePadIndex < Game : : MAX_GAMEPADS ; currentGamePadIndex + + )
{
const auto & gamePad = gamePads [ currentGamePadIndex ] ;
if ( ! gamePad . enabled )
continue ;
XINPUT_STATE inputState ;
if ( XInputGetState ( gamePad . portIndex , & inputState ) ! = ERROR_SUCCESS )
continue ;
GPad_UpdateSticks ( currentGamePadIndex , inputState . Gamepad ) ;
GPad_UpdateDigitals ( currentGamePadIndex , inputState . Gamepad ) ;
GPad_UpdateAnalogs ( currentGamePadIndex , inputState . Gamepad ) ;
}
}
void Gamepad : : IN_GamePadsMove ( )
{
if ( ! gpad_enabled . get < bool > ( ) )
return ;
GPad_UpdateAll ( ) ;
const auto time = Game : : Sys_Milliseconds ( ) ;
bool gpadPresent = false ;
for ( auto gamePadIndex = 0 ; gamePadIndex < Game : : MAX_GAMEPADS ; gamePadIndex + + )
{
const auto & gamePad = gamePads [ gamePadIndex ] ;
if ( gamePad . enabled )
{
gpadPresent = true ;
const auto lx = GPad_GetStick ( gamePadIndex , Game : : GPAD_LX ) ;
const auto ly = GPad_GetStick ( gamePadIndex , Game : : GPAD_LY ) ;
const auto rx = GPad_GetStick ( gamePadIndex , Game : : GPAD_RX ) ;
const auto ry = GPad_GetStick ( gamePadIndex , Game : : GPAD_RY ) ;
const auto leftTrig = GPad_GetButton ( gamePadIndex , Game : : GPAD_L_TRIG ) ;
const auto rightTrig = GPad_GetButton ( gamePadIndex , Game : : GPAD_R_TRIG ) ;
CL_GamepadEvent ( gamePadIndex , Game : : GPAD_PHYSAXIS_LSTICK_X , lx , time ) ;
CL_GamepadEvent ( gamePadIndex , Game : : GPAD_PHYSAXIS_LSTICK_Y , ly , time ) ;
CL_GamepadEvent ( gamePadIndex , Game : : GPAD_PHYSAXIS_RSTICK_X , rx , time ) ;
CL_GamepadEvent ( gamePadIndex , Game : : GPAD_PHYSAXIS_RSTICK_Y , ry , time ) ;
CL_GamepadEvent ( gamePadIndex , Game : : GPAD_PHYSAXIS_LTRIGGER , leftTrig , time ) ;
CL_GamepadEvent ( gamePadIndex , Game : : GPAD_PHYSAXIS_RTRIGGER , rightTrig , time ) ;
for ( const auto & buttonMapping : buttonList )
{
if ( GPad_IsButtonPressed ( gamePadIndex , buttonMapping . padButton ) )
{
CL_GamepadButtonEventForPort (
gamePadIndex ,
buttonMapping . code ,
Game : : GPAD_BUTTON_PRESSED ,
time ) ;
}
else if ( GPad_ButtonRequiresUpdates ( gamePadIndex , buttonMapping . padButton ) )
{
CL_GamepadButtonEventForPort (
gamePadIndex ,
buttonMapping . code ,
Game : : GPAD_BUTTON_UPDATE ,
time ) ;
}
else if ( GPad_IsButtonReleased ( gamePadIndex , buttonMapping . padButton ) )
{
CL_GamepadButtonEventForPort (
gamePadIndex ,
buttonMapping . code ,
Game : : GPAD_BUTTON_RELEASED ,
time ) ;
}
}
}
}
gpad_present . setRaw ( gpadPresent ) ;
}
void Gamepad : : IN_Frame_Hk ( )
{
RawMouse : : IN_MouseMove ( ) ;
IN_GamePadsMove ( ) ;
}
void Gamepad : : Gamepad_WriteBindings ( const int gamePadIndex , const int handle )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
auto & gamePadGlobal = gamePadGlobals [ gamePadIndex ] ;
Game : : FS_Printf ( handle , " unbindallaxis \n " ) ;
for ( auto virtualAxisIndex = 0u ; virtualAxisIndex < Game : : GPAD_VIRTAXIS_COUNT ; virtualAxisIndex + + )
{
const auto & axisMapping = gamePadGlobal . axes . virtualAxes [ virtualAxisIndex ] ;
if ( axisMapping . physicalAxis < = Game : : GPAD_PHYSAXIS_NONE | | axisMapping . physicalAxis > = Game : : GPAD_PHYSAXIS_COUNT
| | axisMapping . mapType < = Game : : GPAD_MAP_NONE | | axisMapping . mapType > = Game : : GPAD_MAP_COUNT )
{
continue ;
}
const auto * physicalAxisName = physicalAxisNames [ axisMapping . physicalAxis ] ;
const auto * virtualAxisName = virtualAxisNames [ virtualAxisIndex ] ;
const auto * mappingName = gamePadMappingTypeNames [ axisMapping . mapType ] ;
Game : : FS_Printf ( handle , " bindaxis %s %s %s \n " , physicalAxisName , virtualAxisName , mappingName ) ;
}
}
void Gamepad : : Key_WriteBindings_Hk ( const int localClientNum , const int handle )
{
// Call original function
Utils : : Hook : : Call < void ( int , int ) > ( 0x4A5A20 ) ( localClientNum , handle ) ;
Gamepad_WriteBindings ( 0 , handle ) ;
}
void __declspec ( naked ) Gamepad : : Com_WriteConfiguration_Modified_Stub ( )
{
__asm
{
mov eax , [ ecx + 0x18 ]
or eax , gamePadBindingsModifiedFlags // Also check for gamePadBindingsModifiedFlags
test al , 1
jz endMethod
mov gamePadBindingsModifiedFlags , 0 // Reset gamePadBindingsModifiedFlags
mov eax , [ ecx + 0x18 ] // Restore eax to dvar_modified_flags
push 0x60B26E
retn
endMethod :
push 0x60B298
retn
}
}
void Gamepad : : Gamepad_BindAxis ( const int gamePadIndex , const Game : : GamepadPhysicalAxis realIndex , const Game : : GamepadVirtualAxis axisIndex , const Game : : GamepadMapping mapType )
{
assert ( gamePadIndex < Game : : MAX_GAMEPADS ) ;
assert ( realIndex > Game : : GPAD_PHYSAXIS_NONE & & realIndex < Game : : GPAD_PHYSAXIS_COUNT ) ;
assert ( axisIndex > Game : : GPAD_VIRTAXIS_NONE & & axisIndex < Game : : GPAD_VIRTAXIS_COUNT ) ;
assert ( mapType > Game : : GPAD_MAP_NONE & & mapType < Game : : GPAD_MAP_COUNT ) ;
auto & gamePadGlobal = gamePadGlobals [ gamePadIndex ] ;
gamePadGlobal . axes . virtualAxes [ axisIndex ] . physicalAxis = realIndex ;
gamePadGlobal . axes . virtualAxes [ axisIndex ] . mapType = mapType ;
gamePadBindingsModifiedFlags | = 1 ;
}
Game : : GamepadPhysicalAxis Gamepad : : StringToPhysicalAxis ( const char * str )
{
for ( auto i = 0u ; i < std : : extent_v < decltype ( physicalAxisNames ) > ; i + + )
{
if ( strcmp ( str , physicalAxisNames [ i ] ) = = 0 )
return static_cast < Game : : GamepadPhysicalAxis > ( i ) ;
}
return Game : : GPAD_PHYSAXIS_NONE ;
}
Game : : GamepadVirtualAxis Gamepad : : StringToVirtualAxis ( const char * str )
{
for ( auto i = 0u ; i < std : : extent_v < decltype ( virtualAxisNames ) > ; i + + )
{
if ( strcmp ( str , virtualAxisNames [ i ] ) = = 0 )
return static_cast < Game : : GamepadVirtualAxis > ( i ) ;
}
return Game : : GPAD_VIRTAXIS_NONE ;
}
Game : : GamepadMapping Gamepad : : StringToGamePadMapping ( const char * str )
{
for ( auto i = 0u ; i < std : : extent_v < decltype ( gamePadMappingTypeNames ) > ; i + + )
{
if ( strcmp ( str , gamePadMappingTypeNames [ i ] ) = = 0 )
return static_cast < Game : : GamepadMapping > ( i ) ;
}
return Game : : GPAD_MAP_NONE ;
}
void Gamepad : : Axis_Bind_f ( Command : : Params * params )
{
if ( params - > size ( ) < 4 )
{
Logger : : Print ( " bindaxis <real axis> <virtual axis> <input type> \n " ) ;
return ;
}
const auto * physicalAxisText = params - > get ( 1 ) ;
const auto * virtualAxisText = params - > get ( 2 ) ;
const auto * mappingText = params - > get ( 3 ) ;
const Game : : GamepadPhysicalAxis physicalAxis = StringToPhysicalAxis ( physicalAxisText ) ;
if ( physicalAxis = = Game : : GPAD_PHYSAXIS_NONE )
{
Logger : : Print ( " \" %s \" isn't a valid physical axis \n " , physicalAxisText ) ;
return ;
}
const Game : : GamepadVirtualAxis virtualAxis = StringToVirtualAxis ( virtualAxisText ) ;
if ( virtualAxis = = Game : : GPAD_VIRTAXIS_NONE )
{
Logger : : Print ( " \" %s \" isn't a valid virtual axis \n " , virtualAxisText ) ;
return ;
}
const Game : : GamepadMapping mapping = StringToGamePadMapping ( mappingText ) ;
if ( mapping = = Game : : GPAD_MAP_NONE )
{
Logger : : Print ( " \" %s \" isn't a valid input type \n " , mappingText ) ;
return ;
}
Gamepad_BindAxis ( 0 , physicalAxis , virtualAxis , mapping ) ;
}
void Gamepad : : Axis_Unbindall_f ( Command : : Params * )
{
auto & gamePadGlobal = gamePadGlobals [ 0 ] ;
for ( auto & virtualAxis : gamePadGlobal . axes . virtualAxes )
{
virtualAxis . physicalAxis = Game : : GPAD_PHYSAXIS_NONE ;
virtualAxis . mapType = Game : : GPAD_MAP_NONE ;
}
}
void Gamepad : : Bind_GP_SticksConfigs_f ( Command : : Params * )
{
const auto * stickConfigName = gpad_sticksConfig . get < const char * > ( ) ;
Game : : Cbuf_AddText ( 0 , Utils : : String : : VA ( " exec %s \n " , stickConfigName ) ) ;
}
void Gamepad : : Bind_GP_ButtonsConfigs_f ( Command : : Params * )
{
const auto * buttonConfigName = gpad_buttonConfig . get < const char * > ( ) ;
Game : : Cbuf_AddText ( 0 , Utils : : String : : VA ( " exec %s \n " , buttonConfigName ) ) ;
}
void Gamepad : : Scores_Toggle_f ( Command : : Params * )
{
if ( Game : : cgArray [ 0 ] . nextSnap )
{
if ( Game : : UI_GetActiveMenu ( 0 ) ! = Game : : UIMENU_SCOREBOARD )
Game : : CG_ScoresDown_f ( ) ;
else
Game : : CG_ScoresUp_f ( ) ;
}
}
void Gamepad : : InitDvars ( )
{
gpad_enabled = Dvar : : Register < bool > ( " gpad_enabled " , false , Game : : DVAR_ARCHIVE , " Game pad enabled " ) ;
gpad_debug = Dvar : : Register < bool > ( " gpad_debug " , false , Game : : DVAR_NONE , " Game pad debugging " ) ;
2022-05-04 11:47:50 -04:00
gpad_present = Dvar : : Register < bool > ( " gpad_present " , false , Game : : DVAR_READONLY , " Game pad present " ) ;
gpad_in_use = Dvar : : Register < bool > ( " gpad_in_use " , false , Game : : DVAR_READONLY , " A game pad is in use " ) ;
2022-05-03 11:59:51 -04:00
gpad_style = Dvar : : Register < bool > ( " gpad_style " , false , Game : : DVAR_ARCHIVE , " Switch between Xbox and PS HUD " ) ;
gpad_sticksConfig = Dvar : : Register < const char * > ( " gpad_sticksConfig " , " " , Game : : DVAR_ARCHIVE , " Game pad stick configuration " ) ;
gpad_buttonConfig = Dvar : : Register < const char * > ( " gpad_buttonConfig " , " " , Game : : DVAR_ARCHIVE , " Game pad button configuration " ) ;
gpad_menu_scroll_delay_first = Dvar : : Register < int > ( " gpad_menu_scroll_delay_first " , 420 , 0 , 1000 , Game : : DVAR_ARCHIVE , " Menu scroll key-repeat delay, for the first repeat, in milliseconds " ) ;
gpad_menu_scroll_delay_rest = Dvar : : Register < int > ( " gpad_menu_scroll_delay_rest " , 210 , 0 , 1000 , Game : : DVAR_ARCHIVE ,
" Menu scroll key-repeat delay, for repeats after the first, in milliseconds " ) ;
gpad_rumble = Dvar : : Register < bool > ( " gpad_rumble " , true , Game : : DVAR_ARCHIVE , " Enable game pad rumble " ) ;
2022-05-04 11:47:50 -04:00
gpad_stick_pressed_hysteresis = Dvar : : Register < float > ( " gpad_stick_pressed_hysteresis " , 0.1f , 0.0f , 1.0f , Game : : DVAR_ARCHIVE ,
2022-05-03 11:59:51 -04:00
" Game pad stick pressed no-change-zone around gpad_stick_pressed to prevent bouncing " ) ;
2022-05-04 11:47:50 -04:00
gpad_stick_pressed = Dvar : : Register < float > ( " gpad_stick_pressed " , 0.4f , 0.0 , 1.0 , Game : : DVAR_ARCHIVE , " Game pad stick pressed threshhold " ) ;
gpad_stick_deadzone_max = Dvar : : Register < float > ( " gpad_stick_deadzone_max " , 0.01f , 0.0f , 1.0f , Game : : DVAR_ARCHIVE , " Game pad maximum stick deadzone " ) ;
gpad_stick_deadzone_min = Dvar : : Register < float > ( " gpad_stick_deadzone_min " , 0.2f , 0.0f , 1.0f , Game : : DVAR_ARCHIVE , " Game pad minimum stick deadzone " ) ;
gpad_button_deadzone = Dvar : : Register < float > ( " gpad_button_deadzone " , 0.13f , 0.0f , 1.0f , Game : : DVAR_ARCHIVE , " Game pad button deadzone threshhold " ) ;
gpad_button_lstick_deflect_max = Dvar : : Register < float > ( " gpad_button_lstick_deflect_max " , 1.0f , 0.0f , 1.0f , Game : : DVAR_ARCHIVE , " Game pad maximum pad stick pressed value " ) ;
gpad_button_rstick_deflect_max = Dvar : : Register < float > ( " gpad_button_rstick_deflect_max " , 1.0f , 0.0f , 1.0f , Game : : DVAR_ARCHIVE , " Game pad maximum pad stick pressed value " ) ;
gpad_use_hold_time = Dvar : : Register < int > ( " gpad_use_hold_time " , 250 , 0 , std : : numeric_limits < int > : : max ( ) , Game : : DVAR_ARCHIVE , " Time to hold the 'use' button on gamepads to activate use " ) ;
2022-05-03 11:59:51 -04:00
gpad_lockon_enabled = Dvar : : Register < bool > ( " gpad_lockon_enabled " , true , Game : : DVAR_ARCHIVE , " Game pad lockon aim assist enabled " ) ;
gpad_slowdown_enabled = Dvar : : Register < bool > ( " gpad_slowdown_enabled " , true , Game : : DVAR_ARCHIVE , " Game pad slowdown aim assist enabled " ) ;
input_viewSensitivity = Dvar : : Register < float > ( " input_viewSensitivity " , 1.0f , 0.0001f , 5.0f , Game : : DVAR_ARCHIVE , " View Sensitivity " ) ;
input_invertPitch = Dvar : : Register < bool > ( " input_invertPitch " , false , Game : : DVAR_ARCHIVE , " Invert gamepad pitch " ) ;
sv_allowAimAssist = Dvar : : Register < bool > ( " sv_allowAimAssist " , true , Game : : DVAR_NONE , " Controls whether aim assist features on clients are enabled " ) ;
aim_turnrate_pitch = Dvar : : Var ( " aim_turnrate_pitch " ) ;
aim_turnrate_pitch_ads = Dvar : : Var ( " aim_turnrate_pitch_ads " ) ;
aim_turnrate_yaw = Dvar : : Var ( " aim_turnrate_yaw " ) ;
aim_turnrate_yaw_ads = Dvar : : Var ( " aim_turnrate_yaw_ads " ) ;
aim_accel_turnrate_enabled = Dvar : : Var ( " aim_accel_turnrate_enabled " ) ;
aim_accel_turnrate_lerp = Dvar : : Var ( " aim_accel_turnrate_lerp " ) ;
aim_input_graph_enabled = Dvar : : Var ( " aim_input_graph_enabled " ) ;
aim_input_graph_index = Dvar : : Var ( " aim_input_graph_index " ) ;
aim_scale_view_axis = Dvar : : Var ( " aim_scale_view_axis " ) ;
cl_bypassMouseInput = Dvar : : Var ( " cl_bypassMouseInput " ) ;
cg_mapLocationSelectionCursorSpeed = Dvar : : Var ( " cg_mapLocationSelectionCursorSpeed " ) ;
aim_aimAssistRangeScale = Dvar : : Var ( " aim_aimAssistRangeScale " ) ;
aim_slowdown_enabled = Dvar : : Var ( " aim_slowdown_enabled " ) ;
aim_slowdown_debug = Dvar : : Var ( " aim_slowdown_debug " ) ;
aim_slowdown_pitch_scale = Dvar : : Var ( " aim_slowdown_pitch_scale " ) ;
aim_slowdown_pitch_scale_ads = Dvar : : Var ( " aim_slowdown_pitch_scale_ads " ) ;
aim_slowdown_yaw_scale = Dvar : : Var ( " aim_slowdown_yaw_scale " ) ;
aim_slowdown_yaw_scale_ads = Dvar : : Var ( " aim_slowdown_yaw_scale_ads " ) ;
aim_lockon_enabled = Dvar : : Var ( " aim_lockon_enabled " ) ;
aim_lockon_deflection = Dvar : : Var ( " aim_lockon_deflection " ) ;
aim_lockon_pitch_strength = Dvar : : Var ( " aim_lockon_pitch_strength " ) ;
aim_lockon_strength = Dvar : : Var ( " aim_lockon_strength " ) ;
}
void Gamepad : : CG_RegisterDvars_Hk ( )
{
// Call original function
Utils : : Hook : : Call < void ( ) > ( 0x4F8DC0 ) ( ) ;
InitDvars ( ) ;
}
const char * Gamepad : : GetGamePadCommand ( const char * command )
{
if ( strcmp ( command , " +activate " ) = = 0 | | strcmp ( command , " +reload " ) = = 0 )
return " +usereload " ;
if ( strcmp ( command , " +melee_breath " ) = = 0 )
return " +holdbreath " ;
return command ;
}
int Gamepad : : Key_GetCommandAssignmentInternal_Hk ( const char * cmd , int ( * keys ) [ 2 ] )
{
auto keyCount = 0 ;
if ( gamePads [ 0 ] . inUse )
{
cmd = GetGamePadCommand ( cmd ) ;
for ( auto keyNum = 0 ; keyNum < Game : : K_LAST_KEY ; keyNum + + )
{
if ( ! Key_IsValidGamePadChar ( keyNum ) )
continue ;
if ( Game : : playerKeys [ 0 ] . keys [ keyNum ] . binding & & strcmp ( Game : : playerKeys [ 0 ] . keys [ keyNum ] . binding , cmd ) = = 0 )
{
( * keys ) [ keyCount + + ] = keyNum ;
if ( keyCount > = 2 )
return keyCount ;
}
}
}
else
{
for ( auto keyNum = 0 ; keyNum < Game : : K_LAST_KEY ; keyNum + + )
{
if ( Key_IsValidGamePadChar ( keyNum ) )
continue ;
if ( Game : : playerKeys [ 0 ] . keys [ keyNum ] . binding & & strcmp ( Game : : playerKeys [ 0 ] . keys [ keyNum ] . binding , cmd ) = = 0 )
{
( * keys ) [ keyCount + + ] = keyNum ;
if ( keyCount > = 2 )
return keyCount ;
}
}
}
return keyCount ;
}
void Gamepad : : CL_KeyEvent_Hk ( const int localClientNum , const int key , const int down , const unsigned time )
{
// A keyboard key has been pressed. Mark controller as unused.
gamePads [ 0 ] . inUse = false ;
gpad_in_use . setRaw ( false ) ;
// Call original function
Utils : : Hook : : Call < void ( int , int , int , unsigned ) > ( 0x4F6480 ) ( localClientNum , key , down , time ) ;
}
bool Gamepad : : IsGamePadInUse ( )
{
return gamePads [ 0 ] . inUse ;
}
int Gamepad : : CL_MouseEvent_Hk ( const int x , const int y , const int dx , const int dy )
{
if ( dx ! = 0 | | dy ! = 0 )
{
gamePads [ 0 ] . inUse = false ;
gpad_in_use . setRaw ( false ) ;
}
// Call original function
return Utils : : Hook : : Call < int ( int , int , int , int ) > ( 0x4D7C50 ) ( x , y , dx , dy ) ;
}
bool Gamepad : : UI_RefreshViewport_Hk ( )
{
return cl_bypassMouseInput . get < bool > ( ) | | IsGamePadInUse ( ) ;
}
Game : : keyname_t * Gamepad : : GetLocalizedKeyNameMap ( )
{
if ( gpad_style . get < bool > ( ) )
return combinedLocalizedKeyNamesPs3 ;
return combinedLocalizedKeyNamesXenon ;
}
void __declspec ( naked ) Gamepad : : GetLocalizedKeyName_Stub ( )
{
__asm
{
push eax
pushad
call GetLocalizedKeyNameMap
mov [ esp + 0x20 ] , eax
popad
pop eax
// Re-execute last instruction from game to set flags again for upcoming jump
test edi , edi
ret
}
}
void Gamepad : : CreateKeyNameMap ( )
{
memcpy ( combinedKeyNames , Game : : keyNames , sizeof ( Game : : keyname_t ) * Game : : KEY_NAME_COUNT ) ;
memcpy ( & combinedKeyNames [ Game : : KEY_NAME_COUNT ] , extendedKeyNames , sizeof ( Game : : keyname_t ) * std : : extent_v < decltype ( extendedKeyNames ) > ) ;
combinedKeyNames [ std : : extent_v < decltype ( combinedKeyNames ) > - 1 ] = { nullptr , 0 } ;
memcpy ( combinedLocalizedKeyNamesXenon , Game : : localizedKeyNames , sizeof ( Game : : keyname_t ) * Game : : LOCALIZED_KEY_NAME_COUNT ) ;
memcpy ( & combinedLocalizedKeyNamesXenon [ Game : : LOCALIZED_KEY_NAME_COUNT ] , extendedLocalizedKeyNamesXenon ,
sizeof ( Game : : keyname_t ) * std : : extent_v < decltype ( extendedLocalizedKeyNamesXenon ) > ) ;
combinedLocalizedKeyNamesXenon [ std : : extent_v < decltype ( combinedLocalizedKeyNamesXenon ) > - 1 ] = { nullptr , 0 } ;
memcpy ( combinedLocalizedKeyNamesPs3 , Game : : localizedKeyNames , sizeof ( Game : : keyname_t ) * Game : : LOCALIZED_KEY_NAME_COUNT ) ;
memcpy ( & combinedLocalizedKeyNamesPs3 [ Game : : LOCALIZED_KEY_NAME_COUNT ] , extendedLocalizedKeyNamesPs3 ,
sizeof ( Game : : keyname_t ) * std : : extent_v < decltype ( extendedLocalizedKeyNamesPs3 ) > ) ;
combinedLocalizedKeyNamesPs3 [ std : : extent_v < decltype ( combinedLocalizedKeyNamesPs3 ) > - 1 ] = { nullptr , 0 } ;
Utils : : Hook : : Set < Game : : keyname_t * > ( 0x4A780A , combinedKeyNames ) ;
Utils : : Hook : : Set < Game : : keyname_t * > ( 0x4A7810 , combinedKeyNames ) ;
Utils : : Hook : : Set < Game : : keyname_t * > ( 0x435C9F , combinedKeyNames ) ;
Utils : : Hook ( 0x435C97 , GetLocalizedKeyName_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
}
Gamepad : : Gamepad ( )
{
if ( ZoneBuilder : : IsEnabled ( ) )
return ;
// Initialize gamepad environment
Utils : : Hook ( 0x4059FE , CG_RegisterDvars_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
// package the forward and right move components in the move buttons
Utils : : Hook ( 0x60E38D , MSG_WriteDeltaUsercmdKeyStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
// send two bytes for sending movement data
Utils : : Hook : : Set < BYTE > ( 0x60E501 , 16 ) ;
Utils : : Hook : : Set < BYTE > ( 0x60E5CD , 16 ) ;
// make sure to parse the movement data properly and apply it
Utils : : Hook ( 0x492127 , MSG_ReadDeltaUsercmdKeyStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x492009 , MSG_ReadDeltaUsercmdKeyStub2 , HOOK_JUMP ) . install ( ) - > quick ( ) ;
// Also rewrite configuration when gamepad config is dirty
Utils : : Hook ( 0x60B264 , Com_WriteConfiguration_Modified_Stub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x60B223 , Key_WriteBindings_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
// Add hold time to gamepad usereload on hold prompts
Utils : : Hook ( 0x5FE396 , Player_UseEntity_Stub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
CreateKeyNameMap ( ) ;
Command : : Add ( " bindaxis " , Axis_Bind_f ) ;
Command : : Add ( " unbindallaxis " , Axis_Unbindall_f ) ;
Command : : Add ( " bindgpsticksconfigs " , Bind_GP_SticksConfigs_f ) ;
Command : : Add ( " bindgpbuttonsconfigs " , Bind_GP_ButtonsConfigs_f ) ;
Command : : Add ( " togglescores " , Scores_Toggle_f ) ;
if ( Dedicated : : IsEnabled ( ) )
return ;
// Gamepad on frame hook
Utils : : Hook ( 0x475E9E , IN_Frame_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-21 09:40:36 -04:00
2022-05-03 11:59:51 -04:00
// Mark controller as unused when keyboard key is pressed
Utils : : Hook ( 0x43D179 , CL_KeyEvent_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-22 18:03:44 -04:00
2022-05-03 11:59:51 -04:00
// Mark controller as unused when mouse is moved
Utils : : Hook ( 0x64C507 , CL_MouseEvent_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-24 05:24:43 -04:00
2022-05-03 11:59:51 -04:00
// Hide cursor when controller is active
Utils : : Hook ( 0x48E527 , UI_RefreshViewport_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-22 19:19:20 -04:00
2022-05-03 11:59:51 -04:00
// Only return gamepad keys when gamepad enabled and only non gamepad keys when not
Utils : : Hook ( 0x5A7A23 , Key_GetCommandAssignmentInternal_Hk , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-22 18:03:44 -04:00
2022-05-03 11:59:51 -04:00
// Add gamepad inputs to remote control (eg predator) handling
Utils : : Hook ( 0x5A6D4E , CL_RemoteControlMove_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-24 11:55:09 -04:00
2022-05-03 11:59:51 -04:00
// Add gamepad inputs to location selection (eg airstrike location) handling
Utils : : Hook ( 0x5A6D72 , CG_HandleLocationSelectionInput_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
2021-08-24 11:20:52 -04:00
2022-05-03 11:59:51 -04:00
// Add gamepad inputs to usercmds
Utils : : Hook ( 0x5A6DAE , CL_MouseMove_Stub , HOOK_CALL ) . install ( ) - > quick ( ) ;
}
2021-01-02 01:49:45 -05:00
}