#include "options.hpp" #include #include #include #include #include "core/defaults.hpp" #include "external/fkyaml_node.hpp" #include "project.h" namespace Options { // ========== FUNCIONS AUXILIARS PER CONVERSIÓ DE CONTROLES ========== // Mapa de SDL_Scancode a string static const std::unordered_map SCANCODE_TO_STRING = { {SDL_SCANCODE_A, "A"}, {SDL_SCANCODE_B, "B"}, {SDL_SCANCODE_C, "C"}, {SDL_SCANCODE_D, "D"}, {SDL_SCANCODE_E, "E"}, {SDL_SCANCODE_F, "F"}, {SDL_SCANCODE_G, "G"}, {SDL_SCANCODE_H, "H"}, {SDL_SCANCODE_I, "I"}, {SDL_SCANCODE_J, "J"}, {SDL_SCANCODE_K, "K"}, {SDL_SCANCODE_L, "L"}, {SDL_SCANCODE_M, "M"}, {SDL_SCANCODE_N, "N"}, {SDL_SCANCODE_O, "O"}, {SDL_SCANCODE_P, "P"}, {SDL_SCANCODE_Q, "Q"}, {SDL_SCANCODE_R, "R"}, {SDL_SCANCODE_S, "S"}, {SDL_SCANCODE_T, "T"}, {SDL_SCANCODE_U, "U"}, {SDL_SCANCODE_V, "V"}, {SDL_SCANCODE_W, "W"}, {SDL_SCANCODE_X, "X"}, {SDL_SCANCODE_Y, "Y"}, {SDL_SCANCODE_Z, "Z"}, {SDL_SCANCODE_1, "1"}, {SDL_SCANCODE_2, "2"}, {SDL_SCANCODE_3, "3"}, {SDL_SCANCODE_4, "4"}, {SDL_SCANCODE_5, "5"}, {SDL_SCANCODE_6, "6"}, {SDL_SCANCODE_7, "7"}, {SDL_SCANCODE_8, "8"}, {SDL_SCANCODE_9, "9"}, {SDL_SCANCODE_0, "0"}, {SDL_SCANCODE_RETURN, "RETURN"}, {SDL_SCANCODE_ESCAPE, "ESCAPE"}, {SDL_SCANCODE_BACKSPACE, "BACKSPACE"}, {SDL_SCANCODE_TAB, "TAB"}, {SDL_SCANCODE_SPACE, "SPACE"}, {SDL_SCANCODE_UP, "UP"}, {SDL_SCANCODE_DOWN, "DOWN"}, {SDL_SCANCODE_LEFT, "LEFT"}, {SDL_SCANCODE_RIGHT, "RIGHT"}, {SDL_SCANCODE_LSHIFT, "LSHIFT"}, {SDL_SCANCODE_RSHIFT, "RSHIFT"}, {SDL_SCANCODE_LCTRL, "LCTRL"}, {SDL_SCANCODE_RCTRL, "RCTRL"}, {SDL_SCANCODE_LALT, "LALT"}, {SDL_SCANCODE_RALT, "RALT"}}; // Mapa invers: string a SDL_Scancode static const std::unordered_map STRING_TO_SCANCODE = { {"A", SDL_SCANCODE_A}, {"B", SDL_SCANCODE_B}, {"C", SDL_SCANCODE_C}, {"D", SDL_SCANCODE_D}, {"E", SDL_SCANCODE_E}, {"F", SDL_SCANCODE_F}, {"G", SDL_SCANCODE_G}, {"H", SDL_SCANCODE_H}, {"I", SDL_SCANCODE_I}, {"J", SDL_SCANCODE_J}, {"K", SDL_SCANCODE_K}, {"L", SDL_SCANCODE_L}, {"M", SDL_SCANCODE_M}, {"N", SDL_SCANCODE_N}, {"O", SDL_SCANCODE_O}, {"P", SDL_SCANCODE_P}, {"Q", SDL_SCANCODE_Q}, {"R", SDL_SCANCODE_R}, {"S", SDL_SCANCODE_S}, {"T", SDL_SCANCODE_T}, {"U", SDL_SCANCODE_U}, {"V", SDL_SCANCODE_V}, {"W", SDL_SCANCODE_W}, {"X", SDL_SCANCODE_X}, {"Y", SDL_SCANCODE_Y}, {"Z", SDL_SCANCODE_Z}, {"1", SDL_SCANCODE_1}, {"2", SDL_SCANCODE_2}, {"3", SDL_SCANCODE_3}, {"4", SDL_SCANCODE_4}, {"5", SDL_SCANCODE_5}, {"6", SDL_SCANCODE_6}, {"7", SDL_SCANCODE_7}, {"8", SDL_SCANCODE_8}, {"9", SDL_SCANCODE_9}, {"0", SDL_SCANCODE_0}, {"RETURN", SDL_SCANCODE_RETURN}, {"ESCAPE", SDL_SCANCODE_ESCAPE}, {"BACKSPACE", SDL_SCANCODE_BACKSPACE}, {"TAB", SDL_SCANCODE_TAB}, {"SPACE", SDL_SCANCODE_SPACE}, {"UP", SDL_SCANCODE_UP}, {"DOWN", SDL_SCANCODE_DOWN}, {"LEFT", SDL_SCANCODE_LEFT}, {"RIGHT", SDL_SCANCODE_RIGHT}, {"LSHIFT", SDL_SCANCODE_LSHIFT}, {"RSHIFT", SDL_SCANCODE_RSHIFT}, {"LCTRL", SDL_SCANCODE_LCTRL}, {"RCTRL", SDL_SCANCODE_RCTRL}, {"LALT", SDL_SCANCODE_LALT}, {"RALT", SDL_SCANCODE_RALT}}; // Mapa de botó de gamepad (int) a string static const std::unordered_map BUTTON_TO_STRING = { {SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, // A (Xbox), Cross (PS) {SDL_GAMEPAD_BUTTON_EAST, "EAST"}, // B (Xbox), Circle (PS) {SDL_GAMEPAD_BUTTON_WEST, "WEST"}, // X (Xbox), Square (PS) {SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, // Y (Xbox), Triangle (PS) {SDL_GAMEPAD_BUTTON_BACK, "BACK"}, {SDL_GAMEPAD_BUTTON_START, "START"}, {SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"}, {SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"}, {SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"}, {SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"}, {SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"}, {SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"}, {100, "L2_AS_BUTTON"}, // Trigger L2 com a botó digital {101, "R2_AS_BUTTON"} // Trigger R2 com a botó digital }; // Mapa invers: string a botó de gamepad static const std::unordered_map STRING_TO_BUTTON = { {"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH}, {"EAST", SDL_GAMEPAD_BUTTON_EAST}, {"WEST", SDL_GAMEPAD_BUTTON_WEST}, {"NORTH", SDL_GAMEPAD_BUTTON_NORTH}, {"BACK", SDL_GAMEPAD_BUTTON_BACK}, {"START", SDL_GAMEPAD_BUTTON_START}, {"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER}, {"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER}, {"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP}, {"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN}, {"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT}, {"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT}, {"L2_AS_BUTTON", 100}, {"R2_AS_BUTTON", 101}}; static auto scancodeToString(SDL_Scancode code) -> std::string { auto it = SCANCODE_TO_STRING.find(code); return (it != SCANCODE_TO_STRING.end()) ? it->second : "UNKNOWN"; } static auto stringToScancode(const std::string& str) -> SDL_Scancode { auto it = STRING_TO_SCANCODE.find(str); return (it != STRING_TO_SCANCODE.end()) ? it->second : SDL_SCANCODE_UNKNOWN; } static auto buttonToString(int button) -> std::string { auto it = BUTTON_TO_STRING.find(button); return (it != BUTTON_TO_STRING.end()) ? it->second : "UNKNOWN"; } static auto stringToButton(const std::string& str) -> int { auto it = STRING_TO_BUTTON.find(str); return (it != STRING_TO_BUTTON.end()) ? it->second : SDL_GAMEPAD_BUTTON_INVALID; } // ========== FI FUNCIONS AUXILIARS ========== // Inicialitzar opcions amb valors per defecte de Defaults:: void init() { #ifdef _DEBUG console = true; #else console = false; #endif // Window window.width = Defaults::Window::WIDTH; window.height = Defaults::Window::HEIGHT; window.fullscreen = false; window.zoom_factor = Defaults::Window::BASE_ZOOM; // Physics physics.rotation_speed = Defaults::Physics::ROTATION_SPEED; physics.acceleration = Defaults::Physics::ACCELERATION; physics.max_velocity = Defaults::Physics::MAX_VELOCITY; physics.friction = Defaults::Physics::FRICTION; physics.enemy_speed = Defaults::Physics::ENEMY_SPEED; physics.bullet_speed = Defaults::Physics::BULLET_SPEED; // Gameplay gameplay.max_enemies = Defaults::Entities::MAX_ORNIS; gameplay.max_bullets = Defaults::Entities::MAX_BALES; // Rendering rendering.vsync = Defaults::Rendering::VSYNC_DEFAULT; // Audio audio.enabled = Defaults::Audio::ENABLED; audio.volume = Defaults::Audio::VOLUME; audio.music.enabled = Defaults::Music::ENABLED; audio.music.volume = Defaults::Music::VOLUME; audio.sound.enabled = Defaults::Sound::ENABLED; audio.sound.volume = Defaults::Sound::VOLUME; // Version version = std::string(Project::VERSION); } // Establir la ruta del fitxer de configuració void setConfigFile(const std::string& path) { config_file_path = path; } // Funcions auxiliars per carregar seccions del YAML static void loadWindowConfigFromYaml(const fkyaml::node& yaml) { if (yaml.contains("window")) { const auto& win = yaml["window"]; if (win.contains("width")) { try { auto val = win["width"].get_value(); window.width = (val >= Defaults::Window::MIN_WIDTH) ? val : Defaults::Window::WIDTH; } catch (...) { window.width = Defaults::Window::WIDTH; } } if (win.contains("height")) { try { auto val = win["height"].get_value(); window.height = (val >= Defaults::Window::MIN_HEIGHT) ? val : Defaults::Window::HEIGHT; } catch (...) { window.height = Defaults::Window::HEIGHT; } } if (win.contains("fullscreen")) { try { window.fullscreen = win["fullscreen"].get_value(); } catch (...) { window.fullscreen = false; } } if (win.contains("zoom_factor")) { try { auto val = win["zoom_factor"].get_value(); window.zoom_factor = (val >= Defaults::Window::MIN_ZOOM && val <= 10.0f) ? val : Defaults::Window::BASE_ZOOM; } catch (...) { window.zoom_factor = Defaults::Window::BASE_ZOOM; } } else { // Legacy config: infer zoom from width window.zoom_factor = static_cast(window.width) / Defaults::Window::WIDTH; window.zoom_factor = std::max(Defaults::Window::MIN_ZOOM, window.zoom_factor); } } } static void loadPhysicsConfigFromYaml(const fkyaml::node& yaml) { if (yaml.contains("physics")) { const auto& phys = yaml["physics"]; if (phys.contains("rotation_speed")) { try { auto val = phys["rotation_speed"].get_value(); physics.rotation_speed = (val > 0) ? val : Defaults::Physics::ROTATION_SPEED; } catch (...) { physics.rotation_speed = Defaults::Physics::ROTATION_SPEED; } } if (phys.contains("acceleration")) { try { auto val = phys["acceleration"].get_value(); physics.acceleration = (val > 0) ? val : Defaults::Physics::ACCELERATION; } catch (...) { physics.acceleration = Defaults::Physics::ACCELERATION; } } if (phys.contains("max_velocity")) { try { auto val = phys["max_velocity"].get_value(); physics.max_velocity = (val > 0) ? val : Defaults::Physics::MAX_VELOCITY; } catch (...) { physics.max_velocity = Defaults::Physics::MAX_VELOCITY; } } if (phys.contains("friction")) { try { auto val = phys["friction"].get_value(); physics.friction = (val > 0) ? val : Defaults::Physics::FRICTION; } catch (...) { physics.friction = Defaults::Physics::FRICTION; } } if (phys.contains("enemy_speed")) { try { auto val = phys["enemy_speed"].get_value(); physics.enemy_speed = (val > 0) ? val : Defaults::Physics::ENEMY_SPEED; } catch (...) { physics.enemy_speed = Defaults::Physics::ENEMY_SPEED; } } if (phys.contains("bullet_speed")) { try { auto val = phys["bullet_speed"].get_value(); physics.bullet_speed = (val > 0) ? val : Defaults::Physics::BULLET_SPEED; } catch (...) { physics.bullet_speed = Defaults::Physics::BULLET_SPEED; } } } } static void loadGameplayConfigFromYaml(const fkyaml::node& yaml) { if (yaml.contains("gameplay")) { const auto& game = yaml["gameplay"]; if (game.contains("max_enemies")) { try { auto val = game["max_enemies"].get_value(); gameplay.max_enemies = (val > 0 && val <= 50) ? val : Defaults::Entities::MAX_ORNIS; } catch (...) { gameplay.max_enemies = Defaults::Entities::MAX_ORNIS; } } if (game.contains("max_bullets")) { try { auto val = game["max_bullets"].get_value(); gameplay.max_bullets = (val > 0 && val <= 10) ? val : Defaults::Entities::MAX_BALES; } catch (...) { gameplay.max_bullets = Defaults::Entities::MAX_BALES; } } } } static void loadRenderingConfigFromYaml(const fkyaml::node& yaml) { if (yaml.contains("rendering")) { const auto& rend = yaml["rendering"]; if (rend.contains("vsync")) { try { int val = rend["vsync"].get_value(); // Validar: només 0 o 1 rendering.vsync = (val == 0 || val == 1) ? val : Defaults::Rendering::VSYNC_DEFAULT; } catch (...) { rendering.vsync = Defaults::Rendering::VSYNC_DEFAULT; } } } } static void loadAudioConfigFromYaml(const fkyaml::node& yaml) { if (yaml.contains("audio")) { const auto& aud = yaml["audio"]; if (aud.contains("enabled")) { try { audio.enabled = aud["enabled"].get_value(); } catch (...) { audio.enabled = Defaults::Audio::ENABLED; } } if (aud.contains("volume")) { try { float val = aud["volume"].get_value(); audio.volume = (val >= 0.0f && val <= 1.0f) ? val : Defaults::Audio::VOLUME; } catch (...) { audio.volume = Defaults::Audio::VOLUME; } } if (aud.contains("music")) { const auto& mus = aud["music"]; if (mus.contains("enabled")) { try { audio.music.enabled = mus["enabled"].get_value(); } catch (...) { audio.music.enabled = Defaults::Music::ENABLED; } } if (mus.contains("volume")) { try { float val = mus["volume"].get_value(); audio.music.volume = (val >= 0.0f && val <= 1.0f) ? val : Defaults::Music::VOLUME; } catch (...) { audio.music.volume = Defaults::Music::VOLUME; } } } if (aud.contains("sound")) { const auto& snd = aud["sound"]; if (snd.contains("enabled")) { try { audio.sound.enabled = snd["enabled"].get_value(); } catch (...) { audio.sound.enabled = Defaults::Sound::ENABLED; } } if (snd.contains("volume")) { try { float val = snd["volume"].get_value(); audio.sound.volume = (val >= 0.0f && val <= 1.0f) ? val : Defaults::Sound::VOLUME; } catch (...) { audio.sound.volume = Defaults::Sound::VOLUME; } } } } } // Carregar controls del jugador 1 des de YAML static void loadPlayer1ControlsFromYaml(const fkyaml::node& yaml) { if (!yaml.contains("player1")) return; const auto& p1 = yaml["player1"]; // Carregar controls de teclat if (p1.contains("keyboard")) { const auto& kb = p1["keyboard"]; if (kb.contains("key_left")) player1.keyboard.key_left = stringToScancode(kb["key_left"].get_value()); if (kb.contains("key_right")) player1.keyboard.key_right = stringToScancode(kb["key_right"].get_value()); if (kb.contains("key_thrust")) player1.keyboard.key_thrust = stringToScancode(kb["key_thrust"].get_value()); if (kb.contains("key_shoot")) player1.keyboard.key_shoot = stringToScancode(kb["key_shoot"].get_value()); } // Carregar controls de gamepad if (p1.contains("gamepad")) { const auto& gp = p1["gamepad"]; if (gp.contains("button_left")) player1.gamepad.button_left = stringToButton(gp["button_left"].get_value()); if (gp.contains("button_right")) player1.gamepad.button_right = stringToButton(gp["button_right"].get_value()); if (gp.contains("button_thrust")) player1.gamepad.button_thrust = stringToButton(gp["button_thrust"].get_value()); if (gp.contains("button_shoot")) player1.gamepad.button_shoot = stringToButton(gp["button_shoot"].get_value()); } // Carregar nom del gamepad if (p1.contains("gamepad_name")) player1.gamepad_name = p1["gamepad_name"].get_value(); } // Carregar controls del jugador 2 des de YAML static void loadPlayer2ControlsFromYaml(const fkyaml::node& yaml) { if (!yaml.contains("player2")) return; const auto& p2 = yaml["player2"]; // Carregar controls de teclat if (p2.contains("keyboard")) { const auto& kb = p2["keyboard"]; if (kb.contains("key_left")) player2.keyboard.key_left = stringToScancode(kb["key_left"].get_value()); if (kb.contains("key_right")) player2.keyboard.key_right = stringToScancode(kb["key_right"].get_value()); if (kb.contains("key_thrust")) player2.keyboard.key_thrust = stringToScancode(kb["key_thrust"].get_value()); if (kb.contains("key_shoot")) player2.keyboard.key_shoot = stringToScancode(kb["key_shoot"].get_value()); } // Carregar controls de gamepad if (p2.contains("gamepad")) { const auto& gp = p2["gamepad"]; if (gp.contains("button_left")) player2.gamepad.button_left = stringToButton(gp["button_left"].get_value()); if (gp.contains("button_right")) player2.gamepad.button_right = stringToButton(gp["button_right"].get_value()); if (gp.contains("button_thrust")) player2.gamepad.button_thrust = stringToButton(gp["button_thrust"].get_value()); if (gp.contains("button_shoot")) player2.gamepad.button_shoot = stringToButton(gp["button_shoot"].get_value()); } // Carregar nom del gamepad if (p2.contains("gamepad_name")) player2.gamepad_name = p2["gamepad_name"].get_value(); } // Carregar configuració des del fitxer YAML auto loadFromFile() -> bool { const std::string CONFIG_VERSION = std::string(Project::VERSION); std::ifstream file(config_file_path); if (!file.good()) { // El fitxer no existeix → crear-ne un de nou amb valors per defecte if (console) { std::cout << "Fitxer de config no trobat, creant-ne un de nou: " << config_file_path << '\n'; } saveToFile(); return true; } // Llegir tot el contingut del fitxer std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); try { // Parsejar YAML auto yaml = fkyaml::node::deserialize(content); // Validar versió if (yaml.contains("version")) { version = yaml["version"].get_value(); } if (CONFIG_VERSION != version) { // Versió incompatible → regenerar config if (console) { std::cout << "Versió de config incompatible (esperada: " << CONFIG_VERSION << ", trobada: " << version << "), regenerant config\n"; } init(); saveToFile(); return true; } // Carregar seccions loadWindowConfigFromYaml(yaml); loadPhysicsConfigFromYaml(yaml); loadGameplayConfigFromYaml(yaml); loadRenderingConfigFromYaml(yaml); loadAudioConfigFromYaml(yaml); loadPlayer1ControlsFromYaml(yaml); loadPlayer2ControlsFromYaml(yaml); if (console) { std::cout << "Config carregada correctament des de: " << config_file_path << '\n'; } return true; } catch (const fkyaml::exception& e) { // Error de parsejat YAML → regenerar config if (console) { std::cerr << "Error parsejant YAML: " << e.what() << '\n'; std::cerr << "Creant config nou amb valors per defecte\n"; } init(); saveToFile(); return true; } } // Guardar controls del jugador 1 a YAML static void savePlayer1ControlsToYaml(std::ofstream& file) { file << "# CONTROLS JUGADOR 1\n"; file << "player1:\n"; file << " keyboard:\n"; file << " key_left: " << scancodeToString(player1.keyboard.key_left) << "\n"; file << " key_right: " << scancodeToString(player1.keyboard.key_right) << "\n"; file << " key_thrust: " << scancodeToString(player1.keyboard.key_thrust) << "\n"; file << " key_shoot: " << scancodeToString(player1.keyboard.key_shoot) << "\n"; file << " gamepad:\n"; file << " button_left: " << buttonToString(player1.gamepad.button_left) << "\n"; file << " button_right: " << buttonToString(player1.gamepad.button_right) << "\n"; file << " button_thrust: " << buttonToString(player1.gamepad.button_thrust) << "\n"; file << " button_shoot: " << buttonToString(player1.gamepad.button_shoot) << "\n"; file << " gamepad_name: \"" << player1.gamepad_name << "\" # Buit = primer disponible\n\n"; } // Guardar controls del jugador 2 a YAML static void savePlayer2ControlsToYaml(std::ofstream& file) { file << "# CONTROLS JUGADOR 2\n"; file << "player2:\n"; file << " keyboard:\n"; file << " key_left: " << scancodeToString(player2.keyboard.key_left) << "\n"; file << " key_right: " << scancodeToString(player2.keyboard.key_right) << "\n"; file << " key_thrust: " << scancodeToString(player2.keyboard.key_thrust) << "\n"; file << " key_shoot: " << scancodeToString(player2.keyboard.key_shoot) << "\n"; file << " gamepad:\n"; file << " button_left: " << buttonToString(player2.gamepad.button_left) << "\n"; file << " button_right: " << buttonToString(player2.gamepad.button_right) << "\n"; file << " button_thrust: " << buttonToString(player2.gamepad.button_thrust) << "\n"; file << " button_shoot: " << buttonToString(player2.gamepad.button_shoot) << "\n"; file << " gamepad_name: \"" << player2.gamepad_name << "\" # Buit = segon disponible\n\n"; } // Guardar configuració al fitxer YAML auto saveToFile() -> bool { std::ofstream file(config_file_path); if (!file.is_open()) { if (console) { std::cerr << "No s'ha pogut obrir el fitxer de config per escriure: " << config_file_path << '\n'; } return false; } // Escriure manualment per controlar format i comentaris file << "# Orni Attack - Fitxer de Configuració\n"; file << "# Auto-generat. Les edicions manuals es preserven si són " "vàlides.\n\n"; file << "version: \"" << Project::VERSION << "\"\n\n"; file << "# FINESTRA\n"; file << "window:\n"; file << " width: " << window.width << " # Calculated from zoom_factor\n"; file << " height: " << window.height << " # Calculated from zoom_factor\n"; file << " fullscreen: " << (window.fullscreen ? "true" : "false") << "\n"; file << " zoom_factor: " << window.zoom_factor << " # 0.5x-max (0.1 increments)\n\n"; file << "# FÍSICA (tots els valors en px/s, rad/s, etc.)\n"; file << "physics:\n"; file << " rotation_speed: " << physics.rotation_speed << " # rad/s\n"; file << " acceleration: " << physics.acceleration << " # px/s²\n"; file << " max_velocity: " << physics.max_velocity << " # px/s\n"; file << " friction: " << physics.friction << " # px/s²\n"; file << " enemy_speed: " << physics.enemy_speed << " # unitats/frame\n"; file << " bullet_speed: " << physics.bullet_speed << " # unitats/frame\n\n"; file << "# GAMEPLAY\n"; file << "gameplay:\n"; file << " max_enemies: " << gameplay.max_enemies << "\n"; file << " max_bullets: " << gameplay.max_bullets << "\n\n"; file << "# RENDERITZACIÓ\n"; file << "rendering:\n"; file << " vsync: " << rendering.vsync << " # 0=disabled, 1=enabled\n\n"; file << "# AUDIO\n"; file << "audio:\n"; file << " enabled: " << (audio.enabled ? "true" : "false") << "\n"; file << " volume: " << audio.volume << " # 0.0 to 1.0\n"; file << " music:\n"; file << " enabled: " << (audio.music.enabled ? "true" : "false") << "\n"; file << " volume: " << audio.music.volume << " # 0.0 to 1.0\n"; file << " sound:\n"; file << " enabled: " << (audio.sound.enabled ? "true" : "false") << "\n"; file << " volume: " << audio.sound.volume << " # 0.0 to 1.0\n\n"; // Guardar controls de jugadors savePlayer1ControlsToYaml(file); savePlayer2ControlsToYaml(file); file.close(); if (console) { std::cout << "Config guardada a: " << config_file_path << '\n'; } return true; } } // namespace Options