diff --git a/SHADE_Engine/src/Input/SHInputManager.cpp b/SHADE_Engine/src/Input/SHInputManager.cpp index 18d9e3e2..c665a9c9 100644 --- a/SHADE_Engine/src/Input/SHInputManager.cpp +++ b/SHADE_Engine/src/Input/SHInputManager.cpp @@ -19,6 +19,10 @@ namespace SHADE /*------------------------------------------------------------------------*/ /* Static defines */ /*------------------------------------------------------------------------*/ + + bool SHInputManager::controllerInUse = false; + + std::map SHInputManager::bindings; unsigned SHInputManager::keyCount = 0; bool SHInputManager::keys[MAX_KEYS]; @@ -41,6 +45,60 @@ namespace SHADE int SHInputManager::mouseWheelVerticalDelta = 0; int SHInputManager::mouseWheelVerticalDeltaPoll = 0; + unsigned char SHInputManager::controllersConnectedCount = 0; + unsigned SHInputManager::controllersInputCount[XUSER_MAX_COUNT]; + unsigned SHInputManager::controllersButtonCount[XUSER_MAX_COUNT]; + short SHInputManager::controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + short SHInputManager::controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + double SHInputManager::controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + double SHInputManager::controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //Internal helper function for splitting between inputs + bool SHInputManager::controllerConsideredHeld(size_t inputIdx, short value) noexcept + { + if (inputIdx >= MAX_CONTROLLER_INPUT) return false; //Bounds check + else + { + if (inputIdx < NUM_CONTROLLER_BUTTON) + { + return static_cast(value); + } + else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER) + { + return (value > XINPUT_GAMEPAD_TRIGGER_THRESHOLD); + } + else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER + NUM_CONTROLLER_TRIGGER) + { + return (std::abs(value) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE); + } + else + { + return (std::abs(value) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE); + } + } + } + + //Internal helper function for getting normalised input value + double SHInputManager::controllerNormalisedValue(size_t inputIdx, short value) noexcept + { + if (inputIdx >= MAX_CONTROLLER_INPUT) return 0.0; //Bounds check + else + { + if (inputIdx < NUM_CONTROLLER_BUTTON) + { + return static_cast(value); + } + else if (inputIdx < NUM_CONTROLLER_BUTTON + NUM_CONTROLLER_TRIGGER) //8-bit triggers, 0 to 255 + { + return static_cast(value) / static_cast(UCHAR_MAX); + } + else //16-bit thumbsticks, -32768 to 32767 + { + return static_cast(value) / static_cast(SHRT_MAX); + } + } + } + void SHInputManager::UpdateInput(double dt) noexcept { //Keyboard and Mouse Buttons//////////////////////////////////////////////// @@ -120,6 +178,273 @@ namespace SHADE mouseWheelVerticalDelta = 0; mouseWheelVerticalDelta = mouseWheelVerticalDeltaPoll; mouseWheelVerticalDeltaPoll = 0; + + //Controllers////////////////////////////////////////////////////////////// + + controllersConnectedCount = 0; + + //Set last controller states + memcpy(controllersLast, controllers, sizeof(controllers)); + + //Reset controller states + SecureZeroMemory(&controllers, sizeof(controllers)); + + //https://learn.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput#getting-controller-state + for (DWORD c = 0; c < XUSER_MAX_COUNT; ++c) + { + controllersInputCount[c] = 0; + controllersButtonCount[c] = 0; + + XINPUT_STATE state; + SecureZeroMemory(&state, sizeof(XINPUT_STATE)); + + //Get the state of controller from XInput + DWORD result = XInputGetState(c, &state); + + //Write gamepad data + if (result == ERROR_SUCCESS) + { + ++controllersConnectedCount; + + //DIGITAL BUTTONS/////////////////////////////////////// + + //DPAD UP + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_UP)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_UP)] = 0; + } + + //DPAD DOWN + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_DOWN)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_DOWN)] = 0; + } + + //DPAD LEFT + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_LEFT)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_LEFT)] = 0; + } + + //DPAD RIGHT + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_RIGHT)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::DPAD_RIGHT)] = 0; + } + + //START + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) + { + controllers[c][static_cast(SH_CONTROLLERCODE::START)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::START)] = 0; + } + + //BACK + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) + { + controllers[c][static_cast(SH_CONTROLLERCODE::BACK)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::BACK)] = 0; + } + + //LEFT THUMBSTICK BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK)] = 0; + } + + //RIGHT THUMBSTICK BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK)] = 0; + } + + //LEFT SHOULDER BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_SHOULDER)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_SHOULDER)] = 0; + } + + //RIGHT SHOULDER BUTTON + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_SHOULDER)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_SHOULDER)] = 0; + } + + //A + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) + { + controllers[c][static_cast(SH_CONTROLLERCODE::A)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::A)] = 0; + } + + //B + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) + { + controllers[c][static_cast(SH_CONTROLLERCODE::B)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::B)] = 0; + } + + //X + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) + { + controllers[c][static_cast(SH_CONTROLLERCODE::X)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::X)] = 0; + } + + //Y + if (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) + { + controllers[c][static_cast(SH_CONTROLLERCODE::Y)] = 1; + ++controllersInputCount[c]; + ++controllersButtonCount[c]; + } + else + { + controllers[c][static_cast(SH_CONTROLLERCODE::Y)] = 0; + } + + //8 BIT VALUES (0 - 255)/////////////////////////////////// + + //LEFT TRIGGER + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_TRIGGER)] = state.Gamepad.bLeftTrigger; + if (state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + { + ++controllersInputCount[c]; //Registered as held + } + + //RIGHT TRIGGER + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_TRIGGER)] = state.Gamepad.bRightTrigger; + if (state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + { + ++controllersInputCount[c]; //Registered as held + } + + //16 BIT VALUES (0 - 65535)//////////////////////////////// + + //LEFT THUMBSTICK X + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK_X)] = state.Gamepad.sThumbLX; + if (std::abs(state.Gamepad.sThumbLX) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + + //LEFT THUMBSTICK Y + controllers[c][static_cast(SH_CONTROLLERCODE::LEFT_THUMBSTICK_Y)] = state.Gamepad.sThumbLY; + if (std::abs(state.Gamepad.sThumbLY) > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + + //RIGHT THUMBSTICK X + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK_X)] = state.Gamepad.sThumbRX; + if (std::abs(state.Gamepad.sThumbRX) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + + //RIGHT THUMBSTICK Y + controllers[c][static_cast(SH_CONTROLLERCODE::RIGHT_THUMBSTICK_Y)] = state.Gamepad.sThumbRY; + if (std::abs(state.Gamepad.sThumbRY) > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) + { + ++controllersInputCount[c]; + } + } + + //Timers and updating if controller is presently in use + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (controllerConsideredHeld(i, controllers[c][i])) //Considered on + { + controllerInUse = true; + if (!controllerConsideredHeld(i, controllersLast[c][i])) //Just on + { + controllersHeldTime[c][i] = 0.0; //Reset timer + } + controllersHeldTime[c][i] += dt; //Tick up + } + else //Considered off + { + if (controllerConsideredHeld(i, controllersLast[c][i])) //Just off + { + controllersReleasedTime[c][i] = 0.0; //Reset timer + } + controllersReleasedTime[c][i] += dt; //Tick up + } + } + } } bool SHInputManager::AnyKeyDown(SH_KEYCODE* firstDetected) noexcept @@ -161,4 +486,336 @@ namespace SHADE return false; } + //Any controller input being held + //For analog, this means going being deadzone values + bool SHInputManager::AnyControllerInput(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerInputDown(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i]) && !controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerInputUp(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < MAX_CONTROLLER_INPUT; ++i) + { + if (!controllerConsideredHeld(i, controllers[cNum][i]) && controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerButton(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerButtonDown(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i) + { + if (controllerConsideredHeld(i, controllers[cNum][i]) && !controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + bool SHInputManager::AnyControllerButtonUp(SH_CONTROLLERCODE* firstDetected, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + for (size_t i = 0; i < NUM_CONTROLLER_BUTTON; ++i) + { + if (!controllerConsideredHeld(i, controllers[cNum][i]) && controllerConsideredHeld(i, controllersLast[cNum][i])) + { + if (firstDetected) *firstDetected = static_cast(i); + return true; + } + } + return false; + } + + //Only get of largest magnitude + double SHInputManager::GetBindingAxis(std::string bindingName, size_t cNum) noexcept + { + //Over keycodes, prioritise positive + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKey(k)) return 1.0; + } + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKey(k)) return -1.0; + } + + double largestMagnitude = 0.0; + + //Over controllerCodes + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + double newValue = 0.0; + if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum)) + if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = newValue; + } + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + double newValue = 0.0; + if (GetControllerInput(c, &newValue, nullptr, nullptr, cNum)) + if (std::abs(newValue) > std::abs(largestMagnitude)) largestMagnitude = -newValue; + } + + return largestMagnitude; + } + + bool SHInputManager::GetBindingPositiveButton(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKey(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingNegativeButton(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKey(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInput(c, nullptr, nullptr, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingPositiveButtonDown(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyDown(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputDown(c, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingNegativeButtonDown(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyDown(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputDown(c, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingPositiveButtonUp(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyUp(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputUp(c, nullptr, cNum)) return true; + } + + return false; + } + + bool SHInputManager::GetBindingNegativeButtonUp(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return false; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyUp(k)) return true; + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputUp(c, nullptr, cNum)) return true; + } + + return false; + } + + //Fetches longest hold time + double SHInputManager::GetBindingPositiveHeldTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double maxHeldTime = 0.0; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c); + } + + return maxHeldTime; + } + + double SHInputManager::GetBindingNegativeHeldTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double maxHeldTime = 0.0; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyHeldTime(k) > maxHeldTime) maxHeldTime = GetKeyHeldTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputHeldTime(c, cNum) > maxHeldTime) maxHeldTime = GetControllerInputHeldTime(c); + } + + return maxHeldTime; + } + + //Fetches shortest release time + double SHInputManager::GetBindingPositiveReleasedTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double minReleaseTime = _HUGE_ENUF; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].positiveKeyCodes) + { + if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].positiveControllerCodes) + { + if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c); + } + + return minReleaseTime; + } + + double SHInputManager::GetBindingNegativeReleasedTime(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + double minReleaseTime = _HUGE_ENUF; + + //Over keycodes + for (SH_KEYCODE k : bindings[bindingName].negativeKeyCodes) + { + if (GetKeyReleasedTime(k) < minReleaseTime) minReleaseTime = GetKeyReleasedTime(k); + } + + //Over controller buttons + for (SH_CONTROLLERCODE c : bindings[bindingName].negativeControllerCodes) + { + if (GetControllerInputReleasedTime(c, cNum) < minReleaseTime) minReleaseTime = GetControllerInputReleasedTime(c); + } + + return minReleaseTime; + } + + //Only for mouse movement + //Get largest delta + double SHInputManager::GetBindingMouseVelocity(std::string bindingName, size_t cNum) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0.0; + + //Mouse velocity + double velX = 0.0; + double velY = 0.0; + GetMouseVelocity(&velX, &velY); + + return bindings[bindingName].mouseXPositiveMultiplier * velX + bindings[bindingName].mouseYPositiveMultiplier * velY; + } + } //namespace SHADE \ No newline at end of file diff --git a/SHADE_Engine/src/Input/SHInputManager.h b/SHADE_Engine/src/Input/SHInputManager.h index d3e31004..04e5871d 100644 --- a/SHADE_Engine/src/Input/SHInputManager.h +++ b/SHADE_Engine/src/Input/SHInputManager.h @@ -10,9 +10,12 @@ *********************************************************************/ #pragma once -//#include -//#include "../../SHADE_Managed/src/SHpch.h" +#include +#include +#include +#include "../../SHADE_Managed/src/SHpch.h" #include "SH_API.h" +#pragma comment(lib, "xinput.lib") namespace SHADE { @@ -268,6 +271,58 @@ namespace SHADE OEM_CLEAR }; + enum class SH_CONTROLLERCODE + { + //Digital + DPAD_UP, + DPAD_DOWN, + DPAD_LEFT, + DPAD_RIGHT, + START, + BACK, + LEFT_THUMBSTICK, + RIGHT_THUMBSTICK, + LEFT_SHOULDER, + RIGHT_SHOULDER, + A, + B, + X, + Y, + + //1 Byte Unsigned Analog + LEFT_TRIGGER, + RIGHT_TRIGGER, + + //2 Byte Signed Analog + LEFT_THUMBSTICK_X, + LEFT_THUMBSTICK_Y, + RIGHT_THUMBSTICK_X, + RIGHT_THUMBSTICK_Y + }; + + private: + /*------------------------------------------------------------------------*/ + /* Struct for logical bindings */ + /*------------------------------------------------------------------------*/ + struct SH_API SHLogicalBindingData + { + //Key codes mapped to positive + std::set positiveKeyCodes; + + //Key codes mapped to negative + std::set negativeKeyCodes; + + //Controller Codes mapped to positive + std::set positiveControllerCodes; + + //Controller Codes mapped to negative + std::set negativeControllerCodes; + + //Mouse movement mapped to axes? + double mouseXPositiveMultiplier; + double mouseYPositiveMultiplier; + }; + public: //Updates current state of the input, with dt to be fetched from FRC //TODO should dt be fixed or variable? @@ -392,7 +447,7 @@ namespace SHADE keysToggleLast[static_cast(key)]); } - //Mouse///////////// + //Mouse///////////////////////////////////////////////////// //Get the mouse location with respect to the screen static inline void GetMouseScreenPosition (int* x = nullptr, @@ -428,7 +483,95 @@ namespace SHADE return mouseWheelVerticalDelta; } - //GET INPUT TIMINGS/////////////////////////////////////////////////////////// + /*------------------------------------------------------------------------*/ + /* Input state accessors (KB & M) */ + /*------------------------------------------------------------------------*/ + + //How many controller inputs of any kind are being used now + static inline unsigned GetControllerInputCount(size_t cNum = 0) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0; + return controllersInputCount[cNum]; + } + + //How many controller buttons are being pressed now + //Subtract from getControllerInputCount() for analog triggers / thumbsticks + static inline unsigned GetControllerButtonCount(size_t cNum = 0) noexcept + { + if (cNum >= XUSER_MAX_COUNT) return 0; + return controllersButtonCount[cNum]; + } + + //Any controller input being held + //For analog, this means going being deadzone values + //controllerNum should be between 0 and 3 + static bool AnyControllerInput(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any controller input activated in THIS FRAME ONLY + //For analog, this means going being deadzone values + //controllerNum should be between 0 and 3 + static bool AnyControllerInputDown(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any controller input deactivated in THIS FRAME ONLY + //For analog, this means going below deadzone values + //controllerNum should be between 0 and 3 + static bool AnyControllerInputUp(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any DIGITAL controller buttons being held + //controllerNum should be between 0 and 3 + static bool AnyControllerButton(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any DIGITAL controller button activated in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static bool AnyControllerButtonDown(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //Any DIGITAL controller button deactivated in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static bool AnyControllerButtonUp(SH_CONTROLLERCODE* firstDetected = nullptr, size_t controllerNum = 0) noexcept; + + //If controller input is being held in the current frame + //normalisedValue is the value of the input between 0 and 1, relevant for trigger and thumbstick data + //controllerNum should be between 0 and 3 + static inline bool GetControllerInput(SH_CONTROLLERCODE input, + double* normalisedValue = nullptr, + double* heldTime = nullptr, + double* releasedTime = nullptr, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return false; + if (normalisedValue) *normalisedValue = controllerNormalisedValue(static_cast(input), controllers[controllerNum][static_cast(input)]); + if (heldTime) *heldTime = controllersHeldTime[controllerNum][static_cast(input)]; + if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast(input)]; + return controllerConsideredHeld(static_cast(input), controllers[controllerNum][static_cast(input)]); + } + + //If controller input was considered to be held down in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static inline bool GetControllerInputDown(SH_CONTROLLERCODE input, + double* releasedTime = nullptr, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return false; + if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast(input)]; + return (controllerConsideredHeld(static_cast(input), controllers[controllerNum][static_cast(input)]) && + !controllerConsideredHeld(static_cast(input), controllersLast[controllerNum][static_cast(input)])); + } + + //If controller input was considered to be released in THIS FRAME ONLY + //controllerNum should be between 0 and 3 + static inline bool GetControllerInputUp(SH_CONTROLLERCODE input, + double* releasedTime = nullptr, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return false; + if (releasedTime) *releasedTime = controllersReleasedTime[controllerNum][static_cast(input)]; + return (!controllerConsideredHeld(static_cast(input), controllers[controllerNum][static_cast(input)]) && + controllerConsideredHeld(static_cast(input), controllersLast[controllerNum][static_cast(input)])); + } + + /*------------------------------------------------------------------------*/ + /* Timing accessors */ + /*------------------------------------------------------------------------*/ //Keyboard///////////// @@ -456,6 +599,191 @@ namespace SHADE return keysToggleOffTime[static_cast(key)]; } + //Controller////////////////////// + + //How long has this controller input been considered to be held down for + static inline double GetControllerInputHeldTime(SH_CONTROLLERCODE code, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return 0.0; + return controllersHeldTime[controllerNum][static_cast(code)]; + } + + //How long has this controller input been considered to be released for + static inline double GetControllerInputReleasedTime(SH_CONTROLLERCODE code, + size_t controllerNum = 0) noexcept + { + if (controllerNum >= XUSER_MAX_COUNT) return 0.0; + return controllersReleasedTime[controllerNum][static_cast(code)]; + } + + /*------------------------------------------------------------------------*/ + /* Binding Functions */ + /*------------------------------------------------------------------------*/ + + //Add a new binding to the map + static inline void BindingsAdd(std::string newBindingName) noexcept + { + bindings.insert({ newBindingName, SHLogicalBindingData() }); + } + + //Remove a binding from the map + //Returns 1 if found and removed, 0 if not found + static inline size_t BindingsRemove(std::string targetBindingName) noexcept + { + return bindings.erase(targetBindingName); + } + + //Clears all bindings from the list + static inline void BindingsClear() noexcept + { + bindings.clear(); + } + + //Get the number of bindings present + static inline size_t BindingsCount() noexcept + { + return bindings.size(); + } + + //Check positive keycodes to binding + static inline std::set const& BindingsGetPositiveKeyCodes(std::string bindingName) noexcept + { + return bindings[bindingName].positiveKeyCodes; + } + + //Add positive SH_KEYCODE to binding + static inline void BindingsAddPositiveKeyCode(std::string targetBindingName, + SH_KEYCODE toAdd) noexcept + { + bindings[targetBindingName].positiveKeyCodes.insert(toAdd); + } + + //Remove positive SH_KEYCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemovePositiveKeyCode(std::string targetBindingName, + SH_KEYCODE toRemove) noexcept + { + return bindings[targetBindingName].positiveKeyCodes.erase(toRemove); + } + + //Check negative keycodes to binding + static inline std::set const& BindingsGetNegativeKeyCodes(std::string bindingName) noexcept + { + return bindings[bindingName].negativeKeyCodes; + } + + //Add negative SH_KEYCODE to binding + static inline void BindingsAddNegativeKeyCode(std::string targetBindingName, + SH_KEYCODE toAdd) noexcept + { + bindings[targetBindingName].negativeKeyCodes.insert(toAdd); + } + + //Remove negative SH_KEYCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemoveNegativeKeyCode(std::string targetBindingName, + SH_KEYCODE toRemove) noexcept + { + return bindings[targetBindingName].negativeKeyCodes.erase(toRemove); + } + + //Check positive controllercodes to binding + static inline std::set const& BindingsGetPositiveControllerCodes(std::string bindingName) noexcept + { + return bindings[bindingName].positiveControllerCodes; + } + + //Add positive SH_CONTROLLERCODE to binding + static inline void BindingsAddPositiveControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toAdd) noexcept + { + bindings[targetBindingName].positiveControllerCodes.insert(toAdd); + } + + //Remove positive SH_CONTROLLERCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemovePositiveControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toRemove) noexcept + { + return bindings[targetBindingName].positiveControllerCodes.erase(toRemove); + } + + //Check negative controllercodes to binding + static inline std::set const& BindingsGetNegativeControllerCodes(std::string bindingName) noexcept + { + return bindings[bindingName].negativeControllerCodes; + } + + //Add negative SH_CONTROLLERCODE to binding + static inline void BindingsAddNegativeControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toAdd) noexcept + { + bindings[targetBindingName].negativeControllerCodes.insert(toAdd); + } + + //Remove negative SH_CONTROLLERCODE from binding + //If toRemove found and removed, returns 1. Otherwise, 0. + static inline size_t BindingsRemoveNegativeControllerCode(std::string targetBindingName, + SH_CONTROLLERCODE toRemove) noexcept + { + return bindings[targetBindingName].negativeControllerCodes.erase(toRemove); + } + + //Mouse movement bindings + + static inline double const BindingsGetMouseXPositiveMultiplier(std::string bindingName) noexcept + { + return bindings[bindingName].mouseXPositiveMultiplier; + } + + static inline void BindingsSetMouseXPositiveMultiplier(std::string bindingName, double newValue) noexcept + { + bindings[bindingName].mouseXPositiveMultiplier = newValue; + } + + static inline double const BindingsGetMouseYPositiveMultiplier(std::string bindingName) noexcept + { + return bindings[bindingName].mouseXPositiveMultiplier; + } + + static inline void BindingsSetMouseYPositiveMultiplier(std::string bindingName, double newValue) noexcept + { + bindings[bindingName].mouseXPositiveMultiplier = newValue; + } + + //Get the axis value of binding, between -1 and 1 + static double GetBindingAxis(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Whether binding is being held or not + //Does not work for mouse movement + static bool GetBindingPositiveButton(std::string bindingName, size_t controllerNumber = 0) noexcept; + static bool GetBindingNegativeButton(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Whether binding is pressed down IN THIS FRAME ONLY + //Does not work for mouse movement + static bool GetBindingPositiveButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept; + static bool GetBindingNegativeButtonDown(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Whether binding is released IN THIS FRAME ONLY + //Does not work for mouse movement + static bool GetBindingPositiveButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept; + static bool GetBindingNegativeButtonUp(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Binding times + + //Does not work for mouse movement + static double GetBindingPositiveHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + static double GetBindingNegativeHeldTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Does not work for mouse movement + static double GetBindingPositiveReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + static double GetBindingNegativeReleasedTime(std::string bindingName, size_t controllerNumber = 0) noexcept; + + //Binding mouse velocity + //Only for mouse movement + static double GetBindingMouseVelocity(std::string bindingName, size_t controllerNumber = 0) noexcept; + /*------------------------------------------------------------------------*/ /* Other Functions */ /*------------------------------------------------------------------------*/ @@ -482,10 +810,36 @@ namespace SHADE /*------------------------------------------------------------------------*/ static constexpr size_t MAX_KEYS = UCHAR_MAX + 1; + //How many recognised controller inputs there are + //To see the list, see the enum class in this file + static constexpr size_t MAX_CONTROLLER_INPUT = 20; + + //On/off button count + static constexpr size_t NUM_CONTROLLER_BUTTON = 14; + + //8-bit trigger values + static constexpr size_t NUM_CONTROLLER_TRIGGER = 2; + + //16-bit thumbstick values + static constexpr size_t NUM_CONTROLLER_THUMBSTICK = 4; + /*------------------------------------------------------------------------*/ /* Data Members */ /*------------------------------------------------------------------------*/ + //If the last input is from controller(s) or KB/M + //True if from controller(s), False if from KB/M + //Useful for switching control hints between controllers and KB/M + static bool controllerInUse; + + //BINDINGS////////////////////////////////////////////////////////////////// + + //Key is for binding names, they must be unique + //Multiple physical inputs per virtual/logical input are to be added to + //sets inside the logicalBinding values + //TODO make this an array of 4 / 5 users for multiplayer support + static std::map bindings; + //KEYBOARD AND MOUSE BUTTONS//////////////////////////////////////////////// //How many keys are presently being pressed @@ -558,15 +912,48 @@ namespace SHADE //CONTROLLER VARIABLES////////////////////////////////////////////////////// - //OTHER VARIABLES/////////////////////////////////////////////////////////// + //Count how many controllers are presently connected + //Useful for if the game is to decide to take in controller or KB/M input + //Between 0 and 4 (inclusive) + static unsigned char controllersConnectedCount; - //Axis bindings - //X - - //Y + //How many inputs (of any kind) on the controller are being used now + //Includes analog triggers and thumbsticks + static unsigned controllersInputCount[XUSER_MAX_COUNT]; - //Other mappings + //How many DIGITAL buttons on the controller are being pressed now + static unsigned controllersButtonCount[XUSER_MAX_COUNT]; - //Buffer + //Current state of controllers + //First index is for controller number + //Second index is for input type + static short controllers[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //State of controllers in the last frame + //First index is for controller number + //Second index is for input type + static short controllersLast[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //Held and released times + + //Controller held durations + //Stops ticking up when released + //Will be reset when held again + static double controllersHeldTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + //Key released durations + //Stops ticking up when held + //Will be reset when off again + static double controllersReleasedTime[XUSER_MAX_COUNT][MAX_CONTROLLER_INPUT]; + + /*------------------------------------------------------------------------*/ + /* Internal Helper Functions */ + /*------------------------------------------------------------------------*/ + + //Internal helper function for checking if input is considered held or not + static bool controllerConsideredHeld(size_t inputIdx, short value) noexcept; + + //Internal helper function for getting normalised controller value + static double controllerNormalisedValue(size_t inputIdx, short value) noexcept; }; } \ No newline at end of file