Implement cod native controller support like xinput value retrieval
This commit is contained in:
parent
64fdd04343
commit
d4d1520f65
@ -101,7 +101,7 @@ namespace Components
|
|||||||
Loader::Register(new StructuredData());
|
Loader::Register(new StructuredData());
|
||||||
Loader::Register(new ConnectProtocol());
|
Loader::Register(new ConnectProtocol());
|
||||||
Loader::Register(new StartupMessages());
|
Loader::Register(new StartupMessages());
|
||||||
Loader::Register(new XInput());
|
Loader::Register(new Gamepad());
|
||||||
|
|
||||||
Loader::Register(new Client());
|
Loader::Register(new Client());
|
||||||
|
|
||||||
|
@ -1,154 +1,117 @@
|
|||||||
#include "STDInclude.hpp"
|
#include "STDInclude.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#define XINPUT_SENSITIVITY_MULTIPLIER 4 // Arbitrary value I multiply the xinput senstivity dvar with to get nicer values (0-10 range or something)
|
#define XINPUT_SENSITIVITY_MULTIPLIER 4 // Arbitrary value I multiply the xinput senstivity dvar with to get nicer values (0-10 range or something)
|
||||||
#define SIGN(d) ((d > 0) - (d < 0))
|
#define SIGN(d) ((d > 0) - (d < 0))
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
XINPUT_STATE XInput::xiStates[XUSER_MAX_COUNT];
|
Gamepad::GamePad Gamepad::gamePads[1]{};
|
||||||
XINPUT_STATE XInput::lastXiState = { 0 };
|
std::chrono::milliseconds Gamepad::timeAtFirstHeldMaxLookX = 0ms; // "For how much time in milliseconds has the player been holding a horizontal direction on their stick, fully" (-1.0 or 1.0)
|
||||||
int XInput::xiPlayerNum = -1;
|
bool Gamepad::isHoldingMaxLookX = false;
|
||||||
std::chrono::milliseconds XInput::timeAtFirstHeldMaxLookX = 0ms; // "For how much time in milliseconds has the player been holding a horizontal direction on their stick, fully" (-1.0 or 1.0)
|
bool Gamepad::isADS;
|
||||||
bool XInput::isHoldingMaxLookX = false;
|
|
||||||
bool XInput::isADS;
|
|
||||||
|
|
||||||
Dvar::Var XInput::xpadSensitivity;
|
Dvar::Var Gamepad::gpad_enabled;
|
||||||
Dvar::Var XInput::xpadEarlyTime;
|
Dvar::Var Gamepad::gpad_debug;
|
||||||
Dvar::Var XInput::xpadEarlyMultiplier;
|
Dvar::Var Gamepad::gpad_present;
|
||||||
Dvar::Var XInput::xpadHorizontalMultiplier;
|
Dvar::Var Gamepad::gpad_sticksConfig;
|
||||||
Dvar::Var XInput::xpadVerticalMultiplier;
|
Dvar::Var Gamepad::gpad_buttonConfig;
|
||||||
Dvar::Var XInput::xpadAdsMultiplier;
|
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;
|
||||||
|
|
||||||
float XInput::lastMenuNavigationDirection = .0f;
|
Dvar::Var Gamepad::xpadSensitivity;
|
||||||
std::chrono::milliseconds XInput::lastNavigationTime = 0ms;
|
Dvar::Var Gamepad::xpadEarlyTime;
|
||||||
std::chrono::milliseconds XInput::msBetweenNavigations = 220ms;
|
Dvar::Var Gamepad::xpadEarlyMultiplier;
|
||||||
|
Dvar::Var Gamepad::xpadHorizontalMultiplier;
|
||||||
|
Dvar::Var Gamepad::xpadVerticalMultiplier;
|
||||||
|
Dvar::Var Gamepad::xpadAdsMultiplier;
|
||||||
|
|
||||||
|
Gamepad::GamePadStickDir Gamepad::lastMenuNavigationDirection = GPAD_STICK_DIR_COUNT;
|
||||||
|
std::chrono::milliseconds Gamepad::lastNavigationTime = 0ms;
|
||||||
|
std::chrono::milliseconds Gamepad::msBetweenNavigations = 220ms;
|
||||||
|
|
||||||
// This should be read from a text file in the players/ folder, most probably / or from config_mp.cfg
|
// This should be read from a text file in the players/ folder, most probably / or from config_mp.cfg
|
||||||
std::vector<XInput::ActionMapping> mappings = {
|
std::vector<Gamepad::ActionMapping> mappings = {
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_A, "gostand"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_A, "gostand"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_B, "stance"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_B, "stance"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_X, "usereload"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_X, "usereload"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_Y, "weapnext", false),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_Y, "weapnext", false),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_LEFT_SHOULDER, "smoke"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_LEFT_SHOULDER, "smoke"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_RIGHT_SHOULDER, "frag"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_RIGHT_SHOULDER, "frag"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_LEFT_THUMB, "breath_sprint"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_LEFT_THUMB, "breath_sprint"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_RIGHT_THUMB, "melee"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_RIGHT_THUMB, "melee"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_START, "togglemenu", false),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_START, "togglemenu", false),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_BACK, "scores"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_BACK, "scores"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_LEFT, "actionslot 3"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_LEFT, "actionslot 3"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_RIGHT, "actionslot 2"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_RIGHT, "actionslot 2"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_DOWN, "actionslot 1"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_DOWN, "actionslot 1"),
|
||||||
XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_UP, "actionslot 4"),
|
Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_UP, "actionslot 4"),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Same thing
|
// Same thing
|
||||||
std::vector<XInput::MenuMapping> menuMappings = {
|
std::vector<Gamepad::MenuMapping> menuMappings = {
|
||||||
XInput::MenuMapping(XINPUT_GAMEPAD_A, Game::keyNum_t::K_KP_ENTER),
|
Gamepad::MenuMapping(XINPUT_GAMEPAD_A, Game::keyNum_t::K_KP_ENTER),
|
||||||
XInput::MenuMapping(XINPUT_GAMEPAD_B, Game::keyNum_t::K_ESCAPE),
|
Gamepad::MenuMapping(XINPUT_GAMEPAD_B, Game::keyNum_t::K_ESCAPE),
|
||||||
XInput::MenuMapping(XINPUT_GAMEPAD_DPAD_RIGHT, Game::keyNum_t::K_KP_RIGHTARROW),
|
Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_RIGHT, Game::keyNum_t::K_KP_RIGHTARROW),
|
||||||
XInput::MenuMapping(XINPUT_GAMEPAD_DPAD_LEFT, Game::keyNum_t::K_KP_LEFTARROW),
|
Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_LEFT, Game::keyNum_t::K_KP_LEFTARROW),
|
||||||
XInput::MenuMapping(XINPUT_GAMEPAD_DPAD_UP, Game::keyNum_t::K_KP_UPARROW),
|
Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_UP, Game::keyNum_t::K_KP_UPARROW),
|
||||||
XInput::MenuMapping(XINPUT_GAMEPAD_DPAD_DOWN, Game::keyNum_t::K_KP_DOWNARROW)
|
Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_DOWN, Game::keyNum_t::K_KP_DOWNARROW)
|
||||||
};
|
};
|
||||||
|
|
||||||
void XInput::GetLeftStick01Value(XINPUT_STATE* xiState, float& x, float& y) {
|
// void Gamepad::Vibrate(int leftVal, int rightVal)
|
||||||
GamepadStickTo01(xiState->Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, x);
|
// {
|
||||||
GamepadStickTo01(xiState->Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, y);
|
// // 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);
|
||||||
|
// }
|
||||||
|
|
||||||
void XInput::GetRightStick01Value(XINPUT_STATE* xiState, float& x, float& y) {
|
void Gamepad::CL_GamepadMove(int, Game::usercmd_s* cmd)
|
||||||
GamepadStickTo01(xiState->Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, x);
|
|
||||||
GamepadStickTo01(xiState->Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XInput::GamepadStickTo01(SHORT value, SHORT deadzone, float& output01) {
|
|
||||||
float maxValue = (float)(std::numeric_limits<SHORT>().max() - deadzone);
|
|
||||||
|
|
||||||
output01 = abs(value) > deadzone ? (value - deadzone * SIGN(value)) / maxValue : .0f;
|
|
||||||
|
|
||||||
// log2 allows for a more neat value curve from 0 to 1
|
|
||||||
// It is not functional yet, because I suck at maths
|
|
||||||
//float test = (log2(abs(output01) + 1.f) - 1.f) * SIGN(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void XInput::Vibrate(int leftVal, int rightVal)
|
|
||||||
{
|
{
|
||||||
// Create a Vibraton State
|
auto& gamePad = gamePads[0];
|
||||||
XINPUT_VIBRATION Vibration;
|
|
||||||
|
|
||||||
// Zeroise the Vibration
|
if (gamePad.enabled)
|
||||||
ZeroMemory(&Vibration, sizeof(XINPUT_VIBRATION));
|
|
||||||
|
|
||||||
// Set the Vibration Values
|
|
||||||
Vibration.wLeftMotorSpeed = leftVal;
|
|
||||||
Vibration.wRightMotorSpeed = rightVal;
|
|
||||||
|
|
||||||
// Vibrate the controller
|
|
||||||
XInputSetState(xiPlayerNum, &Vibration);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void XInput::PollXInputDevices()
|
|
||||||
{
|
|
||||||
XInput::xiPlayerNum = -1;
|
|
||||||
|
|
||||||
for (int i = XUSER_MAX_COUNT - 1; i >= 0; i--)
|
|
||||||
{
|
{
|
||||||
if (XInputGetState(i, &xiStates[i]) == ERROR_SUCCESS)
|
if (std::fabs(gamePad.sticks[0]) > 0.0f || std::fabs(gamePad.sticks[1]) > 0.0f) {
|
||||||
XInput::xiPlayerNum = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__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, 0Ch
|
|
||||||
push ebx
|
|
||||||
push ebp
|
|
||||||
push esi
|
|
||||||
|
|
||||||
// return back to original code
|
|
||||||
push 0x486976
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void XInput::CL_GamepadMove(int, Game::usercmd_s* cmd)
|
|
||||||
{
|
|
||||||
if (XInput::xiPlayerNum != -1)
|
|
||||||
{
|
|
||||||
XINPUT_STATE* xiState = &xiStates[xiPlayerNum];
|
|
||||||
|
|
||||||
// Deadzones
|
|
||||||
float moveStickX, moveStickY;
|
|
||||||
GetLeftStick01Value(xiState, moveStickX, moveStickY);
|
|
||||||
|
|
||||||
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
|
// 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->rightmove = static_cast<char>(gamePad.sticks[0] * static_cast<float>(std::numeric_limits<char>().max()));
|
||||||
cmd->forwardmove = moveStickY * std::numeric_limits<char>().max();
|
cmd->forwardmove = static_cast<char>(gamePad.sticks[1] * static_cast<float>(std::numeric_limits<char>().max()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pressingLeftTrigger = xiState->Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false;
|
const bool pressingLeftTrigger = gamePad.analogs[0] > TRIGGER_THRESHOLD_F;
|
||||||
if (pressingLeftTrigger != XInput::lastXiState.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
|
const bool previouslyPressingLeftTrigger = gamePad.lastAnalogs[0] > TRIGGER_THRESHOLD_F;
|
||||||
|
if (pressingLeftTrigger != previouslyPressingLeftTrigger)
|
||||||
{
|
{
|
||||||
if (pressingLeftTrigger) {
|
if (pressingLeftTrigger) {
|
||||||
Command::Execute("+speed_throw");
|
Command::Execute("+speed_throw");
|
||||||
XInput::isADS = true;
|
isADS = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Command::Execute("-speed_throw");
|
Command::Execute("-speed_throw");
|
||||||
XInput::isADS = false;
|
isADS = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pressingRightTrigger = xiState->Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false;
|
const bool pressingRightTrigger = gamePad.analogs[1] > TRIGGER_THRESHOLD_F;
|
||||||
if (pressingRightTrigger != XInput::lastXiState.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
|
const bool previouslyPressingRightTrigger = gamePad.lastAnalogs[1] > TRIGGER_THRESHOLD_F;
|
||||||
|
if (pressingRightTrigger != previouslyPressingRightTrigger)
|
||||||
{
|
{
|
||||||
if (pressingRightTrigger) {
|
if (pressingRightTrigger) {
|
||||||
Command::Execute("+attack");
|
Command::Execute("+attack");
|
||||||
@ -159,9 +122,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Buttons (on/off) mappings
|
// Buttons (on/off) mappings
|
||||||
for (size_t i = 0; i < mappings.size(); i++)
|
for (auto& i : mappings)
|
||||||
{
|
{
|
||||||
auto mapping = mappings[i];
|
auto mapping = i;
|
||||||
auto action = mapping.action;
|
auto action = mapping.action;
|
||||||
auto antiAction = mapping.action;
|
auto antiAction = mapping.action;
|
||||||
|
|
||||||
@ -170,40 +133,38 @@ namespace Components
|
|||||||
antiAction = "-" + mapping.action;
|
antiAction = "-" + mapping.action;
|
||||||
}
|
}
|
||||||
else if (mapping.wasPressed) {
|
else if (mapping.wasPressed) {
|
||||||
if (xiState->Gamepad.wButtons & mapping.input) {
|
if (gamePad.digitals & mapping.input) {
|
||||||
// Button still pressed, do not send info
|
// Button still pressed, do not send info
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mappings[i].wasPressed = false;
|
i.wasPressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xiState->Gamepad.wButtons & mapping.input) {
|
if (gamePad.digitals & mapping.input) {
|
||||||
if (mapping.spamWhenHeld || !mappings[i].wasPressed) {
|
if (mapping.spamWhenHeld || !i.wasPressed) {
|
||||||
Command::Execute(action.c_str());
|
Command::Execute(action);
|
||||||
}
|
}
|
||||||
mappings[i].wasPressed = true;
|
i.wasPressed = true;
|
||||||
}
|
}
|
||||||
else if (mapping.isReversible && mapping.wasPressed) {
|
else if (mapping.isReversible && mapping.wasPressed) {
|
||||||
mappings[i].wasPressed = false;
|
i.wasPressed = false;
|
||||||
Command::Execute(antiAction.c_str());
|
Command::Execute(antiAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&XInput::lastXiState, xiState, sizeof XINPUT_STATE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void XInput::CL_CreateCmdStub()
|
__declspec(naked) void Gamepad::CL_CreateCmdStub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
// do xinput!
|
// do xinput!
|
||||||
push esi
|
push esi
|
||||||
push ebp
|
push ebp
|
||||||
call XInput::CL_GamepadMove
|
call CL_GamepadMove
|
||||||
add esp, 8h
|
add esp, 8h
|
||||||
|
|
||||||
// execute code we patched over
|
// execute code we patched over
|
||||||
@ -217,7 +178,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void XInput::MSG_WriteDeltaUsercmdKeyStub()
|
__declspec(naked) void Gamepad::MSG_WriteDeltaUsercmdKeyStub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -241,7 +202,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XInput::ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to)
|
void Gamepad::ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to)
|
||||||
{
|
{
|
||||||
char forward;
|
char forward;
|
||||||
char right;
|
char right;
|
||||||
@ -263,7 +224,7 @@ namespace Components
|
|||||||
to->rightmove = right;
|
to->rightmove = right;
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void XInput::MSG_ReadDeltaUsercmdKeyStub()
|
__declspec(naked) void Gamepad::MSG_ReadDeltaUsercmdKeyStub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -271,7 +232,7 @@ namespace Components
|
|||||||
push ebp // from
|
push ebp // from
|
||||||
push edi // key
|
push edi // key
|
||||||
push esi // msg
|
push esi // msg
|
||||||
call XInput::ApplyMovement
|
call ApplyMovement
|
||||||
add esp, 10h
|
add esp, 10h
|
||||||
|
|
||||||
// return back
|
// return back
|
||||||
@ -280,7 +241,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void XInput::MSG_ReadDeltaUsercmdKeyStub2()
|
__declspec(naked) void Gamepad::MSG_ReadDeltaUsercmdKeyStub2()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -288,7 +249,7 @@ namespace Components
|
|||||||
push ebp // from
|
push ebp // from
|
||||||
push edi // key
|
push edi // key
|
||||||
push esi // msg
|
push esi // msg
|
||||||
call XInput::ApplyMovement
|
call ApplyMovement
|
||||||
add esp, 10h
|
add esp, 10h
|
||||||
|
|
||||||
// return back
|
// return back
|
||||||
@ -299,72 +260,66 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void XInput::MenuNavigate() {
|
void Gamepad::MenuNavigate()
|
||||||
|
{
|
||||||
|
auto& gamePad = gamePads[0];
|
||||||
Game::menuDef_t* menuDef = Game::Menu_GetFocused(Game::uiContext);
|
Game::menuDef_t* menuDef = Game::Menu_GetFocused(Game::uiContext);
|
||||||
|
|
||||||
if (menuDef) {
|
if (menuDef)
|
||||||
PollXInputDevices();
|
{
|
||||||
|
if (gamePad.enabled)
|
||||||
if (XInput::xiPlayerNum != -1)
|
|
||||||
{
|
{
|
||||||
XINPUT_STATE* xiState = &xiStates[xiPlayerNum];
|
|
||||||
|
|
||||||
// Up/Down
|
|
||||||
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 now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||||
std::chrono::milliseconds timeSinceLastNavigation = now - lastNavigationTime;
|
std::chrono::milliseconds timeSinceLastNavigation = now - lastNavigationTime;
|
||||||
bool canNavigate = timeSinceLastNavigation > msBetweenNavigations;
|
bool canNavigate = timeSinceLastNavigation > msBetweenNavigations;
|
||||||
|
|
||||||
if (moveStickY > .0f) {
|
if (gamePad.stickDown[1][GPAD_STICK_POS]) {
|
||||||
if (canNavigate || SIGN(moveStickY) != SIGN(lastMenuNavigationDirection)) {
|
if (canNavigate) {
|
||||||
Game::Menu_SetPrevCursorItem(Game::uiContext, menuDef, 1);
|
Game::Menu_SetPrevCursorItem(Game::uiContext, menuDef, 1);
|
||||||
lastMenuNavigationDirection = moveStickY;
|
lastMenuNavigationDirection = GPAD_STICK_POS;
|
||||||
lastNavigationTime = now;
|
lastNavigationTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (moveStickY < .0f) {
|
else if (gamePad.stickDown[1][GPAD_STICK_NEG]) {
|
||||||
if (canNavigate || SIGN(moveStickY) != SIGN(lastMenuNavigationDirection)) {
|
if (canNavigate) {
|
||||||
Game::Menu_SetNextCursorItem(Game::uiContext, menuDef, 1);
|
Game::Menu_SetNextCursorItem(Game::uiContext, menuDef, 1);
|
||||||
lastMenuNavigationDirection = moveStickY;
|
lastMenuNavigationDirection = GPAD_STICK_NEG;
|
||||||
lastNavigationTime = now;
|
lastNavigationTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lastMenuNavigationDirection = .0f;
|
lastMenuNavigationDirection = GPAD_STICK_DIR_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < menuMappings.size(); i++)
|
for (auto& mapping : menuMappings)
|
||||||
{
|
{
|
||||||
MenuMapping mapping = menuMappings[i];
|
|
||||||
|
|
||||||
if (mapping.wasPressed) {
|
if (mapping.wasPressed) {
|
||||||
if (xiState->Gamepad.wButtons & mapping.input) {
|
if (gamePad.digitals & mapping.input) {
|
||||||
// Button still pressed, do not send info
|
// Button still pressed, do not send info
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
menuMappings[i].wasPressed = false;
|
mapping.wasPressed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (xiState->Gamepad.wButtons & mapping.input) {
|
else if (gamePad.digitals & mapping.input) {
|
||||||
Game::UI_KeyEvent(0, mapping.keystroke, 1);
|
Game::UI_KeyEvent(0, mapping.keystroke, 1);
|
||||||
menuMappings[i].wasPressed = true;
|
mapping.wasPressed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int XInput::unk_CheckKeyHook(int localClientNum, Game::keyNum_t keyCode) {
|
int Gamepad::unk_CheckKeyHook(int localClientNum, Game::keyNum_t keyCode)
|
||||||
|
{
|
||||||
|
const auto& gamePad = gamePads[0];
|
||||||
|
|
||||||
if (XInput::xiPlayerNum != -1)
|
if (gamePad.enabled)
|
||||||
{
|
{
|
||||||
XINPUT_STATE* xiState = &xiStates[xiPlayerNum];
|
|
||||||
|
|
||||||
if (keyCode == Game::keyNum_t::K_MOUSE2) {
|
if (keyCode == Game::keyNum_t::K_MOUSE2) {
|
||||||
bool pressingLeftTrigger = xiState->Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false;
|
const bool pressingLeftTrigger = gamePad.analogs[0] > TRIGGER_THRESHOLD_F;
|
||||||
if (pressingLeftTrigger != XInput::lastXiState.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
|
const bool previouslyPressingLeftTrigger = gamePad.lastAnalogs[0] > TRIGGER_THRESHOLD_F;
|
||||||
|
if (pressingLeftTrigger != previouslyPressingLeftTrigger)
|
||||||
{
|
{
|
||||||
if (pressingLeftTrigger) {
|
if (pressingLeftTrigger) {
|
||||||
return 1;
|
return 1;
|
||||||
@ -376,17 +331,17 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<int(int, Game::keyNum_t)>(0x48B2D0)(localClientNum, keyCode);
|
return Utils::Hook::Call<int(int, Game::keyNum_t)>(0x48B2D0)(localClientNum, keyCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XInput::MouseOverride(Game::clientActive_t* clientActive, float* mx, float* my) {
|
void Gamepad::MouseOverride(Game::clientActive_t* clientActive, float* mx, float* my)
|
||||||
|
{
|
||||||
|
Gamepad::CL_GetMouseMovementCl(clientActive, mx, my);
|
||||||
|
|
||||||
XInput::CL_GetMouseMovementCl(clientActive, mx, my);
|
const auto& gamePad = gamePads[0];
|
||||||
|
|
||||||
if (XInput::xiPlayerNum != -1)
|
if (gamePad.enabled)
|
||||||
{
|
{
|
||||||
XINPUT_STATE* xiState = &xiStates[xiPlayerNum];
|
|
||||||
|
|
||||||
float viewSensitivityMultiplier = xpadSensitivity.get<float>() * XINPUT_SENSITIVITY_MULTIPLIER;
|
float viewSensitivityMultiplier = xpadSensitivity.get<float>() * XINPUT_SENSITIVITY_MULTIPLIER;
|
||||||
|
|
||||||
float lockedSensitivityMultiplier = xpadEarlyMultiplier.get<float>();
|
float lockedSensitivityMultiplier = xpadEarlyMultiplier.get<float>();
|
||||||
@ -394,21 +349,17 @@ namespace Components
|
|||||||
float generalYSensitivityMultiplier = xpadVerticalMultiplier.get<float>();
|
float generalYSensitivityMultiplier = xpadVerticalMultiplier.get<float>();
|
||||||
std::chrono::milliseconds msBeforeUnlockingSensitivity = std::chrono::milliseconds(xpadEarlyTime.get<int>());
|
std::chrono::milliseconds msBeforeUnlockingSensitivity = std::chrono::milliseconds(xpadEarlyTime.get<int>());
|
||||||
|
|
||||||
float viewStickX, viewStickY;
|
float viewStickX = gamePad.sticks[2];
|
||||||
GetRightStick01Value(xiState, viewStickX, viewStickY);
|
float viewStickY = gamePad.sticks[3];
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
Components::Logger::Print("X:%f \nY:%f\n(holdingMaxX: %d)\n", viewStickX, viewStickY, XInput::isHoldingMaxLookX);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Gamepad horizontal acceleration on view
|
// Gamepad horizontal acceleration on view
|
||||||
if (abs(viewStickX) > 0.80f) {
|
if (abs(viewStickX) > 0.80f) {
|
||||||
if (!XInput::isHoldingMaxLookX) {
|
if (!Gamepad::isHoldingMaxLookX) {
|
||||||
XInput::isHoldingMaxLookX = true;
|
Gamepad::isHoldingMaxLookX = true;
|
||||||
XInput::timeAtFirstHeldMaxLookX = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
Gamepad::timeAtFirstHeldMaxLookX = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
std::chrono::milliseconds hasBeenHoldingLeftXForMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()) - XInput::timeAtFirstHeldMaxLookX;
|
std::chrono::milliseconds hasBeenHoldingLeftXForMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()) - Gamepad::timeAtFirstHeldMaxLookX;
|
||||||
#ifdef STEP_SENSITIVITY
|
#ifdef STEP_SENSITIVITY
|
||||||
if (hasBeenHoldingLeftXForMs < msBeforeUnlockingSensitivity) {
|
if (hasBeenHoldingLeftXForMs < msBeforeUnlockingSensitivity) {
|
||||||
viewStickX *= lockedSensitivityMultiplier;
|
viewStickX *= lockedSensitivityMultiplier;
|
||||||
@ -420,8 +371,8 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
XInput::isHoldingMaxLookX = false;
|
Gamepad::isHoldingMaxLookX = false;
|
||||||
XInput::timeAtFirstHeldMaxLookX = 0ms;
|
Gamepad::timeAtFirstHeldMaxLookX = 0ms;
|
||||||
viewStickX *= lockedSensitivityMultiplier;
|
viewStickX *= lockedSensitivityMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,7 +381,7 @@ namespace Components
|
|||||||
auto ps = &clientActive->snap.ps;
|
auto ps = &clientActive->snap.ps;
|
||||||
|
|
||||||
// DO NOT use clientActive->usingAds ! It only works for toggle ADS
|
// DO NOT use clientActive->usingAds ! It only works for toggle ADS
|
||||||
if (Game::PM_IsAdsAllowed(ps) && XInput::isADS) {
|
if (Game::PM_IsAdsAllowed(ps) && Gamepad::isADS) {
|
||||||
adsMultiplier = xpadAdsMultiplier.get<float>();
|
adsMultiplier = xpadAdsMultiplier.get<float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,8 +391,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handling killstreaks
|
// Handling killstreaks
|
||||||
bool pressingRightTrigger = xiState->Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false;
|
const bool pressingRightTrigger = gamePad.analogs[1] > TRIGGER_THRESHOLD_F;
|
||||||
if (pressingRightTrigger != XInput::lastXiState.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD)
|
const bool previouslyPressingRightTrigger = gamePad.lastAnalogs[1] > TRIGGER_THRESHOLD_F;
|
||||||
|
if (pressingRightTrigger != previouslyPressingRightTrigger)
|
||||||
{
|
{
|
||||||
bool* isInPredator = reinterpret_cast<bool*>(0x8EE3B8);
|
bool* isInPredator = reinterpret_cast<bool*>(0x8EE3B8);
|
||||||
|
|
||||||
@ -459,7 +411,6 @@ namespace Components
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Game -> Client DLL
|
// Game -> Client DLL
|
||||||
__declspec(naked) void CL_GetMouseMovementStub()
|
__declspec(naked) void CL_GetMouseMovementStub()
|
||||||
{
|
{
|
||||||
@ -468,15 +419,14 @@ namespace Components
|
|||||||
push edx;
|
push edx;
|
||||||
push ecx;
|
push ecx;
|
||||||
push eax;
|
push eax;
|
||||||
call XInput::MouseOverride;
|
call Gamepad::MouseOverride;
|
||||||
add esp, 0xC;
|
add esp, 0xC;
|
||||||
ret;
|
ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Client DLL -> Game
|
// Client DLL -> Game
|
||||||
void XInput::CL_GetMouseMovementCl(Game::clientActive_t* result, float* mx, float* my)
|
void Gamepad::CL_GetMouseMovementCl(Game::clientActive_t* result, float* mx, float* my)
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -494,42 +444,269 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XInput::XInput()
|
bool Gamepad::GPad_Check(const int gamePadIndex, const int portIndex)
|
||||||
{
|
{
|
||||||
// poll xinput devices every client frame
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
Utils::Hook(0x486970, XInput::CL_FrameStub, HOOK_JUMP).install()->quick();
|
|
||||||
|
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 < MAX_GAMEPADS; currentPort++)
|
||||||
|
{
|
||||||
|
if (GPad_Check(currentGamePadNum, currentPort))
|
||||||
|
currentGamePadNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::GPad_ConvertStickToFloat(const short x, const short y, float& outX, float& outY)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::GPad_UpdateSticksDown(int gamePadIndex)
|
||||||
|
{
|
||||||
|
assert(gamePadIndex < MAX_GAMEPADS);
|
||||||
|
|
||||||
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
|
for(auto stickIndex = 0u; stickIndex < std::extent_v<decltype(GamePad::sticks)>; stickIndex++)
|
||||||
|
{
|
||||||
|
for(auto dir = 0; dir < 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 == GPAD_STICK_POS)
|
||||||
|
{
|
||||||
|
gamePad.stickDown[stickIndex][dir] = gamePad.sticks[stickIndex] > threshold;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(dir == GPAD_STICK_NEG);
|
||||||
|
gamePad.stickDown[stickIndex][dir] = gamePad.sticks[stickIndex] < -threshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::GPad_UpdateSticks(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
||||||
|
{
|
||||||
|
assert(gamePadIndex < 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);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
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][GPAD_STICK_POS], gamePad.stickDown[0][GPAD_STICK_NEG],
|
||||||
|
gamePad.stickDown[1][GPAD_STICK_POS], gamePad.stickDown[1][GPAD_STICK_NEG],
|
||||||
|
gamePad.stickDown[2][GPAD_STICK_POS], gamePad.stickDown[2][GPAD_STICK_NEG],
|
||||||
|
gamePad.stickDown[3][GPAD_STICK_POS], gamePad.stickDown[3][GPAD_STICK_NEG]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::GPad_UpdateDigitals(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
||||||
|
{
|
||||||
|
assert(gamePadIndex < MAX_GAMEPADS);
|
||||||
|
|
||||||
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
|
gamePad.lastDigitals = gamePad.digitals;
|
||||||
|
gamePad.digitals = state.wButtons;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (gpad_debug.get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Buttons: %x\n", gamePad.digitals);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::GPad_UpdateAnalogs(const int gamePadIndex, const XINPUT_GAMEPAD& state)
|
||||||
|
{
|
||||||
|
assert(gamePadIndex < MAX_GAMEPADS);
|
||||||
|
|
||||||
|
auto& gamePad = gamePads[gamePadIndex];
|
||||||
|
|
||||||
|
const auto buttonDeadZone = gpad_button_deadzone.get<float>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (gpad_debug.get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Triggers: %f %f\n", gamePad.analogs[0], gamePad.analogs[1]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::GPad_UpdateAll()
|
||||||
|
{
|
||||||
|
GPad_RefreshAll();
|
||||||
|
|
||||||
|
for(auto currentGamePadIndex = 0; currentGamePadIndex < 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()
|
||||||
|
{
|
||||||
|
GPad_UpdateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Gamepad::IN_Frame_Hk()
|
||||||
|
{
|
||||||
|
// Call original method
|
||||||
|
Utils::Hook::Call<void()>(0x64C490)();
|
||||||
|
|
||||||
|
IN_GamePadsMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::InitDvars()
|
||||||
|
{
|
||||||
|
gpad_enabled = Dvar::Register<bool>("gpad_enabled", false, Game::DVAR_FLAG_SAVED, "Game pad enabled");
|
||||||
|
gpad_debug = Dvar::Register<bool>("gpad_debug", false, 0, "Game pad debugging");
|
||||||
|
gpad_present = Dvar::Register<bool>("gpad_present", false, 0, "Game pad present");
|
||||||
|
gpad_sticksConfig = Dvar::Register<const char*>("gpad_sticksConfig", "thumbstick_default", Game::DVAR_FLAG_SAVED, "Game pad stick configuration");
|
||||||
|
gpad_buttonConfig = Dvar::Register<const char*>("gpad_buttonConfig", "buttons_default", Game::DVAR_FLAG_SAVED, "Game pad button configuration");
|
||||||
|
gpad_menu_scroll_delay_first = Dvar::Register<int>("gpad_menu_scroll_delay_first", 420, 0, 1000, Game::DVAR_FLAG_SAVED, "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_FLAG_SAVED, "Menu scroll key-repeat delay, for repeats after the first, in milliseconds");
|
||||||
|
gpad_rumble = Dvar::Register<bool>("gpad_rumble", true, Game::DVAR_FLAG_SAVED, "Enable game pad rumble");
|
||||||
|
gpad_stick_pressed_hysteresis = Dvar::Register<float>("gpad_stick_pressed_hysteresis", 0.1f, 0.0f, 1.0f, 0, "Game pad stick pressed no-change-zone around gpad_stick_pressed to prevent bouncing");
|
||||||
|
gpad_stick_pressed = Dvar::Register<float>("gpad_stick_pressed", 0.4f, 0.0, 1.0, 0, "Game pad stick pressed threshhold");
|
||||||
|
gpad_stick_deadzone_max = Dvar::Register<float>("gpad_stick_deadzone_max", 0.01f, 0.0f, 1.0f, 0, "Game pad maximum stick deadzone");
|
||||||
|
gpad_stick_deadzone_min = Dvar::Register<float>("gpad_stick_deadzone_min", 0.2f, 0.0f, 1.0f, 0, "Game pad minimum stick deadzone");
|
||||||
|
gpad_button_deadzone = Dvar::Register<float>("gpad_button_deadzone", 0.13f, 0.0f, 1.0f, 0, "Game pad button deadzone threshhold");
|
||||||
|
gpad_button_lstick_deflect_max = Dvar::Register<float>("gpad_button_lstick_deflect_max", 1.0f, 0.0f, 1.0f, 0, "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, 0, "Game pad maximum pad stick pressed value");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gamepad::IN_Init_Hk()
|
||||||
|
{
|
||||||
|
// Call original method
|
||||||
|
Utils::Hook::Call<void()>(0x45D620)();
|
||||||
|
|
||||||
|
InitDvars();
|
||||||
|
}
|
||||||
|
|
||||||
|
Gamepad::Gamepad()
|
||||||
|
{
|
||||||
|
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Utils::Hook(0x467C03, IN_Init_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x475E9E, IN_Frame_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// use the xinput state when creating a usercmd
|
// use the xinput state when creating a usercmd
|
||||||
Utils::Hook(0x5A6DB9, XInput::CL_CreateCmdStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5A6DB9, Gamepad::CL_CreateCmdStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// package the forward and right move components in the move buttons
|
// package the forward and right move components in the move buttons
|
||||||
Utils::Hook(0x60E38D, XInput::MSG_WriteDeltaUsercmdKeyStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x60E38D, Gamepad::MSG_WriteDeltaUsercmdKeyStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// send two bytes for sending movement data
|
// send two bytes for sending movement data
|
||||||
Utils::Hook::Set<BYTE>(0x60E501, 16);
|
Utils::Hook::Set<BYTE>(0x60E501, 16);
|
||||||
Utils::Hook::Set<BYTE>(0x60E5CD, 16);
|
Utils::Hook::Set<BYTE>(0x60E5CD, 16);
|
||||||
|
|
||||||
// make sure to parse the movement data properly and apply it
|
// make sure to parse the movement data properly and apply it
|
||||||
Utils::Hook(0x492127, XInput::MSG_ReadDeltaUsercmdKeyStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x492127, Gamepad::MSG_ReadDeltaUsercmdKeyStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x492009, XInput::MSG_ReadDeltaUsercmdKeyStub2, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x492009, Gamepad::MSG_ReadDeltaUsercmdKeyStub2, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x5A617D, CL_GetMouseMovementStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A617D, CL_GetMouseMovementStub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x5A6816, CL_GetMouseMovementStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A6816, CL_GetMouseMovementStub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x5A6829, unk_CheckKeyHook, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A6829, unk_CheckKeyHook, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
XInput::xpadSensitivity = Dvar::Register<float>("xpad_sensitivity", 1.9f, 0.1f, 10.0f, Game::DVAR_FLAG_SAVED, "View sensitivity for XInput-compatible gamepads");
|
|
||||||
XInput::xpadEarlyTime = Dvar::Register<int>("xpad_early_time", 130, 0, 1000, Game::DVAR_FLAG_SAVED, "Time (in milliseconds) of reduced view sensitivity");
|
|
||||||
XInput::xpadEarlyMultiplier = Dvar::Register<float>("xpad_early_multiplier", 0.25f, 0.01f, 1.0f, Game::DVAR_FLAG_SAVED, "By how much the view sensitivity is multiplied during xpad_early_time when moving the view stick");
|
|
||||||
XInput::xpadHorizontalMultiplier = Dvar::Register<float>("xpad_horizontal_multiplier", 1.5f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Horizontal view sensitivity multiplier");
|
|
||||||
XInput::xpadVerticalMultiplier = Dvar::Register<float>("xpad_vertical_multiplier", 0.8f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Vertical view sensitivity multiplier");
|
|
||||||
XInput::xpadAdsMultiplier = Dvar::Register<float>("xpad_ads_multiplier", 0.7f, 0.1f, 1.0f, Game::DVAR_FLAG_SAVED, "By how much the view sensitivity is multiplied when aiming down the sights.");
|
|
||||||
|
|
||||||
PollXInputDevices();
|
|
||||||
|
|
||||||
if (xiPlayerNum >= 0) {
|
|
||||||
Vibrate(3000, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
Scheduler::OnFrame(MenuNavigate);
|
Scheduler::OnFrame(MenuNavigate);
|
||||||
|
|
||||||
|
xpadSensitivity = Dvar::Register<float>("xpad_sensitivity", 1.9f, 0.1f, 10.0f, Game::DVAR_FLAG_SAVED, "View sensitivity for XInput-compatible gamepads");
|
||||||
|
xpadEarlyTime = Dvar::Register<int>("xpad_early_time", 130, 0, 1000, Game::DVAR_FLAG_SAVED, "Time (in milliseconds) of reduced view sensitivity");
|
||||||
|
xpadEarlyMultiplier = Dvar::Register<float>("xpad_early_multiplier", 0.25f, 0.01f, 1.0f, Game::DVAR_FLAG_SAVED, "By how much the view sensitivity is multiplied during xpad_early_time when moving the view stick");
|
||||||
|
xpadHorizontalMultiplier = Dvar::Register<float>("xpad_horizontal_multiplier", 1.5f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Horizontal view sensitivity multiplier");
|
||||||
|
xpadVerticalMultiplier = Dvar::Register<float>("xpad_vertical_multiplier", 0.8f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Vertical view sensitivity multiplier");
|
||||||
|
xpadAdsMultiplier = Dvar::Register<float>("xpad_ads_multiplier", 0.7f, 0.1f, 1.0f, Game::DVAR_FLAG_SAVED, "By how much the view sensitivity is multiplied when aiming down the sights.");
|
||||||
|
|
||||||
|
MessageBoxA(NULL, __FUNCTION__, "", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,81 +2,143 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
class XInput : public Component
|
class Gamepad : public Component
|
||||||
{
|
{
|
||||||
public:
|
static constexpr auto MAX_GAMEPADS = 1;
|
||||||
XInput();
|
static constexpr float TRIGGER_THRESHOLD_F = static_cast<float>(XINPUT_GAMEPAD_TRIGGER_THRESHOLD) / static_cast<float>(0xFF);
|
||||||
|
|
||||||
struct ActionMapping {
|
public:
|
||||||
int input;
|
|
||||||
std::string action;
|
|
||||||
bool isReversible;
|
|
||||||
bool wasPressed = false;
|
|
||||||
bool spamWhenHeld = false;
|
|
||||||
|
|
||||||
ActionMapping(int input, std::string action, bool isReversible = true, bool spamWhenHeld = false)
|
Gamepad();
|
||||||
{
|
enum GamePadStickDir
|
||||||
this->action = action;
|
{
|
||||||
this->isReversible = isReversible;
|
GPAD_STICK_POS = 0x0,
|
||||||
this->input = input;
|
GPAD_STICK_NEG = 0x1,
|
||||||
this->spamWhenHeld = spamWhenHeld;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MenuMapping {
|
GPAD_STICK_DIR_COUNT
|
||||||
int input;
|
};
|
||||||
Game::keyNum_t keystroke;
|
|
||||||
bool wasPressed = false;
|
|
||||||
|
|
||||||
MenuMapping(int input, Game::keyNum_t keystroke)
|
struct GamePad
|
||||||
{
|
{
|
||||||
this->keystroke = keystroke;
|
bool enabled;
|
||||||
this->input = input;
|
int portIndex;
|
||||||
}
|
unsigned short digitals;
|
||||||
};
|
unsigned short lastDigitals;
|
||||||
|
float analogs[2];
|
||||||
|
float lastAnalogs[2];
|
||||||
|
float sticks[4];
|
||||||
|
float lastSticks[4];
|
||||||
|
bool stickDown[4][GPAD_STICK_DIR_COUNT];
|
||||||
|
bool stickDownLast[4][GPAD_STICK_DIR_COUNT];
|
||||||
|
float lowRumble;
|
||||||
|
float highRumble;
|
||||||
|
|
||||||
private:
|
XINPUT_VIBRATION rumble;
|
||||||
static XINPUT_STATE xiStates[XUSER_MAX_COUNT];
|
XINPUT_CAPABILITIES caps;
|
||||||
static int xiPlayerNum;
|
};
|
||||||
static XINPUT_STATE lastXiState;
|
|
||||||
|
|
||||||
static bool isHoldingMaxLookX;
|
struct ActionMapping
|
||||||
static std::chrono::milliseconds timeAtFirstHeldMaxLookX;
|
{
|
||||||
static bool isADS;
|
int input;
|
||||||
|
std::string action;
|
||||||
|
bool isReversible;
|
||||||
|
bool wasPressed = false;
|
||||||
|
bool spamWhenHeld = false;
|
||||||
|
|
||||||
static std::chrono::milliseconds lastNavigationTime;
|
ActionMapping(int input, std::string action, bool isReversible = true, bool spamWhenHeld = false)
|
||||||
static std::chrono::milliseconds msBetweenNavigations;
|
{
|
||||||
static float lastMenuNavigationDirection;
|
this->action = action;
|
||||||
|
this->isReversible = isReversible;
|
||||||
|
this->input = input;
|
||||||
|
this->spamWhenHeld = spamWhenHeld;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static Dvar::Var XInput::xpadSensitivity;
|
struct MenuMapping
|
||||||
static Dvar::Var XInput::xpadEarlyTime;
|
{
|
||||||
static Dvar::Var XInput::xpadEarlyMultiplier;
|
int input;
|
||||||
static Dvar::Var XInput::xpadHorizontalMultiplier;
|
Game::keyNum_t keystroke;
|
||||||
static Dvar::Var XInput::xpadVerticalMultiplier;
|
bool wasPressed = false;
|
||||||
static Dvar::Var XInput::xpadAdsMultiplier;
|
|
||||||
|
|
||||||
static void CL_GetMouseMovementCl(Game::clientActive_t* result, float* mx, float* my);
|
MenuMapping(int input, Game::keyNum_t keystroke)
|
||||||
static int unk_CheckKeyHook(int localClientNum, Game::keyNum_t keyCode);
|
{
|
||||||
|
this->keystroke = keystroke;
|
||||||
|
this->input = input;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static void MouseOverride(Game::clientActive_t* clientActive, float* my, float* mx);
|
private:
|
||||||
static void Vibrate(int leftVal = 0, int rightVal = 0);
|
static GamePad gamePads[MAX_GAMEPADS];
|
||||||
|
|
||||||
static void CL_FrameStub();
|
static bool isHoldingMaxLookX;
|
||||||
static void PollXInputDevices();
|
static std::chrono::milliseconds timeAtFirstHeldMaxLookX;
|
||||||
|
static bool isADS;
|
||||||
|
|
||||||
static void CL_CreateCmdStub();
|
static std::chrono::milliseconds lastNavigationTime;
|
||||||
static void CL_GamepadMove(int, Game::usercmd_s*);
|
static std::chrono::milliseconds msBetweenNavigations;
|
||||||
static void MenuNavigate();
|
static GamePadStickDir lastMenuNavigationDirection;
|
||||||
|
|
||||||
static void MSG_WriteDeltaUsercmdKeyStub();
|
static Dvar::Var gpad_enabled;
|
||||||
|
static Dvar::Var gpad_debug;
|
||||||
|
static Dvar::Var gpad_present;
|
||||||
|
static Dvar::Var gpad_sticksConfig;
|
||||||
|
static Dvar::Var gpad_buttonConfig;
|
||||||
|
static Dvar::Var gpad_menu_scroll_delay_first;
|
||||||
|
static Dvar::Var gpad_menu_scroll_delay_rest;
|
||||||
|
static Dvar::Var gpad_rumble;
|
||||||
|
static Dvar::Var gpad_stick_pressed_hysteresis;
|
||||||
|
static Dvar::Var gpad_stick_pressed;
|
||||||
|
static Dvar::Var gpad_stick_deadzone_max;
|
||||||
|
static Dvar::Var gpad_stick_deadzone_min;
|
||||||
|
static Dvar::Var gpad_button_deadzone;
|
||||||
|
static Dvar::Var gpad_button_rstick_deflect_max;
|
||||||
|
static Dvar::Var gpad_button_lstick_deflect_max;
|
||||||
|
|
||||||
static void ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to);
|
static Dvar::Var xpadSensitivity;
|
||||||
|
static Dvar::Var xpadEarlyTime;
|
||||||
|
static Dvar::Var xpadEarlyMultiplier;
|
||||||
|
static Dvar::Var xpadHorizontalMultiplier;
|
||||||
|
static Dvar::Var xpadVerticalMultiplier;
|
||||||
|
static Dvar::Var xpadAdsMultiplier;
|
||||||
|
|
||||||
static void MSG_ReadDeltaUsercmdKeyStub();
|
static void CL_GetMouseMovementCl(Game::clientActive_t* result, float* mx, float* my);
|
||||||
static void MSG_ReadDeltaUsercmdKeyStub2();
|
static int unk_CheckKeyHook(int localClientNum, Game::keyNum_t keyCode);
|
||||||
|
|
||||||
static void GetLeftStick01Value(XINPUT_STATE* xiState, float& x, float& y);
|
static void MouseOverride(Game::clientActive_t* clientActive, float* my, float* mx);
|
||||||
static void GetRightStick01Value(XINPUT_STATE* xiState, float& x, float& y);
|
static void Vibrate(int leftVal = 0, int rightVal = 0);
|
||||||
static void GamepadStickTo01(SHORT value, SHORT deadzone, float& output01);
|
|
||||||
};
|
static void CL_FrameStub();
|
||||||
|
static void PollXInputDevices();
|
||||||
|
|
||||||
|
static void CL_CreateCmdStub();
|
||||||
|
static void CL_GamepadMove(int, Game::usercmd_s*);
|
||||||
|
static void MenuNavigate();
|
||||||
|
|
||||||
|
static void MSG_WriteDeltaUsercmdKeyStub();
|
||||||
|
|
||||||
|
static void ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to);
|
||||||
|
|
||||||
|
static void MSG_ReadDeltaUsercmdKeyStub();
|
||||||
|
static void MSG_ReadDeltaUsercmdKeyStub2();
|
||||||
|
|
||||||
|
static void GetLeftStick01Value(XINPUT_STATE* xiState, float& x, float& y);
|
||||||
|
static void GetRightStick01Value(XINPUT_STATE* xiState, float& x, float& y);
|
||||||
|
static void GamepadStickTo01(SHORT value, SHORT deadzone, float& output01);
|
||||||
|
|
||||||
|
static void GPad_ConvertStickToFloat(short x, short y, float& outX, float& outY);
|
||||||
|
|
||||||
|
static void GPad_UpdateSticksDown(int gamePadIndex);
|
||||||
|
static void GPad_UpdateSticks(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||||
|
static void GPad_UpdateDigitals(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||||
|
static void GPad_UpdateAnalogs(int gamePadIndex, const XINPUT_GAMEPAD& state);
|
||||||
|
|
||||||
|
static bool GPad_Check(int gamePadIndex, int portIndex);
|
||||||
|
static void GPad_RefreshAll();
|
||||||
|
static void GPad_UpdateAll();
|
||||||
|
static void IN_GamePadsMove();
|
||||||
|
static void IN_Frame_Hk();
|
||||||
|
|
||||||
|
static void InitDvars();
|
||||||
|
static void IN_Init_Hk();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -704,12 +704,23 @@ namespace Game
|
|||||||
return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7));
|
return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vec3Normalize(vec3_t& vec)
|
float Vec2Normalize(vec2_t& vec)
|
||||||
|
{
|
||||||
|
const float length = std::sqrt((vec[0] * vec[0]) + (vec[1] * vec[1]));
|
||||||
|
|
||||||
|
vec[0] /= length;
|
||||||
|
vec[1] /= length;
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
float Vec3Normalize(vec3_t& vec)
|
||||||
{
|
{
|
||||||
const float length = static_cast<float>(std::sqrt(std::pow(vec[0], 2) + std::pow(vec[1], 2) + std::pow(vec[2], 2)));
|
const float length = static_cast<float>(std::sqrt(std::pow(vec[0], 2) + std::pow(vec[1], 2) + std::pow(vec[2], 2)));
|
||||||
vec[0] /= length;
|
vec[0] /= length;
|
||||||
vec[1] /= length;
|
vec[1] /= length;
|
||||||
vec[2] /= length;
|
vec[2] /= length;
|
||||||
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out)
|
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out)
|
||||||
|
@ -936,7 +936,8 @@ namespace Game
|
|||||||
|
|
||||||
void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format);
|
void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format);
|
||||||
|
|
||||||
void Vec3Normalize(vec3_t& vec);
|
float Vec2Normalize(vec2_t& vec);
|
||||||
|
float Vec3Normalize(vec3_t& vec);
|
||||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
||||||
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
||||||
void QuatRot(vec3_t* vec, const vec4_t* quat);
|
void QuatRot(vec3_t* vec, const vec4_t* quat);
|
||||||
|
Loading…
Reference in New Issue
Block a user