308 lines
11 KiB
C++
308 lines
11 KiB
C++
#include "KInput.h"
|
|
#include "Constants.h"
|
|
|
|
#include <SDL.h>
|
|
#include <algorithm>
|
|
|
|
namespace KapitanGame {
|
|
KInput::KInput() : GamePadsCount(0), Initialized(false)
|
|
{
|
|
}
|
|
|
|
void KInput::Init() {
|
|
if (Initialized) return;
|
|
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) != 1)
|
|
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
|
|
|
const int nJoysticks = SDL_NumJoysticks();
|
|
GamePadsCount = 0;
|
|
|
|
// Count how many controllers there are
|
|
for (int i = 0; i < nJoysticks; i++)
|
|
if (SDL_IsGameController(i))
|
|
GamePadsCount++;
|
|
|
|
// If we have some controllers attached
|
|
if (GamePadsCount > 0)
|
|
{
|
|
for (int i = 0; i < GamePadsCount; i++)
|
|
{
|
|
// Open the controller and add it to our list
|
|
SDL_GameController* pad = SDL_GameControllerOpen(i);
|
|
if (SDL_GameControllerGetAttached(pad) == 1)
|
|
ConnectedControllers.push_back(pad);
|
|
else
|
|
printf("SDL_GetError() = %s\n", SDL_GetError());
|
|
}
|
|
SDL_GameControllerEventState(SDL_ENABLE);
|
|
}
|
|
|
|
// Vectors are empty to begin with, this sets their size
|
|
ControllerInputs.resize(GamePadsCount);
|
|
LastControllerInputs.resize(GamePadsCount);
|
|
|
|
// Set the status of the controllers to "nothing is happening"
|
|
for (int i = 0; i < GamePadsCount; i++) {
|
|
for (int a = 0; a < SDL_CONTROLLER_AXIS_MAX; a++) {
|
|
ControllerInputs[i].Axis[a] = 0;
|
|
LastControllerInputs[i].Axis[a] = 0;
|
|
}
|
|
for (int b = 0; b < SDL_CONTROLLER_BUTTON_MAX; b++) {
|
|
ControllerInputs[i].Buttons[b] = false;
|
|
LastControllerInputs[i].Buttons[b] = false;
|
|
}
|
|
}
|
|
Initialized = true;
|
|
}
|
|
|
|
void KInput::Free() {
|
|
for (const auto& pad : ConnectedControllers) {
|
|
SDL_GameControllerClose(pad);
|
|
}
|
|
ConnectedControllers.clear();
|
|
Actions.clear();
|
|
}
|
|
|
|
void KInput::HandleInputPreEvents() {
|
|
for (int i = 0; i < GamePadsCount; i++) {
|
|
for (int a = 0; a < SDL_CONTROLLER_AXIS_MAX; a++) {
|
|
LastControllerInputs[i].Axis[a] = ControllerInputs[i].Axis[a];
|
|
}
|
|
for (int b = 0; b < SDL_CONTROLLER_BUTTON_MAX; b++) {
|
|
LastControllerInputs[i].Buttons[b] = ControllerInputs[i].Buttons[b];
|
|
}
|
|
}
|
|
LastKeyboardInputs = KeyboardInputs;
|
|
}
|
|
void KInput::HandleInputPostEvents(const bool processInput) {
|
|
int size = 0;
|
|
const Uint8* currentKeyStates = SDL_GetKeyboardState(&size);
|
|
KeyboardInputs = std::vector<Uint8>(currentKeyStates, currentKeyStates + size);
|
|
if (processInput) {
|
|
ProcessActions();
|
|
ProcessAxes();
|
|
}
|
|
}
|
|
void KInput::HandleEvent(const SDL_Event& event) {
|
|
switch (event.type) {
|
|
// This happens when a device is added
|
|
// A future improvement is to cope with new controllers being plugged in
|
|
// when the game is running
|
|
case SDL_CONTROLLERDEVICEADDED:
|
|
printf("DEVICEADDED cdevice.which = %d\n", event.cdevice.which);
|
|
break;
|
|
|
|
// If a controller button is pressed
|
|
case SDL_CONTROLLERBUTTONDOWN:
|
|
// Cycle through the controllers
|
|
for (int i = 0; i < GamePadsCount; i++) {
|
|
// Looking for the button that was pressed
|
|
if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) {
|
|
// So the relevant state can be updated
|
|
ControllerInputs[i].Buttons[event.cbutton.button] = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// Do the same for releasing a button
|
|
case SDL_CONTROLLERBUTTONUP:
|
|
for (int i = 0; i < GamePadsCount; i++) {
|
|
if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) {
|
|
ControllerInputs[i].Buttons[event.cbutton.button] = false;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// And something similar for axis motion
|
|
case SDL_CONTROLLERAXISMOTION:
|
|
for (int i = 0; i < GamePadsCount; i++) {
|
|
if (event.cbutton.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(ConnectedControllers[i]))) {
|
|
ControllerInputs[i].Axis[event.caxis.axis] = event.caxis.value;
|
|
}
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void KInput::BindAction(const std::string& name, InputState expectedInputState,
|
|
const std::shared_ptr<KPlayerController>& controllerObject,
|
|
KPlayerCommand command, KPlayer player) {
|
|
Actions.emplace_back(name, expectedInputState, controllerObject, command, player);
|
|
}
|
|
|
|
void KInput::BindAxis(const std::string& name, const std::shared_ptr<KPlayerController>& controllerObject, KPlayerAxisCommand command, KPlayer player)
|
|
{
|
|
Axes.emplace_back(name, controllerObject, command, player);
|
|
}
|
|
|
|
bool KInput::CheckAction(const std::string& name, const InputState expectedInputState, KPlayer player) {
|
|
const auto keyBindRange = KeyBinds[static_cast<int>(player)].equal_range(name);
|
|
for (auto keyBind = keyBindRange.first; keyBind != keyBindRange.second; ++keyBind) {
|
|
switch (expectedInputState) {
|
|
case InputState::Pressed:
|
|
if (IsKeyboardButtonPressed(keyBind->second)) {
|
|
return true;
|
|
}
|
|
break;
|
|
case InputState::Released:
|
|
if (IsKeyboardButtonReleased(keyBind->second)) {
|
|
return true;
|
|
}
|
|
break;
|
|
case InputState::Hold:
|
|
if (IsKeyboardButtonHeld(keyBind->second)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
const auto buttonBindRange = ButtonBinds[static_cast<int>(player)].equal_range(name);
|
|
for (auto buttonBind = buttonBindRange.first; buttonBind != buttonBindRange.second; ++buttonBind) {
|
|
switch (expectedInputState) {
|
|
case InputState::Pressed:
|
|
if (IsControllerButtonPressed(buttonBind->second.first, buttonBind->second.second)) {
|
|
return true;
|
|
}
|
|
break;
|
|
case InputState::Released:
|
|
if (IsControllerButtonReleased(buttonBind->second.first, buttonBind->second.second)) {
|
|
return true;
|
|
}
|
|
break;
|
|
case InputState::Hold:
|
|
if (IsControllerButtonHeld(buttonBind->second.first, buttonBind->second.second)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void KInput::ProcessActions() {
|
|
for (auto& action : Actions) {
|
|
if (CheckAction(action.Name, action.ExpectedInputState, action.Player)) {
|
|
if (auto obj = action.ControllerObject.lock())
|
|
std::invoke(action.Command, obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
void KInput::ProcessAxes()
|
|
{
|
|
for (auto& axis : Axes) {
|
|
if (auto obj = axis.ControllerObject.lock())
|
|
std::invoke(axis.AxisCommand, obj, GetAxisValue(axis.Name, axis.Player));
|
|
}
|
|
}
|
|
|
|
void KInput::AddAxisMapping(const std::string& name, const float& scale, const std::string& key, const KPlayer& player)
|
|
{
|
|
if (key.rfind("Gamepad", 0) == 0) {
|
|
const auto delimiterPosition = key.find('_', 7);
|
|
const auto controllerIndex = static_cast<Controllers>(std::stoi(key.substr(7, delimiterPosition)));
|
|
auto axis = key.substr(delimiterPosition + 1);
|
|
std::for_each(axis.begin(), axis.end(), [](char& c) {
|
|
c = ::tolower(c);
|
|
});
|
|
ControllerAxisBinds[static_cast<int>(player)].emplace(name, KControllerAxisMapping(controllerIndex, SDL_GameControllerGetAxisFromString(axis.c_str()), scale));
|
|
}
|
|
else {
|
|
SDL_Scancode scanCode = SDL_GetScancodeFromName(key.c_str());
|
|
if (scanCode != SDL_SCANCODE_UNKNOWN) {
|
|
KeyAxisBinds[static_cast<int>(player)].emplace(name, std::make_pair(scanCode, scale));
|
|
}
|
|
}
|
|
}
|
|
|
|
void KInput::AddActionMapping(const std::string& name, const std::string& key, const KPlayer& player)
|
|
{
|
|
if (key.rfind("Gamepad", 0) == 0) {
|
|
const auto delimiterPosition = key.find('_', 7);
|
|
const auto controllerIndex = static_cast<Controllers>(std::stoi(key.substr(7, delimiterPosition)));
|
|
auto button = key.substr(delimiterPosition + 1);
|
|
std::for_each(button.begin(), button.end(), [](char& c) {
|
|
c = ::tolower(c);
|
|
});
|
|
ButtonBinds[static_cast<int>(player)].emplace(name, std::make_pair(controllerIndex, SDL_GameControllerGetButtonFromString(button.c_str())));
|
|
}
|
|
else {
|
|
SDL_Scancode scanCode = SDL_GetScancodeFromName(key.c_str());
|
|
if (scanCode != SDL_SCANCODE_UNKNOWN) {
|
|
KeyBinds[static_cast<int>(player)].emplace(name, scanCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool KInput::IsControllerButtonPressed(const Controllers controllerId, const SDL_GameControllerButton button) const
|
|
{
|
|
if (controllerId < Controllers::Controller1 || controllerId > static_cast<Controllers>(GamePadsCount - 1)) return false;
|
|
|
|
return ControllerInputs[static_cast<int>(controllerId)].Buttons[button] && !LastControllerInputs[static_cast<int>(controllerId)].Buttons[button];
|
|
}
|
|
bool KInput::IsControllerButtonReleased(const Controllers controllerId, const SDL_GameControllerButton button) const
|
|
{
|
|
if (controllerId < Controllers::Controller1 || controllerId > static_cast<Controllers>(GamePadsCount - 1)) return false;
|
|
|
|
return !ControllerInputs[static_cast<int>(controllerId)].Buttons[button] && LastControllerInputs[static_cast<int>(controllerId)].Buttons[button];
|
|
}
|
|
|
|
bool KInput::IsControllerButtonHeld(const Controllers controllerId, const SDL_GameControllerButton button) const
|
|
{
|
|
if (controllerId < Controllers::Controller1 || controllerId > static_cast<Controllers>(GamePadsCount - 1)) return false;
|
|
|
|
return ControllerInputs[static_cast<int>(controllerId)].Buttons[button] && LastControllerInputs[static_cast<int>(controllerId)].Buttons[button];
|
|
}
|
|
|
|
float KInput::GetControllerAxis(const Controllers controllerId, const SDL_GameControllerAxis axis) const
|
|
{
|
|
if (controllerId <Controllers::Controller1 || controllerId > static_cast<Controllers>(GamePadsCount - 1)) return 0.0;
|
|
|
|
const int value = ControllerInputs[static_cast<int>(controllerId)].Axis[axis];
|
|
if (std::abs(value) < Constants::JOYSTICK_DEAD_ZONE) return 0.0;
|
|
|
|
return static_cast<float>(value) / 32768.0f;
|
|
}
|
|
|
|
bool KInput::IsKeyboardButtonPressed(const Uint8 scanCode) const
|
|
{
|
|
if (scanCode > KeyboardInputs.size() || scanCode > LastKeyboardInputs.size())
|
|
return false;
|
|
return KeyboardInputs[scanCode] && KeyboardInputs[scanCode] != LastKeyboardInputs[scanCode];
|
|
}
|
|
|
|
bool KInput::IsKeyboardButtonReleased(const Uint8 scanCode) const
|
|
{
|
|
if (scanCode > KeyboardInputs.size() || scanCode > LastKeyboardInputs.size())
|
|
return false;
|
|
return !KeyboardInputs[scanCode] && KeyboardInputs[scanCode] != LastKeyboardInputs[scanCode];
|
|
}
|
|
|
|
float KInput::GetAxisValue(const std::string& name, const KPlayer& player) const
|
|
{
|
|
float value = 0.f;
|
|
const auto keyAxisBindRange = KeyAxisBinds[static_cast<int>(player)].equal_range(name);
|
|
for (auto keyAxisBind = keyAxisBindRange.first; keyAxisBind != keyAxisBindRange.second; ++keyAxisBind) {
|
|
if (IsKeyboardButtonHeld(keyAxisBind->second.first)) {
|
|
value += 1.0f * keyAxisBind->second.second;
|
|
}
|
|
}
|
|
|
|
const auto controllerAxisBindRange = ControllerAxisBinds[static_cast<int>(player)].equal_range(name);
|
|
for (auto controllerAxisBind = controllerAxisBindRange.first; controllerAxisBind != controllerAxisBindRange.second; ++controllerAxisBind) {
|
|
value += GetControllerAxis(controllerAxisBind->second.Controller, controllerAxisBind->second.Axis) * controllerAxisBind->second.Scale;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
bool KInput::IsKeyboardButtonHeld(const Uint8 scanCode) const
|
|
{
|
|
if (scanCode > KeyboardInputs.size())
|
|
return false;
|
|
return KeyboardInputs[scanCode];
|
|
}
|
|
}
|
|
|