migrant input: ja compila, ja no peta... falta descomentar mig codi
This commit is contained in:
@@ -20,7 +20,7 @@ DefineButtons::DefineButtons()
|
||||
clearButtons();
|
||||
|
||||
for (int i = 0; i < input_->getNumControllers(); ++i) {
|
||||
controller_names_.emplace_back(input_->getControllerName(i));
|
||||
// controller_names_.emplace_back(input_->getControllerName(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,18 +28,18 @@ DefineButtons::DefineButtons()
|
||||
void DefineButtons::render() {
|
||||
static auto text = Resource::get()->getText("8bithud");
|
||||
if (enabled_) {
|
||||
text->writeCentered(x_, y_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::controllers.at(index_controller_).player_id));
|
||||
text->writeCentered(x_, y_, controller_names_.at(index_controller_));
|
||||
text->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label);
|
||||
// text->writeCentered(x_, y_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::controllers.at(index_controller_).player_id));
|
||||
// text->writeCentered(x_, y_, controller_names_.at(index_controller_));
|
||||
// text->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label);
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba el botón que se ha pulsado
|
||||
void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) {
|
||||
// Solo pilla botones del mando que toca
|
||||
if (input_->getJoyIndex(event.which) != static_cast<int>(index_controller_)) {
|
||||
return;
|
||||
}
|
||||
// if (input_->getJoyIndex(event.which) != static_cast<int>(index_controller_)) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
|
||||
if (checkButtonNotInUse(BUTTON)) {
|
||||
@@ -99,11 +99,11 @@ void DefineButtons::incIndexButton() {
|
||||
|
||||
// Guarda los cambios en las opciones
|
||||
void DefineButtons::saveBindingsToOptions() {
|
||||
auto &controller = Options::controllers.at(index_controller_);
|
||||
controller.name = input_->getControllerName(index_controller_);
|
||||
for (size_t j = 0; j < controller.inputs.size(); ++j) {
|
||||
controller.buttons.at(j) = input_->getControllerBinding(index_controller_, controller.inputs.at(j));
|
||||
}
|
||||
// auto &controller = Options::controllers.at(index_controller_);
|
||||
// controller.name = input_->getControllerName(index_controller_);
|
||||
// for (size_t j = 0; j < controller.inputs.size(); ++j) {
|
||||
// controller.buttons.at(j) = input_->getControllerBinding(index_controller_, controller.inputs.at(j));
|
||||
// }
|
||||
}
|
||||
|
||||
// Comprueba que un botón no esté ya asignado
|
||||
@@ -126,7 +126,7 @@ void DefineButtons::clearButtons() {
|
||||
// Comprueba si ha finalizado
|
||||
void DefineButtons::checkEnd() {
|
||||
if (finished_) {
|
||||
bindButtons(); // Asigna los botones definidos al input_
|
||||
// bindButtons(); // Asigna los botones definidos al input_
|
||||
saveBindingsToOptions(); // Guarda los cambios en las opciones
|
||||
input_->resetInputStates(); // Reinicia los estados de las pulsaciones de los botones
|
||||
enabled_ = false; // Deshabilita
|
||||
|
||||
@@ -150,7 +150,7 @@ void Director::loadScoreFile() {
|
||||
|
||||
// Asigna los botones y teclas al objeto Input
|
||||
void Director::bindInputs() {
|
||||
static auto input = Input::get();
|
||||
/*static auto input = Input::get();
|
||||
|
||||
// Teclado - Movimiento del jugador
|
||||
input->bindKey(Input::Action::UP, SDL_SCANCODE_UP);
|
||||
@@ -243,7 +243,7 @@ void Director::bindInputs() {
|
||||
// Asegura que algún jugador tenga el teclado asignado
|
||||
if (Options::getPlayerWhoUsesKeyboard() == 0) {
|
||||
Options::setKeyboardToPlayer(1);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
// Crea el indice de ficheros
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include <external/json.hpp>
|
||||
#pragma once
|
||||
#include <external/json.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
@@ -170,8 +170,9 @@ auto checkServiceButton() -> bool {
|
||||
|
||||
// Mandos
|
||||
{
|
||||
for (int i = 0; i < Input::get()->getNumControllers(); ++i) {
|
||||
if (Input::get()->checkAction(Input::Action::SERVICE, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
auto gamepads = Input::get()->getGamepads();
|
||||
for (auto gamepad : gamepads) {
|
||||
if (Input::get()->checkAction(Input::Action::SERVICE, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
toggleServiceMenu();
|
||||
return true;
|
||||
}
|
||||
@@ -227,39 +228,40 @@ auto checkServiceInputs() -> bool {
|
||||
|
||||
// Mandos
|
||||
{
|
||||
for (int i = 0; i < Input::get()->getNumControllers(); ++i) {
|
||||
auto gamepads = Input::get()->getGamepads();
|
||||
for (auto gamepad : gamepads) {
|
||||
// Arriba
|
||||
if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
if (Input::get()->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
ServiceMenu::get()->setSelectorUp();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Abajo
|
||||
if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
if (Input::get()->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
ServiceMenu::get()->setSelectorDown();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Derecha
|
||||
if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
if (Input::get()->checkAction(Input::Action::RIGHT, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
ServiceMenu::get()->adjustOption(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Izquierda
|
||||
if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
if (Input::get()->checkAction(Input::Action::LEFT, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
ServiceMenu::get()->adjustOption(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Aceptar
|
||||
if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
if (Input::get()->checkAction(Input::Action::SM_SELECT, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
ServiceMenu::get()->selectOption();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Atras
|
||||
if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
if (Input::get()->checkAction(Input::Action::SM_BACK, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
ServiceMenu::get()->moveBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -27,56 +27,51 @@ Input::Input(std::string game_controller_db_path)
|
||||
// Inicializa el subsistema SDL_INIT_GAMEPAD
|
||||
initSDLGamePad();
|
||||
|
||||
// Inicializa los vectores
|
||||
// key_bindings_.resize(static_cast<int>(Action::SIZE), KeyBindings());
|
||||
// controller_bindings_.resize(gamepads.size(), std::vector<ControllerBindings>(static_cast<int>(Action::SIZE), ControllerBindings()));
|
||||
|
||||
// Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas
|
||||
button_inputs_ = {Action::FIRE_LEFT, Action::FIRE_CENTER, Action::FIRE_RIGHT, Action::START};
|
||||
}
|
||||
|
||||
// Asigna inputs a teclas
|
||||
void Input::bindKey(Action input, SDL_Scancode code) {
|
||||
key_bindings_.at(static_cast<int>(input)).scancode = code;
|
||||
void Input::bindKey(Action action, SDL_Scancode code) {
|
||||
keyboard_.bindings[action].scancode = code;
|
||||
}
|
||||
|
||||
// Asigna inputs a botones del mando
|
||||
void Input::bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action input, SDL_GamepadButton button) {
|
||||
void Input::bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action action, SDL_GamepadButton button) {
|
||||
if (gamepad != nullptr) {
|
||||
gamepad->button_states.at(static_cast<int>(input)).button = button;
|
||||
gamepad->bindings[action].button = button;
|
||||
}
|
||||
}
|
||||
|
||||
// Asigna inputs a botones del mando
|
||||
void Input::bindGameControllerButton(std::shared_ptr<Gamepad> gamepad, Action input_target, Action input_source) {
|
||||
if (gamepad != nullptr) {
|
||||
gamepad->button_states.at(static_cast<int>(input_target)).button = gamepad->button_states.at(static_cast<int>(input_source)).button;
|
||||
gamepad->bindings[input_target].button = gamepad->bindings[input_source].button;
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba si un input esta activo
|
||||
auto Input::checkAction(Action input, bool repeat, Device device, std::shared_ptr<Gamepad> gamepad) -> bool {
|
||||
auto Input::checkAction(Action action, bool repeat, Device device, std::shared_ptr<Gamepad> gamepad) -> bool {
|
||||
bool success_keyboard = false;
|
||||
bool success_controller = false;
|
||||
const int INPUT_INDEX = static_cast<int>(input);
|
||||
|
||||
if (device == Device::KEYBOARD || device == Device::ANY) {
|
||||
if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido)
|
||||
success_keyboard = key_bindings_[INPUT_INDEX].is_held;
|
||||
success_keyboard = keyboard_.bindings[action].is_held;
|
||||
} else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma)
|
||||
success_keyboard = key_bindings_[INPUT_INDEX].just_pressed;
|
||||
success_keyboard = keyboard_.bindings[action].just_pressed;
|
||||
}
|
||||
}
|
||||
|
||||
if (gamepad != nullptr) {
|
||||
if ((device == Device::CONTROLLER) || (device == Device::ANY)) {
|
||||
success_controller = checkAxisInput(input, gamepad, repeat);
|
||||
success_controller = checkAxisInput(action, gamepad, repeat);
|
||||
|
||||
if (!success_controller) {
|
||||
if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido)
|
||||
success_controller = gamepad->button_states.at(INPUT_INDEX).is_held;
|
||||
success_controller = gamepad->bindings[action].is_held;
|
||||
} else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma)
|
||||
success_controller = gamepad->button_states.at(INPUT_INDEX).just_pressed;
|
||||
success_controller = gamepad->bindings[action].just_pressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,14 +83,13 @@ auto Input::checkAction(Action input, bool repeat, Device device, std::shared_pt
|
||||
// Comprueba si hay almenos un input activo
|
||||
auto Input::checkAnyInput(Device device, std::shared_ptr<Gamepad> gamepad) -> bool {
|
||||
// Obtenemos el número total de acciones posibles para iterar sobre ellas.
|
||||
const int NUM_ACTIONS = static_cast<int>(Action::SIZE);
|
||||
|
||||
// --- Comprobación del Teclado ---
|
||||
if (device == Device::KEYBOARD || device == Device::ANY) {
|
||||
for (int i = 0; i < NUM_ACTIONS; ++i) {
|
||||
for (const auto &pair : keyboard_.bindings) {
|
||||
// Simplemente leemos el estado pre-calculado por Input::update().
|
||||
// Ya no se llama a SDL_GetKeyboardState ni se modifica el estado '.active'.
|
||||
if (key_bindings_.at(i).just_pressed) {
|
||||
if (pair.second.just_pressed) {
|
||||
return true; // Se encontró una acción recién pulsada.
|
||||
}
|
||||
}
|
||||
@@ -106,9 +100,9 @@ auto Input::checkAnyInput(Device device, std::shared_ptr<Gamepad> gamepad) -> bo
|
||||
if (gamepad != nullptr) {
|
||||
if (device == Device::CONTROLLER || device == Device::ANY) {
|
||||
// Iteramos sobre todas las acciones, no sobre el número de mandos.
|
||||
for (int i = 0; i < NUM_ACTIONS; ++i) {
|
||||
for (const auto &pair : gamepad->bindings) {
|
||||
// Leemos el estado pre-calculado para el mando y la acción específicos.
|
||||
if (gamepad->button_states.at(i).just_pressed) {
|
||||
if (pair.second.just_pressed) {
|
||||
return true; // Se encontró una acción recién pulsada en el mando.
|
||||
}
|
||||
}
|
||||
@@ -150,17 +144,17 @@ auto Input::getNumControllers() const -> int { return gamepads_.size(); }
|
||||
|
||||
// Obtiene el indice del controlador a partir de un event.id
|
||||
auto Input::getJoyIndex(SDL_JoystickID id) const -> int {
|
||||
for (int i = 0; i < num_joysticks_; ++i) {
|
||||
if (SDL_GetJoystickID(joysticks_[i]) == id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// for (int i = 0; i < num_joysticks_; ++i) {
|
||||
// if (SDL_GetJoystickID(joysticks_[i]) == id) {
|
||||
// return i;
|
||||
// }
|
||||
// }
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Obtiene el SDL_GamepadButton asignado a un input
|
||||
auto Input::getControllerBinding(std::shared_ptr<Gamepad> gamepad, Action input) const -> SDL_GamepadButton {
|
||||
return gamepad->button_states.at(static_cast<int>(input)).button;
|
||||
return gamepad->bindings[input].button;
|
||||
}
|
||||
|
||||
// Convierte un InputAction a std::string
|
||||
@@ -217,7 +211,7 @@ auto Input::checkAxisInput(Action input, std::shared_ptr<Gamepad> gamepad, bool
|
||||
}
|
||||
|
||||
// Referencia al binding correspondiente
|
||||
auto &binding = gamepad->button_states.at(static_cast<int>(input));
|
||||
auto &binding = gamepad->bindings[input];
|
||||
|
||||
if (repeat) {
|
||||
// Si se permite repetir, simplemente devolvemos el estado actual
|
||||
@@ -252,15 +246,15 @@ void Input::initSDLGamePad() {
|
||||
|
||||
void Input::resetInputStates() {
|
||||
// Resetear todos los KeyBindings.active a false
|
||||
for (auto &key : key_bindings_) {
|
||||
key.is_held = false;
|
||||
key.just_pressed = false;
|
||||
for (auto &key : keyboard_.bindings) {
|
||||
key.second.is_held = false;
|
||||
key.second.just_pressed = false;
|
||||
}
|
||||
// Resetear todos los ControllerBindings.active a false
|
||||
for (auto &gamepad : gamepads_) {
|
||||
for (auto &binding : gamepad->button_states) {
|
||||
binding.is_held = false;
|
||||
binding.just_pressed = false;
|
||||
for (auto &binding : gamepad->bindings) {
|
||||
binding.second.is_held = false;
|
||||
binding.second.just_pressed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,22 +263,22 @@ void Input::update() {
|
||||
// --- TECLADO ---
|
||||
const bool *key_states = SDL_GetKeyboardState(nullptr);
|
||||
|
||||
for (auto &key_binding : key_bindings_) {
|
||||
bool key_is_down_now = key_states[key_binding.scancode];
|
||||
for (auto &binding : keyboard_.bindings) {
|
||||
bool key_is_down_now = key_states[binding.second.scancode];
|
||||
|
||||
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
|
||||
key_binding.just_pressed = key_is_down_now && !key_binding.is_held;
|
||||
key_binding.is_held = key_is_down_now;
|
||||
binding.second.just_pressed = key_is_down_now && !binding.second.is_held;
|
||||
binding.second.is_held = key_is_down_now;
|
||||
}
|
||||
|
||||
// --- MANDOS ---
|
||||
for (auto gamepad : gamepads_) {
|
||||
for (auto &binding : gamepad->button_states) {
|
||||
bool button_is_down_now = static_cast<int>(SDL_GetGamepadButton(gamepad->pad, binding.button)) != 0;
|
||||
for (auto &binding : gamepad->bindings) {
|
||||
bool button_is_down_now = static_cast<int>(SDL_GetGamepadButton(gamepad->pad, binding.second.button)) != 0;
|
||||
|
||||
// El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo
|
||||
binding.just_pressed = button_is_down_now && !binding.is_held;
|
||||
binding.is_held = button_is_down_now;
|
||||
binding.second.just_pressed = button_is_down_now && !binding.second.is_held;
|
||||
binding.second.is_held = button_is_down_now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <iostream>
|
||||
#include <memory> // Para std::unique_ptr
|
||||
#include <string> // Para basic_string, string
|
||||
#include <unordered_map>
|
||||
#include <vector> // Para vector
|
||||
|
||||
/*
|
||||
@@ -92,21 +93,58 @@ class Input {
|
||||
: button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {}
|
||||
};
|
||||
|
||||
struct Keyboard {
|
||||
std::unordered_map<Input::Action, KeyState> bindings;
|
||||
|
||||
Keyboard()
|
||||
: bindings{
|
||||
{Input::Action::UP, KeyState(SDL_SCANCODE_UP)},
|
||||
{Input::Action::DOWN, KeyState(SDL_SCANCODE_DOWN)},
|
||||
{Input::Action::LEFT, KeyState(SDL_SCANCODE_LEFT)},
|
||||
{Input::Action::RIGHT, KeyState(SDL_SCANCODE_RIGHT)},
|
||||
{Input::Action::FIRE_LEFT, KeyState(SDL_SCANCODE_Q)},
|
||||
{Input::Action::FIRE_CENTER, KeyState(SDL_SCANCODE_W)},
|
||||
{Input::Action::FIRE_RIGHT, KeyState(SDL_SCANCODE_E)},
|
||||
{Input::Action::START, KeyState(SDL_SCANCODE_RETURN)},
|
||||
{Input::Action::SERVICE, KeyState(SDL_SCANCODE_0)},
|
||||
{Input::Action::SM_SELECT, KeyState(SDL_SCANCODE_RETURN)},
|
||||
{Input::Action::SM_BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
|
||||
{Input::Action::EXIT, KeyState(SDL_SCANCODE_ESCAPE)},
|
||||
{Input::Action::PAUSE, KeyState(SDL_SCANCODE_P)},
|
||||
{Input::Action::BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
|
||||
{Input::Action::WINDOW_DEC_SIZE, KeyState(SDL_SCANCODE_F1)},
|
||||
{Input::Action::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
|
||||
{Input::Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
|
||||
{Input::Action::TOGGLE_VIDEO_SHADERS, KeyState(SDL_SCANCODE_F4)},
|
||||
{Input::Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
|
||||
{Input::Action::TOGGLE_VIDEO_VSYNC, KeyState(SDL_SCANCODE_F6)},
|
||||
{Input::Action::TOGGLE_AUDIO, KeyState(SDL_SCANCODE_F7)},
|
||||
{Input::Action::TOGGLE_AUTO_FIRE, KeyState(SDL_SCANCODE_F8)},
|
||||
{Input::Action::CHANGE_LANG, KeyState(SDL_SCANCODE_F9)},
|
||||
{Input::Action::RESET, KeyState(SDL_SCANCODE_F10)},
|
||||
{Input::Action::SHOW_INFO, KeyState(SDL_SCANCODE_F12)}} {}
|
||||
};
|
||||
|
||||
struct Gamepad {
|
||||
SDL_Gamepad *pad;
|
||||
SDL_JoystickID instance_id;
|
||||
std::string name;
|
||||
std::vector<ButtonState> button_states;
|
||||
std::unordered_map<Input::Action, ButtonState> bindings;
|
||||
|
||||
Gamepad(SDL_Gamepad *gamepad)
|
||||
: pad(gamepad),
|
||||
instance_id(SDL_GetJoystickID(SDL_GetGamepadJoystick(gamepad))),
|
||||
name(std::string(SDL_GetGamepadName(gamepad)) + " #" + std::to_string(instance_id)) {}
|
||||
name(std::string(SDL_GetGamepadName(gamepad)) + " #" + std::to_string(instance_id)),
|
||||
bindings{
|
||||
{Input::Action::FIRE_LEFT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)},
|
||||
{Input::Action::FIRE_CENTER, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)},
|
||||
{Input::Action::FIRE_RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_EAST)},
|
||||
{Input::Action::START, ButtonState(SDL_GAMEPAD_BUTTON_START)},
|
||||
{Input::Action::SERVICE, ButtonState(SDL_GAMEPAD_BUTTON_BACK)}} {}
|
||||
|
||||
~Gamepad() {
|
||||
if (pad) {
|
||||
SDL_CloseGamepad(pad);
|
||||
// std::cout << "Gamepad cerrado (ID " << instance_id << ")\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -154,7 +192,7 @@ class Input {
|
||||
|
||||
// --- Variables internas ---
|
||||
std::vector<std::shared_ptr<Gamepad>> gamepads_; // Mandos conectados
|
||||
std::vector<KeyState> key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos
|
||||
Keyboard keyboard_; // Estructura con las asociaciones de teclas a acciones
|
||||
std::vector<Action> button_inputs_; // Inputs asignados al jugador y a botones, excluyendo direcciones
|
||||
std::string game_controller_db_path_; // Ruta al archivo gamecontrollerdb.txt
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ VideoOptions video; // Opciones de vídeo
|
||||
AudioOptions audio; // Opciones de audio
|
||||
std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
|
||||
PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||
std::vector<GamepadConfig> gamepad_configs; // Lista con las configuraciones registradas para cada mando
|
||||
//std::vector<GamepadConfig> gamepad_configs; // Lista con las configuraciones registradas para cada mando
|
||||
|
||||
// Declaraciones
|
||||
auto set(const std::string& var, const std::string& value) -> bool;
|
||||
@@ -160,11 +160,6 @@ auto saveToFile() -> bool {
|
||||
file << "controller." << controller_index << ".name=" << controller.name << "\n";
|
||||
file << "controller." << controller_index << ".player=" << controller.player_id << "\n";
|
||||
file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n";
|
||||
file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n";
|
||||
file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n";
|
||||
file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n";
|
||||
file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n";
|
||||
file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n";
|
||||
|
||||
// Incrementa el índice
|
||||
++controller_index;
|
||||
@@ -193,16 +188,6 @@ void parseAndSetController(const std::string& var, const std::string& value) {
|
||||
controller.player_id = std::clamp(std::stoi(value), 1, 2);
|
||||
} else if (setting_key == "type") {
|
||||
controller.type = static_cast<Input::Device>(std::stoi(value));
|
||||
} else if (setting_key == "button.fire_left") {
|
||||
controller.buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
|
||||
} else if (setting_key == "button.fire_center") {
|
||||
controller.buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
|
||||
} else if (setting_key == "button.fire_right") {
|
||||
controller.buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
|
||||
} else if (setting_key == "button.start") {
|
||||
controller.buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
|
||||
} else if (setting_key == "button.service") {
|
||||
controller.buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "difficulty.h" // Para Code
|
||||
#include "gamepad_config_manager.h"
|
||||
// #include "gamepad_config_manager.h"
|
||||
#include "input.h" // Para InputAction, InputDevice
|
||||
#include "lang.h" // Para Code
|
||||
#include "manage_hiscore_table.h" // Para HiScoreEntry
|
||||
@@ -93,10 +93,10 @@ struct SettingsOptions {
|
||||
};
|
||||
|
||||
struct GamepadOptions {
|
||||
Input::Gamepad gamepad = nullptr; // Mando
|
||||
int player_id; // Jugador asociado al mando
|
||||
Input::Device type = Input::Device::CONTROLLER; // Tipo de dispositivo
|
||||
GamepadConfig config;
|
||||
std::shared_ptr<Input::Gamepad> gamepad = nullptr; // Referencia al mando
|
||||
std::string name; // Nombre del mando
|
||||
int player_id; // Jugador asociado al mando
|
||||
Input::Device type = Input::Device::CONTROLLER; // Tipo de dispositivo
|
||||
|
||||
GamepadOptions(int custom_player_id = INVALID_INDEX)
|
||||
: player_id(custom_player_id) {}
|
||||
@@ -113,13 +113,12 @@ struct PendingChanges {
|
||||
};
|
||||
|
||||
// --- Variables globales ---
|
||||
extern WindowOptions window; // Opciones de la ventana
|
||||
extern SettingsOptions settings; // Opciones del juego
|
||||
extern VideoOptions video; // Opciones de vídeo
|
||||
extern AudioOptions audio; // Opciones de audio
|
||||
extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
|
||||
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||
extern std::vector<GamepadConfig> gamepad_configs; // Lista con las configuraciones registradas para cada mando
|
||||
extern WindowOptions window; // Opciones de la ventana
|
||||
extern SettingsOptions settings; // Opciones del juego
|
||||
extern VideoOptions video; // Opciones de vídeo
|
||||
extern AudioOptions audio; // Opciones de audio
|
||||
extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
|
||||
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
|
||||
|
||||
// --- Funciones de configuración ---
|
||||
void init(); // Inicializa las opciones del programa
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
// Constructor
|
||||
Player::Player(int id, float x, int y, bool demo, SDL_FRect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations)
|
||||
: player_sprite_(std::make_unique<AnimatedSprite>(texture[0], animations[0])),
|
||||
power_sprite_(std::make_unique<AnimatedSprite>(texture[1], animations[1])),
|
||||
: player_sprite_(std::make_unique<AnimatedSprite>(texture.at(0), animations.at(0))),
|
||||
power_sprite_(std::make_unique<AnimatedSprite>(texture.at(1), animations.at(1))),
|
||||
enter_name_(std::make_unique<EnterName>()),
|
||||
id_(id),
|
||||
play_area_(play_area),
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
#include <string> // Para string
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "animated_sprite.h" // Para AnimatedSprite
|
||||
#include "enter_name.h" // Para EnterName
|
||||
#include "animated_sprite.h" // Para AnimatedSprite
|
||||
#include "enter_name.h" // Para EnterName
|
||||
#include "input.h"
|
||||
#include "manage_hiscore_table.h" // Para HiScoreEntry
|
||||
#include "options.h" // Para SettingsOptions, settings
|
||||
#include "scoreboard.h" // Para Scoreboard
|
||||
@@ -219,6 +220,10 @@ class Player {
|
||||
int credits_used_ = 0; // Indica el número de veces que ha continuado
|
||||
std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones
|
||||
int waiting_counter_ = 0; // Contador para el estado de espera
|
||||
std::shared_ptr<Input::Gamepad> gamepad_ = nullptr; // Dispositivo asociado
|
||||
// Setter y getter para gamepad_
|
||||
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; }
|
||||
[[nodiscard]] std::shared_ptr<Input::Gamepad> getGamepad() const { return gamepad_; }
|
||||
|
||||
// --- Métodos internos ---
|
||||
void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador
|
||||
|
||||
@@ -125,6 +125,7 @@ Game::~Game() {
|
||||
void Game::setResources() {
|
||||
// Texturas - Game_text
|
||||
{
|
||||
game_text_textures_.clear();
|
||||
game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_1000_points"));
|
||||
game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_2500_points"));
|
||||
game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_5000_points"));
|
||||
@@ -136,6 +137,7 @@ void Game::setResources() {
|
||||
|
||||
// Texturas - Items
|
||||
{
|
||||
item_textures_.clear();
|
||||
item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png"));
|
||||
item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png"));
|
||||
item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png"));
|
||||
@@ -145,6 +147,7 @@ void Game::setResources() {
|
||||
item_textures_.emplace_back(Resource::get()->getTexture("item_coffee_machine.png"));
|
||||
}
|
||||
|
||||
player_textures_.clear();
|
||||
// Texturas - Player1
|
||||
{
|
||||
std::vector<std::shared_ptr<Texture>> player_texture;
|
||||
@@ -163,12 +166,14 @@ void Game::setResources() {
|
||||
|
||||
// Animaciones -- Jugador
|
||||
{
|
||||
player_animations_.clear();
|
||||
player_animations_.emplace_back(Resource::get()->getAnimation("player.ani"));
|
||||
player_animations_.emplace_back(Resource::get()->getAnimation("player_power.ani"));
|
||||
}
|
||||
|
||||
// Animaciones -- Items
|
||||
{
|
||||
item_animations_.clear();
|
||||
item_animations_.emplace_back(Resource::get()->getAnimation("item_points1_disk.ani"));
|
||||
item_animations_.emplace_back(Resource::get()->getAnimation("item_points2_gavina.ani"));
|
||||
item_animations_.emplace_back(Resource::get()->getAnimation("item_points3_pacmar.ani"));
|
||||
@@ -1243,8 +1248,9 @@ void Game::checkInput() {
|
||||
// Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego.
|
||||
void Game::checkPauseInput() {
|
||||
// Comprueba los mandos
|
||||
for (int i = 0; i < input_->getNumControllers(); ++i) {
|
||||
if (input_->checkAction(Input::Action::PAUSE, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, i)) {
|
||||
auto gamepads = input_->getGamepads();
|
||||
for (auto gamepad : gamepads) {
|
||||
if (input_->checkAction(Input::Action::PAUSE, Input::DO_NOT_ALLOW_REPEAT, Input::Device::CONTROLLER, gamepad)) {
|
||||
pause(!paused_);
|
||||
return;
|
||||
}
|
||||
@@ -1360,14 +1366,14 @@ void Game::handlePlayersInput() {
|
||||
|
||||
// Maneja las entradas de movimiento y disparo para un jugador en modo normal.
|
||||
void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) {
|
||||
const auto &controller = Options::controllers.at(player->getController());
|
||||
const auto &controller = Options::controllers.at(0);
|
||||
|
||||
if (input_->checkAction(Input::Action::LEFT, Input::ALLOW_REPEAT, controller.type, controller.index)) {
|
||||
if (input_->checkAction(Input::Action::LEFT, Input::ALLOW_REPEAT, controller.type, controller.gamepad)) {
|
||||
player->setInput(Input::Action::LEFT);
|
||||
#ifdef RECORDING
|
||||
demo_.keys.left = 1;
|
||||
#endif
|
||||
} else if (input_->checkAction(Input::Action::RIGHT, Input::ALLOW_REPEAT, controller.type, controller.index)) {
|
||||
} else if (input_->checkAction(Input::Action::RIGHT, Input::ALLOW_REPEAT, controller.type, controller.gamepad)) {
|
||||
player->setInput(Input::Action::RIGHT);
|
||||
#ifdef RECORDING
|
||||
demo_.keys.right = 1;
|
||||
@@ -1380,23 +1386,23 @@ void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) {
|
||||
}
|
||||
|
||||
const bool AUTOFIRE = player->isPowerUp() || Options::settings.autofire;
|
||||
handleFireInputs(player, AUTOFIRE, player->getController()); // Verifica y maneja todas las posibles entradas de disparo.
|
||||
handleFireInputs(player, AUTOFIRE); // Verifica y maneja todas las posibles entradas de disparo.
|
||||
}
|
||||
|
||||
// Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado.
|
||||
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controller_index) {
|
||||
const auto CONTROLLER = Options::controllers.at(player->getController());
|
||||
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, CONTROLLER.type, CONTROLLER.index)) {
|
||||
void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire) {
|
||||
const auto CONTROLLER = Options::controllers.at(0);
|
||||
if (input_->checkAction(Input::Action::FIRE_CENTER, autofire, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
handleFireInput(player, BulletType::UP);
|
||||
#ifdef RECORDING
|
||||
demo_.keys.fire = 1;
|
||||
#endif
|
||||
} else if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, CONTROLLER.type, CONTROLLER.index)) {
|
||||
} else if (input_->checkAction(Input::Action::FIRE_LEFT, autofire, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
handleFireInput(player, BulletType::LEFT);
|
||||
#ifdef RECORDING
|
||||
demo_.keys.fire_left = 1;
|
||||
#endif
|
||||
} else if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, CONTROLLER.type, CONTROLLER.index)) {
|
||||
} else if (input_->checkAction(Input::Action::FIRE_RIGHT, autofire, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
handleFireInput(player, BulletType::RIGHT);
|
||||
#ifdef RECORDING
|
||||
demo_.keys.fire_right = 1;
|
||||
@@ -1406,17 +1412,17 @@ void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire
|
||||
|
||||
// Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio.
|
||||
void Game::handlePlayerContinueInput(const std::shared_ptr<Player> &player) {
|
||||
const auto CONTROLLER = Options::controllers.at(player->getController());
|
||||
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
const auto CONTROLLER = Options::controllers.at(0);
|
||||
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
player->setPlayingState(Player::State::RESPAWNING);
|
||||
player->addCredit();
|
||||
sendPlayerToTheFront(player);
|
||||
}
|
||||
|
||||
// Disminuye el contador de continuación si se presiona cualquier botón de disparo.
|
||||
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index) ||
|
||||
input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index) ||
|
||||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad) ||
|
||||
input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad) ||
|
||||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
if (player->getContinueCounter() < param.scoreboard.skip_countdown_value) {
|
||||
player->decContinueCounter();
|
||||
}
|
||||
@@ -1425,8 +1431,8 @@ void Game::handlePlayerContinueInput(const std::shared_ptr<Player> &player) {
|
||||
|
||||
// Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio.
|
||||
void Game::handlePlayerWaitingInput(const std::shared_ptr<Player> &player) {
|
||||
const auto CONTROLLER = Options::controllers.at(player->getController());
|
||||
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
const auto CONTROLLER = Options::controllers.at(0);
|
||||
if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
player->setPlayingState(Player::State::ENTERING_SCREEN);
|
||||
player->addCredit();
|
||||
sendPlayerToTheFront(player);
|
||||
@@ -1435,8 +1441,8 @@ void Game::handlePlayerWaitingInput(const std::shared_ptr<Player> &player) {
|
||||
|
||||
// Procesa las entradas para la introducción del nombre del jugador.
|
||||
void Game::handleNameInput(const std::shared_ptr<Player> &player) {
|
||||
const auto CONTROLLER = Options::controllers.at(player->getController());
|
||||
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
const auto CONTROLLER = Options::controllers.at(0);
|
||||
if (input_->checkAction(Input::Action::FIRE_LEFT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
if (player->isShowingName()) {
|
||||
player->setPlayingState(Player::State::CONTINUE);
|
||||
} else if (player->getEnterNamePositionOverflow()) {
|
||||
@@ -1446,18 +1452,18 @@ void Game::handleNameInput(const std::shared_ptr<Player> &player) {
|
||||
} else {
|
||||
player->setInput(Input::Action::RIGHT);
|
||||
}
|
||||
} else if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index) ||
|
||||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
} else if (input_->checkAction(Input::Action::FIRE_CENTER, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad) ||
|
||||
input_->checkAction(Input::Action::FIRE_RIGHT, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
if (player->isShowingName()) {
|
||||
player->setPlayingState(Player::State::CONTINUE);
|
||||
} else {
|
||||
player->setInput(Input::Action::LEFT);
|
||||
}
|
||||
} else if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
} else if (input_->checkAction(Input::Action::UP, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
player->setInput(Input::Action::UP);
|
||||
} else if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
} else if (input_->checkAction(Input::Action::DOWN, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
player->setInput(Input::Action::DOWN);
|
||||
} else if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) {
|
||||
} else if (input_->checkAction(Input::Action::START, Input::DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.gamepad)) {
|
||||
if (player->isShowingName()) {
|
||||
player->setPlayingState(Player::State::CONTINUE);
|
||||
} else {
|
||||
|
||||
@@ -198,13 +198,13 @@ class Game {
|
||||
void checkPauseInput(); // Verifica solicitudes de pausa de controladores
|
||||
|
||||
// --- Entrada de jugadores normales ---
|
||||
void handlePlayersInput(); // Gestiona entrada de todos los jugadores
|
||||
void handleNormalPlayerInput(const std::shared_ptr<Player> &player); // Procesa entrada de un jugador específico
|
||||
void handleFireInput(const std::shared_ptr<Player> &player, BulletType bullet_type); // Gestiona disparo de jugador
|
||||
void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controller_index); // Procesa disparos automáticos
|
||||
void handlePlayerContinueInput(const std::shared_ptr<Player> &player); // Permite continuar al jugador
|
||||
void handlePlayerWaitingInput(const std::shared_ptr<Player> &player); // Permite (re)entrar al jugador
|
||||
void handleNameInput(const std::shared_ptr<Player> &player); // Gestiona entrada de nombre del jugador
|
||||
void handlePlayersInput(); // Gestiona entrada de todos los jugadores
|
||||
void handleNormalPlayerInput(const std::shared_ptr<Player> &player); // Procesa entrada de un jugador específico
|
||||
void handleFireInput(const std::shared_ptr<Player> &player, BulletType bullet_type); // Gestiona disparo de jugador
|
||||
void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire); // Procesa disparos automáticos
|
||||
void handlePlayerContinueInput(const std::shared_ptr<Player> &player); // Permite continuar al jugador
|
||||
void handlePlayerWaitingInput(const std::shared_ptr<Player> &player); // Permite (re)entrar al jugador
|
||||
void handleNameInput(const std::shared_ptr<Player> &player); // Gestiona entrada de nombre del jugador
|
||||
|
||||
// --- Entrada en modo demo ---
|
||||
void demoHandleInput(); // Gestiona entrada durante el modo demostración
|
||||
|
||||
@@ -274,7 +274,7 @@ auto Title::isStartButtonPressed(const Options::GamepadOptions& controller) -> b
|
||||
Input::Action::START,
|
||||
Input::DO_NOT_ALLOW_REPEAT,
|
||||
controller.type,
|
||||
controller.index);
|
||||
controller.gamepad);
|
||||
}
|
||||
|
||||
void Title::handleStartButtonPress(const Options::GamepadOptions& controller) {
|
||||
@@ -357,12 +357,12 @@ void Title::showControllers() {
|
||||
}
|
||||
|
||||
// Genera el texto correspondiente
|
||||
for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
|
||||
const size_t INDEX = player_controller_index.at(i);
|
||||
if (Options::controllers.at(INDEX).plugged) {
|
||||
text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(INDEX).name;
|
||||
}
|
||||
}
|
||||
//for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
|
||||
// const size_t INDEX = player_controller_index.at(i);
|
||||
// if (Options::controllers.at(INDEX).plugged) {
|
||||
// text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(INDEX).name;
|
||||
// }
|
||||
//}
|
||||
|
||||
// Muestra la notificación
|
||||
Notifier::get()->show({text.at(0), text.at(1)});
|
||||
|
||||
Reference in New Issue
Block a user