desacoplament de Player i Options

Player: canviat id de int a enum
migrant input: eliminat Device, keyboard separat de la llista de mandos, llig i guarda configuracions de mandos
falta: definir botons, asignar mandos a jugadors i guardar la asignació
This commit is contained in:
2025-08-03 22:49:28 +02:00
parent de9fb5aa4b
commit 90c080f3e3
19 changed files with 433 additions and 353 deletions

View File

@@ -7,7 +7,7 @@
#include "resource.h" // Para Resource #include "resource.h" // Para Resource
// Constructor // Constructor
Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner) Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, Player::Id owner)
: sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("bullet.png"), Resource::get()->getAnimation("bullet.ani"))), : sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("bullet.png"), Resource::get()->getAnimation("bullet.ani"))),
pos_x_(x), pos_x_(x),
pos_y_(y), pos_y_(y),
@@ -94,7 +94,7 @@ void Bullet::disable() {
bullet_type_ = BulletType::NONE; bullet_type_ = BulletType::NONE;
} }
auto Bullet::getOwner() const -> int { auto Bullet::getOwner() const -> Player::Id {
return owner_; return owner_;
} }

View File

@@ -6,6 +6,7 @@
#include <string> // Para string #include <string> // Para string
#include "animated_sprite.h" // Para AnimatedSprite #include "animated_sprite.h" // Para AnimatedSprite
#include "player.h" // Para Player
#include "utils.h" // Para Circle #include "utils.h" // Para Circle
// Tipos de balas // Tipos de balas
@@ -30,7 +31,7 @@ class Bullet {
static constexpr float HEIGHT = 12.0F; static constexpr float HEIGHT = 12.0F;
// Constructor y Destructor // Constructor y Destructor
Bullet(float x, float y, BulletType bullet_type, bool powered, int owner); Bullet(float x, float y, BulletType bullet_type, bool powered, Player::Id owner);
~Bullet() = default; ~Bullet() = default;
// Métodos principales // Métodos principales
@@ -42,8 +43,8 @@ class Bullet {
void disable(); // Desactiva la bala void disable(); // Desactiva la bala
// Getters // Getters
[[nodiscard]] auto getOwner() const -> int; // Devuelve el identificador del dueño [[nodiscard]] auto getOwner() const -> Player::Id; // Devuelve el identificador del dueño
auto getCollider() -> Circle &; // Devuelve el círculo de colisión auto getCollider() -> Circle &; // Devuelve el círculo de colisión
private: private:
// Constantes // Constantes
@@ -60,7 +61,7 @@ class Bullet {
float vel_x_; // Velocidad en el eje X float vel_x_; // Velocidad en el eje X
BulletType bullet_type_; // Tipo de bala BulletType bullet_type_; // Tipo de bala
int owner_; // Identificador del dueño Player::Id owner_; // Identificador del dueño
Circle collider_; // Círculo de colisión Circle collider_; // Círculo de colisión
// Métodos internos // Métodos internos

View File

@@ -19,8 +19,9 @@ DefineButtons::DefineButtons()
clearButtons(); clearButtons();
for (int i = 0; i < input_->getNumControllers(); ++i) { auto gamepads = input_->getGamepads();
// controller_names_.emplace_back(input_->getControllerName(i)); for (auto gamepad : gamepads) {
controller_names_.emplace_back(input_->getControllerName(gamepad));
} }
} }
@@ -28,9 +29,9 @@ DefineButtons::DefineButtons()
void DefineButtons::render() { void DefineButtons::render() {
static auto text = Resource::get()->getText("8bithud"); static auto text = Resource::get()->getText("8bithud");
if (enabled_) { 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_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(static_cast<int>(gamepad_options_->player_id)));
// text->writeCentered(x_, y_, controller_names_.at(index_controller_)); text->writeCentered(x_, y_, gamepad_options_->instance->name);
// text->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label); text->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label);
} }
} }
@@ -76,15 +77,15 @@ void DefineButtons::checkEvents(const SDL_Event &event) {
} }
// Habilita el objeto // Habilita el objeto
auto DefineButtons::enable(int index) -> bool { auto DefineButtons::enable(std::shared_ptr<Options::Gamepad> gamepad_options) -> bool {
if (index < input_->getNumControllers()) { if (gamepad_options != nullptr) {
gamepad_options_ = gamepad_options;
enabled_ = true; enabled_ = true;
finished_ = false; finished_ = false;
index_button_ = 0; index_button_ = 0;
clearButtons(); clearButtons();
return true; return true;
} }
return false; return false;
} }

View File

@@ -9,6 +9,7 @@
#include <vector> // Para vector #include <vector> // Para vector
#include "input.h" #include "input.h"
#include "options.h"
// Clase DefineButtons // Clase DefineButtons
class DefineButtons { class DefineButtons {
@@ -26,14 +27,14 @@ class DefineButtons {
DefineButtons(); DefineButtons();
~DefineButtons() = default; ~DefineButtons() = default;
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla
void checkEvents(const SDL_Event &event); // Procesa los eventos void checkEvents(const SDL_Event &event); // Procesa los eventos
auto enable(int index_controller) -> bool; // Habilita la redefinición de botones auto enable(std::shared_ptr<Options::Gamepad> gamepad_options) -> bool; // Habilita la redefinición de botones
[[nodiscard]] auto isEnabled() const -> bool { return enabled_; }; // Comprueba si está habilitado [[nodiscard]] auto isEnabled() const -> bool { return enabled_; }; // Comprueba si está habilitado
private: private:
// Objetos // Objetos
Input *input_ = nullptr; // Gestión de entrada Input *input_ = nullptr; // Gestión de entrada
// Variables // Variables
bool enabled_ = false; // Indica si está activo bool enabled_ = false; // Indica si está activo
@@ -42,11 +43,12 @@ class DefineButtons {
size_t index_button_ = 0; // Índice del botón en proceso size_t index_button_ = 0; // Índice del botón en proceso
std::vector<std::string> controller_names_; // Nombres de los mandos std::vector<std::string> controller_names_; // Nombres de los mandos
bool finished_ = false; bool finished_ = false;
std::shared_ptr<Options::Gamepad> gamepad_options_;
// Métodos internos // Métodos internos
void incIndexButton(); // Incrementa el índice de botones void incIndexButton(); // Incrementa el índice de botones
void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones
void bindButtons(std::shared_ptr<Input::Gamepad> gamepad); // Asigna botones al sistema de entrada void bindButtons(std::shared_ptr<Input::Gamepad> gamepad); // Asigna botones al sistema de entrada
void saveBindingsToOptions(); // Guarda configuraciones void saveBindingsToOptions(); // Guarda configuraciones
auto checkButtonNotInUse(SDL_GamepadButton button) -> bool; // Verifica uso de botones auto checkButtonNotInUse(SDL_GamepadButton button) -> bool; // Verifica uso de botones
void clearButtons(); // Limpia asignaciones actuales void clearButtons(); // Limpia asignaciones actuales

View File

@@ -102,7 +102,7 @@ void Director::init() {
auto gamepads = Input::get()->getGamepads(); auto gamepads = Input::get()->getGamepads();
if (!gamepads.empty()) if (!gamepads.empty())
Options::controllers.front().gamepad = gamepads.front(); Options::gamepads.front()->instance = gamepads.front();
ServiceMenu::init(); // Inicializa el menú de servicio ServiceMenu::init(); // Inicializa el menú de servicio
Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones
@@ -543,23 +543,19 @@ void Director::runTitle() {
// Ejecuta la sección donde se juega al juego // Ejecuta la sección donde se juega al juego
void Director::runGame() { void Director::runGame() {
int player_id = 1; Player::Id player_id = Player::Id::PLAYER1;
switch (Section::options) { switch (Section::options) {
case Section::Options::GAME_PLAY_1P: case Section::Options::GAME_PLAY_1P:
player_id = 1; player_id = Player::Id::PLAYER1;
break; break;
case Section::Options::GAME_PLAY_2P: case Section::Options::GAME_PLAY_2P:
player_id = 2; player_id = Player::Id::PLAYER2;
break; break;
case Section::Options::GAME_PLAY_BOTH: case Section::Options::GAME_PLAY_BOTH:
player_id = 0; player_id = Player::Id::BOTH_PLAYERS;
break; break;
default: default:
player_id = 1;
break; break;
} }
@@ -568,7 +564,7 @@ void Director::runGame() {
#else #else
constexpr int CURRENT_STAGE = 0; constexpr int CURRENT_STAGE = 0;
#endif #endif
auto game = std::make_unique<Game>(player_id, CURRENT_STAGE, GAME_MODE_DEMO_OFF); auto game = std::make_unique<Game>(player_id, CURRENT_STAGE, Game::DEMO_OFF);
game->run(); game->run();
} }
@@ -592,9 +588,9 @@ void Director::runHiScoreTable() {
// Ejecuta el juego en modo demo // Ejecuta el juego en modo demo
void Director::runDemoGame() { void Director::runDemoGame() {
const auto PLAYER_ID = (rand() % 2) + 1; const auto PLAYER_ID = static_cast<Player::Id>((rand() % 2) + 1);
constexpr auto CURRENT_STAGE = 0; constexpr auto CURRENT_STAGE = 0;
auto game = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, GAME_MODE_DEMO_ON); auto game = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, Game::DEMO_ON);
game->run(); game->run();
} }

View File

@@ -25,7 +25,7 @@ auto Input::get() -> Input * { return Input::instance; }
// Constructor // Constructor
Input::Input(std::string game_controller_db_path, std::string gamepad_configs_file) Input::Input(std::string game_controller_db_path, std::string gamepad_configs_file)
: game_controller_db_path_(std::move(game_controller_db_path)), : gamepad_mappings_file_(std::move(game_controller_db_path)),
gamepad_configs_file_(std::move(gamepad_configs_file)) { gamepad_configs_file_(std::move(gamepad_configs_file)) {
// Inicializa el subsistema SDL_INIT_GAMEPAD // Inicializa el subsistema SDL_INIT_GAMEPAD
initSDLGamePad(); initSDLGamePad();
@@ -139,7 +139,7 @@ auto Input::gameControllerFound() const -> bool { return !gamepads_.empty(); }
auto Input::getControllerName(std::shared_ptr<Gamepad> gamepad) const -> std::string { return gamepad == nullptr ? std::string() : gamepad->name; } auto Input::getControllerName(std::shared_ptr<Gamepad> gamepad) const -> std::string { return gamepad == nullptr ? std::string() : gamepad->name; }
// Obten el número de mandos conectados // Obten el número de mandos conectados
auto Input::getNumControllers() const -> int { return gamepads_.size(); } auto Input::getNumGamepads() const -> int { return gamepads_.size(); }
// Obtiene el indice del controlador a partir de un event.id // Obtiene el indice del controlador a partir de un event.id
auto Input::getJoyIndex(SDL_JoystickID id) const -> int { auto Input::getJoyIndex(SDL_JoystickID id) const -> int {
@@ -230,8 +230,8 @@ auto Input::checkAxisInput(Action input, std::shared_ptr<Gamepad> gamepad, bool
} }
void Input::addGamepadMappingsFromFile() { void Input::addGamepadMappingsFromFile() {
if (SDL_AddGamepadMappingsFromFile(game_controller_db_path_.c_str()) < 0) { if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) {
std::cout << "Error, could not load " << game_controller_db_path_.c_str() << " file: " << SDL_GetError() << std::endl; std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << std::endl;
} }
} }
@@ -362,14 +362,13 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
} }
// Buscar configuración por nombre del gamepad // Buscar configuración por nombre del gamepad
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad](const GamepadConfig &config) {
[&gamepad](const GamepadConfig& config) { return config.name == gamepad->name;
return config.name == gamepad->name; });
});
if (configIt != gamepad_configs_.end()) { if (configIt != gamepad_configs_.end()) {
// Aplicar la configuración encontrada al gamepad // Aplicar la configuración encontrada al gamepad
for (const auto& [action, button] : configIt->bindings) { for (const auto &[action, button] : configIt->bindings) {
if (gamepad->bindings.find(action) != gamepad->bindings.end()) { if (gamepad->bindings.find(action) != gamepad->bindings.end()) {
gamepad->bindings[action].button = button; gamepad->bindings[action].button = button;
} }
@@ -383,17 +382,16 @@ void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
} }
// Buscar si ya existe una configuración con este nombre // Buscar si ya existe una configuración con este nombre
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad](const GamepadConfig &config) {
[&gamepad](const GamepadConfig& config) { return config.name == gamepad->name;
return config.name == gamepad->name; });
});
// Crear nueva configuración desde el gamepad // Crear nueva configuración desde el gamepad
GamepadConfig newConfig(gamepad->name); GamepadConfig newConfig(gamepad->name);
newConfig.bindings.clear(); // Limpiar bindings por defecto newConfig.bindings.clear(); // Limpiar bindings por defecto
// Copiar todos los bindings del gamepad // Copiar todos los bindings del gamepad
for (const auto& [action, buttonState] : gamepad->bindings) { for (const auto &[action, buttonState] : gamepad->bindings) {
newConfig.bindings[action] = buttonState.button; newConfig.bindings[action] = buttonState.button;
} }
@@ -410,33 +408,31 @@ void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
} }
// Método para establecer el archivo de configuración (opcional) // Método para establecer el archivo de configuración (opcional)
void Input::setGamepadConfigsFile(const std::string& filename) { void Input::setGamepadConfigsFile(const std::string &filename) {
gamepad_configs_file_ = filename; gamepad_configs_file_ = filename;
loadGamepadConfigs(); // Recargar con el nuevo archivo loadGamepadConfigs(); // Recargar con el nuevo archivo
} }
// Método para obtener configuración de un gamepad específico (opcional) // Método para obtener configuración de un gamepad específico (opcional)
GamepadConfig* Input::getGamepadConfig(const std::string& gamepadName) { GamepadConfig *Input::getGamepadConfig(const std::string &gamepadName) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepadName](const GamepadConfig &config) {
[&gamepadName](const GamepadConfig& config) { return config.name == gamepadName;
return config.name == gamepadName; });
});
return (configIt != gamepad_configs_.end()) ? &(*configIt) : nullptr; return (configIt != gamepad_configs_.end()) ? &(*configIt) : nullptr;
} }
// Método para eliminar configuración de gamepad (opcional) // Método para eliminar configuración de gamepad (opcional)
bool Input::removeGamepadConfig(const std::string& gamepadName) { bool Input::removeGamepadConfig(const std::string &gamepadName) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepadName](const GamepadConfig &config) {
[&gamepadName](const GamepadConfig& config) { return config.name == gamepadName;
return config.name == gamepadName; });
});
if (configIt != gamepad_configs_.end()) { if (configIt != gamepad_configs_.end()) {
gamepad_configs_.erase(configIt); gamepad_configs_.erase(configIt);
saveGamepadConfigs(); saveGamepadConfigs();
return true; return true;
} }
return false; return false;
} }

