#pragma once #include // Para SDL_ScaleMode #include // Para copy #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, swap #include // Para out_of_range, invalid_argument #include // Para char_traits, string, allocator, operator==, swap, operator<<, basic_string, stoi #include // Para vector #include "difficulty.h" // Para Code #include "input.h" // Para Input #include "lang.h" // Para Code #include "manage_hiscore_table.h" // Para ManageHiScoreTable, Table #include "player.h" // Para Player namespace Options { // --- Opciones de ventana --- 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 Window() : caption("Coffee Crisis Arcade Edition") {} }; // --- Opciones de vídeo --- 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 bool integer_scale{true}; // Indica si se usa escalado entero bool shaders{false}; // Indica si se usan shaders para los filtros de vídeo std::string info; // Información sobre el modo de vídeo // Constructor por defecto con valores iniciales Video() = default; }; // --- Opciones de música --- struct Music { bool enabled{true}; // Indica si la música suena o no int volume{100}; // Volumen de la música // Constructor por defecto Music() = default; }; // --- Opciones de sonido --- struct Sound { bool enabled{true}; // Indica si los sonidos suenan o no int volume{100}; // Volumen de los sonidos // Constructor por defecto Sound() = default; }; // --- Opciones de audio --- 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 Audio() = default; }; // --- Opciones de configuración --- 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 Table hi_score_table; // Tabla de mejores puntuaciones std::vector 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 Settings() : glowing_entries({ManageHiScoreTable::NO_ENTRY, ManageHiScoreTable::NO_ENTRY}) {} // Reinicia las últimas entradas de puntuación void clearLastHiScoreEntries() { glowing_entries.at(0) = ManageHiScoreTable::NO_ENTRY; glowing_entries.at(1) = ManageHiScoreTable::NO_ENTRY; } }; // --- Estructura para gamepad individual --- 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) {} }; // --- Manager para los gamepads --- 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 = 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); } // Para serialización/deserialización void saveToFile(std::ofstream& file) const { for (size_t i = 0; i < MAX_PLAYERS; ++i) { const auto& gamepad = gamepads_[i]; file << "controller." << i << ".name=" << gamepad.name << "\n"; 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; } // 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 size_t MAX_PLAYERS = 2; std::array gamepads_; // 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); [[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 }; // --- Opciones pendientes de aplicar --- 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 // Constructor por defecto con valores iniciales PendingChanges() = default; }; // --- Variables globales --- 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 de configuración --- 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 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