#include "options.h" #include // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError #include // Para clamp #include // Para basic_ostream, operator<<, basic_ostream::... #include // Para swap #include // Para vector #include "input.h" // Para InputDeviceToUse #include "lang.h" // Para Code #include "utils.h" // Para boolToString, stringToBool, getFileName #include "asset.h" 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 controllers; // Opciones de mando para cada jugador PendingChanges pending_changes; // Opciones que se aplican al cerrar // Vector con las dificultades std::vector difficulties = { {DifficultyCode::EASY, "Easy"}, {DifficultyCode::NORMAL, "Normal"}, {DifficultyCode::HARD, "Hard"}}; // Declaraciones bool set(const std::string &var, const std::string &value); // Inicializa las opciones del programa void init() { // Opciones de ventana window.caption = "Coffee Crisis Arcade Edition"; window.size = 2; // Opciones de video video.fullscreen = false; video.scale_mode = SDL_ScaleMode::SDL_SCALEMODE_NEAREST; video.v_sync = true; video.integer_scale = true; video.shaders = false; // Opciones de audio audio.enabled = true; audio.volume = 100; audio.music.enabled = true; audio.music.volume = 100; audio.sound.enabled = true; audio.sound.volume = 50; // Opciones de configuracion settings.difficulty = DifficultyCode::NORMAL; settings.language = Lang::Code::VALENCIAN; settings.autofire = true; settings.shutdown_enabled = false; settings.is_y_axis_inverted = false; settings.clearLastHiScoreEntries(); settings.config_file = Asset::get()->get("config.txt"); // Opciones de control controllers.clear(); controllers.resize(2); controllers.at(0).player_id = 1; controllers.at(1).player_id = 2; setKeyboardToPlayer(1); // Opciones pendientes pending_changes.new_language = settings.language; pending_changes.new_difficulty = settings.difficulty; pending_changes.has_pending_changes = false; } // Carga el fichero de configuración bool loadFromFile() { // Inicializa las opciones del programa init(); // Indicador de éxito en la carga bool success = true; // Variables para manejar el fichero std::ifstream file(settings.config_file); // Si el fichero se puede abrir if (file.good()) { // Procesa el fichero línea a línea SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str()); std::string line; while (std::getline(file, line)) { // Comprueba que la línea no sea un comentario if (line.substr(0, 1) != "#") { // Encuentra la posición del carácter '=' int pos = line.find("="); // Procesa las dos subcadenas if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str()); success = false; } } } file.close(); } // El fichero no existe else { saveToFile(); // Crea el fichero con los valores por defecto } // Normaliza los valores if (settings.language != Lang::Code::ENGLISH && settings.language != Lang::Code::VALENCIAN && settings.language != Lang::Code::SPANISH) { settings.language = Lang::Code::ENGLISH; } return success; } // Guarda el fichero de configuración bool saveToFile() { std::ofstream file(settings.config_file); if (!file.good()) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str()); return false; } SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str()); applyPendingChanges(); // Opciones de video file << "## VIDEO\n"; file << "## video.scale_mode [" << static_cast(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n"; file << "\n"; file << "window.zoom=" << window.size << "\n"; file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n"; file << "video.scale_mode=" << static_cast(video.scale_mode) << "\n"; file << "video.v_sync=" << boolToString(video.v_sync) << "\n"; file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n"; file << "video.shaders=" << boolToString(video.shaders) << "\n"; // Opciones de audio file << "\n\n## AUDIO\n"; file << "## volume [0 .. 100]\n"; file << "\n"; file << "audio.enabled=" << boolToString(audio.enabled) << "\n"; file << "audio.volume=" << audio.volume << "\n"; file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n"; file << "audio.music.volume=" << audio.music.volume << "\n"; file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n"; file << "audio.sound.volume=" << audio.sound.volume << "\n"; // Opciones del juego file << "\n\n## GAME\n"; file << "## game.language [0: spanish, 1: valencian, 2: english]\n"; file << "## game.difficulty [" << static_cast(DifficultyCode::EASY) << ": easy, " << static_cast(DifficultyCode::NORMAL) << ": normal, " << static_cast(DifficultyCode::HARD) << ": hard]\n"; file << "\n"; file << "game.language=" << static_cast(settings.language) << "\n"; file << "game.difficulty=" << static_cast(settings.difficulty) << "\n"; file << "game.autofire=" << boolToString(settings.autofire) << "\n"; file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n"; file << "game.is_y_axis_inverted=" << boolToString(settings.is_y_axis_inverted) << "\n"; // 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"; file << "controller." << controller_index << ".type=" << static_cast(controller.type) << "\n"; file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n"; file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n"; file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n"; file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n"; file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n"; // Incrementa el índice ++controller_index; } // Cierra el fichero file.close(); return true; } // Asigna variables a partir de dos cadenas bool set(const std::string &var, const std::string &value) { // Indicador de éxito en la asignación auto success = true; // Opciones de video if (var == "video.fullscreen") { video.fullscreen = stringToBool(value); } else if (var == "window.zoom") { window.size = std::stoi(value); } else if (var == "video.scale_mode") { video.scale_mode = static_cast(std::stoi(value)); } else if (var == "video.shaders") { video.shaders = stringToBool(value); } else if (var == "video.integer_scale") { video.integer_scale = stringToBool(value); } else if (var == "video.v_sync") { video.v_sync = stringToBool(value); } // Opciones de audio else if (var == "audio.enabled") { audio.enabled = stringToBool(value); } else if (var == "audio.volume") { audio.volume = std::clamp(std::stoi(value), 0, 100); } else if (var == "audio.music.enabled") { audio.music.enabled = stringToBool(value); } else if (var == "audio.music.volume") { audio.music.volume = std::clamp(std::stoi(value), 0, 100); } else if (var == "audio.sound.enabled") { audio.sound.enabled = stringToBool(value); } else if (var == "audio.sound.volume") { audio.sound.volume = std::clamp(std::stoi(value), 0, 100); } // Opciones de juego else if (var == "game.language") { settings.language = static_cast(std::stoi(value)); pending_changes.new_language = settings.language; } else if (var == "game.difficulty") { settings.difficulty = static_cast(std::stoi(value)); pending_changes.new_difficulty = settings.difficulty; } else if (var == "game.autofire") { settings.autofire = stringToBool(value); } else if (var == "game.shutdown_enabled") { settings.shutdown_enabled = stringToBool(value); } else if (var == "game.is_y_axis_inverted") { settings.is_y_axis_inverted = stringToBool(value); } // Opciones de mandos else if (var == "controller.0.name") { controllers.at(0).name = value; } else if (var == "controller.0.player") { controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2); } else if (var == "controller.0.type") { controllers.at(0).type = static_cast(std::stoi(value)); } else if (var == "controller.0.button.fire_left") { controllers.at(0).buttons.at(0) = static_cast(std::stoi(value)); } else if (var == "controller.0.button.fire_center") { controllers.at(0).buttons.at(1) = static_cast(std::stoi(value)); } else if (var == "controller.0.button.fire_right") { controllers.at(0).buttons.at(2) = static_cast(std::stoi(value)); } else if (var == "controller.0.button.start") { controllers.at(0).buttons.at(3) = static_cast(std::stoi(value)); } else if (var == "controller.0.button.service") { controllers.at(0).buttons.at(4) = static_cast(std::stoi(value)); } else if (var == "controller.1.name") { controllers.at(1).name = value; } else if (var == "controller.1.player") { controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2); } else if (var == "controller.1.type") { controllers.at(1).type = static_cast(std::stoi(value)); } else if (var == "controller.1.button.fire_left") { controllers.at(1).buttons.at(0) = static_cast(std::stoi(value)); } else if (var == "controller.1.button.fire_center") { controllers.at(1).buttons.at(1) = static_cast(std::stoi(value)); } else if (var == "controller.1.button.fire_right") { controllers.at(1).buttons.at(2) = static_cast(std::stoi(value)); } else if (var == "controller.1.button.start") { controllers.at(1).buttons.at(3) = static_cast(std::stoi(value)); } else if (var == "controller.1.button.service") { controllers.at(1).buttons.at(4) = static_cast(std::stoi(value)); } // Lineas vacias o que empiezan por comentario else if (var.empty() || var.starts_with("#")) { } else { success = false; } return success; } // Asigna el teclado al jugador void setKeyboardToPlayer(int player_id) { for (auto &controller : controllers) { if (controller.player_id == player_id) { controller.type = InputDeviceToUse::ANY; } else { controller.type = InputDeviceToUse::CONTROLLER; } } } // Intercambia el teclado de jugador void swapKeyboard() { std::swap(controllers.at(0).type, controllers.at(1).type); } // 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); } // Averigua quien está usando el teclado int getPlayerWhoUsesKeyboard() { for (const auto &controller : controllers) { if (controller.type == InputDeviceToUse::ANY) { return controller.player_id; } } return 0; } // Aplica los cambios pendientes copiando los valores a sus variables void applyPendingChanges() { if (pending_changes.has_pending_changes) { settings.language = pending_changes.new_language; settings.difficulty = pending_changes.new_difficulty; pending_changes.has_pending_changes = false; } } void checkPendingChanges() { if (settings.language != pending_changes.new_language || settings.difficulty != pending_changes.new_difficulty) { pending_changes.has_pending_changes = true; } else { pending_changes.has_pending_changes = false; } } DifficultyCode getDifficultyCodeFromName(const std::string &name) { for (const auto &difficulty : difficulties) { if (difficulty.name == name) return difficulty.code; } // Si no se encuentra, devuelve el primero por defecto return difficulties[0].code; } std::string getDifficultyNameFromCode(DifficultyCode code) { for (const auto &difficulty : difficulties) { if (difficulty.code == code) return difficulty.name; } // Si no se encuentra, devuelve el nombre del primero por defecto return difficulties[0].name; } } // namespace Options