diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 85dfabb6..514a33a8 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -101,7 +101,7 @@ namespace Components Loader::Register(new StructuredData()); Loader::Register(new ConnectProtocol()); Loader::Register(new StartupMessages()); - Loader::Register(new XInput()); + Loader::Register(new Gamepad()); Loader::Register(new Client()); diff --git a/src/Components/Modules/Gamepad.cpp b/src/Components/Modules/Gamepad.cpp index 26d1b079..c5a6c8b7 100644 --- a/src/Components/Modules/Gamepad.cpp +++ b/src/Components/Modules/Gamepad.cpp @@ -1,154 +1,117 @@ #include "STDInclude.hpp" +#include + #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)) namespace Components { - XINPUT_STATE XInput::xiStates[XUSER_MAX_COUNT]; - XINPUT_STATE XInput::lastXiState = { 0 }; - int XInput::xiPlayerNum = -1; - 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 XInput::isHoldingMaxLookX = false; - bool XInput::isADS; - - Dvar::Var XInput::xpadSensitivity; - Dvar::Var XInput::xpadEarlyTime; - Dvar::Var XInput::xpadEarlyMultiplier; - Dvar::Var XInput::xpadHorizontalMultiplier; - Dvar::Var XInput::xpadVerticalMultiplier; - Dvar::Var XInput::xpadAdsMultiplier; + Gamepad::GamePad Gamepad::gamePads[1]{}; + 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) + bool Gamepad::isHoldingMaxLookX = false; + bool Gamepad::isADS; - float XInput::lastMenuNavigationDirection = .0f; - std::chrono::milliseconds XInput::lastNavigationTime = 0ms; - std::chrono::milliseconds XInput::msBetweenNavigations = 220ms; + Dvar::Var Gamepad::gpad_enabled; + Dvar::Var Gamepad::gpad_debug; + Dvar::Var Gamepad::gpad_present; + 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::xpadSensitivity; + Dvar::Var Gamepad::xpadEarlyTime; + 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 - std::vector mappings = { - XInput::ActionMapping(XINPUT_GAMEPAD_A, "gostand"), - XInput::ActionMapping(XINPUT_GAMEPAD_B, "stance"), - 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_LEFT, "actionslot 3"), - XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_RIGHT, "actionslot 2"), - XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_DOWN, "actionslot 1"), - XInput::ActionMapping(XINPUT_GAMEPAD_DPAD_UP, "actionslot 4"), + std::vector mappings = { + Gamepad::ActionMapping(XINPUT_GAMEPAD_A, "gostand"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_B, "stance"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_X, "usereload"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_Y, "weapnext", false), + Gamepad::ActionMapping(XINPUT_GAMEPAD_LEFT_SHOULDER, "smoke"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_RIGHT_SHOULDER, "frag"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_LEFT_THUMB, "breath_sprint"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_RIGHT_THUMB, "melee"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_START, "togglemenu", false), + Gamepad::ActionMapping(XINPUT_GAMEPAD_BACK, "scores"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_LEFT, "actionslot 3"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_RIGHT, "actionslot 2"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_DOWN, "actionslot 1"), + Gamepad::ActionMapping(XINPUT_GAMEPAD_DPAD_UP, "actionslot 4"), }; // Same thing - std::vector 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) + std::vector menuMappings = { + Gamepad::MenuMapping(XINPUT_GAMEPAD_A, Game::keyNum_t::K_KP_ENTER), + Gamepad::MenuMapping(XINPUT_GAMEPAD_B, Game::keyNum_t::K_ESCAPE), + Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_RIGHT, Game::keyNum_t::K_KP_RIGHTARROW), + Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_LEFT, Game::keyNum_t::K_KP_LEFTARROW), + Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_UP, Game::keyNum_t::K_KP_UPARROW), + Gamepad::MenuMapping(XINPUT_GAMEPAD_DPAD_DOWN, Game::keyNum_t::K_KP_DOWNARROW) }; - void XInput::GetLeftStick01Value(XINPUT_STATE* xiState, float& x, float& y) { - GamepadStickTo01(xiState->Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, x); - GamepadStickTo01(xiState->Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, y); - } +// void Gamepad::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); +// } - void XInput::GetRightStick01Value(XINPUT_STATE* xiState, float& x, float& y) { - 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().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) + void Gamepad::CL_GamepadMove(int, Game::usercmd_s* cmd) { - // Create a Vibraton State - XINPUT_VIBRATION Vibration; + auto& gamePad = gamePads[0]; - // 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::PollXInputDevices() - { - XInput::xiPlayerNum = -1; - - for (int i = XUSER_MAX_COUNT - 1; i >= 0; i--) + if (gamePad.enabled) { - if (XInputGetState(i, &xiStates[i]) == ERROR_SUCCESS) - 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) { + if (std::fabs(gamePad.sticks[0]) > 0.0f || std::fabs(gamePad.sticks[1]) > 0.0f) { // 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().max(); - cmd->forwardmove = moveStickY * std::numeric_limits().max(); + cmd->rightmove = static_cast(gamePad.sticks[0] * static_cast(std::numeric_limits().max())); + cmd->forwardmove = static_cast(gamePad.sticks[1] * static_cast(std::numeric_limits().max())); } - bool pressingLeftTrigger = xiState->Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false; - if (pressingLeftTrigger != XInput::lastXiState.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + const bool pressingLeftTrigger = gamePad.analogs[0] > TRIGGER_THRESHOLD_F; + const bool previouslyPressingLeftTrigger = gamePad.lastAnalogs[0] > TRIGGER_THRESHOLD_F; + if (pressingLeftTrigger != previouslyPressingLeftTrigger) { if (pressingLeftTrigger) { Command::Execute("+speed_throw"); - XInput::isADS = true; + isADS = true; } else { Command::Execute("-speed_throw"); - XInput::isADS = false; + isADS = false; } } - bool pressingRightTrigger = xiState->Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false; - if (pressingRightTrigger != XInput::lastXiState.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + const bool pressingRightTrigger = gamePad.analogs[1] > TRIGGER_THRESHOLD_F; + const bool previouslyPressingRightTrigger = gamePad.lastAnalogs[1] > TRIGGER_THRESHOLD_F; + if (pressingRightTrigger != previouslyPressingRightTrigger) { if (pressingRightTrigger) { Command::Execute("+attack"); @@ -159,9 +122,9 @@ namespace Components } // Buttons (on/off) mappings - for (size_t i = 0; i < mappings.size(); i++) - { - auto mapping = mappings[i]; + for (auto& i : mappings) + { + auto mapping = i; auto action = mapping.action; auto antiAction = mapping.action; @@ -170,40 +133,38 @@ namespace Components antiAction = "-" + mapping.action; } else if (mapping.wasPressed) { - if (xiState->Gamepad.wButtons & mapping.input) { + if (gamePad.digitals & mapping.input) { // Button still pressed, do not send info } else { - mappings[i].wasPressed = false; + i.wasPressed = false; } continue; } - if (xiState->Gamepad.wButtons & mapping.input) { - if (mapping.spamWhenHeld || !mappings[i].wasPressed) { - Command::Execute(action.c_str()); + if (gamePad.digitals & mapping.input) { + if (mapping.spamWhenHeld || !i.wasPressed) { + Command::Execute(action); } - mappings[i].wasPressed = true; + i.wasPressed = true; } else if (mapping.isReversible && mapping.wasPressed) { - mappings[i].wasPressed = false; - Command::Execute(antiAction.c_str()); + i.wasPressed = false; + Command::Execute(antiAction); } } - - memcpy(&XInput::lastXiState, xiState, sizeof XINPUT_STATE); } } - __declspec(naked) void XInput::CL_CreateCmdStub() + __declspec(naked) void Gamepad::CL_CreateCmdStub() { __asm { // do xinput! push esi push ebp - call XInput::CL_GamepadMove + call CL_GamepadMove add esp, 8h // execute code we patched over @@ -217,7 +178,7 @@ namespace Components } } - __declspec(naked) void XInput::MSG_WriteDeltaUsercmdKeyStub() + __declspec(naked) void Gamepad::MSG_WriteDeltaUsercmdKeyStub() { __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 right; @@ -263,7 +224,7 @@ namespace Components to->rightmove = right; } - __declspec(naked) void XInput::MSG_ReadDeltaUsercmdKeyStub() + __declspec(naked) void Gamepad::MSG_ReadDeltaUsercmdKeyStub() { __asm { @@ -271,7 +232,7 @@ namespace Components push ebp // from push edi // key push esi // msg - call XInput::ApplyMovement + call ApplyMovement add esp, 10h // return back @@ -280,7 +241,7 @@ namespace Components } } - __declspec(naked) void XInput::MSG_ReadDeltaUsercmdKeyStub2() + __declspec(naked) void Gamepad::MSG_ReadDeltaUsercmdKeyStub2() { __asm { @@ -288,7 +249,7 @@ namespace Components push ebp // from push edi // key push esi // msg - call XInput::ApplyMovement + call ApplyMovement add esp, 10h // 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); - if (menuDef) { - PollXInputDevices(); - - if (XInput::xiPlayerNum != -1) + if (menuDef) + { + if (gamePad.enabled) { - 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().max() : .0f; - std::chrono::milliseconds now = std::chrono::duration_cast(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)) { + if (gamePad.stickDown[1][GPAD_STICK_POS]) { + if (canNavigate) { Game::Menu_SetPrevCursorItem(Game::uiContext, menuDef, 1); - lastMenuNavigationDirection = moveStickY; + lastMenuNavigationDirection = GPAD_STICK_POS; lastNavigationTime = now; } } - else if (moveStickY < .0f) { - if (canNavigate || SIGN(moveStickY) != SIGN(lastMenuNavigationDirection)) { + else if (gamePad.stickDown[1][GPAD_STICK_NEG]) { + if (canNavigate) { Game::Menu_SetNextCursorItem(Game::uiContext, menuDef, 1); - lastMenuNavigationDirection = moveStickY; + lastMenuNavigationDirection = GPAD_STICK_NEG; lastNavigationTime = now; } } else { - lastMenuNavigationDirection = .0f; + lastMenuNavigationDirection = GPAD_STICK_DIR_COUNT; } - for (size_t i = 0; i < menuMappings.size(); i++) - { - MenuMapping mapping = menuMappings[i]; - + for (auto& mapping : menuMappings) + { if (mapping.wasPressed) { - if (xiState->Gamepad.wButtons & mapping.input) { + if (gamePad.digitals & mapping.input) { // Button still pressed, do not send info } 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); - 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) { - bool pressingLeftTrigger = xiState->Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false; - if (pressingLeftTrigger != XInput::lastXiState.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + const bool pressingLeftTrigger = gamePad.analogs[0] > TRIGGER_THRESHOLD_F; + const bool previouslyPressingLeftTrigger = gamePad.lastAnalogs[0] > TRIGGER_THRESHOLD_F; + if (pressingLeftTrigger != previouslyPressingLeftTrigger) { if (pressingLeftTrigger) { return 1; @@ -376,17 +331,17 @@ namespace Components } } - Utils::Hook::Call(0x48B2D0)(localClientNum, keyCode); + return Utils::Hook::Call(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() * XINPUT_SENSITIVITY_MULTIPLIER; float lockedSensitivityMultiplier = xpadEarlyMultiplier.get(); @@ -394,21 +349,17 @@ namespace Components float generalYSensitivityMultiplier = xpadVerticalMultiplier.get(); std::chrono::milliseconds msBeforeUnlockingSensitivity = std::chrono::milliseconds(xpadEarlyTime.get()); - float viewStickX, viewStickY; - GetRightStick01Value(xiState, viewStickX, viewStickY); - -#ifdef DEBUG - Components::Logger::Print("X:%f \nY:%f\n(holdingMaxX: %d)\n", viewStickX, viewStickY, XInput::isHoldingMaxLookX); -#endif + float viewStickX = gamePad.sticks[2]; + float viewStickY = gamePad.sticks[3]; // Gamepad horizontal acceleration on view if (abs(viewStickX) > 0.80f) { - if (!XInput::isHoldingMaxLookX) { - XInput::isHoldingMaxLookX = true; - XInput::timeAtFirstHeldMaxLookX = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); + if (!Gamepad::isHoldingMaxLookX) { + Gamepad::isHoldingMaxLookX = true; + Gamepad::timeAtFirstHeldMaxLookX = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()); } else { - std::chrono::milliseconds hasBeenHoldingLeftXForMs = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) - XInput::timeAtFirstHeldMaxLookX; + std::chrono::milliseconds hasBeenHoldingLeftXForMs = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) - Gamepad::timeAtFirstHeldMaxLookX; #ifdef STEP_SENSITIVITY if (hasBeenHoldingLeftXForMs < msBeforeUnlockingSensitivity) { viewStickX *= lockedSensitivityMultiplier; @@ -420,8 +371,8 @@ namespace Components } } else { - XInput::isHoldingMaxLookX = false; - XInput::timeAtFirstHeldMaxLookX = 0ms; + Gamepad::isHoldingMaxLookX = false; + Gamepad::timeAtFirstHeldMaxLookX = 0ms; viewStickX *= lockedSensitivityMultiplier; } @@ -430,7 +381,7 @@ namespace Components auto ps = &clientActive->snap.ps; // 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(); } @@ -440,8 +391,9 @@ namespace Components } // Handling killstreaks - bool pressingRightTrigger = xiState->Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD ? true : false; - if (pressingRightTrigger != XInput::lastXiState.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + const bool pressingRightTrigger = gamePad.analogs[1] > TRIGGER_THRESHOLD_F; + const bool previouslyPressingRightTrigger = gamePad.lastAnalogs[1] > TRIGGER_THRESHOLD_F; + if (pressingRightTrigger != previouslyPressingRightTrigger) { bool* isInPredator = reinterpret_cast(0x8EE3B8); @@ -459,7 +411,6 @@ namespace Components } - // Game -> Client DLL __declspec(naked) void CL_GetMouseMovementStub() { @@ -468,15 +419,14 @@ namespace Components push edx; push ecx; push eax; - call XInput::MouseOverride; + call Gamepad::MouseOverride; add esp, 0xC; ret; } } - // 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 { @@ -494,42 +444,269 @@ namespace Components } } - XInput::XInput() + bool Gamepad::GPad_Check(const int gamePadIndex, const int portIndex) { - // poll xinput devices every client frame - Utils::Hook(0x486970, XInput::CL_FrameStub, HOOK_JUMP).install()->quick(); + 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 < 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(x) / static_cast(std::numeric_limits::max()); + stickVec[1] = static_cast(y) / static_cast(std::numeric_limits::max()); + + const auto deadZoneTotal = gpad_stick_deadzone_min.get() + gpad_stick_deadzone_max.get(); + auto len = Game::Vec2Normalize(stickVec); + + if (gpad_stick_deadzone_min.get() <= len) + { + if (1.0f - gpad_stick_deadzone_max.get() >= len) + len = (len - gpad_stick_deadzone_min.get()) / (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; 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(); + + if (gamePad.stickDownLast[stickIndex][dir]) + threshold -= gpad_stick_pressed_hysteresis.get(); + else + threshold += gpad_stick_pressed_hysteresis.get(); + + 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()) + { + 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(); + if(std::fabs(gamePad.sticks[0]) > leftDeflect || std::fabs(gamePad.sticks[1]) > leftDeflect) + gamePad.digitals &= ~static_cast(XINPUT_GAMEPAD_LEFT_THUMB); + const auto rightDeflect = gpad_button_rstick_deflect_max.get(); + if(std::fabs(gamePad.sticks[2]) > leftDeflect || std::fabs(gamePad.sticks[3]) > rightDeflect) + gamePad.digitals &= ~static_cast(XINPUT_GAMEPAD_RIGHT_THUMB); + +#ifdef DEBUG + if (gpad_debug.get()) + { + 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(); + + gamePad.lastAnalogs[0] = gamePad.analogs[0]; + gamePad.analogs[0] = static_cast(state.bLeftTrigger) / static_cast(std::numeric_limits::max()); + if (gamePad.analogs[0] < buttonDeadZone) + gamePad.analogs[0] = 0.0f; + + + gamePad.lastAnalogs[1] = gamePad.analogs[1]; + gamePad.analogs[1] = static_cast(state.bRightTrigger) / static_cast(std::numeric_limits::max()); + if (gamePad.analogs[1] < buttonDeadZone) + gamePad.analogs[1] = 0.0f; + +#ifdef DEBUG + if (gpad_debug.get()) + { + 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(0x64C490)(); + + IN_GamePadsMove(); + } + + void Gamepad::InitDvars() + { + gpad_enabled = Dvar::Register("gpad_enabled", false, Game::DVAR_FLAG_SAVED, "Game pad enabled"); + gpad_debug = Dvar::Register("gpad_debug", false, 0, "Game pad debugging"); + gpad_present = Dvar::Register("gpad_present", false, 0, "Game pad present"); + gpad_sticksConfig = Dvar::Register("gpad_sticksConfig", "thumbstick_default", Game::DVAR_FLAG_SAVED, "Game pad stick configuration"); + gpad_buttonConfig = Dvar::Register("gpad_buttonConfig", "buttons_default", Game::DVAR_FLAG_SAVED, "Game pad button configuration"); + gpad_menu_scroll_delay_first = Dvar::Register("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("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("gpad_rumble", true, Game::DVAR_FLAG_SAVED, "Enable game pad rumble"); + gpad_stick_pressed_hysteresis = Dvar::Register("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("gpad_stick_pressed", 0.4f, 0.0, 1.0, 0, "Game pad stick pressed threshhold"); + gpad_stick_deadzone_max = Dvar::Register("gpad_stick_deadzone_max", 0.01f, 0.0f, 1.0f, 0, "Game pad maximum stick deadzone"); + gpad_stick_deadzone_min = Dvar::Register("gpad_stick_deadzone_min", 0.2f, 0.0f, 1.0f, 0, "Game pad minimum stick deadzone"); + gpad_button_deadzone = Dvar::Register("gpad_button_deadzone", 0.13f, 0.0f, 1.0f, 0, "Game pad button deadzone threshhold"); + gpad_button_lstick_deflect_max = Dvar::Register("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("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(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 - 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 - 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 Utils::Hook::Set(0x60E501, 16); Utils::Hook::Set(0x60E5CD, 16); // make sure to parse the movement data properly and apply it - Utils::Hook(0x492127, XInput::MSG_ReadDeltaUsercmdKeyStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x492009, XInput::MSG_ReadDeltaUsercmdKeyStub2, HOOK_JUMP).install()->quick(); + Utils::Hook(0x492127, Gamepad::MSG_ReadDeltaUsercmdKeyStub, 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(0x5A6816, CL_GetMouseMovementStub, HOOK_CALL).install()->quick(); Utils::Hook(0x5A6829, unk_CheckKeyHook, HOOK_CALL).install()->quick(); - XInput::xpadSensitivity = Dvar::Register("xpad_sensitivity", 1.9f, 0.1f, 10.0f, Game::DVAR_FLAG_SAVED, "View sensitivity for XInput-compatible gamepads"); - XInput::xpadEarlyTime = Dvar::Register("xpad_early_time", 130, 0, 1000, Game::DVAR_FLAG_SAVED, "Time (in milliseconds) of reduced view sensitivity"); - XInput::xpadEarlyMultiplier = Dvar::Register("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("xpad_horizontal_multiplier", 1.5f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Horizontal view sensitivity multiplier"); - XInput::xpadVerticalMultiplier = Dvar::Register("xpad_vertical_multiplier", 0.8f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Vertical view sensitivity multiplier"); - XInput::xpadAdsMultiplier = Dvar::Register("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); + + xpadSensitivity = Dvar::Register("xpad_sensitivity", 1.9f, 0.1f, 10.0f, Game::DVAR_FLAG_SAVED, "View sensitivity for XInput-compatible gamepads"); + xpadEarlyTime = Dvar::Register("xpad_early_time", 130, 0, 1000, Game::DVAR_FLAG_SAVED, "Time (in milliseconds) of reduced view sensitivity"); + xpadEarlyMultiplier = Dvar::Register("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("xpad_horizontal_multiplier", 1.5f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Horizontal view sensitivity multiplier"); + xpadVerticalMultiplier = Dvar::Register("xpad_vertical_multiplier", 0.8f, 1.0f, 20.0f, Game::DVAR_FLAG_SAVED, "Vertical view sensitivity multiplier"); + xpadAdsMultiplier = Dvar::Register("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); } } diff --git a/src/Components/Modules/Gamepad.hpp b/src/Components/Modules/Gamepad.hpp index 60f2821e..c68d97ca 100644 --- a/src/Components/Modules/Gamepad.hpp +++ b/src/Components/Modules/Gamepad.hpp @@ -2,81 +2,143 @@ namespace Components { - class XInput : public Component - { - public: - XInput(); + class Gamepad : public Component + { + static constexpr auto MAX_GAMEPADS = 1; + static constexpr float TRIGGER_THRESHOLD_F = static_cast(XINPUT_GAMEPAD_TRIGGER_THRESHOLD) / static_cast(0xFF); - struct ActionMapping { - int input; - std::string action; - bool isReversible; - bool wasPressed = false; - bool spamWhenHeld = false; + public: - ActionMapping(int input, std::string action, bool isReversible = true, bool spamWhenHeld = false) - { - this->action = action; - this->isReversible = isReversible; - this->input = input; - this->spamWhenHeld = spamWhenHeld; - } - }; + Gamepad(); + enum GamePadStickDir + { + GPAD_STICK_POS = 0x0, + GPAD_STICK_NEG = 0x1, - struct MenuMapping { - int input; - Game::keyNum_t keystroke; - bool wasPressed = false; + GPAD_STICK_DIR_COUNT + }; + + struct GamePad + { + bool enabled; + 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; - MenuMapping(int input, Game::keyNum_t keystroke) - { - this->keystroke = keystroke; - this->input = input; - } - }; + XINPUT_VIBRATION rumble; + XINPUT_CAPABILITIES caps; + }; - private: - static XINPUT_STATE xiStates[XUSER_MAX_COUNT]; - static int xiPlayerNum; - static XINPUT_STATE lastXiState; + struct ActionMapping + { + int input; + std::string action; + bool isReversible; + bool wasPressed = false; + bool spamWhenHeld = false; - static bool isHoldingMaxLookX; - static std::chrono::milliseconds timeAtFirstHeldMaxLookX; - static bool isADS; + ActionMapping(int input, std::string action, bool isReversible = true, bool spamWhenHeld = false) + { + this->action = action; + this->isReversible = isReversible; + this->input = input; + this->spamWhenHeld = spamWhenHeld; + } + }; - static std::chrono::milliseconds lastNavigationTime; - static std::chrono::milliseconds msBetweenNavigations; - static float lastMenuNavigationDirection; + struct MenuMapping + { + int input; + Game::keyNum_t keystroke; + bool wasPressed = false; - static Dvar::Var XInput::xpadSensitivity; - static Dvar::Var XInput::xpadEarlyTime; - static Dvar::Var XInput::xpadEarlyMultiplier; - static Dvar::Var XInput::xpadHorizontalMultiplier; - static Dvar::Var XInput::xpadVerticalMultiplier; - static Dvar::Var XInput::xpadAdsMultiplier; + MenuMapping(int input, Game::keyNum_t keystroke) + { + this->keystroke = keystroke; + this->input = input; + } + }; - static void CL_GetMouseMovementCl(Game::clientActive_t* result, float* mx, float* my); - static int unk_CheckKeyHook(int localClientNum, Game::keyNum_t keyCode); + private: + static GamePad gamePads[MAX_GAMEPADS]; - static void MouseOverride(Game::clientActive_t* clientActive, float* my, float* mx); - static void Vibrate(int leftVal = 0, int rightVal = 0); + static bool isHoldingMaxLookX; + static std::chrono::milliseconds timeAtFirstHeldMaxLookX; + static bool isADS; - static void CL_FrameStub(); - static void PollXInputDevices(); + static std::chrono::milliseconds lastNavigationTime; + static std::chrono::milliseconds msBetweenNavigations; + static GamePadStickDir lastMenuNavigationDirection; - static void CL_CreateCmdStub(); - static void CL_GamepadMove(int, Game::usercmd_s*); - static void MenuNavigate(); + 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 MSG_WriteDeltaUsercmdKeyStub(); + 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 ApplyMovement(Game::msg_t* msg, int key, Game::usercmd_s* from, Game::usercmd_s* to); + static void CL_GetMouseMovementCl(Game::clientActive_t* result, float* mx, float* my); + static int unk_CheckKeyHook(int localClientNum, Game::keyNum_t keyCode); - static void MSG_ReadDeltaUsercmdKeyStub(); - static void MSG_ReadDeltaUsercmdKeyStub2(); + static void MouseOverride(Game::clientActive_t* clientActive, float* my, float* mx); + static void Vibrate(int leftVal = 0, int rightVal = 0); - 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 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(); + }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index a2b43cdb..8415b93d 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -704,12 +704,23 @@ namespace Game 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(std::sqrt(std::pow(vec[0], 2) + std::pow(vec[1], 2) + std::pow(vec[2], 2))); vec[0] /= length; vec[1] /= length; vec[2] /= length; + return length; } void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out) diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 5166eb5b..37a243d6 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -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 Vec3Normalize(vec3_t& vec); + float Vec2Normalize(vec2_t& vec); + float Vec3Normalize(vec3_t& vec); void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out); void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution); void QuatRot(vec3_t* vec, const vec4_t* quat);