2dkg/2dkg_zad5/2dgk_zad5/KInput.cpp
2022-01-20 08:52:14 +01:00

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];
}
}