#include "input.h" #include // Constructor Input::Input(std::string file) { // Inicializa variables verbose = false; enabled = true; // Fichero gamecontrollerdb.txt dbPath = file; // Busca si hay mandos conectados discoverGameControllers(); // Inicializa las vectores keyBindings_t kb; kb.scancode = 0; kb.active = false; keyBindings.resize(input_number_of_inputs, kb); GameControllerBindings_t gcb; gcb.button = SDL_CONTROLLER_BUTTON_INVALID; gcb.active = false; gameControllerBindings.resize(numGamepads); for (int i = 0; i < numGamepads; ++i) { gameControllerBindings[i].resize(input_number_of_inputs, gcb); } // Listado de los inputs usados para jugar, excluyendo botones para la interfaz gameInputs.clear(); gameInputs.push_back(input_fire_left); gameInputs.push_back(input_fire_center); gameInputs.push_back(input_fire_right); gameInputs.push_back(input_up); gameInputs.push_back(input_down); gameInputs.push_back(input_left); gameInputs.push_back(input_right); // Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas buttonInputs.clear(); buttonInputs.push_back(input_fire_left); buttonInputs.push_back(input_fire_center); buttonInputs.push_back(input_fire_right); buttonInputs.push_back(input_start); } // Actualiza el estado del objeto void Input::update() { if (disabledUntil == d_keyPressed && !checkAnyInput()) { enable(); } } // Asigna inputs a teclas void Input::bindKey(inputs_e input, SDL_Scancode code) { keyBindings[input].scancode = code; } // Asigna inputs a botones del mando void Input::bindGameControllerButton(int index, inputs_e input, SDL_GameControllerButton button) { if (index < numGamepads) { gameControllerBindings[index][input].button = button; } } // Asigna inputs a botones del mando void Input::bindGameControllerButton(int index, inputs_e inputTarget, inputs_e inputSource) { if (index < numGamepads) { gameControllerBindings[index][inputTarget].button = gameControllerBindings[index][inputSource].button; } } // Comprueba si un input esta activo bool Input::checkInput(inputs_e input, bool repeat, int device, int index) { if (!enabled) { return false; } bool successKeyboard = false; bool successGameController = false; if (device == INPUT_USE_ANY) { index = 0; } if (device == INPUT_USE_KEYBOARD || device == INPUT_USE_ANY) { const Uint8 *keyStates = SDL_GetKeyboardState(nullptr); if (repeat) { if (keyStates[keyBindings[input].scancode] != 0) { successKeyboard = true; } else { successKeyboard = false; } } else { if (!keyBindings[input].active) { if (keyStates[keyBindings[input].scancode] != 0) { keyBindings[input].active = true; successKeyboard = true; } else { successKeyboard = false; } } else { if (keyStates[keyBindings[input].scancode] == 0) { keyBindings[input].active = false; successKeyboard = false; } else { successKeyboard = false; } } } } if (gameControllerFound() && index < numGamepads) if ((device == INPUT_USE_GAMECONTROLLER) || (device == INPUT_USE_ANY)) { successGameController = checkAxisInput(input, index); if (!successGameController) { if (repeat) { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][input].button) != 0) { successGameController = true; } else { successGameController = false; } } else { if (!gameControllerBindings[index][input].active) { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][input].button) != 0) { gameControllerBindings[index][input].active = true; successGameController = true; } else { successGameController = false; } } else { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][input].button) == 0) { gameControllerBindings[index][input].active = false; successGameController = false; } else { successGameController = false; } } } } } return (successKeyboard || successGameController); } // Comprueba si un input con modificador esta activo bool Input::checkModInput(inputs_e inputMod, inputs_e input, bool repeat, int device, int index) { if (!enabled || index >= numGamepads || !checkInput(inputMod, INPUT_ALLOW_REPEAT, device, index)) { return false; } bool successKeyboard = false; bool successGameController = false; if (device == INPUT_USE_ANY) { index = 0; } if (device == INPUT_USE_KEYBOARD || device == INPUT_USE_ANY) { const Uint8 *keyStates = SDL_GetKeyboardState(nullptr); if (repeat) { if (keyStates[keyBindings[input].scancode] != 0) { successKeyboard = true; } else { successKeyboard = false; } } else { if (!keyBindings[input].active) { if (keyStates[keyBindings[input].scancode] != 0) { keyBindings[input].active = true; successKeyboard = true; } else { successKeyboard = false; } } else { if (keyStates[keyBindings[input].scancode] == 0) { keyBindings[input].active = false; successKeyboard = false; } else { successKeyboard = false; } } } } if (gameControllerFound() && index < numGamepads) if ((device == INPUT_USE_GAMECONTROLLER) || (device == INPUT_USE_ANY)) { successGameController = checkAxisInput(input, index); if (!successGameController) { if (repeat) { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][input].button) != 0) { successGameController = true; } else { successGameController = false; } } else { if (!gameControllerBindings[index][input].active) { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][input].button) != 0) { gameControllerBindings[index][input].active = true; successGameController = true; } else { successGameController = false; } } else { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][input].button) == 0) { gameControllerBindings[index][input].active = false; successGameController = false; } else { successGameController = false; } } } } } return (successKeyboard || successGameController); } // Comprueba si hay almenos un input activo bool Input::checkAnyInput(int device, int index) { if (device == INPUT_USE_ANY) { index = 0; } if (device == INPUT_USE_KEYBOARD || device == INPUT_USE_ANY) { const Uint8 *mKeystates = SDL_GetKeyboardState(nullptr); for (int i = 0; i < (int)keyBindings.size(); ++i) { if (mKeystates[keyBindings[i].scancode] != 0 && !keyBindings[i].active) { keyBindings[i].active = true; return true; } } } if (gameControllerFound()) { if (device == INPUT_USE_GAMECONTROLLER || device == INPUT_USE_ANY) { for (int i = 0; i < (int)gameControllerBindings.size(); ++i) { if (SDL_GameControllerGetButton(connectedControllers[index], gameControllerBindings[index][i].button) != 0 && !gameControllerBindings[index][i].active) { gameControllerBindings[index][i].active = true; return true; } } } } return false; } // Comprueba si hay algún botón pulsado. Devuelve 0 en caso de no encontrar nada o el indice del dispositivo + 1. Se hace así para poder gastar el valor devuelto como un valor "booleano" int Input::checkAnyButtonPressed(bool repeat) { // Si está pulsado el botón de servicio, ningún botón se puede considerar pulsado if (checkInput(input_service, INPUT_ALLOW_REPEAT, INPUT_USE_ANY)) { return 0; } // Solo comprueba los botones definidos previamente for (auto bi : buttonInputs) { // Comprueba el teclado if (checkInput(bi, repeat, INPUT_USE_KEYBOARD)) { return 1; } // Comprueba los mandos for (int i = 0; i < numGamepads; ++i) { if (checkInput(bi, repeat, INPUT_USE_GAMECONTROLLER, i)) { return i + 1; } } } return 0; } // Busca si hay mandos conectados bool Input::discoverGameControllers() { bool found = false; if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) != 1) { SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); } if (SDL_GameControllerAddMappingsFromFile(dbPath.c_str()) < 0) { if (verbose) { std::cout << "Error, could not load " << dbPath.c_str() << " file: " << SDL_GetError() << std::endl; } } numJoysticks = SDL_NumJoysticks(); numGamepads = 0; // Cuenta el número de mandos joysticks.clear(); for (int i = 0; i < numJoysticks; ++i) { SDL_Joystick *joy = SDL_JoystickOpen(i); joysticks.push_back(joy); if (SDL_IsGameController(i)) { numGamepads++; } } if (verbose) { std::cout << "\nChecking for game controllers...\n"; std::cout << numJoysticks << " joysticks found, " << numGamepads << " are gamepads\n"; } if (numGamepads > 0) { found = true; for (int i = 0; i < numGamepads; i++) { // Abre el mando y lo añade a la lista SDL_GameController *pad = SDL_GameControllerOpen(i); if (SDL_GameControllerGetAttached(pad) == 1) { connectedControllers.push_back(pad); const std::string separator(" #"); std::string name = SDL_GameControllerNameForIndex(i); // name.resize(25); // name = name + separator + std::to_string(i); if (verbose) { std::cout << name << std::endl; } controllerNames.push_back(name); } else { if (verbose) { std::cout << "SDL_GetError() = " << SDL_GetError() << std::endl; } } } SDL_GameControllerEventState(SDL_ENABLE); } return found; } // Comprueba si hay algun mando conectado bool Input::gameControllerFound() { return numGamepads > 0 ? true : false; } // Obten el nombre de un mando de juego std::string Input::getControllerName(int index) { return numGamepads > 0 ? controllerNames[index] : ""; } // Obten el número de mandos conectados int Input::getNumControllers() { return numGamepads; } // Establece si ha de mostrar mensajes void Input::setVerbose(bool value) { verbose = value; } // Deshabilita las entradas durante un periodo de tiempo void Input::disableUntil(i_disable_e value) { disabledUntil = value; enabled = false; } // Hablita las entradas void Input::enable() { enabled = true; disabledUntil = d_notDisabled; } // Obtiene el indice del controlador a partir de un event.id int Input::getJoyIndex(int id) { for (int i = 0; i < numJoysticks; ++i) { if (SDL_JoystickInstanceID(joysticks[i]) == id) { return i; } } return -1; } // Muestra por consola los controles asignados void Input::printBindings(int device, int index) { if (device == INPUT_USE_ANY || device == INPUT_USE_KEYBOARD) { return; } if (device == INPUT_USE_GAMECONTROLLER) { if (index >= numGamepads) { return; } // Muestra el nombre del mando std::cout << "\n" << controllerNames[index] << std::endl; // Muestra los botones asignados for (auto bi : buttonInputs) { std::cout << to_string(bi) << " : " << gameControllerBindings[index][bi].button << std::endl; } } } // Obtiene el SDL_GameControllerButton asignado a un input SDL_GameControllerButton Input::getControllerBinding(int index, inputs_e input) { return gameControllerBindings[index][input].button; } // Obtiene el indice a partir del nombre del mando int Input::getIndexByName(std::string name) { for (int i = 0; i < numGamepads; ++i) { if (controllerNames[i] == name) { return i; } } return -1; } // Convierte un inputs_e a std::string std::string Input::to_string(inputs_e input) { if (input == input_fire_left) { return "input_fire_left"; } if (input == input_fire_center) { return "input_fire_center"; } if (input == input_fire_right) { return "input_fire_right"; } if (input == input_start) { return "input_start"; } if (input == input_service) { return "input_service"; } return ""; } // Convierte un std::string a inputs_e inputs_e Input::to_inputs_e(std::string name) { if (name == "input_fire_left") { return input_fire_left; } if (name == "input_fire_center") { return input_fire_center; } if (name == "input_fire_right") { return input_fire_right; } if (name == "input_start") { return input_start; } if (name == "input_service") { return input_service; } return input_null; } // Activa todos los inputs. Sirve para evitar inputs sin repeticiones pero que ya vienen pulsados cuando checkInput no estaba monitorizando void Input::allActive(int index) { for (int i = 0; i < (int)buttonInputs.size(); ++i) { gameControllerBindings[index][i].active = true; } } // Comprueba el eje del mando bool Input::checkAxisInput(inputs_e input, int index) { bool success = false; switch (input) { case input_left: if (SDL_GameControllerGetAxis(connectedControllers[index], SDL_CONTROLLER_AXIS_LEFTX) < -30000) { success = true; } break; case input_right: if (SDL_GameControllerGetAxis(connectedControllers[index], SDL_CONTROLLER_AXIS_LEFTX) > 30000) { success = true; } break; case input_up: if (SDL_GameControllerGetAxis(connectedControllers[index], SDL_CONTROLLER_AXIS_LEFTY) < -30000) { success = true; } break; case input_down: if (SDL_GameControllerGetAxis(connectedControllers[index], SDL_CONTROLLER_AXIS_LEFTY) > 30000) { success = true; } break; default: break; } return success; }