diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 69e7701..24decac 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -9,10 +9,6 @@ #include #include -#include "debug_overlay.hpp" -#include "scene.hpp" -#include "scene_context.hpp" -#include "global_events.hpp" #include "core/audio/audio.hpp" #include "core/audio/audio_adapter.hpp" #include "core/defaults.hpp" @@ -22,11 +18,15 @@ #include "core/resources/resource_helper.hpp" #include "core/resources/resource_loader.hpp" #include "core/utils/path_utils.hpp" +#include "debug_overlay.hpp" +#include "game/options.hpp" #include "game/scenes/game_scene.hpp" #include "game/scenes/logo_scene.hpp" #include "game/scenes/title_scene.hpp" -#include "game/options.hpp" +#include "global_events.hpp" #include "project.h" +#include "scene.hpp" +#include "scene_context.hpp" #ifndef _WIN32 #include @@ -106,8 +106,6 @@ Director::Director(std::vector const& args) { std::cout << "Configuración carregada\n"; std::cout << " Finestra: " << Options::window.width << "×" << Options::window.height << '\n'; - std::cout << " Física: rotation=" << Options::physics.rotation_speed - << " rad/s\n"; std::cout << " Input: " << Input::get()->getNumGamepads() << " gamepad(s) detectat(s)\n"; } @@ -291,8 +289,7 @@ auto Director::buildScene(SceneType type, SDLManager& sdl, SceneContext& context } } -void Director::runFrameLoop(Scene& scene, SDLManager& sdl, SceneContext& context, - System::DebugOverlay& debug_overlay) { +void Director::runFrameLoop(Scene& scene, SDLManager& sdl, SceneContext& context, System::DebugOverlay& debug_overlay) { SDL_Event event; Uint64 last_time = SDL_GetTicks(); @@ -315,8 +312,7 @@ void Director::runFrameLoop(Scene& scene, SDLManager& sdl, SceneContext& context if (GlobalEvents::handle(event, sdl, context)) { continue; } - if (event.type == SDL_EVENT_KEY_DOWN - && event.key.scancode == SDL_SCANCODE_F11) { + if (event.type == SDL_EVENT_KEY_DOWN && event.key.scancode == SDL_SCANCODE_F11) { debug_overlay.toggle(); continue; } diff --git a/source/game/options.cpp b/source/game/options.cpp index 9b4cf1b..8a383bd 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -11,629 +11,499 @@ namespace Options { -// ========== FUNCIONS AUXILIARS PER CONVERSIÓ DE CONTROLES ========== + // ========== 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 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 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 como a botó digital - {101, "R2_AS_BUTTON"} // Trigger R2 como a botó digital -}; + // 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 como a botó digital + {101, "R2_AS_BUTTON"} // Trigger R2 como 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}}; + // 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 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 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 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; -} + 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 ========== + // ========== FI FUNCIONS AUXILIARS ========== -// Inicialitzar opciones con valors per defecte de Defaults:: -void init() { + // Inicialitzar opciones con valors per defecte de Defaults:: + void init() { #ifdef _DEBUG - console = true; + console = true; #else - console = false; + console = false; #endif - // Window - window.width = Defaults::Window::WIDTH; - window.height = Defaults::Window::HEIGHT; - window.fullscreen = Defaults::Window::FULLSCREEN; - window.zoom_factor = Defaults::Window::BASE_ZOOM; + // Window + window.width = Defaults::Window::WIDTH; + window.height = Defaults::Window::HEIGHT; + window.fullscreen = Defaults::Window::FULLSCREEN; + 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; + // Rendering + rendering.vsync = Defaults::Rendering::VSYNC_DEFAULT; - // 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::Audio::MUSIC_ENABLED; - audio.music.volume = Defaults::Audio::MUSIC_VOLUME; - audio.sound.enabled = Defaults::Audio::SOUND_ENABLED; - audio.sound.volume = Defaults::Audio::SOUND_VOLUME; - - // Version - version = std::string(Project::VERSION); -} - -// Establir la ruta del file de configuración -void setConfigFile(const std::string& path) { config_file_path = path; } - -// Funciones auxiliars per load seccions del YAML - -// Lee un campo escalar del YAML aplicando un validador; si la clau no -// existe, deja `dest` intacto; si la conversió o la validació fallen, -// asigna `fallback`. Estàtic per quedar dins de la unitat de traducció. -template -static void readField(const fkyaml::node& parent, const char* key, T& dest, - T fallback, Validator&& validate) { - if (!parent.contains(key)) { - return; + // Version + version = std::string(Project::VERSION); } - try { - auto val = parent[key].template get_value(); - dest = validate(val) ? val : fallback; - } catch (...) { - dest = fallback; - } -} -// Variant sin validador: només lectura amb fallback en cas d'error. -template -static void readField(const fkyaml::node& parent, const char* key, T& dest, T fallback) { - if (!parent.contains(key)) { - return; - } - try { - dest = parent[key].template get_value(); - } catch (...) { - dest = fallback; - } -} + // Establir la ruta del file de configuración + void setConfigFile(const std::string& path) { config_file_path = path; } -static void loadWindowConfigFromYaml(const fkyaml::node& yaml) { - if (!yaml.contains("window")) { - return; - } - const auto& win = yaml["window"]; + // Funciones auxiliars per load seccions del YAML - readField(win, "width", window.width, Defaults::Window::WIDTH, - [](int v) { return v >= Defaults::Window::MIN_WIDTH; }); - readField(win, "height", window.height, Defaults::Window::HEIGHT, - [](int v) { return v >= Defaults::Window::MIN_HEIGHT; }); - readField(win, "fullscreen", window.fullscreen, false); - - if (win.contains("zoom_factor")) { - readField(win, "zoom_factor", window.zoom_factor, Defaults::Window::BASE_ZOOM, - [](float v) { return v >= Defaults::Window::MIN_ZOOM && v <= 10.0F; }); - } 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")) { - return; - } - const auto& phys = yaml["physics"]; - constexpr auto POSITIVE = [](float v) { return v > 0.0F; }; - - readField(phys, "rotation_speed", physics.rotation_speed, Defaults::Physics::ROTATION_SPEED, POSITIVE); - readField(phys, "acceleration", physics.acceleration, Defaults::Physics::ACCELERATION, POSITIVE); - readField(phys, "max_velocity", physics.max_velocity, Defaults::Physics::MAX_VELOCITY, POSITIVE); - readField(phys, "friction", physics.friction, Defaults::Physics::FRICTION, POSITIVE); - readField(phys, "enemy_speed", physics.enemy_speed, Defaults::Physics::ENEMY_SPEED, POSITIVE); - readField(phys, "bullet_speed", physics.bullet_speed, Defaults::Physics::BULLET_SPEED, POSITIVE); -} - -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; - } + // Lee un campo escalar del YAML aplicando un validador; si la clau no + // existe, deja `dest` intacto; si la conversió o la validació fallen, + // asigna `fallback`. Estàtic per quedar dins de la unitat de traducció. + template + static void readField(const fkyaml::node& parent, const char* key, T& dest, T fallback, Validator&& validate) { + if (!parent.contains(key)) { + return; } + try { + auto val = parent[key].template get_value(); + dest = validate(val) ? val : fallback; + } catch (...) { + dest = fallback; + } + } - 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; + // Variant sin validador: només lectura amb fallback en cas d'error. + template + static void readField(const fkyaml::node& parent, const char* key, T& dest, T fallback) { + if (!parent.contains(key)) { + return; + } + try { + dest = parent[key].template get_value(); + } catch (...) { + dest = fallback; + } + } + + static void loadWindowConfigFromYaml(const fkyaml::node& yaml) { + if (!yaml.contains("window")) { + return; + } + const auto& win = yaml["window"]; + + readField(win, "width", window.width, Defaults::Window::WIDTH, [](int v) { return v >= Defaults::Window::MIN_WIDTH; }); + readField(win, "height", window.height, Defaults::Window::HEIGHT, [](int v) { return v >= Defaults::Window::MIN_HEIGHT; }); + readField(win, "fullscreen", window.fullscreen, false); + + if (win.contains("zoom_factor")) { + readField(win, "zoom_factor", window.zoom_factor, Defaults::Window::BASE_ZOOM, [](float v) { return v >= Defaults::Window::MIN_ZOOM && v <= 10.0F; }); + } 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 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: solo 0 o 1 + rendering.vsync = (val == 0 || val == 1) ? val : Defaults::Rendering::VSYNC_DEFAULT; + } catch (...) { + rendering.vsync = Defaults::Rendering::VSYNC_DEFAULT; + } } } } -} -static void loadRenderingConfigFromYaml(const fkyaml::node& yaml) { - if (yaml.contains("rendering")) { - const auto& rend = yaml["rendering"]; + // Carregar controls del player 1 desde YAML + static void loadPlayer1ControlsFromYaml(const fkyaml::node& yaml) { + if (!yaml.contains("player1")) { + return; + } - if (rend.contains("vsync")) { - try { - int val = rend["vsync"].get_value(); - // Validar: solo 0 o 1 - rendering.vsync = (val == 0 || val == 1) ? val : Defaults::Rendering::VSYNC_DEFAULT; - } catch (...) { - rendering.vsync = Defaults::Rendering::VSYNC_DEFAULT; + 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()); } } - } -} -static void loadAudioMusicSection(const fkyaml::node& aud) { - if (!aud.contains("music")) { - return; - } - const auto& mus = aud["music"]; - constexpr auto UNIT_RANGE = [](float v) { return v >= 0.0F && v <= 1.0F; }; - - readField(mus, "enabled", audio.music.enabled, Defaults::Audio::MUSIC_ENABLED); - readField(mus, "volume", audio.music.volume, Defaults::Audio::MUSIC_VOLUME, UNIT_RANGE); -} - -static void loadAudioSoundSection(const fkyaml::node& aud) { - if (!aud.contains("sound")) { - return; - } - const auto& snd = aud["sound"]; - constexpr auto UNIT_RANGE = [](float v) { return v >= 0.0F && v <= 1.0F; }; - - readField(snd, "enabled", audio.sound.enabled, Defaults::Audio::SOUND_ENABLED); - readField(snd, "volume", audio.sound.volume, Defaults::Audio::SOUND_VOLUME, UNIT_RANGE); -} - -static void loadAudioConfigFromYaml(const fkyaml::node& yaml) { - if (!yaml.contains("audio")) { - return; - } - const auto& aud = yaml["audio"]; - constexpr auto UNIT_RANGE = [](float v) { return v >= 0.0F && v <= 1.0F; }; - - readField(aud, "enabled", audio.enabled, Defaults::Audio::ENABLED); - readField(aud, "volume", audio.volume, Defaults::Audio::VOLUME, UNIT_RANGE); - loadAudioMusicSection(aud); - loadAudioSoundSection(aud); -} - -// Carregar controls del player 1 desde 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()); + // 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()); + } } - 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 nom del gamepad + if (p1.contains("gamepad_name")) { + player1.gamepad_name = p1["gamepad_name"].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()); + // Carregar controls del player 2 desde YAML + static void loadPlayer2ControlsFromYaml(const fkyaml::node& yaml) { + if (!yaml.contains("player2")) { + return; } - if (gp.contains("button_right")) { - player1.gamepad.button_right = stringToButton(gp["button_right"].get_value()); + + 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()); + } } - if (gp.contains("button_thrust")) { - player1.gamepad.button_thrust = stringToButton(gp["button_thrust"].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()); + } } - if (gp.contains("button_shoot")) { - player1.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 nom del gamepad - if (p1.contains("gamepad_name")) { - player1.gamepad_name = p1["gamepad_name"].get_value(); - } -} + // Carregar configuración des del file YAML + auto loadFromFile() -> bool { + const std::string CONFIG_VERSION = std::string(Project::VERSION); -// Carregar controls del player 2 desde 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ón des del file YAML -auto loadFromFile() -> bool { - const std::string CONFIG_VERSION = std::string(Project::VERSION); - - std::ifstream file(config_file_path); - if (!file.good()) { - // El file no existeix → crear-ne un de nuevo con valors per defecte - if (console) { - std::cout << "Archivo de config no trobat, creant-ne un de nuevo: " - << config_file_path << '\n'; - } - saveToFile(); - return true; - } - - // Llegir todo el contingut del file - std::string content((std::istreambuf_iterator(file)), - std::istreambuf_iterator()); - file.close(); - - try { - // Parsejar YAML - auto yaml = fkyaml::node::deserialize(content); - - // Validar versión - if (yaml.contains("version")) { - version = yaml["version"].get_value(); - } - - if (CONFIG_VERSION != version) { - // Versión incompatible → regenerar config + std::ifstream file(config_file_path); + if (!file.good()) { + // El file no existeix → crear-ne un de nuevo con valors per defecte if (console) { - std::cout << "Versión de config incompatible (esperada: " - << CONFIG_VERSION << ", trobada: " << version - << "), regenerant config\n"; + std::cout << "Archivo de config no trobat, creant-ne un de nuevo: " + << config_file_path << '\n'; + } + saveToFile(); + return true; + } + + // Llegir todo el contingut del file + std::string content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + try { + // Parsejar YAML + auto yaml = fkyaml::node::deserialize(content); + + // Validar versión + if (yaml.contains("version")) { + version = yaml["version"].get_value(); + } + + if (CONFIG_VERSION != version) { + // Versión incompatible → regenerar config + if (console) { + std::cout << "Versión de config incompatible (esperada: " + << CONFIG_VERSION << ", trobada: " << version + << "), regenerant config\n"; + } + init(); + saveToFile(); + return true; + } + + // Carregar seccions + loadWindowConfigFromYaml(yaml); + loadRenderingConfigFromYaml(yaml); + loadPlayer1ControlsFromYaml(yaml); + loadPlayer2ControlsFromYaml(yaml); + + if (console) { + std::cout << "Config carregada correctament desde: " << 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 nuevo con valors per defecte\n"; } init(); saveToFile(); return true; } + } - // Carregar seccions - loadWindowConfigFromYaml(yaml); - loadPhysicsConfigFromYaml(yaml); - loadGameplayConfigFromYaml(yaml); - loadRenderingConfigFromYaml(yaml); - loadAudioConfigFromYaml(yaml); - loadPlayer1ControlsFromYaml(yaml); - loadPlayer2ControlsFromYaml(yaml); + // Guardar controls del player 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 player 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ón al file YAML + auto saveToFile() -> bool { + std::ofstream file(config_file_path); + if (!file.is_open()) { + if (console) { + std::cerr << "No s'ha pogut obrir el file de config per escriure: " + << config_file_path << '\n'; + } + return false; + } + + // Escriure manualment per controlar format i comentaris + file << "# Orni Attack - Archivo de Configuración\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 << "# RENDERITZACIÓ\n"; + file << "rendering:\n"; + file << " vsync: " << rendering.vsync << " # 0=disabled, 1=enabled\n\n"; + + // Guardar controls de jugadors + savePlayer1ControlsToYaml(file); + savePlayer2ControlsToYaml(file); + + file.close(); if (console) { - std::cout << "Config carregada correctament desde: " << config_file_path - << '\n'; + std::cout << "Config guardada a: " << 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 nuevo con valors per defecte\n"; - } - init(); - saveToFile(); - return true; } -} - -// Guardar controls del player 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 player 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ón al file YAML -auto saveToFile() -> bool { - std::ofstream file(config_file_path); - if (!file.is_open()) { - if (console) { - std::cerr << "No s'ha pogut obrir el file de config per escriure: " - << config_file_path << '\n'; - } - return false; - } - - // Escriure manualment per controlar format i comentaris - file << "# Orni Attack - Archivo de Configuración\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 (todos los 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 diff --git a/source/game/options.hpp b/source/game/options.hpp index 4fccd64..e4b0f01 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -6,116 +6,82 @@ namespace Options { -// Estructures de configuración + // Estructures de configuración -struct Window { + struct Window { int width{1280}; int height{720}; bool fullscreen{false}; float zoom_factor{1.0F}; // Zoom level (0.5x to max_zoom) -}; + }; -struct Physics { - float rotation_speed{3.14F}; // rad/s - float acceleration{400.0F}; // px/s² - float max_velocity{120.0F}; // px/s - float friction{20.0F}; // px/s² - float enemy_speed{2.0F}; // unitats/frame - float bullet_speed{6.0F}; // unitats/frame -}; - -struct Gameplay { - int max_enemies{15}; - int max_bullets{3}; -}; - -struct Rendering { + struct Rendering { int vsync{1}; // 0=disabled, 1=enabled -}; + }; -struct Music { - bool enabled{true}; - float volume{0.8F}; -}; + // Controles de jugadors -struct Sound { - bool enabled{true}; - float volume{1.0F}; -}; - -struct Audio { - Music music{}; - Sound sound{}; - bool enabled{true}; - float volume{1.0F}; -}; - -// Controles de jugadors - -struct KeyboardControls { + struct KeyboardControls { SDL_Scancode key_left{SDL_SCANCODE_LEFT}; SDL_Scancode key_right{SDL_SCANCODE_RIGHT}; SDL_Scancode key_thrust{SDL_SCANCODE_UP}; SDL_Scancode key_shoot{SDL_SCANCODE_SPACE}; SDL_Scancode key_start{SDL_SCANCODE_1}; -}; + }; -struct GamepadControls { + struct GamepadControls { int button_left{SDL_GAMEPAD_BUTTON_DPAD_LEFT}; int button_right{SDL_GAMEPAD_BUTTON_DPAD_RIGHT}; int button_thrust{SDL_GAMEPAD_BUTTON_WEST}; // X button int button_shoot{SDL_GAMEPAD_BUTTON_SOUTH}; // A button -}; + }; -struct PlayerControls { + struct PlayerControls { KeyboardControls keyboard{}; GamepadControls gamepad{}; std::string gamepad_name; // Buit = auto-assignar per índex -}; + }; -// Variables globals (inline per evitar ODR violations) + // Variables globals (inline per evitar ODR violations) -inline std::string version{}; // Versión del config per validació -inline bool console{false}; // Eixida de debug -inline Window window{}; -inline Physics physics{}; -inline Gameplay gameplay{}; -inline Rendering rendering{}; -inline Audio audio{}; + inline std::string version{}; // Versión del config per validació + inline bool console{false}; // Eixida de debug + inline Window window{}; + inline Rendering rendering{}; -// Controles per player -inline PlayerControls player1{ - .keyboard = - {.key_left = SDL_SCANCODE_LEFT, - .key_right = SDL_SCANCODE_RIGHT, - .key_thrust = SDL_SCANCODE_UP, - .key_shoot = SDL_SCANCODE_SPACE, - .key_start = SDL_SCANCODE_1}, - .gamepad_name = "" // Primer gamepad disponible -}; + // Controles per player + inline PlayerControls player1{ + .keyboard = + {.key_left = SDL_SCANCODE_LEFT, + .key_right = SDL_SCANCODE_RIGHT, + .key_thrust = SDL_SCANCODE_UP, + .key_shoot = SDL_SCANCODE_SPACE, + .key_start = SDL_SCANCODE_1}, + .gamepad_name = "" // Primer gamepad disponible + }; -inline PlayerControls player2{ - .keyboard = - {.key_left = SDL_SCANCODE_A, - .key_right = SDL_SCANCODE_D, - .key_thrust = SDL_SCANCODE_W, - .key_shoot = SDL_SCANCODE_LSHIFT, - .key_start = SDL_SCANCODE_2}, - .gamepad_name = "" // Segon gamepad disponible -}; + inline PlayerControls player2{ + .keyboard = + {.key_left = SDL_SCANCODE_A, + .key_right = SDL_SCANCODE_D, + .key_thrust = SDL_SCANCODE_W, + .key_shoot = SDL_SCANCODE_LSHIFT, + .key_start = SDL_SCANCODE_2}, + .gamepad_name = "" // Segon gamepad disponible + }; -// Per compatibilitat con pollo (no utilitzat en orni, pero necessari per Input) -inline KeyboardControls keyboard_controls{}; -inline GamepadControls gamepad_controls{}; + // Per compatibilitat con pollo (no utilitzat en orni, pero necessari per Input) + inline KeyboardControls keyboard_controls{}; + inline GamepadControls gamepad_controls{}; -inline std::string config_file_path{}; // Establert per setConfigFile() + inline std::string config_file_path{}; // Establert per setConfigFile() -// Funciones públiques + // Funciones públiques -void init(); // Inicialitzar con valors per defecte -void setConfigFile( - const std::string& path); // Establir ruta del file de config -auto loadFromFile() -> bool; // Carregar config YAML -auto saveToFile() -> bool; // Guardar config YAML + void init(); // Inicialitzar con valors per defecte + void setConfigFile( + const std::string& path); // Establir ruta del file de config + auto loadFromFile() -> bool; // Carregar config YAML + auto saveToFile() -> bool; // Guardar config YAML } // namespace Options