#pragma once #include // Para SDL_GamepadButton, SDL_ScaleMode #include // Para copy #include // Para array #include // Para ofstream #include // Para excepciones #include // Para allocator, string #include #include // Para vector #include "difficulty.h" // Para Code #include "input.h" // Para InputAction, InputDevice #include "lang.h" // Para Code #include "manage_hiscore_table.h" // Para HiScoreEntry #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 { private: static constexpr size_t MAX_PLAYERS = 2; std::array gamepads; // Convierte Player::Id a índice del array size_t playerIdToIndex(Player::Id player_id) const { switch (player_id) { case Player::Id::PLAYER1: return 0; case Player::Id::PLAYER2: return 1; default: throw std::invalid_argument("Invalid player ID"); } } public: void init() { gamepads[0] = Gamepad(Player::Id::PLAYER1); gamepads[1] = Gamepad(Player::Id::PLAYER2); } // Acceso directo por player_id (más intuitivo) Gamepad& getGamepad(Player::Id player_id) { return gamepads[playerIdToIndex(player_id)]; } const Gamepad& getGamepad(Player::Id player_id) const { return gamepads[playerIdToIndex(player_id)]; } // Acceso por índice (más eficiente si ya tienes el índice) Gamepad& operator[](size_t index) { if (index >= MAX_PLAYERS) throw std::out_of_range("Invalid gamepad index"); return gamepads[index]; } const Gamepad& operator[](size_t index) const { if (index >= MAX_PLAYERS) throw std::out_of_range("Invalid gamepad index"); return gamepads[index]; } bool assignGamepadToPlayer(Player::Id player_id, std::shared_ptr instance, const std::string& name) { 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 bool setControllerProperty(size_t controller_index, const std::string& property, const std::string& value) { if (controller_index >= MAX_PLAYERS) return false; auto& gamepad = gamepads[controller_index]; if (property == "name") { gamepad.name = value; return true; } else if (property == "path") { gamepad.path = value; return true; } else 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(); } auto begin() const { return gamepads.begin(); } auto end() const { return gamepads.end(); } size_t size() const { return MAX_PLAYERS; } }; 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 bool assignGamepadByName(const std::string& gamepad_name, Player::Id player_id); // Buscar y asignar un mando disponible por nombre } // namespace Options