View File

@@ -49,54 +49,54 @@ class Input {
}; };
struct Keyboard { struct Keyboard {
std::unordered_map<InputAction, KeyState> bindings; std::unordered_map<Action, KeyState> bindings;
Keyboard() Keyboard()
: bindings{ : bindings{
// Teclado - Movimiento del jugador // Teclado - Movimiento del jugador
{InputAction::UP, KeyState(SDL_SCANCODE_UP)}, {Action::UP, KeyState(SDL_SCANCODE_UP)},
{InputAction::DOWN, KeyState(SDL_SCANCODE_DOWN)}, {Action::DOWN, KeyState(SDL_SCANCODE_DOWN)},
{InputAction::LEFT, KeyState(SDL_SCANCODE_LEFT)}, {Action::LEFT, KeyState(SDL_SCANCODE_LEFT)},
{InputAction::RIGHT, KeyState(SDL_SCANCODE_RIGHT)}, {Action::RIGHT, KeyState(SDL_SCANCODE_RIGHT)},
// Teclado - Disparo del jugador // Teclado - Disparo del jugador
{InputAction::FIRE_LEFT, KeyState(SDL_SCANCODE_Q)}, {Action::FIRE_LEFT, KeyState(SDL_SCANCODE_Q)},
{InputAction::FIRE_CENTER, KeyState(SDL_SCANCODE_W)}, {Action::FIRE_CENTER, KeyState(SDL_SCANCODE_W)},
{InputAction::FIRE_RIGHT, KeyState(SDL_SCANCODE_E)}, {Action::FIRE_RIGHT, KeyState(SDL_SCANCODE_E)},
// Teclado - Interfaz // Teclado - Interfaz
{InputAction::START, KeyState(SDL_SCANCODE_RETURN)}, {Action::START, KeyState(SDL_SCANCODE_RETURN)},
// Teclado - Menu de servicio // Teclado - Menu de servicio
{InputAction::SERVICE, KeyState(SDL_SCANCODE_0)}, {Action::SERVICE, KeyState(SDL_SCANCODE_0)},
{InputAction::SM_SELECT, KeyState(SDL_SCANCODE_RETURN)}, {Action::SM_SELECT, KeyState(SDL_SCANCODE_RETURN)},
{InputAction::SM_BACK, KeyState(SDL_SCANCODE_BACKSPACE)}, {Action::SM_BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
// Teclado - Control del programa // Teclado - Control del programa
{InputAction::EXIT, KeyState(SDL_SCANCODE_ESCAPE)}, {Action::EXIT, KeyState(SDL_SCANCODE_ESCAPE)},
{InputAction::PAUSE, KeyState(SDL_SCANCODE_P)}, {Action::PAUSE, KeyState(SDL_SCANCODE_P)},
{InputAction::BACK, KeyState(SDL_SCANCODE_BACKSPACE)}, {Action::BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
{InputAction::WINDOW_DEC_SIZE, KeyState(SDL_SCANCODE_F1)}, {Action::WINDOW_DEC_SIZE, KeyState(SDL_SCANCODE_F1)},
{InputAction::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)}, {Action::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
{InputAction::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)}, {Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
{InputAction::TOGGLE_VIDEO_SHADERS, KeyState(SDL_SCANCODE_F4)}, {Action::TOGGLE_VIDEO_SHADERS, KeyState(SDL_SCANCODE_F4)},
{InputAction::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)}, {Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
{InputAction::TOGGLE_VIDEO_VSYNC, KeyState(SDL_SCANCODE_F6)}, {Action::TOGGLE_VIDEO_VSYNC, KeyState(SDL_SCANCODE_F6)},
{InputAction::TOGGLE_AUDIO, KeyState(SDL_SCANCODE_F7)}, {Action::TOGGLE_AUDIO, KeyState(SDL_SCANCODE_F7)},
{InputAction::TOGGLE_AUTO_FIRE, KeyState(SDL_SCANCODE_F8)}, {Action::TOGGLE_AUTO_FIRE, KeyState(SDL_SCANCODE_F8)},
{InputAction::CHANGE_LANG, KeyState(SDL_SCANCODE_F9)}, {Action::CHANGE_LANG, KeyState(SDL_SCANCODE_F9)},
{InputAction::RESET, KeyState(SDL_SCANCODE_F10)}, {Action::RESET, KeyState(SDL_SCANCODE_F10)},
{InputAction::SHOW_INFO, KeyState(SDL_SCANCODE_F12)}} {} {Action::SHOW_INFO, KeyState(SDL_SCANCODE_F12)}} {}
}; };
struct Gamepad { struct Gamepad {
SDL_Gamepad *pad; SDL_Gamepad *pad;
SDL_JoystickID instance_id; SDL_JoystickID instance_id;
std::string name; std::string name;
std::unordered_map<InputAction, ButtonState> bindings; std::unordered_map<Action, ButtonState> bindings;
Gamepad(SDL_Gamepad *gamepad) Gamepad(SDL_Gamepad *gamepad)
: pad(gamepad), : pad(gamepad),
@@ -104,19 +104,19 @@ class Input {
name(std::string(SDL_GetGamepadName(gamepad))), name(std::string(SDL_GetGamepadName(gamepad))),
bindings{ bindings{
// Mando - Movimiento del jugador // Mando - Movimiento del jugador
{InputAction::UP, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_UP)}, {Action::UP, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_UP)},
{InputAction::DOWN, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_DOWN)}, {Action::DOWN, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_DOWN)},
{InputAction::LEFT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_LEFT)}, {Action::LEFT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_LEFT)},
{InputAction::RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_RIGHT)}, {Action::RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_RIGHT)},
// Mando - Disparo del jugador // Mando - Disparo del jugador
{InputAction::FIRE_LEFT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)}, {Action::FIRE_LEFT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)},
{InputAction::FIRE_CENTER, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)}, {Action::FIRE_CENTER, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)},
{InputAction::FIRE_RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_EAST)}, {Action::FIRE_RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_EAST)},
// Mando - Interfaz // Mando - Interfaz
{InputAction::START, ButtonState(SDL_GAMEPAD_BUTTON_START)}, {Action::START, ButtonState(SDL_GAMEPAD_BUTTON_START)},
{InputAction::SERVICE, ButtonState(SDL_GAMEPAD_BUTTON_BACK)}} {} {Action::SERVICE, ButtonState(SDL_GAMEPAD_BUTTON_BACK)}} {}
~Gamepad() { ~Gamepad() {
if (pad) { if (pad) {
@@ -125,7 +125,7 @@ class Input {
} }
// Reasigna un botón a una acción // Reasigna un botón a una acción
void rebindAction(InputAction action, SDL_GamepadButton new_button) { void rebindAction(Action action, SDL_GamepadButton new_button) {
bindings[action] = new_button; bindings[action] = new_button;
} }
}; };
@@ -149,7 +149,7 @@ class Input {
// --- Métodos de gestión de mandos --- // --- Métodos de gestión de mandos ---
[[nodiscard]] auto gameControllerFound() const -> bool; [[nodiscard]] auto gameControllerFound() const -> bool;
auto getControllerName(std::shared_ptr<Gamepad> gamepad) const -> std::string; auto getControllerName(std::shared_ptr<Gamepad> gamepad) const -> std::string;
[[nodiscard]] auto getNumControllers() const -> int; [[nodiscard]] auto getNumGamepads() const -> int;
[[nodiscard]] auto getJoyIndex(SDL_JoystickID id) const -> int; [[nodiscard]] auto getJoyIndex(SDL_JoystickID id) const -> int;
// --- Métodos de consulta y utilidades --- // --- Métodos de consulta y utilidades ---
@@ -175,7 +175,7 @@ class Input {
std::vector<std::shared_ptr<Gamepad>> gamepads_; std::vector<std::shared_ptr<Gamepad>> gamepads_;
Keyboard keyboard_; Keyboard keyboard_;
std::vector<Action> button_inputs_; std::vector<Action> button_inputs_;
std::string game_controller_db_path_; std::string gamepad_mappings_file_;
std::string gamepad_configs_file_; std::string gamepad_configs_file_;
GamepadConfigs gamepad_configs_; GamepadConfigs gamepad_configs_;
@@ -186,17 +186,17 @@ class Input {
void remove_gamepad(SDL_JoystickID id); void remove_gamepad(SDL_JoystickID id);
void addGamepadMappingsFromFile(); void addGamepadMappingsFromFile();
void discoverGamepads(); void discoverGamepads();
// --- Métodos para integración con GamepadConfigManager --- // --- Métodos para integración con GamepadConfigManager ---
void loadGamepadConfigs(); void loadGamepadConfigs();
void saveGamepadConfigs(); void saveGamepadConfigs();
void applyGamepadConfig(std::shared_ptr<Gamepad> gamepad); void applyGamepadConfig(std::shared_ptr<Gamepad> gamepad);
void saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad); void saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad);
// Métodos auxiliares opcionales // Métodos auxiliares opcionales
void setGamepadConfigsFile(const std::string& filename); void setGamepadConfigsFile(const std::string &filename);
GamepadConfig* getGamepadConfig(const std::string& gamepadName); GamepadConfig *getGamepadConfig(const std::string &gamepadName);
bool removeGamepadConfig(const std::string& gamepadName); bool removeGamepadConfig(const std::string &gamepadName);
// --- Constructor y destructor --- // --- Constructor y destructor ---
explicit Input(std::string game_controller_db_path, std::string gamepad_configs_file); explicit Input(std::string game_controller_db_path, std::string gamepad_configs_file);

View File

@@ -51,7 +51,7 @@ auto ManageHiScoreTable::add(const HiScoreEntry &entry) -> int {
// Si el nuevo elemento quedó fuera del top 10 // Si el nuevo elemento quedó fuera del top 10
if (position >= 10) { if (position >= 10) {
position = -1; // No entró en el top 10 position = NO_ENTRY; // No entró en el top 10
} }
} }

View File

@@ -22,32 +22,29 @@ struct HiScoreEntry {
: name(n.substr(0, 6)), score(s), one_credit_complete(occ) {} : name(n.substr(0, 6)), score(s), one_credit_complete(occ) {}
}; };
using Table = std::vector<HiScoreEntry>;
// --- Clase ManageHiScoreTable --- // --- Clase ManageHiScoreTable ---
class ManageHiScoreTable { class ManageHiScoreTable {
public: public:
// Constructor // --- Constantes ---
explicit ManageHiScoreTable(std::vector<HiScoreEntry> &table) static constexpr int NO_ENTRY = -1;
: table_(table) {}
// Destructor // Constructor y destructor
explicit ManageHiScoreTable(Table &table)
: table_(table) {}
~ManageHiScoreTable() = default; ~ManageHiScoreTable() = default;
// Resetea la tabla a los valores por defecto // --- Métodos públicos ---
void clear(); void clear(); // Resetea la tabla a los valores por defecto
auto add(const HiScoreEntry &entry) -> int; // Añade un elemento a la tabla (devuelve la posición en la que se inserta)
// Añade un elemento a la tabla (devuelve la posición en la que se inserta) auto loadFromFile(const std::string &file_path) -> bool; // Carga la tabla con los datos de un fichero
auto add(const HiScoreEntry &entry) -> int; auto saveToFile(const std::string &file_path) -> bool; // Guarda la tabla en un fichero
// Carga la tabla con los datos de un fichero
auto loadFromFile(const std::string &file_path) -> bool;
// Guarda la tabla en un fichero
auto saveToFile(const std::string &file_path) -> bool;
private: private:
// Referencia a la tabla con los records // --- Variables privadas ---
std::vector<HiScoreEntry> &table_; Table &table_; // Referencia a la tabla con los records
// Ordena la tabla internamente // --- Métodos privados ---
void sort(); void sort(); // Ordena la tabla
}; };

