#include "options.hpp" #include #include #include #include "../core/defaults.hpp" #include "../external/fkyaml_node.hpp" #include "project.h" namespace Options { // 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 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); 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 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"; file.close(); if (console) { std::cout << "Config guardada a: " << config_file_path << '\n'; } return true; } } // namespace Options