2021-01-02 01:49:45 -05:00
# include "STDInclude.hpp"
namespace Components
{
2021-01-02 02:17:37 -05:00
XINPUT_STATE XInput : : xiStates [ XUSER_MAX_COUNT ] ;
2021-05-02 07:13:13 -04:00
XINPUT_STATE XInput : : lastXiState = { 0 } ;
2021-01-02 14:42:52 -05:00
int XInput : : xiPlayerNum = - 1 ;
2021-05-02 07:13:13 -04:00
std : : chrono : : milliseconds XInput : : timeAtFirstHeldMaxLookX = 0 ms ; // "For how much time in miliseconds has the player been holding a horizontal direction on their stick, fully" (-1.0 or 1.0)
bool XInput : : isHoldingMaxLookX = false ;
2021-05-04 09:47:46 -04:00
float XInput : : lockedSensitivityMultiplier = 0.45f ;
float XInput : : generalXSensitivityMultiplier = 1.5f ;
2021-05-02 10:13:33 -04:00
float XInput : : generalYSensitivityMultiplier = 0.8f ;
2021-05-02 07:13:13 -04:00
2021-05-04 09:47:46 -04:00
float XInput : : lastMenuNavigationDirection = .0f ;
std : : chrono : : milliseconds XInput : : lastNavigationTime = 0 ms ;
std : : chrono : : milliseconds XInput : : msBetweenNavigations = 220 ms ;
std : : chrono : : milliseconds XInput : : msBeforeUnlockingSensitivity = 350 ms ;
2021-05-02 07:13:13 -04:00
std : : vector < XInput : : ActionMapping > mappings = {
XInput : : ActionMapping ( XINPUT_GAMEPAD_A , " gostand " ) ,
2021-05-02 10:13:33 -04:00
XInput : : ActionMapping ( XINPUT_GAMEPAD_B , " stance " ) ,
2021-05-02 07:13:13 -04:00
XInput : : ActionMapping ( XINPUT_GAMEPAD_X , " usereload " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_Y , " weapnext " , false ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_LEFT_SHOULDER , " smoke " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_RIGHT_SHOULDER , " frag " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_LEFT_THUMB , " breath_sprint " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_RIGHT_THUMB , " melee " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_START , " togglemenu " , false ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_BACK , " scores " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_DPAD_RIGHT , " actionslot 3 " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_DPAD_LEFT , " actionslot 2 " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_DPAD_UP , " actionslot 1 " ) ,
XInput : : ActionMapping ( XINPUT_GAMEPAD_DPAD_DOWN , " actionslot 4 " ) ,
} ;
2021-05-04 09:47:46 -04:00
std : : vector < XInput : : MenuMapping > menuMappings = {
XInput : : MenuMapping ( XINPUT_GAMEPAD_A , Game : : keyNum_t : : K_KP_ENTER ) ,
XInput : : MenuMapping ( XINPUT_GAMEPAD_B , Game : : keyNum_t : : K_ESCAPE ) ,
XInput : : MenuMapping ( XINPUT_GAMEPAD_DPAD_RIGHT , Game : : keyNum_t : : K_KP_RIGHTARROW ) ,
XInput : : MenuMapping ( XINPUT_GAMEPAD_DPAD_LEFT , Game : : keyNum_t : : K_KP_LEFTARROW ) ,
XInput : : MenuMapping ( XINPUT_GAMEPAD_DPAD_UP , Game : : keyNum_t : : K_KP_UPARROW ) ,
XInput : : MenuMapping ( XINPUT_GAMEPAD_DPAD_DOWN , Game : : keyNum_t : : K_KP_DOWNARROW )
} ;
2021-05-02 07:13:13 -04:00
void XInput : : Vibrate ( int leftVal , int rightVal )
{
// Create a Vibraton State
XINPUT_VIBRATION Vibration ;
// Zeroise the Vibration
ZeroMemory ( & Vibration , sizeof ( XINPUT_VIBRATION ) ) ;
// Set the Vibration Values
Vibration . wLeftMotorSpeed = leftVal ;
Vibration . wRightMotorSpeed = rightVal ;
// Vibrate the controller
XInputSetState ( xiPlayerNum , & Vibration ) ;
}
2021-01-02 02:17:37 -05:00
void XInput : : PollXInputDevices ( )
{
2021-01-02 14:42:52 -05:00
XInput : : xiPlayerNum = - 1 ;
2021-01-03 03:33:12 -05:00
for ( int i = XUSER_MAX_COUNT - 1 ; i > = 0 ; i - - )
2021-01-02 02:17:37 -05:00
{
2021-01-02 14:42:52 -05:00
if ( XInputGetState ( i , & xiStates [ i ] ) = = ERROR_SUCCESS )
XInput : : xiPlayerNum = i ;
2021-01-02 02:17:37 -05:00
}
}
__declspec ( naked ) void XInput : : CL_FrameStub ( )
{
__asm
{
// poll the xinput devices on every client frame
call XInput : : PollXInputDevices
// execute the code we patched over
sub esp , 0 Ch
push ebx
push ebp
push esi
// return back to original code
push 0x486976
retn
}
}
2021-01-02 17:48:48 -05:00
void XInput : : CL_GamepadMove ( int , Game : : usercmd_s * cmd )
2021-01-02 14:42:52 -05:00
{
if ( XInput : : xiPlayerNum ! = - 1 )
{
XINPUT_STATE * xiState = & xiStates [ xiPlayerNum ] ;
2021-05-02 07:13:13 -04:00
// Deadzones
float moveStickX = abs ( xiState - > Gamepad . sThumbLX ) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ? xiState - > Gamepad . sThumbLX / ( float ) std : : numeric_limits < SHORT > ( ) . max ( ) : .0f ;
float moveStickY = abs ( xiState - > Gamepad . sThumbLY ) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ? xiState - > Gamepad . sThumbLY / ( float ) std : : numeric_limits < SHORT > ( ) . max ( ) : .0f ;
float viewStickX = abs ( xiState - > Gamepad . sThumbRX ) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ? xiState - > Gamepad . sThumbRX / ( float ) std : : numeric_limits < SHORT > ( ) . max ( ) : .0f ;
float viewStickY = abs ( xiState - > Gamepad . sThumbRY ) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE ? xiState - > Gamepad . sThumbRY / ( float ) std : : numeric_limits < SHORT > ( ) . max ( ) : .0f ;
2021-05-04 09:54:12 -04:00
if ( moveStickX ! = 0 | | moveStickY ! = 0 ) {
// We check for 0:0 again so we don't overwrite keyboard input in case the user doesn't feel like using their gamepad, even though its plugged in
cmd - > rightmove = moveStickX * std : : numeric_limits < char > ( ) . max ( ) ;
cmd - > forwardmove = moveStickY * std : : numeric_limits < char > ( ) . max ( ) ;
}
2021-05-02 07:13:13 -04:00
// Gamepad horizontal acceleration on view
if ( abs ( viewStickX ) > 0.9f ) {
if ( ! XInput : : isHoldingMaxLookX ) {
XInput : : isHoldingMaxLookX = true ;
XInput : : timeAtFirstHeldMaxLookX = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) ;
}
else {
std : : chrono : : milliseconds hasBeenHoldingLeftXForMs = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) - XInput : : timeAtFirstHeldMaxLookX ;
# ifdef STEP_SENSITIVITY
if ( hasBeenHoldingLeftXForMs < XInput : : msBeforeUnlockingSensitivity ) {
viewStickX * = XInput : : lockedSensitivityMultiplier ;
}
# else
2021-05-04 09:47:46 -04:00
float coeff = std : : clamp ( hasBeenHoldingLeftXForMs . count ( ) / ( float ) XInput : : msBeforeUnlockingSensitivity . count ( ) , 0.0F , 1.0F ) ;
viewStickX * = XInput : : lockedSensitivityMultiplier + coeff * ( 1.0f - XInput : : lockedSensitivityMultiplier ) ;
2021-05-02 07:13:13 -04:00
# endif
}
}
2021-05-04 09:47:46 -04:00
else {
2021-05-02 07:13:13 -04:00
XInput : : isHoldingMaxLookX = false ;
XInput : : timeAtFirstHeldMaxLookX = 0 ms ;
}
2021-01-03 01:32:58 -05:00
2021-01-03 03:33:12 -05:00
2021-05-02 10:13:33 -04:00
Game : : cl_angles [ 0 ] - = viewStickY * generalYSensitivityMultiplier ;
Game : : cl_angles [ 1 ] - = viewStickX * generalXSensitivityMultiplier ;
2021-05-02 07:13:13 -04:00
bool pressingLeftTrigger = xiState - > Gamepad . bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false ;
if ( pressingLeftTrigger ! = XInput : : lastXiState . Gamepad . bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD )
2021-01-03 03:33:12 -05:00
{
if ( pressingLeftTrigger )
Command : : Execute ( " +speed " ) ;
else
Command : : Execute ( " -speed " ) ;
}
2021-05-02 07:13:13 -04:00
bool pressingRightTrigger = xiState - > Gamepad . bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false ;
if ( pressingRightTrigger ! = XInput : : lastXiState . Gamepad . bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD )
2021-01-03 03:33:12 -05:00
{
if ( pressingRightTrigger )
Command : : Execute ( " +attack " ) ;
else
Command : : Execute ( " -attack " ) ;
}
2021-05-02 07:13:13 -04:00
// Buttons (on/off) mappings
for ( size_t i = 0 ; i < mappings . size ( ) ; i + + )
2021-01-03 03:33:12 -05:00
{
2021-05-02 07:13:13 -04:00
auto mapping = mappings [ i ] ;
auto action = mapping . action ;
auto antiAction = mapping . action ;
if ( mapping . isReversible ) {
action = " + " + mapping . action ;
antiAction = " - " + mapping . action ;
}
else if ( mapping . wasPressed ) {
if ( xiState - > Gamepad . wButtons & mapping . input ) {
// Button still pressed, do not send info
}
else {
mappings [ i ] . wasPressed = false ;
}
continue ;
}
if ( xiState - > Gamepad . wButtons & mapping . input ) {
2021-05-02 10:13:33 -04:00
if ( mapping . spamWhenHeld | | ! mappings [ i ] . wasPressed ) {
Command : : Execute ( action . c_str ( ) ) ;
}
2021-05-02 07:13:13 -04:00
mappings [ i ] . wasPressed = true ;
}
else if ( mapping . isReversible & & mapping . wasPressed ) {
mappings [ i ] . wasPressed = false ;
Command : : Execute ( antiAction . c_str ( ) ) ;
}
2021-01-03 03:33:12 -05:00
}
2021-05-02 07:13:13 -04:00
memcpy ( & XInput : : lastXiState , xiState , sizeof XINPUT_STATE ) ;
2021-01-02 14:42:52 -05:00
}
}
__declspec ( naked ) void XInput : : CL_CreateCmdStub ( )
{
__asm
{
// do xinput!
push esi
push ebp
call XInput : : CL_GamepadMove
add esp , 8 h
// execute code we patched over
add esp , 4
fld st
pop ebx
// return back
push 0x5A6DBF
retn
}
}
2021-01-02 17:48:48 -05:00
__declspec ( naked ) void XInput : : MSG_WriteDeltaUsercmdKeyStub ( )
{
__asm
{
// fix stack pointer
add esp , 0 Ch
// put both forward move and rightmove values in the movement button
2021-05-02 07:13:13 -04:00
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
2021-01-02 17:48:48 -05:00
2021-05-02 07:13:13 -04:00
mov dl , byte ptr [ ebp + 1 Ah ] // from_forwardMove
mov dh , byte ptr [ ebp + 1 Bh ] // from_rightMove
2021-01-02 17:48:48 -05:00
2021-05-02 07:13:13 -04:00
mov [ esp + 2 Ch ] , dx // from_buttons
2021-01-02 17:48:48 -05:00
// return back
push 0x60E40E
retn
}
}
2021-01-02 20:06:40 -05:00
void XInput : : 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 ;
}
2021-05-02 07:13:13 -04:00
2021-01-02 20:06:40 -05:00
to - > forwardmove = forward ;
to - > rightmove = right ;
}
2021-01-02 17:48:48 -05:00
__declspec ( naked ) void XInput : : MSG_ReadDeltaUsercmdKeyStub ( )
{
2021-01-02 18:37:46 -05:00
__asm
{
2021-01-02 20:06:40 -05:00
push ebx // to
push ebp // from
push edi // key
push esi // msg
call XInput : : ApplyMovement
add esp , 10 h
2021-01-02 18:37:46 -05:00
// return back
push 0x4921BF
ret
}
}
2021-01-02 17:48:48 -05:00
2021-01-02 18:37:46 -05:00
__declspec ( naked ) void XInput : : MSG_ReadDeltaUsercmdKeyStub2 ( )
{
__asm
{
2021-01-02 20:06:40 -05:00
push ebx // to
push ebp // from
push edi // key
push esi // msg
call XInput : : ApplyMovement
add esp , 10 h
2021-01-02 18:37:46 -05:00
// return back
push 3
push esi
push 0x492085
ret
}
2021-01-02 17:48:48 -05:00
}
2021-05-04 09:47:46 -04:00
void XInput : : MenuNavigate ( ) {
Game : : menuDef_t * menuDef = Game : : Menu_GetFocused ( Game : : uiContext ) ;
# define SIGN(d) ((d > 0) - (d < 0))
if ( menuDef ) {
PollXInputDevices ( ) ;
if ( XInput : : xiPlayerNum ! = - 1 )
{
XINPUT_STATE * xiState = & xiStates [ xiPlayerNum ] ;
// Up/Down
float moveStickX = abs ( xiState - > Gamepad . sThumbLX ) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ? xiState - > Gamepad . sThumbLX / ( float ) std : : numeric_limits < SHORT > ( ) . max ( ) : .0f ;
float moveStickY = abs ( xiState - > Gamepad . sThumbLY ) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE ? xiState - > Gamepad . sThumbLY / ( float ) std : : numeric_limits < SHORT > ( ) . max ( ) : .0f ;
std : : chrono : : milliseconds now = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( std : : chrono : : system_clock : : now ( ) . time_since_epoch ( ) ) ;
std : : chrono : : milliseconds timeSinceLastNavigation = now - lastNavigationTime ;
bool canNavigate = timeSinceLastNavigation > msBetweenNavigations ;
if ( moveStickY > .0f ) {
if ( canNavigate | | SIGN ( moveStickY ) ! = SIGN ( lastMenuNavigationDirection ) ) {
Game : : Menu_SetPrevCursorItem ( Game : : uiContext , menuDef , 1 ) ;
lastMenuNavigationDirection = moveStickY ;
lastNavigationTime = now ;
}
}
else if ( moveStickY < .0f ) {
if ( canNavigate | | SIGN ( moveStickY ) ! = SIGN ( lastMenuNavigationDirection ) ) {
Game : : Menu_SetNextCursorItem ( Game : : uiContext , menuDef , 1 ) ;
lastMenuNavigationDirection = moveStickY ;
lastNavigationTime = now ;
}
}
else {
lastMenuNavigationDirection = .0f ;
}
for ( size_t i = 0 ; i < menuMappings . size ( ) ; i + + )
{
MenuMapping mapping = menuMappings [ i ] ;
auto action = mapping . keystroke ;
if ( mapping . wasPressed ) {
if ( xiState - > Gamepad . wButtons & mapping . input ) {
// Button still pressed, do not send info
}
else {
menuMappings [ i ] . wasPressed = false ;
}
}
else if ( xiState - > Gamepad . wButtons & mapping . input ) {
Game : : UI_KeyEvent ( 0 , mapping . keystroke , 1 ) ;
menuMappings [ i ] . wasPressed = true ;
}
}
}
}
}
2021-01-02 01:49:45 -05:00
XInput : : XInput ( )
{
2021-01-02 18:37:46 -05:00
// poll xinput devices every client frame
2021-01-02 02:17:37 -05:00
Utils : : Hook ( 0x486970 , XInput : : CL_FrameStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2021-01-02 14:42:52 -05:00
2021-01-02 18:37:46 -05:00
// use the xinput state when creating a usercmd
2021-01-02 14:42:52 -05:00
Utils : : Hook ( 0x5A6DB9 , XInput : : CL_CreateCmdStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2021-01-02 17:48:48 -05:00
// package the forward and right move components in the move buttons
Utils : : Hook ( 0x60E38D , XInput : : MSG_WriteDeltaUsercmdKeyStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2021-01-02 20:06:40 -05:00
// send two bytes for sending movement data
Utils : : Hook : : Set < BYTE > ( 0x60E501 , 16 ) ;
Utils : : Hook : : Set < BYTE > ( 0x60E5CD , 16 ) ;
2021-01-02 17:48:48 -05:00
2021-05-04 09:42:22 -04:00
// make sure to parse the movement data properly and apply it
2021-01-02 20:06:40 -05:00
Utils : : Hook ( 0x492127 , XInput : : MSG_ReadDeltaUsercmdKeyStub , HOOK_JUMP ) . install ( ) - > quick ( ) ;
Utils : : Hook ( 0x492009 , XInput : : MSG_ReadDeltaUsercmdKeyStub2 , HOOK_JUMP ) . install ( ) - > quick ( ) ;
2021-05-04 09:42:22 -04:00
PollXInputDevices ( ) ;
if ( xiPlayerNum > = 0 ) {
Vibrate ( 3000 , 3000 ) ;
}
2021-05-04 09:47:46 -04:00
Scheduler : : OnFrame ( MenuNavigate ) ;
2021-01-02 01:49:45 -05:00
}
}