#pragma once #include // Para SDL_ScaleMode #include // Para array #include // Para size_t #include // Para exception #include // Para basic_ostream, operator<<, basic_ofstream, basic_ostream::operator<<, ofstream #include // Para shared_ptr, __shared_ptr_access, swap #include // Para out_of_range, invalid_argument #include // Para char_traits, basic_string, string, operator==, operator<<, swap, stoi #include // Para string_view #include // Para move #include // Para vector #include "defaults.hpp" // for AUDIO_ENABLED, AUDIO_VOLUME, MUSIC_ENABLED, MUSIC_VOLUME, PARAMS_FILE, SETTINGS_AUTOFIRE, SETTINGS_SHUTDOWN_ENABLED, SOUND_ENABLED, SOUND_VOLUME, VIDEO_FULLSCREEN, VIDEO_INTEGER_SCALE, VIDEO_SCALE_MODE, VIDEO_SHADERS, VIDEO_VSYNC, WINDOW_CAPTION, WINDOW_MAX_ZOOM, WINDOW_ZOOM #include "difficulty.hpp" // for Code #include "input.hpp" // for Input #include "lang.hpp" // for Code #include "manage_hiscore_table.hpp" // for ManageHiScoreTable, Table #include "player.hpp" // for Player // --- Namespace Options: gestión de configuración y opciones del juego --- namespace Options { // --- Estructuras --- struct Window { std::string caption = GameDefaults::Options::WINDOW_CAPTION; // Texto que aparece en la barra de título de la ventana int zoom = GameDefaults::Options::WINDOW_ZOOM; // Valor por el que se multiplica el tamaño de la ventana int max_zoom = GameDefaults::Options::WINDOW_MAX_ZOOM; // Tamaño máximo para que la ventana no sea mayor que la pantalla }; struct Video { SDL_ScaleMode scale_mode = GameDefaults::Options::VIDEO_SCALE_MODE; // Filtro usado para el escalado de la imagen bool fullscreen = GameDefaults::Options::VIDEO_FULLSCREEN; // Indica si se usa pantalla completa bool vsync = GameDefaults::Options::VIDEO_VSYNC; // Indica si se usa vsync bool integer_scale = GameDefaults::Options::VIDEO_INTEGER_SCALE; // Indica si se usa escalado entero bool shaders = GameDefaults::Options::VIDEO_SHADERS; // Indica si se usan shaders para los filtros de vídeo std::string info; // Información sobre el modo de vídeo }; struct Music { bool enabled = GameDefaults::Options::MUSIC_ENABLED; // Indica si la música suena o no int volume = GameDefaults::Options::MUSIC_VOLUME; // Volumen de la música }; struct Sound { bool enabled = GameDefaults::Options::SOUND_ENABLED; // Indica si los sonidos suenan o no int volume = GameDefaults::Options::SOUND_VOLUME; // Volumen de los sonidos }; struct Audio { Music music; // Opciones para la música Sound sound; // Opciones para los efectos de sonido bool enabled = GameDefaults::Options::AUDIO_ENABLED; // Indica si el audio está activo o no int volume = GameDefaults::Options::AUDIO_VOLUME; // Volumen general del audio }; struct Settings { int config_version = 2; // Versión del archivo de configuración Difficulty::Code difficulty = Difficulty::Code::NORMAL; // Dificultad del juego Lang::Code language = Lang::Code::VALENCIAN; // Idioma usado en el juego bool autofire = GameDefaults::Options::SETTINGS_AUTOFIRE; // Indicador de autofire bool shutdown_enabled = GameDefaults::Options::SETTINGS_SHUTDOWN_ENABLED; // Especifica si se puede apagar el sistema Table hi_score_table; // Tabla de mejores puntuaciones std::vector glowing_entries = {ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}; // Ú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 std::string params_file = GameDefaults::Options::PARAMS_FILE; // Ruta al fichero de parámetros del juego // Reinicia las últimas entradas de puntuación void clearLastHiScoreEntries() { glowing_entries.at(0) = ManageHiScoreTable::NO_ENTRY; glowing_entries.at(1) = ManageHiScoreTable::NO_ENTRY; } }; struct Gamepad { std::shared_ptr instance = nullptr; // Referencia al mando std::string name; // Nombre del mando std::string path; // Ruta física del dispositivo Player::Id player_id; // Jugador asociado al mando Gamepad(Player::Id custom_player_id = Player::Id::NO_PLAYER) : player_id(custom_player_id) {} }; // --- Clases --- class GamepadManager { public: void init() { gamepads_[0] = Gamepad(Player::Id::PLAYER1); gamepads_[1] = Gamepad(Player::Id::PLAYER2); } // Acceso directo por player_id (más intuitivo) auto getGamepad(Player::Id player_id) -> Gamepad& { return gamepads_[playerIdToIndex(player_id)]; } [[nodiscard]] auto getGamepad(Player::Id player_id) const -> const Gamepad& { return gamepads_[playerIdToIndex(player_id)]; } // Acceso por índice (más eficiente si ya tienes el índice) auto operator[](size_t index) -> Gamepad& { if (index >= MAX_PLAYERS) { throw std::out_of_range("Invalid gamepad index"); } return gamepads_[index]; } auto operator[](size_t index) const -> const Gamepad& { if (index >= MAX_PLAYERS) { throw std::out_of_range("Invalid gamepad index"); } return gamepads_[index]; } auto assignGamepadToPlayer(Player::Id player_id, std::shared_ptr instance, const std::string& name) -> bool { try { auto& gamepad = getGamepad(player_id); gamepad.instance = std::move(instance); gamepad.name = name; return true; } catch (const std::exception&) { return false; } } void swapPlayers() { std::swap(gamepads_[0].instance, gamepads_[1].instance); std::swap(gamepads_[0].name, gamepads_[1].name); std::swap(gamepads_[0].path, gamepads_[1].path); resyncGamepadsWithPlayers(); } void resyncGamepadsWithPlayers() { for (const auto& player : players_) { switch (player->getId()) { case Player::Id::PLAYER1: player->setGamepad(gamepads_[0].instance); break; case Player::Id::PLAYER2: player->setGamepad(gamepads_[1].instance); break; default: break; } } } void saveToFile(std::ofstream& file) const { for (size_t i = 0; i < MAX_PLAYERS; ++i) { const auto& gamepad = gamepads_[i]; // Guardar el nombre solo si hay path (mando real asignado) if (!gamepad.path.empty()) { file << "controller." << i << ".name " << gamepad.name << "\n"; } else { file << "controller." << i << ".name \n"; // vacío } file << "controller." << i << ".path " << gamepad.path << "\n"; file << "controller." << i << ".player " << static_cast(gamepad.player_id) << "\n"; } } // Método helper para parseAndSetController auto setControllerProperty(size_t controller_index, const std::string& property, const std::string& value) -> bool { if (controller_index >= MAX_PLAYERS) { return false; } auto& gamepad = gamepads_[controller_index]; if (property == "name") { gamepad.name = value; return true; } if (property == "path") { gamepad.path = value; return true; } if (property == "player") { try { int player_int = std::stoi(value); if (player_int == 1) { gamepad.player_id = Player::Id::PLAYER1; } else if (player_int == 2) { gamepad.player_id = Player::Id::PLAYER2; } else { return false; } return true; } catch (const std::exception&) { return false; } } return false; } void addPlayer(const std::shared_ptr& player) { players_.push_back(player); } // Añade un jugador a la lista void clearPlayers() { players_.clear(); } // Limpia la lista de jugadores // Asigna el mando a un jugador void assignTo(const Input::Gamepad& gamepad, Player::Id player_id) { } // Asigna los mandos físicos basándose en la configuración actual de nombres. void assignAndLinkGamepads(); // Iteradores auto begin() { return gamepads_.begin(); } auto end() { return gamepads_.end(); } [[nodiscard]] auto begin() const { return gamepads_.begin(); } [[nodiscard]] auto end() const { return gamepads_.end(); } [[nodiscard]] static auto size() -> size_t { return MAX_PLAYERS; } private: static constexpr std::string_view UNASSIGNED_TEXT = "---"; static constexpr size_t MAX_PLAYERS = 2; std::array gamepads_; // Punteros a las estructuras de mandos de Options std::vector> players_; // Punteros a los jugadores // Convierte Player::Id a índice del array [[nodiscard]] static auto playerIdToIndex(Player::Id player_id) -> size_t { switch (player_id) { case Player::Id::PLAYER1: return 0; case Player::Id::PLAYER2: return 1; default: throw std::invalid_argument("Invalid player ID"); } } void assignGamepadsByPath( const std::array& desired_paths, const std::vector>& physical_gamepads, std::vector>& assigned_instances); void assignRemainingGamepads( const std::vector>& physical_gamepads, std::vector>& assigned_instances); void clearUnassignedGamepadSlots(); [[nodiscard]] static auto isGamepadAssigned( const std::shared_ptr& physical_gamepad, const std::vector>& assigned_instances) -> bool; }; struct Keyboard { Player::Id player_id = Player::Id::PLAYER1; // Jugador asociado al teclado std::vector> players; // Punteros a los jugadores void addPlayer(const std::shared_ptr& player) { players.push_back(player); } // Añade un jugador a la lista void clearPlayers() { players.clear(); } // Limpia la lista de jugadores // Asigna el teclado a un jugador void assignTo(Player::Id player_id) { this->player_id = player_id; for (auto& player : players) { player->setUsesKeyboard(player->getId() == player_id); } } }; struct PendingChanges { Lang::Code new_language = Lang::Code::VALENCIAN; // Idioma en espera de aplicar Difficulty::Code new_difficulty = Difficulty::Code::NORMAL; // Dificultad en espera de aplicar bool has_pending_changes = false; // Indica si hay cambios pendientes }; // --- Variables --- 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 GamepadManager gamepad_manager; // Manager de mandos para cada jugador extern Keyboard keyboard; // Opciones para el teclado extern PendingChanges pending_changes; // Opciones que se aplican al cerrar // --- Funciones --- void init(); // Inicializa las opciones del programa void setConfigFile(const std::string& file_path); // Establece el fichero de configuración 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(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() -> Player::Id; // Averigua quién está usando el teclado auto playerIdToString(Player::Id player_id) -> std::string; // Convierte un player id a texto segun Lang auto stringToPlayerId(const std::string& name) -> Player::Id; // Convierte un texto a player id segun Lang void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables void checkPendingChanges(); // Verifica si hay cambios pendientes auto assignGamepadByName(const std::string& gamepad_name, Player::Id player_id) -> bool; // Buscar y asignar un mando disponible por nombre } // namespace Options