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
// 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"))),
pos_x_(x),
pos_y_(y),
@@ -94,7 +94,7 @@ void Bullet::disable() {
bullet_type_ = BulletType::NONE;
}
auto Bullet::getOwner() const -> int {
auto Bullet::getOwner() const -> Player::Id {
return owner_;
}

View File

@@ -6,6 +6,7 @@
#include <string> // Para string
#include "animated_sprite.h" // Para AnimatedSprite
#include "player.h" // Para Player
#include "utils.h" // Para Circle
// Tipos de balas
@@ -30,7 +31,7 @@ class Bullet {
static constexpr float HEIGHT = 12.0F;
// 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;
// Métodos principales
@@ -42,7 +43,7 @@ class Bullet {
void disable(); // Desactiva la bala
// 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
private:
@@ -60,7 +61,7 @@ class Bullet {
float vel_x_; // Velocidad en el eje X
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
// Métodos internos

View File

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

View File

@@ -9,6 +9,7 @@
#include <vector> // Para vector
#include "input.h"
#include "options.h"
// Clase DefineButtons
class DefineButtons {
@@ -28,7 +29,7 @@ class DefineButtons {
void render(); // Dibuja el objeto en pantalla
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
private:
@@ -42,6 +43,7 @@ class DefineButtons {
size_t index_button_ = 0; // Índice del botón en proceso
std::vector<std::string> controller_names_; // Nombres de los mandos
bool finished_ = false;
std::shared_ptr<Options::Gamepad> gamepad_options_;
// Métodos internos
void incIndexButton(); // Incrementa el índice de botones

View File

@@ -102,7 +102,7 @@ void Director::init() {
auto gamepads = Input::get()->getGamepads();
if (!gamepads.empty())
Options::controllers.front().gamepad = gamepads.front();
Options::gamepads.front()->instance = gamepads.front();
ServiceMenu::init(); // Inicializa el menú de servicio
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
void Director::runGame() {
int player_id = 1;
Player::Id player_id = Player::Id::PLAYER1;
switch (Section::options) {
case Section::Options::GAME_PLAY_1P:
player_id = 1;
player_id = Player::Id::PLAYER1;
break;
case Section::Options::GAME_PLAY_2P:
player_id = 2;
player_id = Player::Id::PLAYER2;
break;
case Section::Options::GAME_PLAY_BOTH:
player_id = 0;
player_id = Player::Id::BOTH_PLAYERS;
break;
default:
player_id = 1;
break;
}
@@ -568,7 +564,7 @@ void Director::runGame() {
#else
constexpr int CURRENT_STAGE = 0;
#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();
}
@@ -592,9 +588,9 @@ void Director::runHiScoreTable() {
// Ejecuta el juego en modo demo
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;
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();
}

View File

@@ -25,7 +25,7 @@ auto Input::get() -> Input * { return Input::instance; }
// Constructor
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)) {
// Inicializa el subsistema SDL_INIT_GAMEPAD
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; }
// 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
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() {
if (SDL_AddGamepadMappingsFromFile(game_controller_db_path_.c_str()) < 0) {
std::cout << "Error, could not load " << game_controller_db_path_.c_str() << " file: " << SDL_GetError() << std::endl;
if (SDL_AddGamepadMappingsFromFile(gamepad_mappings_file_.c_str()) < 0) {
std::cout << "Error, could not load " << gamepad_mappings_file_.c_str() << " file: " << SDL_GetError() << std::endl;
}
}
@@ -362,8 +362,7 @@ void Input::applyGamepadConfig(std::shared_ptr<Gamepad> gamepad) {
}
// Buscar configuración por nombre del gamepad
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(),
[&gamepad](const GamepadConfig& config) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad](const GamepadConfig &config) {
return config.name == gamepad->name;
});
@@ -383,8 +382,7 @@ void Input::saveGamepadConfigFromGamepad(std::shared_ptr<Gamepad> gamepad) {
}
// Buscar si ya existe una configuración con este nombre
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(),
[&gamepad](const GamepadConfig& config) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepad](const GamepadConfig &config) {
return config.name == gamepad->name;
});
@@ -417,8 +415,7 @@ void Input::setGamepadConfigsFile(const std::string& filename) {
// Método para obtener configuración de un gamepad específico (opcional)
GamepadConfig *Input::getGamepadConfig(const std::string &gamepadName) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(),
[&gamepadName](const GamepadConfig& config) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepadName](const GamepadConfig &config) {
return config.name == gamepadName;
});
@@ -427,8 +424,7 @@ GamepadConfig* Input::getGamepadConfig(const std::string& gamepadName) {
// Método para eliminar configuración de gamepad (opcional)
bool Input::removeGamepadConfig(const std::string &gamepadName) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(),
[&gamepadName](const GamepadConfig& config) {
auto configIt = std::find_if(gamepad_configs_.begin(), gamepad_configs_.end(), [&gamepadName](const GamepadConfig &config) {
return config.name == gamepadName;
});

View File

@@ -49,54 +49,54 @@ class Input {
};
struct Keyboard {
std::unordered_map<InputAction, KeyState> bindings;
std::unordered_map<Action, KeyState> bindings;
Keyboard()
: bindings{
// Teclado - Movimiento del jugador
{InputAction::UP, KeyState(SDL_SCANCODE_UP)},
{InputAction::DOWN, KeyState(SDL_SCANCODE_DOWN)},
{InputAction::LEFT, KeyState(SDL_SCANCODE_LEFT)},
{InputAction::RIGHT, KeyState(SDL_SCANCODE_RIGHT)},
{Action::UP, KeyState(SDL_SCANCODE_UP)},
{Action::DOWN, KeyState(SDL_SCANCODE_DOWN)},
{Action::LEFT, KeyState(SDL_SCANCODE_LEFT)},
{Action::RIGHT, KeyState(SDL_SCANCODE_RIGHT)},
// Teclado - Disparo del jugador
{InputAction::FIRE_LEFT, KeyState(SDL_SCANCODE_Q)},
{InputAction::FIRE_CENTER, KeyState(SDL_SCANCODE_W)},
{InputAction::FIRE_RIGHT, KeyState(SDL_SCANCODE_E)},
{Action::FIRE_LEFT, KeyState(SDL_SCANCODE_Q)},
{Action::FIRE_CENTER, KeyState(SDL_SCANCODE_W)},
{Action::FIRE_RIGHT, KeyState(SDL_SCANCODE_E)},
// Teclado - Interfaz
{InputAction::START, KeyState(SDL_SCANCODE_RETURN)},
{Action::START, KeyState(SDL_SCANCODE_RETURN)},
// Teclado - Menu de servicio
{InputAction::SERVICE, KeyState(SDL_SCANCODE_0)},
{InputAction::SM_SELECT, KeyState(SDL_SCANCODE_RETURN)},
{InputAction::SM_BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
{Action::SERVICE, KeyState(SDL_SCANCODE_0)},
{Action::SM_SELECT, KeyState(SDL_SCANCODE_RETURN)},
{Action::SM_BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
// Teclado - Control del programa
{InputAction::EXIT, KeyState(SDL_SCANCODE_ESCAPE)},
{InputAction::PAUSE, KeyState(SDL_SCANCODE_P)},
{InputAction::BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
{Action::EXIT, KeyState(SDL_SCANCODE_ESCAPE)},
{Action::PAUSE, KeyState(SDL_SCANCODE_P)},
{Action::BACK, KeyState(SDL_SCANCODE_BACKSPACE)},
{InputAction::WINDOW_DEC_SIZE, KeyState(SDL_SCANCODE_F1)},
{InputAction::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
{InputAction::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
{InputAction::TOGGLE_VIDEO_SHADERS, KeyState(SDL_SCANCODE_F4)},
{InputAction::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
{InputAction::TOGGLE_VIDEO_VSYNC, KeyState(SDL_SCANCODE_F6)},
{Action::WINDOW_DEC_SIZE, KeyState(SDL_SCANCODE_F1)},
{Action::WINDOW_INC_SIZE, KeyState(SDL_SCANCODE_F2)},
{Action::WINDOW_FULLSCREEN, KeyState(SDL_SCANCODE_F3)},
{Action::TOGGLE_VIDEO_SHADERS, KeyState(SDL_SCANCODE_F4)},
{Action::TOGGLE_VIDEO_INTEGER_SCALE, KeyState(SDL_SCANCODE_F5)},
{Action::TOGGLE_VIDEO_VSYNC, KeyState(SDL_SCANCODE_F6)},
{InputAction::TOGGLE_AUDIO, KeyState(SDL_SCANCODE_F7)},
{InputAction::TOGGLE_AUTO_FIRE, KeyState(SDL_SCANCODE_F8)},
{InputAction::CHANGE_LANG, KeyState(SDL_SCANCODE_F9)},
{Action::TOGGLE_AUDIO, KeyState(SDL_SCANCODE_F7)},
{Action::TOGGLE_AUTO_FIRE, KeyState(SDL_SCANCODE_F8)},
{Action::CHANGE_LANG, KeyState(SDL_SCANCODE_F9)},
{InputAction::RESET, KeyState(SDL_SCANCODE_F10)},
{InputAction::SHOW_INFO, KeyState(SDL_SCANCODE_F12)}} {}
{Action::RESET, KeyState(SDL_SCANCODE_F10)},
{Action::SHOW_INFO, KeyState(SDL_SCANCODE_F12)}} {}
};
struct Gamepad {
SDL_Gamepad *pad;
SDL_JoystickID instance_id;
std::string name;
std::unordered_map<InputAction, ButtonState> bindings;
std::unordered_map<Action, ButtonState> bindings;
Gamepad(SDL_Gamepad *gamepad)
: pad(gamepad),
@@ -104,19 +104,19 @@ class Input {
name(std::string(SDL_GetGamepadName(gamepad))),
bindings{
// Mando - Movimiento del jugador
{InputAction::UP, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_UP)},
{InputAction::DOWN, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_DOWN)},
{InputAction::LEFT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_LEFT)},
{InputAction::RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_RIGHT)},
{Action::UP, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_UP)},
{Action::DOWN, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_DOWN)},
{Action::LEFT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_LEFT)},
{Action::RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_DPAD_RIGHT)},
// Mando - Disparo del jugador
{InputAction::FIRE_LEFT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)},
{InputAction::FIRE_CENTER, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)},
{InputAction::FIRE_RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_EAST)},
{Action::FIRE_LEFT, ButtonState(SDL_GAMEPAD_BUTTON_WEST)},
{Action::FIRE_CENTER, ButtonState(SDL_GAMEPAD_BUTTON_NORTH)},
{Action::FIRE_RIGHT, ButtonState(SDL_GAMEPAD_BUTTON_EAST)},
// Mando - Interfaz
{InputAction::START, ButtonState(SDL_GAMEPAD_BUTTON_START)},
{InputAction::SERVICE, ButtonState(SDL_GAMEPAD_BUTTON_BACK)}} {}
{Action::START, ButtonState(SDL_GAMEPAD_BUTTON_START)},
{Action::SERVICE, ButtonState(SDL_GAMEPAD_BUTTON_BACK)}} {}
~Gamepad() {
if (pad) {
@@ -125,7 +125,7 @@ class Input {
}
// 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;
}
};
@@ -149,7 +149,7 @@ class Input {
// --- Métodos de gestión de mandos ---
[[nodiscard]] auto gameControllerFound() const -> bool;
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;
// --- Métodos de consulta y utilidades ---
@@ -175,7 +175,7 @@ class Input {
std::vector<std::shared_ptr<Gamepad>> gamepads_;
Keyboard keyboard_;
std::vector<Action> button_inputs_;
std::string game_controller_db_path_;
std::string gamepad_mappings_file_;
std::string gamepad_configs_file_;
GamepadConfigs gamepad_configs_;

View File

@@ -51,7 +51,7 @@ auto ManageHiScoreTable::add(const HiScoreEntry &entry) -> int {
// Si el nuevo elemento quedó fuera del top 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) {}
};
using Table = std::vector<HiScoreEntry>;
// --- Clase ManageHiScoreTable ---
class ManageHiScoreTable {
public:
// Constructor
explicit ManageHiScoreTable(std::vector<HiScoreEntry> &table)
: table_(table) {}
// --- Constantes ---
static constexpr int NO_ENTRY = -1;
// Destructor
// Constructor y destructor
explicit ManageHiScoreTable(Table &table)
: table_(table) {}
~ManageHiScoreTable() = default;
// Resetea la tabla a los valores por defecto
void clear();
// Añade un elemento a la tabla (devuelve la posición en la que se inserta)
auto add(const HiScoreEntry &entry) -> int;
// 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;
// --- Métodos públicos ---
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)
auto loadFromFile(const std::string &file_path) -> bool; // Carga la tabla con los datos de un fichero
auto saveToFile(const std::string &file_path) -> bool; // Guarda la tabla en un fichero
private:
// Referencia a la tabla con los records
std::vector<HiScoreEntry> &table_;
// --- Variables privadas ---
Table &table_; // Referencia a la tabla con los records
// Ordena la tabla internamente
void sort();
// --- Métodos privados ---
void sort(); // Ordena la tabla
};

View File

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

View File

@@ -9,27 +9,25 @@
#include <vector> // Para vector
#include "difficulty.h" // Para Code
// #include "gamepad_config_manager.h"
#include "input.h" // Para InputAction, InputDevice
#include "lang.h" // Para Code
#include "manage_hiscore_table.h" // Para HiScoreEntry
static constexpr int INVALID_INDEX = -1;
#include "player.h" // Para Player
namespace Options {
// --- Opciones de ventana ---
struct WindowOptions {
struct Window {
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 max_zoom{2}; // Tamaño máximo para que la ventana no sea mayor que la pantalla
// Constructor por defecto con valores iniciales
WindowOptions()
Window()
: caption("Coffee Crisis Arcade Edition") {}
};
// --- 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
bool fullscreen{false}; // Indica si se usa pantalla completa
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
// Constructor por defecto con valores iniciales
VideoOptions() = default;
Video() = default;
};
// --- Opciones de música ---
struct MusicOptions {
struct Music {
bool enabled{true}; // Indica si la música suena o no
int volume{100}; // Volumen de la música
// Constructor por defecto
MusicOptions() = default;
Music() = default;
};
// --- Opciones de sonido ---
struct SoundOptions {
struct Sound {
bool enabled{true}; // Indica si los sonidos suenan o no
int volume{100}; // Volumen de los sonidos
// Constructor por defecto
SoundOptions() = default;
Sound() = default;
};
// --- Opciones de audio ---
struct AudioOptions {
MusicOptions music; // Opciones para la música
SoundOptions sound; // Opciones para los efectos de sonido
struct Audio {
Music music; // Opciones para la música
Sound sound; // Opciones para los efectos de sonido
bool enabled{true}; // Indica si el audio está activo o no
int volume{100}; // Volumen general del audio
// Constructor por defecto
AudioOptions() = default;
Audio() = default;
};
// --- Opciones de configuración ---
struct SettingsOptions {
struct Settings {
Difficulty::Code difficulty{Difficulty::Code::NORMAL}; // Dificultad del juego
Lang::Code language{Lang::Code::VALENCIAN}; // Idioma usado en el juego
bool autofire{true}; // Indicador de autofire
bool shutdown_enabled{false}; // Especifica si se puede apagar el sistema
std::vector<HiScoreEntry> hi_score_table; // Tabla de mejores puntuaciones
std::vector<int> last_hi_score_entry; // Últimas posiciones de entrada en la tabla
Table hi_score_table; // Tabla de mejores puntuaciones
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 controllers_file; // Ruta al fichero con las configuraciones de los mandos
// Constructor por defecto con valores iniciales
SettingsOptions()
: last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}) {}
Settings()
: glowing_entries({ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}) {}
// Reinicia las últimas entradas de puntuación
void clearLastHiScoreEntries() {
last_hi_score_entry.at(0) = INVALID_INDEX;
last_hi_score_entry.at(1) = INVALID_INDEX;
glowing_entries.at(0) = ManageHiScoreTable::NO_ENTRY;
glowing_entries.at(1) = ManageHiScoreTable::NO_ENTRY;
}
};
struct GamepadOptions {
std::shared_ptr<Input::Gamepad> gamepad = nullptr; // Referencia al mando
struct Gamepad {
std::shared_ptr<Input::Gamepad> instance = nullptr; // Referencia al 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) {}
};
struct Keyboard {
Player::Id player_id = Player::Id::PLAYER1; // Jugador asociado al teclado
};
// --- Opciones pendientes de aplicar ---
struct PendingChanges {
Lang::Code new_language{Lang::Code::VALENCIAN}; // Idioma en espera de aplicar
@@ -112,11 +114,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 Window window; // Opciones de la ventana
extern Settings settings; // Opciones del juego
extern Video video; // Opciones de vídeo
extern Audio audio; // Opciones de audio
extern std::vector<std::shared_ptr<Gamepad>> gamepads; // Opciones de mando para cada jugador
extern Keyboard keyboard; // Opciones para el teclado
extern PendingChanges pending_changes; // Opciones que se aplican al cerrar
// --- Funciones de configuración ---
@@ -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
auto loadFromFile() -> bool; // Carga 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 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 checkPendingChanges(); // Verifica si hay cambios pendientes

View File

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

View File

@@ -10,7 +10,6 @@
#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
#include "utils.h" // Para Circle
@@ -25,6 +24,14 @@ class Player {
static constexpr int WIDTH = 32; // Anchura
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 ---
enum class State {
// Estados de movimiento
@@ -68,8 +75,20 @@ class Player {
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 ---
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;
// --- Inicialización y ciclo de vida ---
@@ -93,7 +112,7 @@ class Player {
void updateCooldown(); // Actualiza el cooldown de disparo
// --- 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 decScoreMultiplier(); // Decrementa el multiplicador
@@ -128,7 +147,7 @@ class Player {
[[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 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 isPowerUp() const -> bool { return power_up_; }
auto getCollider() -> Circle & { return collider_; }
@@ -137,7 +156,7 @@ class Player {
[[nodiscard]] auto getContinueCounter() const -> int { return continue_counter_; }
[[nodiscard]] auto getController() const -> int { return controller_index_; }
[[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 getPosX() const -> int { return static_cast<int>(pos_x_); }
[[nodiscard]] auto getPosY() const -> int { return pos_y_; }
@@ -165,11 +184,13 @@ class Player {
void setWalkingState(State state) { walking_state_ = state; }
void addCredit() { ++credits_used_; }
// 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_; }
void setUsesKeyboard(bool value) { uses_keyboard_ = value; }
[[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:
// --- Constantes ---
@@ -186,7 +207,7 @@ class Player {
std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre
// --- 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
float pos_x_ = 0.0F; // Posición en el eje X
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 score_ = 0; // Puntos del jugador
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 firing_state_ = State::FIRING_NONE; // Estado del jugador al disparar
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;
constexpr bool DEMO = false;
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()->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()->setPlayingState(Player::State::CREDITS);
}

View File

@@ -42,7 +42,7 @@
#include "ui/service_menu.h" // Para ServiceMenu
// 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()),
screen_(Screen::get()),
input_(Input::get()),
@@ -365,7 +365,7 @@ void Game::updateGameStateCompleted() {
for (auto &player : players_) {
if (player->isPlaying()) {
player->addScore(1000000);
player->addScore(1000000, Options::settings.hi_score_table.back().score);
player->setPlayingState(Player::State::CELEBRATING);
} else {
player->setPlayingState(Player::State::GAME_OVER);
@@ -379,7 +379,7 @@ void Game::updateGameStateCompleted() {
if (game_completed_counter_ == END_CELEBRATIONS) {
for (auto &player : players_) {
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())) {
switch (item->getType()) {
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;
createItemText(X, game_text_textures_.at(0));
playSound("item_pickup.wav");
break;
}
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;
createItemText(X, game_text_textures_.at(1));
playSound("item_pickup.wav");
break;
}
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;
createItemText(X, game_text_textures_.at(2));
playSound("item_pickup.wav");
break;
}
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;
createItemText(X, game_text_textures_.at(6));
playSound("debian_pickup.wav");
@@ -471,7 +471,7 @@ void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) {
}
case ItemType::COFFEE: {
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;
createItemText(X, game_text_textures_.at(2));
} 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
void Game::handleBalloonDestruction(std::shared_ptr<Balloon> balloon, std::shared_ptr<Player> player) {
const auto SCORE = balloon_manager_->popBalloon(balloon);
evaluateAndSetMenace();
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();
}
updateHiScore();
@@ -621,7 +621,7 @@ void Game::renderBullets() {
}
// 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));
}
@@ -1174,7 +1174,7 @@ void Game::pause(bool value) {
void Game::addScoreToScoreBoard(const std::shared_ptr<Player> &player) {
const auto ENTRY = HiScoreEntry(trim(player->getLastEnterName()), player->getScore(), player->get1CC());
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"));
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"
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; });
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
auto Game::getController(int 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 Game::getController(Player::Id player_id) -> int {
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()) {
return std::distance(Options::controllers.begin(), it);
if (it != Options::gamepads.end()) {
return std::distance(Options::gamepads.begin(), it);
}
return -1;
@@ -1286,7 +1286,7 @@ void Game::demoHandleInput() {
// Procesa las entradas para un jugador específico durante el modo demo.
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) {
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
void Game::initDemo(int player_id) {
void Game::initDemo(Player::Id player_id) {
if (demo_.enabled) {
// Cambia el estado del juego
setState(State::PLAYING);
@@ -1497,7 +1497,7 @@ void Game::initDemo(int player_id) {
// Activa o no al otro jugador
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);
other_player->setPlayingState(Player::State::PLAYING);
}
@@ -1581,30 +1581,54 @@ void Game::initDifficultyVars() {
}
// 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
// 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_));
players_.back()->setScoreBoardPanel(Scoreboard::Id::LEFT);
players_.back()->setName(Lang::getText("[SCOREBOARD] 1"));
//players_.back()->setController(getController(players_.back()->getId()));
players_.back()->setGamepad(Options::controllers.front().gamepad);
players_.back()->setUsesKeyboard(true);
players_.back()->setPlayingState(Player::State::WAITING);
Player::Config config_player1;
config_player1.id = Player::Id::PLAYER1;
config_player1.x = param.game.play_area.first_quarter_x - (Player::WIDTH / 2);
config_player1.y = Y;
config_player1.demo = demo_.enabled;
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);
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
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_));
players_.back()->setScoreBoardPanel(Scoreboard::Id::RIGHT);
players_.back()->setName(Lang::getText("[SCOREBOARD] 2"));
//players_.back()->setController(getController(players_.back()->getId()));
players_.back()->setPlayingState(Player::State::WAITING);
Player::Config config_player2;
config_player2.id = Player::Id::PLAYER2;
config_player2.x = param.game.play_area.third_quarter_x - (Player::WIDTH / 2);
config_player2.y = Y;
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"
if (player_id == 0) {
if (player_id == Player::Id::BOTH_PLAYERS) {
// Activa ambos jugadores
getPlayer(1)->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::PLAYER1)->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 {
// Activa el jugador elegido
auto player = getPlayer(player_id);

View File

@@ -30,18 +30,15 @@ namespace Difficulty {
enum class Code;
} // 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
class Game {
public:
// --- Constantes ---
static constexpr bool DEMO_OFF = false;
static constexpr bool DEMO_ON = true;
// --- Constructor y destructor ---
Game(int player_id, int current_stage, bool demo);
Game(Player::Id player_id, int current_stage, bool demo);
~Game();
// --- Bucle principal ---
@@ -174,12 +171,12 @@ class Game {
void updateGameStateGameOver(); // Gestiona el estado de fin de partida
// --- 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 renderPlayers(); // Renderiza todos los jugadores en pantalla
void sortPlayersByZOrder(); // Reorganiza el orden de dibujado de jugadores
auto getPlayer(int 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
auto getPlayer(Player::Id id) -> std::shared_ptr<Player>; // Obtiene un jugador por su identificador
static auto getController(Player::Id player_id) -> int; // Obtiene el controlador asignado a un jugador
// --- Estado de 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 ---
void updateBullets(); // Actualiza posición y estado de todas las balas
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 freeBullets(); // Libera memoria del vector de balas
@@ -276,7 +273,7 @@ class Game {
void initScoreboard(); // Inicializa el sistema de puntuació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
// --- Recursos y renderizado ---

View File

@@ -348,7 +348,7 @@ void HiScoreTable::iniEntryColors() {
// Hace brillar los nombres de la tabla de records
void HiScoreTable::glowEntryNames() {
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) {
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)),
mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))),
define_buttons_(std::make_unique<DefineButtons>()),
num_controllers_(Input::get()->getNumControllers()),
num_controllers_(Input::get()->getNumGamepads()),
state_(TitleState::LOGO_ANIMATING) {
// Configura objetos
tiled_bg_->setColor(param.title.bg_color);
@@ -213,12 +213,12 @@ void Title::printColorValue(const Color& color) {
void Title::handleControlKeys(SDL_Keycode key) {
switch (key) {
case SDLK_1:
define_buttons_->enable(0);
define_buttons_->enable(Options::gamepads.at(0));
resetCounter();
break;
case SDLK_2:
define_buttons_->enable(1);
define_buttons_->enable(Options::gamepads.at(1));
resetCounter();
break;
@@ -262,29 +262,29 @@ auto Title::shouldSkipInputCheck() const -> bool {
}
void Title::processControllerInputs() {
for (const auto& controller : Options::controllers) {
for (const auto& controller : Options::gamepads) {
if (isStartButtonPressed(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(
Input::Action::START,
Input::DO_NOT_ALLOW_REPEAT,
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()) {
return;
}
if (controller.player_id == 1) {
if (controller->player_id == Player::Id::PLAYER1) {
processPlayer1Start();
} else if (controller.player_id == 2) {
} else if (controller->player_id == Player::Id::PLAYER2) {
processPlayer2Start();
}
}
@@ -296,18 +296,18 @@ auto Title::canProcessStartButton() const -> bool {
void Title::processPlayer1Start() {
if (!player1_start_pressed_) {
player1_start_pressed_ = true;
activatePlayerAndSetState(1);
activatePlayerAndSetState(Player::Id::PLAYER1);
}
}
void Title::processPlayer2Start() {
if (!player2_start_pressed_) {
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);
setState(TitleState::START_HAS_BEEN_PRESSED);
counter_ = 0;
@@ -328,7 +328,7 @@ void Title::resetCounter() { counter_ = 0; }
// Intercambia la asignación de mandos a los jugadores
void Title::swapControllers() {
if (Input::get()->getNumControllers() == 0) {
if (Input::get()->getNumGamepads() == 0) {
return;
}
@@ -339,13 +339,13 @@ void Title::swapControllers() {
// Intercambia el teclado de jugador
void Title::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});
}
// Muestra información sobre los controles y los jugadores
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;
std::vector<std::string> text(NUM_CONTROLLERS);
std::vector<int> player_controller_index(NUM_CONTROLLERS, -1);
@@ -353,19 +353,20 @@ void Title::showControllers() {
// Obtiene de cada jugador el índice del mando que tiene asignado
for (size_t i = 0; i < NUM_CONTROLLERS; ++i) {
// 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
//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)});
*/
}
// Actualiza el fade
@@ -558,10 +559,31 @@ void Title::initPlayers() {
constexpr int PLAYER_HEIGHT = 32;
const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2);
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_.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);
}
@@ -580,7 +602,7 @@ void Title::renderPlayers() {
}
// 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; });
if (it != players_.end()) {

View File

@@ -7,17 +7,17 @@
#include <string_view> // Para string_view
#include <vector> // Para vector
#include "player.h" // Para Player
#include "section.hpp" // Para Options, Name (ptr only)
class DefineButtons;
class Fade;
class GameLogo;
class Player;
class Sprite;
class Text;
class TiledBG;
namespace Options {
struct GamepadOptions;
struct Gamepad;
} // namespace Options
struct Color;
@@ -93,18 +93,18 @@ class Title {
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
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
void handleStartButtonPress(const Options::GamepadOptions& controller); // Maneja la pulsación del 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(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
void processPlayer1Start(); // Procesa el inicio del jugador 1
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 ---
void initPlayers(); // Inicializa los jugadores
void updatePlayers(); // Actualiza 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 ---
void render(); // Dibuja el objeto en pantalla