View File

@@ -19,13 +19,13 @@
namespace Options { namespace Options {
// --- Variables globales --- // --- Variables globales ---
WindowOptions window; // Opciones de la ventana Window window; // Opciones de la ventana
SettingsOptions settings; // Opciones del juego Settings settings; // Opciones del juego
VideoOptions video; // Opciones de vídeo Video video; // Opciones de vídeo
AudioOptions audio; // Opciones de audio Audio audio; // Opciones de audio
std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador std::vector<std::shared_ptr<Gamepad>> gamepads; // Opciones de mando para cada jugador
PendingChanges pending_changes; // Opciones que se aplican al cerrar Keyboard keyboard; // Opciones para el teclado
// std::vector<GamepadConfig> gamepad_configs; // Lista con las configuraciones registradas para cada mando PendingChanges pending_changes; // Opciones que se aplican al cerrar
// Declaraciones // Declaraciones
auto set(const std::string& var, const std::string& value) -> bool; auto set(const std::string& var, const std::string& value) -> bool;
@@ -42,10 +42,10 @@ void init() {
Difficulty::init(); Difficulty::init();
// Opciones de control // Opciones de control
controllers.clear(); gamepads.clear();
controllers.emplace_back(GamepadOptions(1)); gamepads.emplace_back(std::make_shared<Gamepad>(Player::Id::PLAYER1));
controllers.emplace_back(GamepadOptions(2)); gamepads.emplace_back(std::make_shared<Gamepad>(Player::Id::PLAYER2));
setKeyboardToPlayer(1); setKeyboardToPlayer(Player::Id::PLAYER1);
// Opciones de cambios pendientes // Opciones de cambios pendientes
pending_changes.new_language = settings.language; pending_changes.new_language = settings.language;
@@ -154,15 +154,15 @@ auto saveToFile() -> bool {
// Opciones de mandos // Opciones de mandos
file << "\n\n## CONTROLLERS\n"; file << "\n\n## CONTROLLERS\n";
int controller_index = 0; // int controller_index = 0;
for (const auto& controller : controllers) { // for (const auto& controller : gamepads) {
file << "\n"; // file << "\n";
file << "controller." << controller_index << ".name=" << controller.name << "\n"; // file << "controller." << controller_index << ".name=" << controller->name << "\n";
file << "controller." << controller_index << ".player=" << controller.player_id << "\n"; // file << "controller." << controller_index << ".player=" << controller->player_id << "\n";
//
// Incrementa el índice // // Incrementa el índice
++controller_index; // ++controller_index;
} // }
// Cierra el fichero // Cierra el fichero
file.close(); file.close();
@@ -173,19 +173,19 @@ auto saveToFile() -> bool {
// Función auxiliar para analizar la configuración del mando y reducir duplicación // Función auxiliar para analizar la configuración del mando y reducir duplicación
void parseAndSetController(const std::string& var, const std::string& value) { void parseAndSetController(const std::string& var, const std::string& value) {
// Lógica básica de análisis (puede hacerse más robusta) // Lógica básica de análisis (puede hacerse más robusta)
size_t first_dot = var.find('.'); // size_t first_dot = var.find('.');
size_t second_dot = var.find('.', first_dot + 1); // size_t second_dot = var.find('.', first_dot + 1);
//
int controller_index = std::stoi(var.substr(first_dot + 1, second_dot - first_dot - 1)); // int controller_index = std::stoi(var.substr(first_dot + 1, second_dot - first_dot - 1));
std::string setting_key = var.substr(second_dot + 1); // std::string setting_key = var.substr(second_dot + 1);
//
auto& controller = controllers.at(controller_index); // auto& controller = gamepads.at(controller_index);
//
if (setting_key == "name") { // if (setting_key == "name") {
controller.name = value; // controller.name = value;
} else if (setting_key == "player") { // } else if (setting_key == "player") {
controller.player_id = std::clamp(std::stoi(value), 1, 2); // controller.player_id = std::clamp(std::stoi(value), 1, 2);
} // }
} }
auto set(const std::string& var, const std::string& value) -> bool { auto set(const std::string& var, const std::string& value) -> bool {
@@ -254,35 +254,23 @@ auto set(const std::string& var, const std::string& value) -> bool {
} }
// Asigna el teclado al jugador // Asigna el teclado al jugador
void setKeyboardToPlayer(int player_id) { void setKeyboardToPlayer(Player::Id player_id) {
for (auto& controller : controllers) { keyboard.player_id = player_id;
if (controller.player_id == player_id) {
// controller.type = Input::Device::ANY;
} else {
// controller.type = Input::Device::CONTROLLER;
}
}
} }
// Intercambia el teclado de jugador // Intercambia el teclado de jugador
void swapKeyboard() { void swapKeyboard() {
// std::swap(controllers.at(0).type, controllers.at(1).type); keyboard.player_id = keyboard.player_id == Player::Id::PLAYER1 ? Player::Id::PLAYER2 : Player::Id::PLAYER1;
} }
// Intercambia los jugadores asignados a los dos primeros mandos // Intercambia los jugadores asignados a los dos primeros mandos
void swapControllers() { void swapControllers() {
std::swap(controllers.at(0).player_id, controllers.at(1).player_id); std::swap(gamepads.at(0)->player_id, gamepads.at(1)->player_id);
// std::swap(controllers.at(0).type, controllers.at(1).type);
} }
// Averigua quien está usando el teclado // Averigua quien está usando el teclado
auto getPlayerWhoUsesKeyboard() -> int { auto getPlayerWhoUsesKeyboard() -> Player::Id {
// for (const auto& controller : controllers) { return keyboard.player_id;
// if (controller.type == Input::Device::ANY) {
// return controller.player_id;
// }
//}
return 0;
} }
// Aplica los cambios pendientes copiando los valores a sus variables // Aplica los cambios pendientes copiando los valores a sus variables

View File

@@ -8,28 +8,26 @@
#include <unordered_map> #include <unordered_map>
#include <vector> // Para vector #include <vector> // Para vector
#include "difficulty.h" // Para Code #include "difficulty.h" // Para Code
// #include "gamepad_config_manager.h"
#include "input.h" // Para InputAction, InputDevice #include "input.h" // Para InputAction, InputDevice
#include "lang.h" // Para Code #include "lang.h" // Para Code
#include "manage_hiscore_table.h" // Para HiScoreEntry #include "manage_hiscore_table.h" // Para HiScoreEntry
#include "player.h" // Para Player
static constexpr int INVALID_INDEX = -1;
namespace Options { namespace Options {
// --- Opciones de ventana --- // --- Opciones de ventana ---
struct WindowOptions { struct Window {
std::string caption; // Texto que aparece en la barra de título de la ventana std::string caption; // Texto que aparece en la barra de título de la ventana
int zoom{2}; // Valor por el que se multiplica el tamaño de la ventana int zoom{2}; // Valor por el que se multiplica el tamaño de la ventana
int max_zoom{2}; // Tamaño máximo para que la ventana no sea mayor que la pantalla int max_zoom{2}; // Tamaño máximo para que la ventana no sea mayor que la pantalla
// Constructor por defecto con valores iniciales // Constructor por defecto con valores iniciales
WindowOptions() Window()
: caption("Coffee Crisis Arcade Edition") {} : caption("Coffee Crisis Arcade Edition") {}
}; };
// --- Opciones de vídeo --- // --- Opciones de vídeo ---
struct VideoOptions { struct Video {
SDL_ScaleMode scale_mode{SDL_ScaleMode::SDL_SCALEMODE_NEAREST}; // Filtro usado para el escalado de la imagen SDL_ScaleMode scale_mode{SDL_ScaleMode::SDL_SCALEMODE_NEAREST}; // Filtro usado para el escalado de la imagen
bool fullscreen{false}; // Indica si se usa pantalla completa bool fullscreen{false}; // Indica si se usa pantalla completa
bool vsync{true}; // Indica si se usa vsync bool vsync{true}; // Indica si se usa vsync
@@ -38,69 +36,73 @@ struct VideoOptions {
std::string info; // Información sobre el modo de vídeo std::string info; // Información sobre el modo de vídeo
// Constructor por defecto con valores iniciales // Constructor por defecto con valores iniciales
VideoOptions() = default; Video() = default;
}; };
// --- Opciones de música --- // --- Opciones de música ---
struct MusicOptions { struct Music {
bool enabled{true}; // Indica si la música suena o no bool enabled{true}; // Indica si la música suena o no
int volume{100}; // Volumen de la música int volume{100}; // Volumen de la música
// Constructor por defecto // Constructor por defecto
MusicOptions() = default; Music() = default;
}; };
// --- Opciones de sonido --- // --- Opciones de sonido ---
struct SoundOptions { struct Sound {
bool enabled{true}; // Indica si los sonidos suenan o no bool enabled{true}; // Indica si los sonidos suenan o no
int volume{100}; // Volumen de los sonidos int volume{100}; // Volumen de los sonidos
// Constructor por defecto // Constructor por defecto
SoundOptions() = default; Sound() = default;
}; };
// --- Opciones de audio --- // --- Opciones de audio ---
struct AudioOptions { struct Audio {
MusicOptions music; // Opciones para la música Music music; // Opciones para la música
SoundOptions sound; // Opciones para los efectos de sonido Sound sound; // Opciones para los efectos de sonido
bool enabled{true}; // Indica si el audio está activo o no bool enabled{true}; // Indica si el audio está activo o no
int volume{100}; // Volumen general del audio int volume{100}; // Volumen general del audio
// Constructor por defecto // Constructor por defecto
AudioOptions() = default; Audio() = default;
}; };
// --- Opciones de configuración --- // --- Opciones de configuración ---
struct SettingsOptions { struct Settings {
Difficulty::Code difficulty{Difficulty::Code::NORMAL}; // Dificultad del juego Difficulty::Code difficulty{Difficulty::Code::NORMAL}; // Dificultad del juego
Lang::Code language{Lang::Code::VALENCIAN}; // Idioma usado en el juego Lang::Code language{Lang::Code::VALENCIAN}; // Idioma usado en el juego
bool autofire{true}; // Indicador de autofire bool autofire{true}; // Indicador de autofire
bool shutdown_enabled{false}; // Especifica si se puede apagar el sistema bool shutdown_enabled{false}; // Especifica si se puede apagar el sistema
std::vector<HiScoreEntry> hi_score_table; // Tabla de mejores puntuaciones Table hi_score_table; // Tabla de mejores puntuaciones
std::vector<int> last_hi_score_entry; // Últimas posiciones de entrada en la tabla std::vector<int> glowing_entries; // Últimas posiciones de entrada en la tabla
std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego
std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos std::string controllers_file; // Ruta al fichero con las configuraciones de los mandos
// Constructor por defecto con valores iniciales // Constructor por defecto con valores iniciales
SettingsOptions() Settings()
: last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}) {} : glowing_entries({ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}) {}
// Reinicia las últimas entradas de puntuación // Reinicia las últimas entradas de puntuación
void clearLastHiScoreEntries() { void clearLastHiScoreEntries() {
last_hi_score_entry.at(0) = INVALID_INDEX; glowing_entries.at(0) = ManageHiScoreTable::NO_ENTRY;
last_hi_score_entry.at(1) = INVALID_INDEX; glowing_entries.at(1) = ManageHiScoreTable::NO_ENTRY;
} }
}; };
struct GamepadOptions { struct Gamepad {
std::shared_ptr<Input::Gamepad> gamepad = nullptr; // Referencia al mando std::shared_ptr<Input::Gamepad> instance = nullptr; // Referencia al mando
std::string name; // Nombre del mando std::string name; // Nombre del mando
int player_id; // Jugador asociado al mando Player::Id player_id; // Jugador asociado al mando
GamepadOptions(int custom_player_id = INVALID_INDEX) Gamepad(Player::Id custom_player_id = Player::Id::NO_PLAYER)
: player_id(custom_player_id) {} : player_id(custom_player_id) {}
}; };
struct Keyboard {
Player::Id player_id = Player::Id::PLAYER1; // Jugador asociado al teclado
};
// --- Opciones pendientes de aplicar --- // --- Opciones pendientes de aplicar ---
struct PendingChanges { struct PendingChanges {
Lang::Code new_language{Lang::Code::VALENCIAN}; // Idioma en espera de aplicar Lang::Code new_language{Lang::Code::VALENCIAN}; // Idioma en espera de aplicar
@@ -112,12 +114,13 @@ struct PendingChanges {
}; };
// --- Variables globales --- // --- Variables globales ---
extern WindowOptions window; // Opciones de la ventana extern Window window; // Opciones de la ventana
extern SettingsOptions settings; // Opciones del juego extern Settings settings; // Opciones del juego
extern VideoOptions video; // Opciones de vídeo extern Video video; // Opciones de vídeo
extern AudioOptions audio; // Opciones de audio extern Audio audio; // Opciones de audio
extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador extern std::vector<std::shared_ptr<Gamepad>> gamepads; // Opciones de mando para cada jugador
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar extern Keyboard keyboard; // Opciones para el teclado
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
// --- Funciones de configuración --- // --- Funciones de configuración ---
void init(); // Inicializa las opciones del programa void init(); // Inicializa las opciones del programa
@@ -125,10 +128,10 @@ void setConfigFile(const std::string &file_path); // Establece el fichero
void setControllersFile(const std::string &file_path); // Establece el fichero de configuración de mandos void setControllersFile(const std::string &file_path); // Establece el fichero de configuración de mandos
auto loadFromFile() -> bool; // Carga el fichero de configuración auto loadFromFile() -> bool; // Carga el fichero de configuración
auto saveToFile() -> bool; // Guarda el fichero de configuración auto saveToFile() -> bool; // Guarda el fichero de configuración
void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador void setKeyboardToPlayer(Player::Id player_id); // Asigna el teclado al jugador
void swapKeyboard(); // Intercambia el teclado de jugador void swapKeyboard(); // Intercambia el teclado de jugador
void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos
auto getPlayerWhoUsesKeyboard() -> int; // Averigua quién está usando el teclado auto getPlayerWhoUsesKeyboard() -> Player::Id; // Averigua quién está usando el teclado
void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables
void checkPendingChanges(); // Verifica si hay cambios pendientes void checkPendingChanges(); // Verifica si hay cambios pendientes

View File

@@ -16,20 +16,22 @@
#include "texture.h" // Para Texture #include "texture.h" // Para Texture
// Constructor // 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::Player(const Config& config)
: player_sprite_(std::make_unique<AnimatedSprite>(texture.at(0), animations.at(0))), : player_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(0), config.animations.at(0))),
power_sprite_(std::make_unique<AnimatedSprite>(texture.at(1), animations.at(1))), power_sprite_(std::make_unique<AnimatedSprite>(config.texture.at(1), config.animations.at(1))),
enter_name_(std::make_unique<EnterName>()), enter_name_(std::make_unique<EnterName>()),
id_(id), id_(config.id),
play_area_(play_area), play_area_(*config.play_area),
default_pos_x_(x), default_pos_x_(config.x),
default_pos_y_(y), default_pos_y_(config.y),
demo_(demo) { hi_score_table_(*config.hi_score_table),
glowing_entry_(*config.glowing_entry),
demo_(config.demo) {
// Configura objetos // Configura objetos
player_sprite_->getTexture()->setPalette(coffees_); player_sprite_->getTexture()->setPalette(coffees_);
power_sprite_->getTexture()->setAlpha(224); power_sprite_->getTexture()->setAlpha(224);
power_up_x_offset_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2; power_up_x_offset_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2;
power_sprite_->setPosY(y - (power_sprite_->getHeight() - player_sprite_->getHeight())); power_sprite_->setPosY(default_pos_y_ - (power_sprite_->getHeight() - player_sprite_->getHeight()));
// Inicializa variables // Inicializa variables
pos_x_ = default_pos_x_; pos_x_ = default_pos_x_;
@@ -219,8 +221,8 @@ void Player::handleRollingGroundCollision() {
} }
void Player::handleRollingStop() { void Player::handleRollingStop() {
const auto NEXT_PLAYER_STATUS = isEligibleForHighScore() ? State::ENTERING_NAME : State::CONTINUE; const auto NEXT_PLAYER_STATE = qualifiesForHighScore() ? State::ENTERING_NAME : State::CONTINUE;
const auto NEXT_STATE = demo_ ? State::LYING_ON_THE_FLOOR_FOREVER : NEXT_PLAYER_STATUS; const auto NEXT_STATE = demo_ ? State::LYING_ON_THE_FLOOR_FOREVER : NEXT_PLAYER_STATE;
setPlayingState(NEXT_STATE); setPlayingState(NEXT_STATE);
pos_x_ = player_sprite_->getPosX(); pos_x_ = player_sprite_->getPosX();
@@ -278,10 +280,15 @@ void Player::handleLeavingScreen() {
void Player::handleEnteringScreen() { void Player::handleEnteringScreen() {
updateStepCounter(); updateStepCounter();
if (id_ == 1) { switch (id_) {
handlePlayer1Entering(); case Id::PLAYER1:
} else if (id_ == 2) { handlePlayer1Entering();
handlePlayer2Entering(); break;
case Id::PLAYER2:
handlePlayer2Entering();
break;
default:
break;
} }
shiftSprite(); shiftSprite();
@@ -350,10 +357,10 @@ void Player::updateWalkingStateForCredits() {
void Player::setInputBasedOnPlayerId() { void Player::setInputBasedOnPlayerId() {
switch (id_) { switch (id_) {
case 1: case Id::PLAYER1:
setInputPlaying(Input::Action::LEFT); setInputPlaying(Input::Action::LEFT);
break; break;
case 2: case Id::PLAYER2:
setInputPlaying(Input::Action::RIGHT); setInputPlaying(Input::Action::RIGHT);
break; break;
default: default:
@@ -552,9 +559,10 @@ void Player::update() {
} }
// Incrementa la puntuación del jugador // Incrementa la puntuación del jugador
void Player::addScore(int score) { void Player::addScore(int score, int last_hi_score_entry) {
if (isPlaying()) { if (isPlaying()) {
score_ += score; score_ += score;
qualifies_for_high_score_ = score_ > last_hi_score_entry;
} }
} }
@@ -611,10 +619,10 @@ void Player::setPlayingState(State state) {
} }
case State::WAITING: { case State::WAITING: {
switch (id_) { switch (id_) {
case 1: case Id::PLAYER1:
pos_x_ = param.game.game_area.rect.x; pos_x_ = param.game.game_area.rect.x;
break; break;
case 2: case Id::PLAYER2:
pos_x_ = param.game.game_area.rect.w - WIDTH; pos_x_ = param.game.game_area.rect.w - WIDTH;
break; break;
default: default:
@@ -698,11 +706,11 @@ void Player::setPlayingState(State state) {
step_counter_ = 0; step_counter_ = 0;
setScoreboardMode(Scoreboard::Mode::SCORE); setScoreboardMode(Scoreboard::Mode::SCORE);
switch (id_) { switch (id_) {
case 1: case Id::PLAYER1:
pos_x_ = param.game.game_area.rect.x - WIDTH; pos_x_ = param.game.game_area.rect.x - WIDTH;
break; break;
case 2: case Id::PLAYER2:
pos_x_ = param.game.game_area.rect.x + param.game.game_area.rect.w; pos_x_ = param.game.game_area.rect.x + param.game.game_area.rect.w;
break; break;
@@ -898,7 +906,7 @@ auto Player::isRenderable() const -> bool {
// Añade una puntuación a la tabla de records // Añade una puntuación a la tabla de records
void Player::addScoreToScoreBoard() const { void Player::addScoreToScoreBoard() const {
const auto ENTRY = HiScoreEntry(trim(getLastEnterName()), getScore(), get1CC()); const auto ENTRY = HiScoreEntry(trim(getLastEnterName()), getScore(), get1CC());
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); auto manager = std::make_unique<ManageHiScoreTable>(hi_score_table_);
Options::settings.last_hi_score_entry.at(getId() - 1) = manager->add(ENTRY); glowing_entry_ = manager->add(ENTRY);
manager->saveToFile(Asset::get()->get("score.bin")); manager->saveToFile(Asset::get()->get("score.bin"));
} }

View File

@@ -10,7 +10,6 @@
#include "enter_name.h" // Para EnterName #include "enter_name.h" // Para EnterName
#include "input.h" #include "input.h"
#include "manage_hiscore_table.h" // Para HiScoreEntry #include "manage_hiscore_table.h" // Para HiScoreEntry
#include "options.h" // Para SettingsOptions, settings
#include "scoreboard.h" // Para Scoreboard #include "scoreboard.h" // Para Scoreboard
#include "utils.h" // Para Circle #include "utils.h" // Para Circle
@@ -25,6 +24,14 @@ class Player {
static constexpr int WIDTH = 32; // Anchura static constexpr int WIDTH = 32; // Anchura
static constexpr int HEIGHT = 32; // Altura static constexpr int HEIGHT = 32; // Altura
// --- Id para los jugadores ---
enum class Id : int {
NO_PLAYER = -1,
BOTH_PLAYERS = 0,
PLAYER1 = 1,
PLAYER2 = 2
};
// --- Estados posibles del jugador --- // --- Estados posibles del jugador ---
enum class State { enum class State {
// Estados de movimiento // Estados de movimiento
@@ -68,8 +75,20 @@ class Player {
RESPAWNING, // Tras continuar y volver al juego RESPAWNING, // Tras continuar y volver al juego
}; };
struct Config {
Id id;
float x;
int y;
bool demo;
SDL_FRect *play_area; // Usamos puntero para mantener la referencia
std::vector<std::shared_ptr<Texture>> texture;
std::vector<std::vector<std::string>> animations;
Table *hi_score_table; // También como puntero para referencia
int *glowing_entry; // Puntero para mantener la referencia
};
// --- Constructor y destructor --- // --- Constructor y destructor ---
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(const Config& config);
~Player() = default; ~Player() = default;
// --- Inicialización y ciclo de vida --- // --- Inicialización y ciclo de vida ---
@@ -93,9 +112,9 @@ class Player {
void updateCooldown(); // Actualiza el cooldown de disparo void updateCooldown(); // Actualiza el cooldown de disparo
// --- Puntuación y marcador --- // --- Puntuación y marcador ---
void addScore(int score); // Añade puntos void addScore(int score, int last_hi_score_entry); // Añade puntos
void incScoreMultiplier(); // Incrementa el multiplicador void incScoreMultiplier(); // Incrementa el multiplicador
void decScoreMultiplier(); // Decrementa el multiplicador void decScoreMultiplier(); // Decrementa el multiplicador
// --- Estados de juego --- // --- Estados de juego ---
void setPlayingState(State state); // Cambia el estado de juego void setPlayingState(State state); // Cambia el estado de juego
@@ -128,7 +147,7 @@ class Player {
[[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; } [[nodiscard]] auto hasExtraHit() const -> bool { return extra_hit_; }
[[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; } [[nodiscard]] auto isCooling() const -> bool { return firing_state_ == State::COOLING_LEFT || firing_state_ == State::COOLING_UP || firing_state_ == State::COOLING_RIGHT; }
[[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; } [[nodiscard]] auto isRecoiling() const -> bool { return firing_state_ == State::RECOILING_LEFT || firing_state_ == State::RECOILING_UP || firing_state_ == State::RECOILING_RIGHT; }
[[nodiscard]] auto isEligibleForHighScore() const -> bool { return score_ > Options::settings.hi_score_table.back().score; } [[nodiscard]] auto qualifiesForHighScore() const -> bool { return qualifies_for_high_score_; }
[[nodiscard]] auto isInvulnerable() const -> bool { return invulnerable_; } [[nodiscard]] auto isInvulnerable() const -> bool { return invulnerable_; }
[[nodiscard]] auto isPowerUp() const -> bool { return power_up_; } [[nodiscard]] auto isPowerUp() const -> bool { return power_up_; }
auto getCollider() -> Circle & { return collider_; } auto getCollider() -> Circle & { return collider_; }
@@ -137,7 +156,7 @@ class Player {
[[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; } [[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; }
[[nodiscard]] auto getController() const -> int { return controller_index_; } [[nodiscard]] auto getController() const -> int { return controller_index_; }
[[nodiscard]] static auto getHeight() -> int { return HEIGHT; } [[nodiscard]] static auto getHeight() -> int { return HEIGHT; }
[[nodiscard]] auto getId() const -> int { return id_; } [[nodiscard]] auto getId() const -> Player::Id { return id_; }
[[nodiscard]] auto getInvulnerableCounter() const -> int { return invulnerable_counter_; } [[nodiscard]] auto getInvulnerableCounter() const -> int { return invulnerable_counter_; }
[[nodiscard]] auto getPosX() const -> int { return static_cast<int>(pos_x_); } [[nodiscard]] auto getPosX() const -> int { return static_cast<int>(pos_x_); }
[[nodiscard]] auto getPosY() const -> int { return pos_y_; } [[nodiscard]] auto getPosY() const -> int { return pos_y_; }
@@ -165,11 +184,13 @@ class Player {
void setWalkingState(State state) { walking_state_ = state; } void setWalkingState(State state) { walking_state_ = state; }
void addCredit() { ++credits_used_; } void addCredit() { ++credits_used_; }
// Setter y getter para gamepad_
void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; } void setGamepad(std::shared_ptr<Input::Gamepad> gamepad) { gamepad_ = gamepad; }
[[nodiscard]] std::shared_ptr<Input::Gamepad> getGamepad() const { return gamepad_; } [[nodiscard]] std::shared_ptr<Input::Gamepad> getGamepad() const { return gamepad_; }
void setUsesKeyboard(bool value) { uses_keyboard_ = value; } void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
[[nodiscard]] bool getUsesKeyboard() const { return uses_keyboard_; } [[nodiscard]] bool getUsesKeyboard() const { return uses_keyboard_; }
void setHiScoreTable(const Table &table) { hi_score_table_ = table; }
const Table &getHiScoreTable() const { return hi_score_table_; }
void setGlowingEntry(const int &entry) { glowing_entry_ = entry; }
private: private:
// --- Constantes --- // --- Constantes ---
@@ -186,7 +207,7 @@ class Player {
std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre
// --- Variables de estado --- // --- Variables de estado ---
int id_; // Número de identificación para el jugador. Player1 = 1, Player2 = 2 Id id_; // Identificador para el jugador
SDL_FRect play_area_; // Rectángulo con la zona de juego SDL_FRect play_area_; // Rectángulo con la zona de juego
float pos_x_ = 0.0F; // Posición en el eje X float pos_x_ = 0.0F; // Posición en el eje X
int pos_y_ = 0; // Posición en el eje Y int pos_y_ = 0; // Posición en el eje Y
@@ -200,6 +221,9 @@ class Player {
int cooling_state_counter_ = 0; // Contador para la animación del estado cooling int cooling_state_counter_ = 0; // Contador para la animación del estado cooling
int score_ = 0; // Puntos del jugador int score_ = 0; // Puntos del jugador
float score_multiplier_ = 1.0F; // Multiplicador de puntos float score_multiplier_ = 1.0F; // Multiplicador de puntos
bool qualifies_for_high_score_ = false; // Indica si tiene una puntuación que le permite entrar en la tabla de records
Table &hi_score_table_; // Tabla de maximas puntuaciones
int &glowing_entry_; // Entrada de la tabla de puntuaciones para hacerla brillar
State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse State walking_state_ = State::WALKING_STOP; // Estado del jugador al moverse
State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar State firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar
State playing_state_ = State::WAITING; // Estado del jugador en el juego State playing_state_ = State::WAITING; // Estado del jugador en el juego

View File

@@ -353,11 +353,32 @@ void Credits::initPlayers() {
const int Y = play_area_.y + play_area_.h - PLAYER_WIDTH; const int Y = play_area_.y + play_area_.h - PLAYER_WIDTH;
constexpr bool DEMO = false; constexpr bool DEMO = false;
constexpr int AWAY_DISTANCE = 700; constexpr int AWAY_DISTANCE = 700;
players_.emplace_back(std::make_unique<Player>(1, play_area_.x - AWAY_DISTANCE - PLAYER_WIDTH, Y, DEMO, play_area_, player_textures.at(0), player_animations));
Player::Config config_player1;
config_player1.id = Player::Id::PLAYER1;
config_player1.x = play_area_.x - AWAY_DISTANCE - PLAYER_WIDTH;
config_player1.y = Y;
config_player1.demo = DEMO;
config_player1.play_area = &play_area_;
config_player1.texture = player_textures.at(0);
config_player1.animations = player_animations;
config_player1.hi_score_table = &Options::settings.hi_score_table;
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1);
players_.emplace_back(std::make_unique<Player>(config_player1));
players_.back()->setWalkingState(Player::State::WALKING_RIGHT); players_.back()->setWalkingState(Player::State::WALKING_RIGHT);
players_.back()->setPlayingState(Player::State::CREDITS); players_.back()->setPlayingState(Player::State::CREDITS);
players_.emplace_back(std::make_unique<Player>(2, play_area_.x + play_area_.w + AWAY_DISTANCE, Y, DEMO, play_area_, player_textures.at(1), player_animations)); Player::Config config_player2;
config_player2.id = Player::Id::PLAYER2;
config_player2.x = play_area_.x + play_area_.w + AWAY_DISTANCE;
config_player2.y = Y;
config_player2.demo = DEMO;
config_player2.play_area = &play_area_;
config_player2.texture = player_textures.at(1);
config_player2.animations = player_animations;
config_player2.hi_score_table = &Options::settings.hi_score_table;
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1);
players_.emplace_back(std::make_unique<Player>(config_player2));
players_.back()->setWalkingState(Player::State::WALKING_LEFT); players_.back()->setWalkingState(Player::State::WALKING_LEFT);
players_.back()->setPlayingState(Player::State::CREDITS); players_.back()->setPlayingState(Player::State::CREDITS);
} }

View File

@@ -42,7 +42,7 @@
#include "ui/service_menu.h" // Para ServiceMenu #include "ui/service_menu.h" // Para ServiceMenu
// Constructor // Constructor
Game::Game(int player_id, int current_stage, bool demo) Game::Game(Player::Id player_id, int current_stage, bool demo)
: renderer_(Screen::get()->getRenderer()), : renderer_(Screen::get()->getRenderer()),
screen_(Screen::get()), screen_(Screen::get()),
input_(Input::get()), input_(Input::get()),
@@ -365,7 +365,7 @@ void Game::updateGameStateCompleted() {
for (auto &player : players_) { for (auto &player : players_) {
if (player->isPlaying()) { if (player->isPlaying()) {
player->addScore(1000000); player->addScore(1000000, Options::settings.hi_score_table.back().score);
player->setPlayingState(Player::State::CELEBRATING); player->setPlayingState(Player::State::CELEBRATING);
} else { } else {
player->setPlayingState(Player::State::GAME_OVER); player->setPlayingState(Player::State::GAME_OVER);
@@ -379,7 +379,7 @@ void Game::updateGameStateCompleted() {
if (game_completed_counter_ == END_CELEBRATIONS) { if (game_completed_counter_ == END_CELEBRATIONS) {
for (auto &player : players_) { for (auto &player : players_) {
if (player->isCelebrating()) { if (player->isCelebrating()) {
player->setPlayingState(player->isEligibleForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN); player->setPlayingState(player->qualifiesForHighScore() ? Player::State::ENTERING_NAME_GAME_COMPLETED : Player::State::LEAVING_SCREEN);
} }
} }
} }
@@ -435,28 +435,28 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) {
if (checkCollision(player->getCollider(), item->getCollider())) { if (checkCollision(player->getCollider(), item->getCollider())) {
switch (item->getType()) { switch (item->getType()) {
case ItemType::DISK: { case ItemType::DISK: {
player->addScore(1000); player->addScore(1000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(0)->getWidth()) / 2; const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(0)->getWidth()) / 2;
createItemText(X, game_text_textures_.at(0)); createItemText(X, game_text_textures_.at(0));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
} }
case ItemType::GAVINA: { case ItemType::GAVINA: {
player->addScore(2500); player->addScore(2500, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(1)->getWidth()) / 2; const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(1)->getWidth()) / 2;
createItemText(X, game_text_textures_.at(1)); createItemText(X, game_text_textures_.at(1));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
} }
case ItemType::PACMAR: { case ItemType::PACMAR: {
player->addScore(5000); player->addScore(5000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2;
createItemText(X, game_text_textures_.at(2)); createItemText(X, game_text_textures_.at(2));
playSound("item_pickup.wav"); playSound("item_pickup.wav");
break; break;
} }
case ItemType::DEBIAN: { case ItemType::DEBIAN: {
player->addScore(100000); player->addScore(100000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(6)->getWidth()) / 2; const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(6)->getWidth()) / 2;
createItemText(X, game_text_textures_.at(6)); createItemText(X, game_text_textures_.at(6));
playSound("debian_pickup.wav"); playSound("debian_pickup.wav");
@@ -471,7 +471,7 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) {
} }
case ItemType::COFFEE: { case ItemType::COFFEE: {
if (player->getCoffees() == 2) { if (player->getCoffees() == 2) {
player->addScore(5000); player->addScore(5000, Options::settings.hi_score_table.back().score);
const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; const auto X = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2;
createItemText(X, game_text_textures_.at(2)); createItemText(X, game_text_textures_.at(2));
} else { } else {
@@ -594,11 +594,11 @@ void Game::handleItemDrop(std::shared_ptr<Balloon> balloon, std::shared_ptr<Play
// Maneja la destrucción del globo y puntuación // Maneja la destrucción del globo y puntuación
void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player) { void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player) {
const auto SCORE = balloon_manager_->popBalloon(balloon);
evaluateAndSetMenace(); evaluateAndSetMenace();
if (player->isPlaying()) { if (player->isPlaying()) {
player->addScore(SCORE * player->getScoreMultiplier() * difficulty_score_multiplier_); auto const SCORE = balloon_manager_->popBalloon(balloon) * player->getScoreMultiplier() * difficulty_score_multiplier_;
player->addScore(SCORE, Options::settings.hi_score_table.back().score);
player->incScoreMultiplier(); player->incScoreMultiplier();
} }
updateHiScore(); updateHiScore();
@@ -621,7 +621,7 @@ void Game::renderBullets() {
} }
// Crea un objeto bala // Crea un objeto bala
void Game::createBullet(int x, int y, BulletType kind, bool powered_up, int owner) { void Game::createBullet(int x, int y, BulletType kind, bool powered_up, Player::Id owner) {
bullets_.emplace_back(std::make_shared<Bullet>(x, y, kind, powered_up, owner)); bullets_.emplace_back(std::make_shared<Bullet>(x, y, kind, powered_up, owner));
} }
@@ -1174,7 +1174,7 @@ void Game::pause(bool value) {
void Game::addScoreToScoreBoard(const std::shared_ptr<Player> &player) { void Game::addScoreToScoreBoard(const std::shared_ptr<Player> &player) {
const auto ENTRY = HiScoreEntry(trim(player->getLastEnterName()), player->getScore(), player->get1CC()); const auto ENTRY = HiScoreEntry(trim(player->getLastEnterName()), player->getScore(), player->get1CC());
auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table);
Options::settings.last_hi_score_entry.at(player->getId() - 1) = manager->add(ENTRY); Options::settings.glowing_entries.at(static_cast<int>(player->getId()) - 1) = manager->add(ENTRY);
manager->saveToFile(Asset::get()->get("score.bin")); manager->saveToFile(Asset::get()->get("score.bin"));
hi_score_.name = Options::settings.hi_score_table.front().name; hi_score_.name = Options::settings.hi_score_table.front().name;
} }
@@ -1206,7 +1206,7 @@ void Game::checkPlayersStatusPlaying() {
} }
// Obtiene un jugador a partir de su "id" // Obtiene un jugador a partir de su "id"
auto Game::getPlayer(int id) -> std::shared_ptr<Player> { auto Game::getPlayer(Player::Id id) -> std::shared_ptr<Player> {
auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) { return player->getId() == id; }); auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) { return player->getId() == id; });
if (it != players_.end()) { if (it != players_.end()) {
@@ -1216,11 +1216,11 @@ auto Game::getPlayer(int id) -> std::shared_ptr<Player> {
} }
// Obtiene un controlador a partir del "id" del jugador // Obtiene un controlador a partir del "id" del jugador
auto Game::getController(int player_id) -> int { auto Game::getController(Player::Id player_id) -> int {
auto it = std::find_if(Options::controllers.begin(), Options::controllers.end(), [player_id](const auto &controller) { return controller.player_id == player_id; }); auto it = std::find_if(Options::gamepads.begin(), Options::gamepads.end(), [player_id](const auto &controller) { return controller->player_id == player_id; });
if (it != Options::controllers.end()) { if (it != Options::gamepads.end()) {
return std::distance(Options::controllers.begin(), it); return std::distance(Options::gamepads.begin(), it);
} }
return -1; return -1;
@@ -1286,7 +1286,7 @@ void Game::demoHandleInput() {
// Procesa las entradas para un jugador específico durante el modo demo. // Procesa las entradas para un jugador específico durante el modo demo.
void Game::demoHandlePlayerInput(const std::shared_ptr<Player> &player, int index) { void Game::demoHandlePlayerInput(const std::shared_ptr<Player> &player, int index) {
const auto &demo_data = demo_.data[index][demo_.counter]; const auto &demo_data = demo_.data.at(index).at(demo_.counter);
if (demo_data.left == 1) { if (demo_data.left == 1) {
player->setInput(Input::Action::LEFT); player->setInput(Input::Action::LEFT);
@@ -1469,7 +1469,7 @@ void Game::handleNameInput(const std::shared_ptr<Player> &player) {
} }
// Inicializa las variables para el modo DEMO // Inicializa las variables para el modo DEMO
void Game::initDemo(int player_id) { void Game::initDemo(Player::Id player_id) {
if (demo_.enabled) { if (demo_.enabled) {
// Cambia el estado del juego // Cambia el estado del juego
setState(State::PLAYING); setState(State::PLAYING);
@@ -1497,7 +1497,7 @@ void Game::initDemo(int player_id) {
// Activa o no al otro jugador // Activa o no al otro jugador
if (rand() % 3 != 0) { if (rand() % 3 != 0) {
const auto OTHER_PLAYER_ID = player_id == 1 ? 2 : 1; const auto OTHER_PLAYER_ID = player_id == Player::Id::PLAYER1 ? Player::Id::PLAYER2 : Player::Id::PLAYER1;
auto other_player = getPlayer(OTHER_PLAYER_ID); auto other_player = getPlayer(OTHER_PLAYER_ID);
other_player->setPlayingState(Player::State::PLAYING); other_player->setPlayingState(Player::State::PLAYING);
} }
@@ -1581,30 +1581,54 @@ void Game::initDifficultyVars() {
} }
// Inicializa los jugadores // Inicializa los jugadores
void Game::initPlayers(int player_id) { void Game::initPlayers(Player::Id player_id) {
const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies const int Y = param.game.play_area.rect.h - Player::HEIGHT + 1; // Se hunde un pixel para esconder el outline de los pies
// Crea al jugador uno y lo pone en modo espera // Crea al jugador uno y lo pone en modo espera
players_.emplace_back(std::make_unique<Player>(1, param.game.play_area.first_quarter_x - (Player::WIDTH / 2), Y, demo_.enabled, param.game.play_area.rect, player_textures_.at(0), player_animations_)); Player::Config config_player1;
players_.back()->setScoreBoardPanel(Scoreboard::Id::LEFT); config_player1.id = Player::Id::PLAYER1;
players_.back()->setName(Lang::getText("[SCOREBOARD] 1")); config_player1.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2);
//players_.back()->setController(getController(players_.back()->getId())); config_player1.y = Y;
players_.back()->setGamepad(Options::controllers.front().gamepad); config_player1.demo = demo_.enabled;
players_.back()->setUsesKeyboard(true); config_player1.play_area = &param.game.play_area.rect;
players_.back()->setPlayingState(Player::State::WAITING); config_player1.texture = player_textures_.at(0);
config_player1.animations = player_animations_;
config_player1.hi_score_table = &Options::settings.hi_score_table;
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1);
auto player1 = std::make_unique<Player>(config_player1);
player1->setScoreBoardPanel(Scoreboard::Id::LEFT);
player1->setName(Lang::getText("[SCOREBOARD] 1"));
player1->setGamepad(Options::gamepads.front()->instance);
player1->setUsesKeyboard(Player::Id::PLAYER1 == Options::keyboard.player_id);
player1->setPlayingState(Player::State::WAITING);
players_.push_back(std::move(player1));
// Crea al jugador dos y lo pone en modo espera // Crea al jugador dos y lo pone en modo espera
players_.emplace_back(std::make_unique<Player>(2, param.game.play_area.third_quarter_x - (Player::WIDTH / 2), Y, demo_.enabled, param.game.play_area.rect, player_textures_.at(1), player_animations_)); Player::Config config_player2;
players_.back()->setScoreBoardPanel(Scoreboard::Id::RIGHT); config_player2.id = Player::Id::PLAYER2;
players_.back()->setName(Lang::getText("[SCOREBOARD] 2")); config_player2.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2);
//players_.back()->setController(getController(players_.back()->getId())); config_player2.y = Y;
players_.back()->setPlayingState(Player::State::WAITING); config_player2.demo = demo_.enabled;
config_player2.play_area = &param.game.play_area.rect;
config_player2.texture = player_textures_.at(1);
config_player2.animations = player_animations_;
config_player2.hi_score_table = &Options::settings.hi_score_table;
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1);
auto player2 = std::make_unique<Player>(config_player2);
player2->setScoreBoardPanel(Scoreboard::Id::RIGHT);
player2->setName(Lang::getText("[SCOREBOARD] 2"));
player2->setGamepad(Options::gamepads.back()->instance);
player2->setUsesKeyboard(Player::Id::PLAYER2 == Options::keyboard.player_id);
player2->setPlayingState(Player::State::WAITING);
players_.push_back(std::move(player2));
// Activa el jugador que coincide con el "player_id" o ambos si es "0" // Activa el jugador que coincide con el "player_id" o ambos si es "0"
if (player_id == 0) { if (player_id == Player::Id::BOTH_PLAYERS) {
// Activa ambos jugadores // Activa ambos jugadores
getPlayer(1)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN); getPlayer(Player::Id::PLAYER1)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
getPlayer(2)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN); getPlayer(Player::Id::PLAYER2)->setPlayingState(demo_.enabled ? Player::State::PLAYING : Player::State::ENTERING_SCREEN);
} else { } else {
// Activa el jugador elegido // Activa el jugador elegido
auto player = getPlayer(player_id); auto player = getPlayer(player_id);

View File

@@ -30,18 +30,15 @@ namespace Difficulty {
enum class Code; enum class Code;
} // namespace Difficulty } // namespace Difficulty
// Modo demo
constexpr bool GAME_MODE_DEMO_OFF = false;
constexpr bool GAME_MODE_DEMO_ON = true;
// Cantidad de elementos a escribir en los ficheros de datos
constexpr int TOTAL_SCORE_DATA = 3;
// Clase Game // Clase Game
class Game { class Game {
public: public:
// --- Constantes ---
static constexpr bool DEMO_OFF = false;
static constexpr bool DEMO_ON = true;
// --- Constructor y destructor --- // --- Constructor y destructor ---
Game(int player_id, int current_stage, bool demo); Game(Player::Id player_id, int current_stage, bool demo);
~Game(); ~Game();
// --- Bucle principal --- // --- Bucle principal ---
@@ -174,12 +171,12 @@ class Game {
void updateGameStateGameOver(); // Gestiona el estado de fin de partida void updateGameStateGameOver(); // Gestiona el estado de fin de partida
// --- Gestión de jugadores --- // --- Gestión de jugadores ---
void initPlayers(int player_id); // Inicializa los datos de los jugadores void initPlayers(Player::Id player_id); // Inicializa los datos de los jugadores
void updatePlayers(); // Actualiza las variables y estados de los jugadores void updatePlayers(); // Actualiza las variables y estados de los jugadores
void renderPlayers(); // Renderiza todos los jugadores en pantalla void renderPlayers(); // Renderiza todos los jugadores en pantalla
void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores
auto getPlayer(int id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador
static auto getController(int player_id) -> int; // Obtiene el controlador asignado a un jugador static auto getController(Player::Id player_id) -> int; // Obtiene el controlador asignado a un jugador
// --- Estado de jugadores --- // --- Estado de jugadores ---
void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index); // Actualiza estado entre jugadores void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index); // Actualiza estado entre jugadores
@@ -214,7 +211,7 @@ class Game {
// --- Sistema de balas y proyectiles --- // --- Sistema de balas y proyectiles ---
void updateBullets(); // Actualiza posición y estado de todas las balas void updateBullets(); // Actualiza posición y estado de todas las balas
void renderBullets(); // Renderiza todas las balas activas void renderBullets(); // Renderiza todas las balas activas
void createBullet(int x, int y, BulletType kind, bool powered_up, int owner); // Crea una nueva bala void createBullet(int x, int y, BulletType kind, bool powered_up, Player::Id owner); // Crea una nueva bala
void checkBulletCollision(); // Verifica colisiones de todas las balas void checkBulletCollision(); // Verifica colisiones de todas las balas
void freeBullets(); // Libera memoria del vector de balas void freeBullets(); // Libera memoria del vector de balas
@@ -276,7 +273,7 @@ class Game {
void initScoreboard(); // Inicializa el sistema de puntuación void initScoreboard(); // Inicializa el sistema de puntuación
// --- Modo demostración --- // --- Modo demostración ---
void initDemo(int player_id); // Inicializa variables para el modo demostración void initDemo(Player::Id player_id); // Inicializa variables para el modo demostración
void updateDemo(); // Actualiza lógica específica del modo demo void updateDemo(); // Actualiza lógica específica del modo demo
// --- Recursos y renderizado --- // --- Recursos y renderizado ---

View File

@@ -348,7 +348,7 @@ void HiScoreTable::iniEntryColors() {
// Hace brillar los nombres de la tabla de records // Hace brillar los nombres de la tabla de records
void HiScoreTable::glowEntryNames() { void HiScoreTable::glowEntryNames() {
const Color ENTRY_COLOR = getEntryColor(counter_ / 5); const Color ENTRY_COLOR = getEntryColor(counter_ / 5);
for (const auto &entry_index : Options::settings.last_hi_score_entry) { for (const auto &entry_index : Options::settings.glowing_entries) {
if (entry_index != -1) { if (entry_index != -1) {
entry_names_.at(entry_index)->getTexture()->setColor(ENTRY_COLOR); entry_names_.at(entry_index)->getTexture()->setColor(ENTRY_COLOR);
} }

View File

@@ -44,7 +44,7 @@ Title::Title()
game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)), game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)),
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))), mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
define_buttons_(std::make_unique<DefineButtons>()), define_buttons_(std::make_unique<DefineButtons>()),
num_controllers_(Input::get()->getNumControllers()), num_controllers_(Input::get()->getNumGamepads()),
state_(TitleState::LOGO_ANIMATING) { state_(TitleState::LOGO_ANIMATING) {
// Configura objetos // Configura objetos
tiled_bg_->setColor(param.title.bg_color); tiled_bg_->setColor(param.title.bg_color);
@@ -213,12 +213,12 @@ void Title::printColorValue(const Color& color) {
void Title::handleControlKeys(SDL_Keycode key) { void Title::handleControlKeys(SDL_Keycode key) {
switch (key) { switch (key) {
case SDLK_1: case SDLK_1:
define_buttons_->enable(0); define_buttons_->enable(Options::gamepads.at(0));
resetCounter(); resetCounter();
break; break;
case SDLK_2: case SDLK_2:
define_buttons_->enable(1); define_buttons_->enable(Options::gamepads.at(1));
resetCounter(); resetCounter();
break; break;
@@ -262,29 +262,29 @@ auto Title::shouldSkipInputCheck() const -> bool {
} }
void Title::processControllerInputs() { void Title::processControllerInputs() {
for (const auto& controller : Options::controllers) { for (const auto& controller : Options::gamepads) {
if (isStartButtonPressed(controller)) { if (isStartButtonPressed(controller)) {
handleStartButtonPress(controller); handleStartButtonPress(controller);
} }
} }
} }
auto Title::isStartButtonPressed(const Options::GamepadOptions& controller) -> bool { auto Title::isStartButtonPressed(std::shared_ptr<Options::Gamepad> controller) -> bool {
return Input::get()->checkAction( return Input::get()->checkAction(
Input::Action::START, Input::Action::START,
Input::DO_NOT_ALLOW_REPEAT, Input::DO_NOT_ALLOW_REPEAT,
Input::CHECK_KEYBOARD, Input::CHECK_KEYBOARD,
controller.gamepad); controller->instance);
} }
void Title::handleStartButtonPress(const Options::GamepadOptions& controller) { void Title::handleStartButtonPress(std::shared_ptr<Options::Gamepad> controller) {
if (!canProcessStartButton()) { if (!canProcessStartButton()) {
return; return;
} }
if (controller.player_id == 1) { if (controller->player_id == Player::Id::PLAYER1) {
processPlayer1Start(); processPlayer1Start();
} else if (controller.player_id == 2) { } else if (controller->player_id == Player::Id::PLAYER2) {
processPlayer2Start(); processPlayer2Start();
} }
} }
@@ -296,18 +296,18 @@ auto Title::canProcessStartButton() const -> bool {
void Title::processPlayer1Start() { void Title::processPlayer1Start() {
if (!player1_start_pressed_) { if (!player1_start_pressed_) {
player1_start_pressed_ = true; player1_start_pressed_ = true;
activatePlayerAndSetState(1); activatePlayerAndSetState(Player::Id::PLAYER1);
} }
} }
void Title::processPlayer2Start() { void Title::processPlayer2Start() {
if (!player2_start_pressed_) { if (!player2_start_pressed_) {
player2_start_pressed_ = true; player2_start_pressed_ = true;
activatePlayerAndSetState(2); activatePlayerAndSetState(Player::Id::PLAYER2);
} }
} }
void Title::activatePlayerAndSetState(int player_id) { void Title::activatePlayerAndSetState(Player::Id player_id) {
getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION); getPlayer(player_id)->setPlayingState(Player::State::TITLE_ANIMATION);
setState(TitleState::START_HAS_BEEN_PRESSED); setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0; counter_ = 0;
@@ -328,7 +328,7 @@ void Title::resetCounter() { counter_ = 0; }
// Intercambia la asignación de mandos a los jugadores // Intercambia la asignación de mandos a los jugadores
void Title::swapControllers() { void Title::swapControllers() {
if (Input::get()->getNumControllers() == 0) { if (Input::get()->getNumGamepads() == 0) {
return; return;
} }
@@ -339,33 +339,34 @@ void Title::swapControllers() {
// Intercambia el teclado de jugador // Intercambia el teclado de jugador
void Title::swapKeyboard() { void Title::swapKeyboard() {
Options::swapKeyboard(); Options::swapKeyboard();
std::string text = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::getPlayerWhoUsesKeyboard()) + ": " + Lang::getText("[DEFINE_BUTTONS] KEYBOARD"); std::string text = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(static_cast<int>(Options::getPlayerWhoUsesKeyboard())) + ": " + Lang::getText("[DEFINE_BUTTONS] KEYBOARD");
Notifier::get()->show({text}); Notifier::get()->show({text});
} }
// Muestra información sobre los controles y los jugadores // Muestra información sobre los controles y los jugadores
void Title::showControllers() { void Title::showControllers() {
// Crea vectores de texto vacíos para un número máximo de mandos /* // Crea vectores de texto vacíos para un número máximo de mandos
constexpr size_t NUM_CONTROLLERS = 2; constexpr size_t NUM_CONTROLLERS = 2;
std::vector<std::string> text(NUM_CONTROLLERS); std::vector<std::string> text(NUM_CONTROLLERS);
std::vector<int> player_controller_index(NUM_CONTROLLERS, -1); std::vector<int> player_controller_index(NUM_CONTROLLERS, -1);
// Obtiene de cada jugador el índice del mando que tiene asignado // Obtiene de cada jugador el índice del mando que tiene asignado
for (size_t i = 0; i < NUM_CONTROLLERS; ++i) { for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
// Ejemplo: el jugador 1 tiene el mando 2 // Ejemplo: el jugador 1 tiene el mando 2
player_controller_index.at(Options::controllers.at(i).player_id - 1) = i; player_controller_index.at(Options::controllers.at(i)->player_id - 1) = i;
} }
// Genera el texto correspondiente // Genera el texto correspondiente
//for (size_t i = 0; i < NUM_CONTROLLERS; ++i) { for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
// const size_t INDEX = player_controller_index.at(i); const size_t INDEX = player_controller_index.at(i);
// if (Options::controllers.at(INDEX).plugged) { if (Options::controllers.at(INDEX).plugged) {
// text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(INDEX).name; text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(INDEX).name;
// } }
//} }
// Muestra la notificación // Muestra la notificación
Notifier::get()->show({text.at(0), text.at(1)}); Notifier::get()->show({text.at(0), text.at(1)});
*/
} }
// Actualiza el fade // Actualiza el fade
@@ -558,10 +559,31 @@ void Title::initPlayers() {
constexpr int PLAYER_HEIGHT = 32; constexpr int PLAYER_HEIGHT = 32;
const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2); const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2);
constexpr bool DEMO = false; constexpr bool DEMO = false;
players_.emplace_back(std::make_unique<Player>(1, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(0), player_animations));
Player::Config config_player1;
config_player1.id = Player::Id::PLAYER1;
config_player1.x = param.game.game_area.center_x - (PLAYER_WIDTH / 2);
config_player1.y = Y;
config_player1.demo = DEMO;
config_player1.play_area = &param.game.play_area.rect;
config_player1.texture = player_textures.at(0);
config_player1.animations = player_animations;
config_player1.hi_score_table = &Options::settings.hi_score_table;
config_player1.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER1) - 1);
players_.emplace_back(std::make_unique<Player>(config_player1));
players_.back()->setPlayingState(Player::State::TITLE_HIDDEN); players_.back()->setPlayingState(Player::State::TITLE_HIDDEN);
players_.emplace_back(std::make_unique<Player>(2, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(1), player_animations)); Player::Config config_player2;
config_player2.id = Player::Id::PLAYER2;
config_player2.x = param.game.game_area.center_x - (PLAYER_WIDTH / 2);
config_player2.y = Y;
config_player2.demo = DEMO;
config_player2.play_area = &param.game.play_area.rect;
config_player2.texture = player_textures.at(1);
config_player2.animations = player_animations;
config_player2.hi_score_table = &Options::settings.hi_score_table;
config_player2.glowing_entry = &Options::settings.glowing_entries.at(static_cast<int>(Player::Id::PLAYER2) - 1);
players_.emplace_back(std::make_unique<Player>(config_player2));
players_.back()->setPlayingState(Player::State::TITLE_HIDDEN); players_.back()->setPlayingState(Player::State::TITLE_HIDDEN);
} }
@@ -580,7 +602,7 @@ void Title::renderPlayers() {
} }
// Obtiene un jugador a partir de su "id" // Obtiene un jugador a partir de su "id"
auto Title::getPlayer(int id) -> std::shared_ptr<Player> { auto Title::getPlayer(Player::Id id) -> std::shared_ptr<Player> {
auto it = std::find_if(players_.begin(), players_.end(), [id](const auto& player) { return player->getId() == id; }); auto it = std::find_if(players_.begin(), players_.end(), [id](const auto& player) { return player->getId() == id; });
if (it != players_.end()) { if (it != players_.end()) {

View File

@@ -7,17 +7,17 @@
#include <string_view> // Para string_view #include <string_view> // Para string_view
#include <vector> // Para vector #include <vector> // Para vector
#include "player.h" // Para Player
#include "section.hpp" // Para Options, Name (ptr only) #include "section.hpp" // Para Options, Name (ptr only)
class DefineButtons; class DefineButtons;
class Fade; class Fade;
class GameLogo; class GameLogo;
class Player;
class Sprite; class Sprite;
class Text; class Text;
class TiledBG; class TiledBG;
namespace Options { namespace Options {
struct GamepadOptions; struct Gamepad;
} // namespace Options } // namespace Options
struct Color; struct Color;
@@ -87,24 +87,24 @@ class Title {
void resetCounter(); // Reinicia el contador interno void resetCounter(); // Reinicia el contador interno
// --- Entrada de usuario --- // --- Entrada de usuario ---
void checkEvents(); // Comprueba los eventos void checkEvents(); // Comprueba los eventos
void checkInput(); // Comprueba las entradas void checkInput(); // Comprueba las entradas
void handleKeyDownEvent(const SDL_Event& event); // Maneja el evento de tecla presionada void handleKeyDownEvent(const SDL_Event& event); // Maneja el evento de tecla presionada
void handleControlKeys(SDL_Keycode key); // Maneja las teclas de control específicas void handleControlKeys(SDL_Keycode key); // Maneja las teclas de control específicas
[[nodiscard]] auto shouldSkipInputCheck() const -> bool; // Determina si se debe omitir la comprobación de entrada [[nodiscard]] auto shouldSkipInputCheck() const -> bool; // Determina si se debe omitir la comprobación de entrada
void processControllerInputs(); // Procesa las entradas de los mandos void processControllerInputs(); // Procesa las entradas de los mandos
[[nodiscard]] static auto isStartButtonPressed(const Options::GamepadOptions& controller) -> bool; // Comprueba si se ha pulsado el botón Start [[nodiscard]] static auto isStartButtonPressed(std::shared_ptr<Options::Gamepad> controller) -> bool; // Comprueba si se ha pulsado el botón Start
void handleStartButtonPress(const Options::GamepadOptions& controller); // Maneja la pulsación del botón Start void handleStartButtonPress(std::shared_ptr<Options::Gamepad> controller); // Maneja la pulsación del botón Start
[[nodiscard]] auto canProcessStartButton() const -> bool; // Verifica si se puede procesar la pulsación del botón Start [[nodiscard]] auto canProcessStartButton() const -> bool; // Verifica si se puede procesar la pulsación del botón Start
void processPlayer1Start(); // Procesa el inicio del jugador 1 void processPlayer1Start(); // Procesa el inicio del jugador 1
void processPlayer2Start(); // Procesa el inicio del jugador 2 void processPlayer2Start(); // Procesa el inicio del jugador 2
void activatePlayerAndSetState(int player_id); // Activa al jugador y cambia el estado del título void activatePlayerAndSetState(Player::Id player_id); // Activa al jugador y cambia el estado del título
// --- Gestión de jugadores --- // --- Gestión de jugadores ---
void initPlayers(); // Inicializa los jugadores void initPlayers(); // Inicializa los jugadores
void updatePlayers(); // Actualiza los jugadores void updatePlayers(); // Actualiza los jugadores
void renderPlayers(); // Renderiza los jugadores void renderPlayers(); // Renderiza los jugadores
auto getPlayer(int id) -> std::shared_ptr<Player>; // Obtiene un jugador a partir de su "id" auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador a partir de su "id"
// --- Visualización / Renderizado --- // --- Visualización / Renderizado ---
void render(); // Dibuja el objeto en pantalla void render(); // Dibuja el objeto en pantalla