From 20538af4c6e4655dae17e8a36fcc6d530e620717 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 2 Dec 2025 08:50:38 +0100 Subject: [PATCH] LOGO explota --- .clang-format | 21 ++ source/core/rendering/sdl_manager.cpp | 422 ++++++++++----------- source/core/rendering/sdl_manager.hpp | 78 ++-- source/external/.clang-format | 2 + source/game/constants.hpp | 19 +- source/game/effects/debris.hpp | 32 +- source/game/effects/debris_manager.cpp | 427 +++++++++++----------- source/game/effects/debris_manager.hpp | 74 ++-- source/game/entities/bala.cpp | 110 +++--- source/game/entities/bala.hpp | 49 +-- source/game/entities/enemic.cpp | 168 +++++---- source/game/entities/enemic.hpp | 53 +-- source/game/entities/nau.cpp | 201 +++++----- source/game/entities/nau.hpp | 55 +-- source/game/escenes/escena_joc.cpp | 345 ++++++++--------- source/game/escenes/escena_joc.hpp | 68 ++-- source/game/escenes/escena_logo.cpp | 488 ++++++++++++++----------- source/game/escenes/escena_logo.hpp | 109 +++--- source/game/options.cpp | 467 +++++++++++------------ source/game/options.hpp | 40 +- source/legacy/asteroids.cpp | 107 +++--- source/main.cpp | 17 +- 22 files changed, 1754 insertions(+), 1598 deletions(-) create mode 100644 .clang-format create mode 100644 source/external/.clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..58bffd5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,21 @@ +BasedOnStyle: Google +IndentWidth: 4 +IndentAccessModifiers: true +ColumnLimit: 0 # Sin límite de longitud de línea +BreakBeforeBraces: Attach # Llaves en la misma línea +AllowShortIfStatementsOnASingleLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AlignOperands: DontAlign +AlignAfterOpenBracket: DontAlign +BinPackArguments: false +BinPackParameters: false +ContinuationIndentWidth: 4 +ConstructorInitializerIndentWidth: 4 +IndentWrappedFunctionNames: false +Cpp11BracedListStyle: true +BreakConstructorInitializers: BeforeColon +AllowAllConstructorInitializersOnNextLine: false +PackConstructorInitializers: Never +AllowAllArgumentsOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false diff --git a/source/core/rendering/sdl_manager.cpp b/source/core/rendering/sdl_manager.cpp index 1dcd8d9..1e3dada 100644 --- a/source/core/rendering/sdl_manager.cpp +++ b/source/core/rendering/sdl_manager.cpp @@ -2,302 +2,306 @@ // © 2025 Port a C++20 amb SDL3 #include "sdl_manager.hpp" -#include "core/defaults.hpp" -#include "core/rendering/line_renderer.hpp" -#include "game/options.hpp" -#include "project.h" + #include #include #include +#include "core/defaults.hpp" +#include "core/rendering/line_renderer.hpp" +#include "game/options.hpp" +#include "project.h" + SDLManager::SDLManager() - : finestra_(nullptr), renderer_(nullptr), + : finestra_(nullptr), + renderer_(nullptr), current_width_(Defaults::Window::WIDTH), - current_height_(Defaults::Window::HEIGHT), is_fullscreen_(false), - max_width_(1920), max_height_(1080) { - // Inicialitzar SDL3 - if (!SDL_Init(SDL_INIT_VIDEO)) { - std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << std::endl; - return; - } + current_height_(Defaults::Window::HEIGHT), + is_fullscreen_(false), + max_width_(1920), + max_height_(1080) { + // Inicialitzar SDL3 + if (!SDL_Init(SDL_INIT_VIDEO)) { + std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << std::endl; + return; + } - // Calcular mida màxima des del display - calculateMaxWindowSize(); + // Calcular mida màxima des del display + calculateMaxWindowSize(); - // Construir títol dinàmic igual que en pollo - std::string window_title = std::format("{} v{} ({})", Project::LONG_NAME, - Project::VERSION, Project::COPYRIGHT); + // Construir títol dinàmic igual que en pollo + std::string window_title = std::format("{} v{} ({})", Project::LONG_NAME, Project::VERSION, Project::COPYRIGHT); - // Crear finestra CENTRADA (SDL ho fa automàticament amb CENTERED) - finestra_ = - SDL_CreateWindow(window_title.c_str(), current_width_, current_height_, - SDL_WINDOW_RESIZABLE // Permetre resize manual també - ); + // Crear finestra CENTRADA (SDL ho fa automàticament amb CENTERED) + finestra_ = + SDL_CreateWindow(window_title.c_str(), current_width_, current_height_, + SDL_WINDOW_RESIZABLE // Permetre resize manual també + ); - if (!finestra_) { - std::cerr << "Error creant finestra: " << SDL_GetError() << std::endl; - SDL_Quit(); - return; - } + if (!finestra_) { + std::cerr << "Error creant finestra: " << SDL_GetError() << std::endl; + SDL_Quit(); + return; + } - // IMPORTANT: Centrar explícitament la finestra - SDL_SetWindowPosition(finestra_, SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED); + // IMPORTANT: Centrar explícitament la finestra + SDL_SetWindowPosition(finestra_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); - // Crear renderer amb acceleració - renderer_ = SDL_CreateRenderer(finestra_, nullptr); + // Crear renderer amb acceleració + renderer_ = SDL_CreateRenderer(finestra_, nullptr); - if (!renderer_) { - std::cerr << "Error creant renderer: " << SDL_GetError() << std::endl; - SDL_DestroyWindow(finestra_); - SDL_Quit(); - return; - } + if (!renderer_) { + std::cerr << "Error creant renderer: " << SDL_GetError() << std::endl; + SDL_DestroyWindow(finestra_); + SDL_Quit(); + return; + } - // CRÍTIC: Configurar viewport scaling - updateLogicalPresentation(); + // CRÍTIC: Configurar viewport scaling + updateLogicalPresentation(); - std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_ - << " (logic: " << Defaults::Game::WIDTH << "x" - << Defaults::Game::HEIGHT << ")" << std::endl; + std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_ + << " (logic: " << Defaults::Game::WIDTH << "x" + << Defaults::Game::HEIGHT << ")" << std::endl; } // Constructor amb configuració SDLManager::SDLManager(int width, int height, bool fullscreen) - : finestra_(nullptr), renderer_(nullptr), current_width_(width), - current_height_(height), is_fullscreen_(fullscreen), max_width_(1920), + : finestra_(nullptr), + renderer_(nullptr), + current_width_(width), + current_height_(height), + is_fullscreen_(fullscreen), + max_width_(1920), max_height_(1080) { - // Inicialitzar SDL3 - if (!SDL_Init(SDL_INIT_VIDEO)) { - std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << std::endl; - return; - } + // Inicialitzar SDL3 + if (!SDL_Init(SDL_INIT_VIDEO)) { + std::cerr << "Error inicialitzant SDL3: " << SDL_GetError() << std::endl; + return; + } - // Calcular mida màxima des del display - calculateMaxWindowSize(); + // Calcular mida màxima des del display + calculateMaxWindowSize(); - // Construir títol dinàmic - std::string window_title = std::format("{} v{} ({})", Project::LONG_NAME, - Project::VERSION, Project::COPYRIGHT); + // Construir títol dinàmic + std::string window_title = std::format("{} v{} ({})", Project::LONG_NAME, Project::VERSION, Project::COPYRIGHT); - // Configurar flags de la finestra - SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE; - if (is_fullscreen_) { - flags = static_cast(flags | SDL_WINDOW_FULLSCREEN); - } + // Configurar flags de la finestra + SDL_WindowFlags flags = SDL_WINDOW_RESIZABLE; + if (is_fullscreen_) { + flags = static_cast(flags | SDL_WINDOW_FULLSCREEN); + } - // Crear finestra - finestra_ = SDL_CreateWindow(window_title.c_str(), current_width_, - current_height_, flags); + // Crear finestra + finestra_ = SDL_CreateWindow(window_title.c_str(), current_width_, current_height_, flags); - if (!finestra_) { - std::cerr << "Error creant finestra: " << SDL_GetError() << std::endl; - SDL_Quit(); - return; - } + if (!finestra_) { + std::cerr << "Error creant finestra: " << SDL_GetError() << std::endl; + SDL_Quit(); + return; + } - // Centrar explícitament la finestra (si no és fullscreen) - if (!is_fullscreen_) { - SDL_SetWindowPosition(finestra_, SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED); - } + // Centrar explícitament la finestra (si no és fullscreen) + if (!is_fullscreen_) { + SDL_SetWindowPosition(finestra_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + } - // Crear renderer amb acceleració - renderer_ = SDL_CreateRenderer(finestra_, nullptr); + // Crear renderer amb acceleració + renderer_ = SDL_CreateRenderer(finestra_, nullptr); - if (!renderer_) { - std::cerr << "Error creant renderer: " << SDL_GetError() << std::endl; - SDL_DestroyWindow(finestra_); - SDL_Quit(); - return; - } + if (!renderer_) { + std::cerr << "Error creant renderer: " << SDL_GetError() << std::endl; + SDL_DestroyWindow(finestra_); + SDL_Quit(); + return; + } - // Configurar viewport scaling - updateLogicalPresentation(); + // Configurar viewport scaling + updateLogicalPresentation(); - std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_ - << " (logic: " << Defaults::Game::WIDTH << "x" - << Defaults::Game::HEIGHT << ")"; - if (is_fullscreen_) { - std::cout << " [FULLSCREEN]"; - } - std::cout << std::endl; + std::cout << "SDL3 inicialitzat: " << current_width_ << "x" << current_height_ + << " (logic: " << Defaults::Game::WIDTH << "x" + << Defaults::Game::HEIGHT << ")"; + if (is_fullscreen_) { + std::cout << " [FULLSCREEN]"; + } + std::cout << std::endl; } SDLManager::~SDLManager() { - if (renderer_) { - SDL_DestroyRenderer(renderer_); - renderer_ = nullptr; - } + if (renderer_) { + SDL_DestroyRenderer(renderer_); + renderer_ = nullptr; + } - if (finestra_) { - SDL_DestroyWindow(finestra_); - finestra_ = nullptr; - } + if (finestra_) { + SDL_DestroyWindow(finestra_); + finestra_ = nullptr; + } - SDL_Quit(); - std::cout << "SDL3 netejat correctament" << std::endl; + SDL_Quit(); + std::cout << "SDL3 netejat correctament" << std::endl; } void SDLManager::calculateMaxWindowSize() { - SDL_DisplayID display = SDL_GetPrimaryDisplay(); - const SDL_DisplayMode *mode = SDL_GetCurrentDisplayMode(display); + SDL_DisplayID display = SDL_GetPrimaryDisplay(); + const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display); - if (mode) { - // Deixar marge de 100px per a decoracions de l'OS - max_width_ = mode->w - 100; - max_height_ = mode->h - 100; - std::cout << "Display detectat: " << mode->w << "x" << mode->h - << " (max finestra: " << max_width_ << "x" << max_height_ << ")" - << std::endl; - } else { - // Fallback conservador - max_width_ = 1920; - max_height_ = 1080; - std::cerr << "No s'ha pogut detectar el display, usant fallback: " - << max_width_ << "x" << max_height_ << std::endl; - } + if (mode) { + // Deixar marge de 100px per a decoracions de l'OS + max_width_ = mode->w - 100; + max_height_ = mode->h - 100; + std::cout << "Display detectat: " << mode->w << "x" << mode->h + << " (max finestra: " << max_width_ << "x" << max_height_ << ")" + << std::endl; + } else { + // Fallback conservador + max_width_ = 1920; + max_height_ = 1080; + std::cerr << "No s'ha pogut detectar el display, usant fallback: " + << max_width_ << "x" << max_height_ << std::endl; + } } void SDLManager::updateLogicalPresentation() { - // AIXÒ ÉS LA MÀGIA: El joc SEMPRE dibuixa en 640x480, - // SDL escala automàticament a la mida física de la finestra - SDL_SetRenderLogicalPresentation( - renderer_, - Defaults::Game::WIDTH, // 640 (lògic) - Defaults::Game::HEIGHT, // 480 (lògic) - SDL_LOGICAL_PRESENTATION_LETTERBOX // Mantenir aspect ratio 4:3 - ); + // AIXÒ ÉS LA MÀGIA: El joc SEMPRE dibuixa en 640x480, + // SDL escala automàticament a la mida física de la finestra + SDL_SetRenderLogicalPresentation( + renderer_, + Defaults::Game::WIDTH, // 640 (lògic) + Defaults::Game::HEIGHT, // 480 (lògic) + SDL_LOGICAL_PRESENTATION_LETTERBOX // Mantenir aspect ratio 4:3 + ); } void SDLManager::increaseWindowSize() { - if (is_fullscreen_) - return; // No operar en fullscreen + if (is_fullscreen_) + return; // No operar en fullscreen - int new_width = current_width_ + Defaults::Window::SIZE_INCREMENT; - int new_height = current_height_ + Defaults::Window::SIZE_INCREMENT; + int new_width = current_width_ + Defaults::Window::SIZE_INCREMENT; + int new_height = current_height_ + Defaults::Window::SIZE_INCREMENT; - // Clamp a màxim - new_width = std::min(new_width, max_width_); - new_height = std::min(new_height, max_height_); + // Clamp a màxim + new_width = std::min(new_width, max_width_); + new_height = std::min(new_height, max_height_); - if (new_width != current_width_ || new_height != current_height_) { - applyWindowSize(new_width, new_height); + if (new_width != current_width_ || new_height != current_height_) { + applyWindowSize(new_width, new_height); - // Persistir canvis a Options (es guardarà a config.yaml al tancar) - Options::window.width = current_width_; - Options::window.height = current_height_; + // Persistir canvis a Options (es guardarà a config.yaml al tancar) + Options::window.width = current_width_; + Options::window.height = current_height_; - std::cout << "F2: Finestra augmentada a " << new_width << "x" << new_height - << std::endl; - } + std::cout << "F2: Finestra augmentada a " << new_width << "x" << new_height + << std::endl; + } } void SDLManager::decreaseWindowSize() { - if (is_fullscreen_) - return; + if (is_fullscreen_) + return; - int new_width = current_width_ - Defaults::Window::SIZE_INCREMENT; - int new_height = current_height_ - Defaults::Window::SIZE_INCREMENT; + int new_width = current_width_ - Defaults::Window::SIZE_INCREMENT; + int new_height = current_height_ - Defaults::Window::SIZE_INCREMENT; - // Clamp a mínim - new_width = std::max(new_width, Defaults::Window::MIN_WIDTH); - new_height = std::max(new_height, Defaults::Window::MIN_HEIGHT); + // Clamp a mínim + new_width = std::max(new_width, Defaults::Window::MIN_WIDTH); + new_height = std::max(new_height, Defaults::Window::MIN_HEIGHT); - if (new_width != current_width_ || new_height != current_height_) { - applyWindowSize(new_width, new_height); + if (new_width != current_width_ || new_height != current_height_) { + applyWindowSize(new_width, new_height); - // Persistir canvis a Options (es guardarà a config.yaml al tancar) - Options::window.width = current_width_; - Options::window.height = current_height_; + // Persistir canvis a Options (es guardarà a config.yaml al tancar) + Options::window.width = current_width_; + Options::window.height = current_height_; - std::cout << "F1: Finestra reduïda a " << new_width << "x" << new_height - << std::endl; - } + std::cout << "F1: Finestra reduïda a " << new_width << "x" << new_height + << std::endl; + } } void SDLManager::applyWindowSize(int new_width, int new_height) { - // Obtenir posició actual ABANS del resize - int old_x, old_y; - SDL_GetWindowPosition(finestra_, &old_x, &old_y); + // Obtenir posició actual ABANS del resize + int old_x, old_y; + SDL_GetWindowPosition(finestra_, &old_x, &old_y); - int old_width = current_width_; - int old_height = current_height_; + int old_width = current_width_; + int old_height = current_height_; - // Actualitzar mida - SDL_SetWindowSize(finestra_, new_width, new_height); - current_width_ = new_width; - current_height_ = new_height; + // Actualitzar mida + SDL_SetWindowSize(finestra_, new_width, new_height); + current_width_ = new_width; + current_height_ = new_height; - // CENTRADO INTEL·LIGENT (algoritme de pollo) - // Calcular nova posició per mantenir la finestra centrada sobre si mateixa - int delta_width = old_width - new_width; - int delta_height = old_height - new_height; + // CENTRADO INTEL·LIGENT (algoritme de pollo) + // Calcular nova posició per mantenir la finestra centrada sobre si mateixa + int delta_width = old_width - new_width; + int delta_height = old_height - new_height; - int new_x = old_x + (delta_width / 2); - int new_y = old_y + (delta_height / 2); + int new_x = old_x + (delta_width / 2); + int new_y = old_y + (delta_height / 2); - // Evitar que la finestra surti de la pantalla - constexpr int TITLEBAR_HEIGHT = 35; // Alçada aproximada de la barra de títol - new_x = std::max(new_x, 0); - new_y = std::max(new_y, TITLEBAR_HEIGHT); + // Evitar que la finestra surti de la pantalla + constexpr int TITLEBAR_HEIGHT = 35; // Alçada aproximada de la barra de títol + new_x = std::max(new_x, 0); + new_y = std::max(new_y, TITLEBAR_HEIGHT); - SDL_SetWindowPosition(finestra_, new_x, new_y); + SDL_SetWindowPosition(finestra_, new_x, new_y); - // NO cal actualitzar el logical presentation aquí, - // SDL ho maneja automàticament + // NO cal actualitzar el logical presentation aquí, + // SDL ho maneja automàticament } void SDLManager::toggleFullscreen() { - is_fullscreen_ = !is_fullscreen_; - SDL_SetWindowFullscreen(finestra_, is_fullscreen_); + is_fullscreen_ = !is_fullscreen_; + SDL_SetWindowFullscreen(finestra_, is_fullscreen_); - // Persistir canvis a Options (es guardarà a config.yaml al tancar) - Options::window.fullscreen = is_fullscreen_; + // Persistir canvis a Options (es guardarà a config.yaml al tancar) + Options::window.fullscreen = is_fullscreen_; - std::cout << "F3: Fullscreen " << (is_fullscreen_ ? "activat" : "desactivat") - << std::endl; + std::cout << "F3: Fullscreen " << (is_fullscreen_ ? "activat" : "desactivat") + << std::endl; - // En fullscreen, SDL gestiona tot automàticament - // En sortir, restaura la mida anterior + // En fullscreen, SDL gestiona tot automàticament + // En sortir, restaura la mida anterior } -bool SDLManager::handleWindowEvent(const SDL_Event &event) { - if (event.type == SDL_EVENT_WINDOW_RESIZED) { - // Usuari ha redimensionat manualment (arrossegar vora) - // Actualitzar el nostre tracking - SDL_GetWindowSize(finestra_, ¤t_width_, ¤t_height_); - std::cout << "Finestra redimensionada manualment a " << current_width_ - << "x" << current_height_ << std::endl; - return true; - } - return false; +bool SDLManager::handleWindowEvent(const SDL_Event& event) { + if (event.type == SDL_EVENT_WINDOW_RESIZED) { + // Usuari ha redimensionat manualment (arrossegar vora) + // Actualitzar el nostre tracking + SDL_GetWindowSize(finestra_, ¤t_width_, ¤t_height_); + std::cout << "Finestra redimensionada manualment a " << current_width_ + << "x" << current_height_ << std::endl; + return true; + } + return false; } void SDLManager::neteja(uint8_t r, uint8_t g, uint8_t b) { - if (!renderer_) - return; + if (!renderer_) + return; - // [MODIFICAT] Usar color oscil·lat del fons en lloc dels paràmetres - (void)r; - (void)g; - (void)b; // Suprimir warnings - SDL_Color bg = color_oscillator_.getCurrentBackgroundColor(); - SDL_SetRenderDrawColor(renderer_, bg.r, bg.g, bg.b, 255); - SDL_RenderClear(renderer_); + // [MODIFICAT] Usar color oscil·lat del fons en lloc dels paràmetres + (void)r; + (void)g; + (void)b; // Suprimir warnings + SDL_Color bg = color_oscillator_.getCurrentBackgroundColor(); + SDL_SetRenderDrawColor(renderer_, bg.r, bg.g, bg.b, 255); + SDL_RenderClear(renderer_); } void SDLManager::presenta() { - if (!renderer_) - return; + if (!renderer_) + return; - SDL_RenderPresent(renderer_); + SDL_RenderPresent(renderer_); } // [NUEVO] Actualitzar colors amb oscil·lació void SDLManager::updateColors(float delta_time) { - color_oscillator_.update(delta_time); + color_oscillator_.update(delta_time); - // Actualitzar color global de línies - Rendering::setLineColor(color_oscillator_.getCurrentLineColor()); + // Actualitzar color global de línies + Rendering::setLineColor(color_oscillator_.getCurrentLineColor()); } diff --git a/source/core/rendering/sdl_manager.hpp b/source/core/rendering/sdl_manager.hpp index 0af3ac6..2a0ecb7 100644 --- a/source/core/rendering/sdl_manager.hpp +++ b/source/core/rendering/sdl_manager.hpp @@ -4,56 +4,58 @@ #ifndef SDL_MANAGER_HPP #define SDL_MANAGER_HPP -#include "core/rendering/color_oscillator.hpp" #include + #include +#include "core/rendering/color_oscillator.hpp" + class SDLManager { -public: - SDLManager(); // Constructor per defecte (usa Defaults::) - SDLManager(int width, int height, - bool fullscreen); // Constructor amb configuració - ~SDLManager(); + public: + SDLManager(); // Constructor per defecte (usa Defaults::) + SDLManager(int width, int height, + bool fullscreen); // Constructor amb configuració + ~SDLManager(); - // No permetre còpia ni assignació - SDLManager(const SDLManager &) = delete; - SDLManager &operator=(const SDLManager &) = delete; + // No permetre còpia ni assignació + SDLManager(const SDLManager&) = delete; + SDLManager& operator=(const SDLManager&) = delete; - // [NUEVO] Gestió de finestra dinàmica - void increaseWindowSize(); // F2: +100px - void decreaseWindowSize(); // F1: -100px - void toggleFullscreen(); // F3 - bool - handleWindowEvent(const SDL_Event &event); // Per a SDL_EVENT_WINDOW_RESIZED + // [NUEVO] Gestió de finestra dinàmica + void increaseWindowSize(); // F2: +100px + void decreaseWindowSize(); // F1: -100px + void toggleFullscreen(); // F3 + bool + handleWindowEvent(const SDL_Event& event); // Per a SDL_EVENT_WINDOW_RESIZED - // Funcions principals (renderitzat) - void neteja(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0); - void presenta(); + // Funcions principals (renderitzat) + void neteja(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0); + void presenta(); - // [NUEVO] Actualització de colors (oscil·lació) - void updateColors(float delta_time); + // [NUEVO] Actualització de colors (oscil·lació) + void updateColors(float delta_time); - // Getters - SDL_Renderer *obte_renderer() { return renderer_; } + // Getters + SDL_Renderer* obte_renderer() { return renderer_; } -private: - SDL_Window *finestra_; - SDL_Renderer *renderer_; + private: + SDL_Window* finestra_; + SDL_Renderer* renderer_; - // [NUEVO] Estat de la finestra - int current_width_; // Mida física actual - int current_height_; - bool is_fullscreen_; - int max_width_; // Calculat des del display - int max_height_; + // [NUEVO] Estat de la finestra + int current_width_; // Mida física actual + int current_height_; + bool is_fullscreen_; + int max_width_; // Calculat des del display + int max_height_; - // [NUEVO] Funcions internes - void calculateMaxWindowSize(); // Llegir resolució del display - void applyWindowSize(int width, int height); // Canviar mida + centrar - void updateLogicalPresentation(); // Actualitzar viewport + // [NUEVO] Funcions internes + void calculateMaxWindowSize(); // Llegir resolució del display + void applyWindowSize(int width, int height); // Canviar mida + centrar + void updateLogicalPresentation(); // Actualitzar viewport - // [NUEVO] Oscil·lador de colors - Rendering::ColorOscillator color_oscillator_; + // [NUEVO] Oscil·lador de colors + Rendering::ColorOscillator color_oscillator_; }; -#endif // SDL_MANAGER_HPP +#endif // SDL_MANAGER_HPP diff --git a/source/external/.clang-format b/source/external/.clang-format new file mode 100644 index 0000000..47a38a9 --- /dev/null +++ b/source/external/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/source/game/constants.hpp b/source/game/constants.hpp index c8fed3a..3aeba03 100644 --- a/source/game/constants.hpp +++ b/source/game/constants.hpp @@ -28,16 +28,15 @@ constexpr float PI = Defaults::Math::PI; // Helpers per comprovar límits de zona inline bool dins_zona_joc(float x, float y) { - const SDL_FPoint punt = {x, y}; - return SDL_PointInRectFloat(&punt, &Defaults::Zones::PLAYAREA); + const SDL_FPoint punt = {x, y}; + return SDL_PointInRectFloat(&punt, &Defaults::Zones::PLAYAREA); } -inline void obtenir_limits_zona(float &min_x, float &max_x, float &min_y, - float &max_y) { - const auto &zona = Defaults::Zones::PLAYAREA; - min_x = zona.x; - max_x = zona.x + zona.w; - min_y = zona.y; - max_y = zona.y + zona.h; +inline void obtenir_limits_zona(float& min_x, float& max_x, float& min_y, float& max_y) { + const auto& zona = Defaults::Zones::PLAYAREA; + min_x = zona.x; + max_x = zona.x + zona.w; + min_y = zona.y; + max_y = zona.y + zona.h; } -} // namespace Constants +} // namespace Constants diff --git a/source/game/effects/debris.hpp b/source/game/effects/debris.hpp index 99ab204..a6b73a0 100644 --- a/source/game/effects/debris.hpp +++ b/source/game/effects/debris.hpp @@ -9,25 +9,25 @@ namespace Effects { // Debris: un segment de línia que vola perpendicular a sí mateix // Representa un fragment d'una forma destruïda (nau, enemic, bala) struct Debris { - // Geometria del segment (2 punts en coordenades mundials) - Punt p1; // Punt inicial del segment - Punt p2; // Punt final del segment + // Geometria del segment (2 punts en coordenades mundials) + Punt p1; // Punt inicial del segment + Punt p2; // Punt final del segment - // Física - Punt velocitat; // Velocitat en px/s (components x, y) - float acceleracio; // Acceleració negativa (fricció) en px/s² + // Física + Punt velocitat; // Velocitat en px/s (components x, y) + float acceleracio; // Acceleració negativa (fricció) en px/s² - // Rotació - float angle_rotacio; // Angle de rotació acumulat (radians) - float velocitat_rot; // Velocitat de rotació en rad/s + // Rotació + float angle_rotacio; // Angle de rotació acumulat (radians) + float velocitat_rot; // Velocitat de rotació en rad/s - // Estat de vida - float temps_vida; // Temps transcorregut (segons) - float temps_max; // Temps de vida màxim (segons) - bool actiu; // Està actiu? + // Estat de vida + float temps_vida; // Temps transcorregut (segons) + float temps_max; // Temps de vida màxim (segons) + bool actiu; // Està actiu? - // Shrinking (reducció de distància entre punts) - float factor_shrink; // Factor de reducció per segon (0.0-1.0) + // Shrinking (reducció de distància entre punts) + float factor_shrink; // Factor de reducció per segon (0.0-1.0) }; -} // namespace Effects +} // namespace Effects diff --git a/source/game/effects/debris_manager.cpp b/source/game/effects/debris_manager.cpp index a61e0de..382afaf 100644 --- a/source/game/effects/debris_manager.cpp +++ b/source/game/effects/debris_manager.cpp @@ -2,274 +2,275 @@ // © 2025 Port a C++20 amb SDL3 #include "debris_manager.hpp" -#include "core/defaults.hpp" -#include "core/rendering/line_renderer.hpp" + #include #include #include +#include "core/defaults.hpp" +#include "core/rendering/line_renderer.hpp" + namespace Effects { // Helper: transformar punt amb rotació, escala i trasllació // (Copiat de shape_renderer.cpp:12-34) -static Punt transform_point(const Punt &point, const Punt &shape_centre, - const Punt &posicio, float angle, float escala) { - // 1. Centrar el punt respecte al centre de la forma - float centered_x = point.x - shape_centre.x; - float centered_y = point.y - shape_centre.y; +static Punt transform_point(const Punt& point, const Punt& shape_centre, const Punt& posicio, float angle, float escala) { + // 1. Centrar el punt respecte al centre de la forma + float centered_x = point.x - shape_centre.x; + float centered_y = point.y - shape_centre.y; - // 2. Aplicar escala al punt centrat - float scaled_x = centered_x * escala; - float scaled_y = centered_y * escala; + // 2. Aplicar escala al punt centrat + float scaled_x = centered_x * escala; + float scaled_y = centered_y * escala; - // 3. Aplicar rotació - float cos_a = std::cos(angle); - float sin_a = std::sin(angle); + // 3. Aplicar rotació + float cos_a = std::cos(angle); + float sin_a = std::sin(angle); - float rotated_x = scaled_x * cos_a - scaled_y * sin_a; - float rotated_y = scaled_x * sin_a + scaled_y * cos_a; + float rotated_x = scaled_x * cos_a - scaled_y * sin_a; + float rotated_y = scaled_x * sin_a + scaled_y * cos_a; - // 4. Aplicar trasllació a posició mundial - return {rotated_x + posicio.x, rotated_y + posicio.y}; + // 4. Aplicar trasllació a posició mundial + return {rotated_x + posicio.x, rotated_y + posicio.y}; } -DebrisManager::DebrisManager(SDL_Renderer *renderer) : renderer_(renderer) { - // Inicialitzar tots els debris com inactius - for (auto &debris : debris_pool_) { - debris.actiu = false; - } +DebrisManager::DebrisManager(SDL_Renderer* renderer) + : renderer_(renderer) { + // Inicialitzar tots els debris com inactius + for (auto& debris : debris_pool_) { + debris.actiu = false; + } } -void DebrisManager::explotar(const std::shared_ptr &shape, - const Punt ¢re, float angle, float escala, - float velocitat_base) { - if (!shape || !shape->es_valida()) { - return; - } - - // Obtenir centre de la forma per a transformacions - const Punt &shape_centre = shape->get_centre(); - - // Iterar sobre totes les primitives de la forma - for (const auto &primitive : shape->get_primitives()) { - // Processar cada segment de línia - std::vector> segments; - - if (primitive.type == Graphics::PrimitiveType::POLYLINE) { - // Polyline: extreure segments consecutius - for (size_t i = 0; i < primitive.points.size() - 1; i++) { - segments.push_back({primitive.points[i], primitive.points[i + 1]}); - } - } else { // PrimitiveType::LINE - // Line: un únic segment - if (primitive.points.size() >= 2) { - segments.push_back({primitive.points[0], primitive.points[1]}); - } +void DebrisManager::explotar(const std::shared_ptr& shape, + const Punt& centre, + float angle, + float escala, + float velocitat_base) { + if (!shape || !shape->es_valida()) { + return; } - // Crear debris per a cada segment - for (const auto &[local_p1, local_p2] : segments) { - // 1. Transformar punts locals → coordenades mundials - Punt world_p1 = - transform_point(local_p1, shape_centre, centre, angle, escala); - Punt world_p2 = - transform_point(local_p2, shape_centre, centre, angle, escala); + // Obtenir centre de la forma per a transformacions + const Punt& shape_centre = shape->get_centre(); - // 2. Trobar slot lliure - Debris *debris = trobar_slot_lliure(); - if (!debris) { - std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n"; - return; // Pool ple - } + // Iterar sobre totes les primitives de la forma + for (const auto& primitive : shape->get_primitives()) { + // Processar cada segment de línia + std::vector> segments; - // 3. Inicialitzar geometria - debris->p1 = world_p1; - debris->p2 = world_p2; + if (primitive.type == Graphics::PrimitiveType::POLYLINE) { + // Polyline: extreure segments consecutius + for (size_t i = 0; i < primitive.points.size() - 1; i++) { + segments.push_back({primitive.points[i], primitive.points[i + 1]}); + } + } else { // PrimitiveType::LINE + // Line: un únic segment + if (primitive.points.size() >= 2) { + segments.push_back({primitive.points[0], primitive.points[1]}); + } + } - // 4. Calcular direcció perpendicular - Punt direccio = calcular_direccio_perpendicular(world_p1, world_p2); + // Crear debris per a cada segment + for (const auto& [local_p1, local_p2] : segments) { + // 1. Transformar punts locals → coordenades mundials + Punt world_p1 = + transform_point(local_p1, shape_centre, centre, angle, escala); + Punt world_p2 = + transform_point(local_p2, shape_centre, centre, angle, escala); - // 5. Velocitat inicial (base ± variació aleatòria) - float speed = - velocitat_base + - ((std::rand() / static_cast(RAND_MAX)) * 2.0f - 1.0f) * - Defaults::Physics::Debris::VARIACIO_VELOCITAT; + // 2. Trobar slot lliure + Debris* debris = trobar_slot_lliure(); + if (!debris) { + std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n"; + return; // Pool ple + } - debris->velocitat.x = direccio.x * speed; - debris->velocitat.y = direccio.y * speed; - debris->acceleracio = Defaults::Physics::Debris::ACCELERACIO; + // 3. Inicialitzar geometria + debris->p1 = world_p1; + debris->p2 = world_p2; - // 6. Rotació lenta aleatòria - debris->velocitat_rot = - Defaults::Physics::Debris::ROTACIO_MIN + - (std::rand() / static_cast(RAND_MAX)) * - (Defaults::Physics::Debris::ROTACIO_MAX - - Defaults::Physics::Debris::ROTACIO_MIN); + // 4. Calcular direcció perpendicular + Punt direccio = calcular_direccio_perpendicular(world_p1, world_p2); - // 50% probabilitat de rotació en sentit contrari - if (std::rand() % 2 == 0) { - debris->velocitat_rot = -debris->velocitat_rot; - } + // 5. Velocitat inicial (base ± variació aleatòria) + float speed = + velocitat_base + + ((std::rand() / static_cast(RAND_MAX)) * 2.0f - 1.0f) * + Defaults::Physics::Debris::VARIACIO_VELOCITAT; - debris->angle_rotacio = 0.0f; + debris->velocitat.x = direccio.x * speed; + debris->velocitat.y = direccio.y * speed; + debris->acceleracio = Defaults::Physics::Debris::ACCELERACIO; - // 7. Configurar vida i shrinking - debris->temps_vida = 0.0f; - debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA; - debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE; + // 6. Rotació lenta aleatòria + debris->velocitat_rot = + Defaults::Physics::Debris::ROTACIO_MIN + + (std::rand() / static_cast(RAND_MAX)) * + (Defaults::Physics::Debris::ROTACIO_MAX - + Defaults::Physics::Debris::ROTACIO_MIN); - // 8. Activar - debris->actiu = true; + // 50% probabilitat de rotació en sentit contrari + if (std::rand() % 2 == 0) { + debris->velocitat_rot = -debris->velocitat_rot; + } + + debris->angle_rotacio = 0.0f; + + // 7. Configurar vida i shrinking + debris->temps_vida = 0.0f; + debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA; + debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE; + + // 8. Activar + debris->actiu = true; + } } - } } void DebrisManager::actualitzar(float delta_time) { - for (auto &debris : debris_pool_) { - if (!debris.actiu) - continue; + for (auto& debris : debris_pool_) { + if (!debris.actiu) + continue; - // 1. Actualitzar temps de vida - debris.temps_vida += delta_time; + // 1. Actualitzar temps de vida + debris.temps_vida += delta_time; - // Desactivar si ha superat temps màxim - if (debris.temps_vida >= debris.temps_max) { - debris.actiu = false; - continue; + // Desactivar si ha superat temps màxim + if (debris.temps_vida >= debris.temps_max) { + debris.actiu = false; + continue; + } + + // 2. Actualitzar velocitat (desacceleració) + // Aplicar fricció en la direcció del moviment + float speed = std::sqrt(debris.velocitat.x * debris.velocitat.x + + debris.velocitat.y * debris.velocitat.y); + + if (speed > 1.0f) { + // Calcular direcció normalitzada + float dir_x = debris.velocitat.x / speed; + float dir_y = debris.velocitat.y / speed; + + // Aplicar acceleració negativa (fricció) + float nova_speed = speed + debris.acceleracio * delta_time; + if (nova_speed < 0.0f) + nova_speed = 0.0f; + + debris.velocitat.x = dir_x * nova_speed; + debris.velocitat.y = dir_y * nova_speed; + } else { + // Velocitat molt baixa, aturar + debris.velocitat.x = 0.0f; + debris.velocitat.y = 0.0f; + } + + // 3. Calcular centre del segment + Punt centre = {(debris.p1.x + debris.p2.x) / 2.0f, + (debris.p1.y + debris.p2.y) / 2.0f}; + + // 4. Actualitzar posició del centre + centre.x += debris.velocitat.x * delta_time; + centre.y += debris.velocitat.y * delta_time; + + // 5. Actualitzar rotació + debris.angle_rotacio += debris.velocitat_rot * delta_time; + + // 6. Aplicar shrinking (reducció de distància entre punts) + float shrink_factor = + 1.0f - (debris.factor_shrink * debris.temps_vida / debris.temps_max); + shrink_factor = std::max(0.0f, shrink_factor); // No negatiu + + // Calcular distància original entre punts + float dx = debris.p2.x - debris.p1.x; + float dy = debris.p2.y - debris.p1.y; + + // 7. Reconstruir segment amb nova mida i rotació + float half_length = std::sqrt(dx * dx + dy * dy) * shrink_factor / 2.0f; + float original_angle = std::atan2(dy, dx); + float new_angle = original_angle + debris.angle_rotacio; + + debris.p1.x = centre.x - half_length * std::cos(new_angle); + debris.p1.y = centre.y - half_length * std::sin(new_angle); + debris.p2.x = centre.x + half_length * std::cos(new_angle); + debris.p2.y = centre.y + half_length * std::sin(new_angle); } - - // 2. Actualitzar velocitat (desacceleració) - // Aplicar fricció en la direcció del moviment - float speed = std::sqrt(debris.velocitat.x * debris.velocitat.x + - debris.velocitat.y * debris.velocitat.y); - - if (speed > 1.0f) { - // Calcular direcció normalitzada - float dir_x = debris.velocitat.x / speed; - float dir_y = debris.velocitat.y / speed; - - // Aplicar acceleració negativa (fricció) - float nova_speed = speed + debris.acceleracio * delta_time; - if (nova_speed < 0.0f) - nova_speed = 0.0f; - - debris.velocitat.x = dir_x * nova_speed; - debris.velocitat.y = dir_y * nova_speed; - } else { - // Velocitat molt baixa, aturar - debris.velocitat.x = 0.0f; - debris.velocitat.y = 0.0f; - } - - // 3. Calcular centre del segment - Punt centre = {(debris.p1.x + debris.p2.x) / 2.0f, - (debris.p1.y + debris.p2.y) / 2.0f}; - - // 4. Actualitzar posició del centre - centre.x += debris.velocitat.x * delta_time; - centre.y += debris.velocitat.y * delta_time; - - // 5. Actualitzar rotació - debris.angle_rotacio += debris.velocitat_rot * delta_time; - - // 6. Aplicar shrinking (reducció de distància entre punts) - float shrink_factor = - 1.0f - (debris.factor_shrink * debris.temps_vida / debris.temps_max); - shrink_factor = std::max(0.0f, shrink_factor); // No negatiu - - // Calcular distància original entre punts - float dx = debris.p2.x - debris.p1.x; - float dy = debris.p2.y - debris.p1.y; - - // 7. Reconstruir segment amb nova mida i rotació - float half_length = std::sqrt(dx * dx + dy * dy) * shrink_factor / 2.0f; - float original_angle = std::atan2(dy, dx); - float new_angle = original_angle + debris.angle_rotacio; - - debris.p1.x = centre.x - half_length * std::cos(new_angle); - debris.p1.y = centre.y - half_length * std::sin(new_angle); - debris.p2.x = centre.x + half_length * std::cos(new_angle); - debris.p2.y = centre.y + half_length * std::sin(new_angle); - } } void DebrisManager::dibuixar() const { - for (const auto &debris : debris_pool_) { - if (!debris.actiu) - continue; + for (const auto& debris : debris_pool_) { + if (!debris.actiu) + continue; - // Dibuixar segment de línia - Rendering::linea(renderer_, static_cast(debris.p1.x), - static_cast(debris.p1.y), - static_cast(debris.p2.x), - static_cast(debris.p2.y), true); - } -} - -Debris *DebrisManager::trobar_slot_lliure() { - for (auto &debris : debris_pool_) { - if (!debris.actiu) { - return &debris; + // Dibuixar segment de línia + Rendering::linea(renderer_, static_cast(debris.p1.x), static_cast(debris.p1.y), static_cast(debris.p2.x), static_cast(debris.p2.y), true); } - } - return nullptr; // Pool ple } -Punt DebrisManager::calcular_direccio_perpendicular(const Punt &p1, - const Punt &p2) const { - // 1. Calcular vector de la línia (p1 → p2) - float dx = p2.x - p1.x; - float dy = p2.y - p1.y; +Debris* DebrisManager::trobar_slot_lliure() { + for (auto& debris : debris_pool_) { + if (!debris.actiu) { + return &debris; + } + } + return nullptr; // Pool ple +} - // 2. Normalitzar (obtenir vector unitari) - float length = std::sqrt(dx * dx + dy * dy); - if (length < 0.001f) { - // Línia degenerada, retornar direcció aleatòria - float angle_rand = - (std::rand() / static_cast(RAND_MAX)) * 2.0f * Defaults::Math::PI; - return {std::cos(angle_rand), std::sin(angle_rand)}; - } +Punt DebrisManager::calcular_direccio_perpendicular(const Punt& p1, + const Punt& p2) const { + // 1. Calcular vector de la línia (p1 → p2) + float dx = p2.x - p1.x; + float dy = p2.y - p1.y; - dx /= length; - dy /= length; + // 2. Normalitzar (obtenir vector unitari) + float length = std::sqrt(dx * dx + dy * dy); + if (length < 0.001f) { + // Línia degenerada, retornar direcció aleatòria + float angle_rand = + (std::rand() / static_cast(RAND_MAX)) * 2.0f * Defaults::Math::PI; + return {std::cos(angle_rand), std::sin(angle_rand)}; + } - // 3. Rotar 90° (perpendicular) - // Rotació 90° sentit antihorari: (x,y) → (-y, x) - float perp_x = -dy; - float perp_y = dx; + dx /= length; + dy /= length; - // 4. Afegir variació aleatòria petita (±15°) - float angle_variacio = - ((std::rand() % 30) - 15) * Defaults::Math::PI / 180.0f; + // 3. Rotar 90° (perpendicular) + // Rotació 90° sentit antihorari: (x,y) → (-y, x) + float perp_x = -dy; + float perp_y = dx; - float cos_v = std::cos(angle_variacio); - float sin_v = std::sin(angle_variacio); + // 4. Afegir variació aleatòria petita (±15°) + float angle_variacio = + ((std::rand() % 30) - 15) * Defaults::Math::PI / 180.0f; - float final_x = perp_x * cos_v - perp_y * sin_v; - float final_y = perp_x * sin_v + perp_y * cos_v; + float cos_v = std::cos(angle_variacio); + float sin_v = std::sin(angle_variacio); - // 5. Afegir ± direcció aleatòria (50% probabilitat d'invertir) - if (std::rand() % 2 == 0) { - final_x = -final_x; - final_y = -final_y; - } + float final_x = perp_x * cos_v - perp_y * sin_v; + float final_y = perp_x * sin_v + perp_y * cos_v; - return {final_x, final_y}; + // 5. Afegir ± direcció aleatòria (50% probabilitat d'invertir) + if (std::rand() % 2 == 0) { + final_x = -final_x; + final_y = -final_y; + } + + return {final_x, final_y}; } void DebrisManager::reiniciar() { - for (auto &debris : debris_pool_) { - debris.actiu = false; - } + for (auto& debris : debris_pool_) { + debris.actiu = false; + } } int DebrisManager::get_num_actius() const { - int count = 0; - for (const auto &debris : debris_pool_) { - if (debris.actiu) - count++; - } - return count; + int count = 0; + for (const auto& debris : debris_pool_) { + if (debris.actiu) + count++; + } + return count; } -} // namespace Effects +} // namespace Effects diff --git a/source/game/effects/debris_manager.hpp b/source/game/effects/debris_manager.hpp index fc91357..22790fc 100644 --- a/source/game/effects/debris_manager.hpp +++ b/source/game/effects/debris_manager.hpp @@ -2,58 +2,62 @@ // © 2025 Port a C++20 amb SDL3 #pragma once -#include "debris.hpp" -#include "core/graphics/shape.hpp" -#include "core/types.hpp" #include + #include #include +#include "core/graphics/shape.hpp" +#include "core/types.hpp" +#include "debris.hpp" + namespace Effects { // Gestor de fragments d'explosions // Manté un pool d'objectes Debris i gestiona el seu cicle de vida class DebrisManager { -public: - explicit DebrisManager(SDL_Renderer *renderer); + public: + explicit DebrisManager(SDL_Renderer* renderer); - // Crear explosió a partir d'una forma - // - shape: forma vectorial a explotar - // - centre: posició del centre de l'objecte - // - angle: orientació de l'objecte (radians) - // - escala: escala de l'objecte (1.0 = normal) - // - velocitat_base: velocitat inicial dels fragments (px/s) - void explotar(const std::shared_ptr &shape, - const Punt ¢re, float angle, float escala, - float velocitat_base); + // Crear explosió a partir d'una forma + // - shape: forma vectorial a explotar + // - centre: posició del centre de l'objecte + // - angle: orientació de l'objecte (radians) + // - escala: escala de l'objecte (1.0 = normal) + // - velocitat_base: velocitat inicial dels fragments (px/s) + void explotar(const std::shared_ptr& shape, + const Punt& centre, + float angle, + float escala, + float velocitat_base); - // Actualitzar tots els fragments actius - void actualitzar(float delta_time); + // Actualitzar tots els fragments actius + void actualitzar(float delta_time); - // Dibuixar tots els fragments actius - void dibuixar() const; + // Dibuixar tots els fragments actius + void dibuixar() const; - // Reiniciar tots els fragments (neteja) - void reiniciar(); + // Reiniciar tots els fragments (neteja) + void reiniciar(); - // Obtenir número de fragments actius - int get_num_actius() const; + // Obtenir número de fragments actius + int get_num_actius() const; -private: - SDL_Renderer *renderer_; + private: + SDL_Renderer* renderer_; - // Pool de fragments (màxim concurrent) - // Un pentàgon té 5 línies, 15 enemics = 75 línies - // + nau (3 línies) + bales (5 línies * 3) = 93 línies màxim - // Arrodonit a 100 per seguretat - static constexpr int MAX_DEBRIS = 100; - std::array debris_pool_; + // Pool de fragments (màxim concurrent) + // Un pentàgon té 5 línies, 15 enemics = 75 línies + // + nau (3 línies) + bales (5 línies * 3) = 93 línies màxim + // Arrodonit a 100 per seguretat + static constexpr int MAX_DEBRIS = 100; + std::array debris_pool_; - // Trobar primer slot inactiu - Debris *trobar_slot_lliure(); + // Trobar primer slot inactiu + Debris* trobar_slot_lliure(); - // Calcular direcció perpendicular a un segment - Punt calcular_direccio_perpendicular(const Punt &p1, const Punt &p2) const; + // Calcular direcció perpendicular a un segment + Punt calcular_direccio_perpendicular(const Punt& p1, const Punt& p2) const; }; -} // namespace Effects +} // namespace Effects diff --git a/source/game/entities/bala.cpp b/source/game/entities/bala.cpp index a6a5070..37885fa 100644 --- a/source/game/entities/bala.cpp +++ b/source/game/entities/bala.cpp @@ -3,84 +3,88 @@ // © 2025 Port a C++20 amb SDL3 #include "game/entities/bala.hpp" -#include "core/graphics/shape_loader.hpp" -#include "core/rendering/shape_renderer.hpp" -#include "game/constants.hpp" + #include #include -Bala::Bala(SDL_Renderer *renderer) - : renderer_(renderer), centre_({0.0f, 0.0f}), angle_(0.0f), - velocitat_(0.0f), esta_(false) { +#include "core/graphics/shape_loader.hpp" +#include "core/rendering/shape_renderer.hpp" +#include "game/constants.hpp" - // [NUEVO] Carregar forma compartida des de fitxer - forma_ = Graphics::ShapeLoader::load("bullet.shp"); +Bala::Bala(SDL_Renderer* renderer) + : renderer_(renderer), + centre_({0.0f, 0.0f}), + angle_(0.0f), + velocitat_(0.0f), + esta_(false) { + // [NUEVO] Carregar forma compartida des de fitxer + forma_ = Graphics::ShapeLoader::load("bullet.shp"); - if (!forma_ || !forma_->es_valida()) { - std::cerr << "[Bala] Error: no s'ha pogut carregar bullet.shp" << std::endl; - } + if (!forma_ || !forma_->es_valida()) { + std::cerr << "[Bala] Error: no s'ha pogut carregar bullet.shp" << std::endl; + } } void Bala::inicialitzar() { - // Inicialment inactiva - esta_ = false; - centre_ = {0.0f, 0.0f}; - angle_ = 0.0f; - velocitat_ = 0.0f; + // Inicialment inactiva + esta_ = false; + centre_ = {0.0f, 0.0f}; + angle_ = 0.0f; + velocitat_ = 0.0f; } -void Bala::disparar(const Punt &posicio, float angle) { - // Activar bala i posicionar-la a la nau - // Basat en joc_asteroides.cpp línies 188-200 +void Bala::disparar(const Punt& posicio, float angle) { + // Activar bala i posicionar-la a la nau + // Basat en joc_asteroides.cpp línies 188-200 - // Activar bala - esta_ = true; + // Activar bala + esta_ = true; - // Posició inicial = centre de la nau - centre_.x = posicio.x; - centre_.y = posicio.y; + // Posició inicial = centre de la nau + centre_.x = posicio.x; + centre_.y = posicio.y; - // Angle = angle de la nau (dispara en la direcció que apunta) - angle_ = angle; + // Angle = angle de la nau (dispara en la direcció que apunta) + angle_ = angle; - // Velocitat alta (el joc Pascal original usava 7 px/frame) - // 7 px/frame × 20 FPS = 140 px/s - velocitat_ = 140.0f; + // Velocitat alta (el joc Pascal original usava 7 px/frame) + // 7 px/frame × 20 FPS = 140 px/s + velocitat_ = 140.0f; } void Bala::actualitzar(float delta_time) { - if (esta_) { - mou(delta_time); - } + if (esta_) { + mou(delta_time); + } } void Bala::dibuixar() const { - if (esta_ && forma_) { - // [NUEVO] Usar render_shape en lloc de rota_pol - // Les bales no roten visualment (angle sempre 0.0f) - Rendering::render_shape(renderer_, forma_, centre_, 0.0f, 1.0f, true); - } + if (esta_ && forma_) { + // [NUEVO] Usar render_shape en lloc de rota_pol + // Les bales no roten visualment (angle sempre 0.0f) + Rendering::render_shape(renderer_, forma_, centre_, 0.0f, 1.0f, true); + } } void Bala::mou(float delta_time) { - // Moviment rectilini de la bala - // Basat en el codi Pascal original: procedure mou_bales - // Copiat EXACTAMENT de joc_asteroides.cpp línies 396-419 + // Moviment rectilini de la bala + // Basat en el codi Pascal original: procedure mou_bales + // Copiat EXACTAMENT de joc_asteroides.cpp línies 396-419 - // Calcular nova posició (moviment polar time-based) - // velocitat ja està en px/s (140 px/s), només cal multiplicar per delta_time - float velocitat_efectiva = velocitat_ * delta_time; + // Calcular nova posició (moviment polar time-based) + // velocitat ja està en px/s (140 px/s), només cal multiplicar per delta_time + float velocitat_efectiva = velocitat_ * delta_time; - // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt) - float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f); - float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f); + // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt) + float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f); + float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f); - // Acumulació directa amb precisió subpíxel - centre_.y += dy; - centre_.x += dx; + // Acumulació directa amb precisió subpíxel + centre_.y += dy; + centre_.x += dx; - // Desactivar si surt de la zona de joc (no rebota com els ORNIs) - if (!Constants::dins_zona_joc(centre_.x, centre_.y)) { - esta_ = false; - } + // Desactivar si surt de la zona de joc (no rebota com els ORNIs) + if (!Constants::dins_zona_joc(centre_.x, centre_.y)) { + esta_ = false; + } } diff --git a/source/game/entities/bala.hpp b/source/game/entities/bala.hpp index deb1b4b..dd4b005 100644 --- a/source/game/entities/bala.hpp +++ b/source/game/entities/bala.hpp @@ -3,37 +3,40 @@ // © 2025 Port a C++20 amb SDL3 #pragma once -#include "core/graphics/shape.hpp" -#include "core/types.hpp" #include + #include +#include "core/graphics/shape.hpp" +#include "core/types.hpp" + class Bala { -public: - Bala() : renderer_(nullptr) {} - Bala(SDL_Renderer *renderer); + public: + Bala() + : renderer_(nullptr) {} + Bala(SDL_Renderer* renderer); - void inicialitzar(); - void disparar(const Punt &posicio, float angle); - void actualitzar(float delta_time); - void dibuixar() const; + void inicialitzar(); + void disparar(const Punt& posicio, float angle); + void actualitzar(float delta_time); + void dibuixar() const; - // Getters (API pública sense canvis) - bool esta_activa() const { return esta_; } - const Punt &get_centre() const { return centre_; } - void desactivar() { esta_ = false; } + // Getters (API pública sense canvis) + bool esta_activa() const { return esta_; } + const Punt& get_centre() const { return centre_; } + void desactivar() { esta_ = false; } -private: - SDL_Renderer *renderer_; + private: + SDL_Renderer* renderer_; - // [NUEVO] Forma vectorial (compartida entre totes les bales) - std::shared_ptr forma_; + // [NUEVO] Forma vectorial (compartida entre totes les bales) + std::shared_ptr forma_; - // [NUEVO] Estat de la instància (separat de la geometria) - Punt centre_; - float angle_; - float velocitat_; - bool esta_; + // [NUEVO] Estat de la instància (separat de la geometria) + Punt centre_; + float angle_; + float velocitat_; + bool esta_; - void mou(float delta_time); + void mou(float delta_time); }; diff --git a/source/game/entities/enemic.cpp b/source/game/entities/enemic.cpp index 297ffa1..0e11852 100644 --- a/source/game/entities/enemic.cpp +++ b/source/game/entities/enemic.cpp @@ -3,117 +3,123 @@ // © 2025 Port a C++20 amb SDL3 #include "game/entities/enemic.hpp" -#include "core/graphics/shape_loader.hpp" -#include "core/rendering/shape_renderer.hpp" -#include "game/constants.hpp" + #include #include #include -Enemic::Enemic(SDL_Renderer *renderer) - : renderer_(renderer), centre_({0.0f, 0.0f}), angle_(0.0f), - velocitat_(0.0f), drotacio_(0.0f), rotacio_(0.0f), esta_(false) { +#include "core/graphics/shape_loader.hpp" +#include "core/rendering/shape_renderer.hpp" +#include "game/constants.hpp" - // [NUEVO] Carregar forma compartida des de fitxer - forma_ = Graphics::ShapeLoader::load("enemy_pentagon.shp"); +Enemic::Enemic(SDL_Renderer* renderer) + : renderer_(renderer), + centre_({0.0f, 0.0f}), + angle_(0.0f), + velocitat_(0.0f), + drotacio_(0.0f), + rotacio_(0.0f), + esta_(false) { + // [NUEVO] Carregar forma compartida des de fitxer + forma_ = Graphics::ShapeLoader::load("enemy_pentagon.shp"); - if (!forma_ || !forma_->es_valida()) { - std::cerr << "[Enemic] Error: no s'ha pogut carregar enemy_pentagon.shp" - << std::endl; - } + if (!forma_ || !forma_->es_valida()) { + std::cerr << "[Enemic] Error: no s'ha pogut carregar enemy_pentagon.shp" + << std::endl; + } } void Enemic::inicialitzar() { - // Inicialitzar enemic (pentàgon) - // Copiat de joc_asteroides.cpp línies 41-54 + // Inicialitzar enemic (pentàgon) + // Copiat de joc_asteroides.cpp línies 41-54 - // [NUEVO] Ja no cal crear_poligon_regular - la geometria es carrega del - // fitxer Només inicialitzem l'estat de la instància + // [NUEVO] Ja no cal crear_poligon_regular - la geometria es carrega del + // fitxer Només inicialitzem l'estat de la instància - // Posició aleatòria dins de l'àrea de joc - centre_.x = static_cast((std::rand() % 580) + 30); // 30-610 - centre_.y = static_cast((std::rand() % 420) + 30); // 30-450 + // Posició aleatòria dins de l'àrea de joc + centre_.x = static_cast((std::rand() % 580) + 30); // 30-610 + centre_.y = static_cast((std::rand() % 420) + 30); // 30-450 - // Angle aleatori de moviment - angle_ = (std::rand() % 360) * Constants::PI / 180.0f; + // Angle aleatori de moviment + angle_ = (std::rand() % 360) * Constants::PI / 180.0f; - // Velocitat (2 px/frame original * 20 FPS = 40 px/s) - velocitat_ = 40.0f; + // Velocitat (2 px/frame original * 20 FPS = 40 px/s) + velocitat_ = 40.0f; - // Rotació visual aleatòria (rad/s) - // Original Pascal: random * 0.1 rad/frame * 20 FPS ≈ 2 rad/s - drotacio_ = (static_cast(std::rand()) / RAND_MAX) * 2.0f; - rotacio_ = 0.0f; + // Rotació visual aleatòria (rad/s) + // Original Pascal: random * 0.1 rad/frame * 20 FPS ≈ 2 rad/s + drotacio_ = (static_cast(std::rand()) / RAND_MAX) * 2.0f; + rotacio_ = 0.0f; - // Activar - esta_ = true; + // Activar + esta_ = true; } void Enemic::actualitzar(float delta_time) { - if (esta_) { - // Moviment autònom - mou(delta_time); + if (esta_) { + // Moviment autònom + mou(delta_time); - // Rotació visual (time-based: drotacio_ està en rad/s) - rotacio_ += drotacio_ * delta_time; - } + // Rotació visual (time-based: drotacio_ està en rad/s) + rotacio_ += drotacio_ * delta_time; + } } void Enemic::dibuixar() const { - if (esta_ && forma_) { - // [NUEVO] Usar render_shape en lloc de rota_pol - Rendering::render_shape(renderer_, forma_, centre_, rotacio_, 1.0f, true); - } + if (esta_ && forma_) { + // [NUEVO] Usar render_shape en lloc de rota_pol + Rendering::render_shape(renderer_, forma_, centre_, rotacio_, 1.0f, true); + } } void Enemic::mou(float delta_time) { - // Moviment autònom d'ORNI (enemic pentàgon) - // Basat EXACTAMENT en el codi Pascal original: ASTEROID.PAS lines 279-293 - // Copiat EXACTAMENT de joc_asteroides.cpp línies 348-394 - // - // IMPORTANT: El Pascal original NO té canvi aleatori continu! - // Només ajusta l'angle quan toca una paret. + // Moviment autònom d'ORNI (enemic pentàgon) + // Basat EXACTAMENT en el codi Pascal original: ASTEROID.PAS lines 279-293 + // Copiat EXACTAMENT de joc_asteroides.cpp línies 348-394 + // + // IMPORTANT: El Pascal original NO té canvi aleatori continu! + // Només ajusta l'angle quan toca una paret. - // Calcular nova posició PROPUESTA (time-based, però lògica Pascal) - // velocitat_ ja està en px/s (40 px/s), multiplicar per delta_time - float velocitat_efectiva = velocitat_ * delta_time; + // Calcular nova posició PROPUESTA (time-based, però lògica Pascal) + // velocitat_ ja està en px/s (40 px/s), multiplicar per delta_time + float velocitat_efectiva = velocitat_ * delta_time; - // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt) - float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f); - float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f); + // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt) + float dy = velocitat_efectiva * std::sin(angle_ - Constants::PI / 2.0f); + float dx = velocitat_efectiva * std::cos(angle_ - Constants::PI / 2.0f); - float new_y = centre_.y + dy; - float new_x = centre_.x + dx; + float new_y = centre_.y + dy; + float new_x = centre_.x + dx; - // Obtenir límits de la zona de joc - float min_x, max_x, min_y, max_y; - Constants::obtenir_limits_zona(min_x, max_x, min_y, max_y); + // Obtenir límits de la zona de joc + float min_x, max_x, min_y, max_y; + Constants::obtenir_limits_zona(min_x, max_x, min_y, max_y); - // Lògica Pascal: Actualitza Y si dins, sinó ajusta angle aleatòriament - // if (dy>marge_dalt) and (dy min_y && new_y < max_y) { - centre_.y = new_y; - } else { - // Pequeño ajuste aleatorio: (random(256)/512)*(random(3)-1) - // random(256) = 0..255, /512 = 0..0.498 - // random(3) = 0,1,2, -1 = -1,0,1 - // Resultado: ±0.5 rad aprox - float rand1 = (static_cast(std::rand() % 256) / 512.0f); - int rand2 = (std::rand() % 3) - 1; // -1, 0, o 1 - angle_ += rand1 * static_cast(rand2); - } + // Lògica Pascal: Actualitza Y si dins, sinó ajusta angle aleatòriament + // if (dy>marge_dalt) and (dy min_y && new_y < max_y) { + centre_.y = new_y; + } else { + // Pequeño ajuste aleatorio: (random(256)/512)*(random(3)-1) + // random(256) = 0..255, /512 = 0..0.498 + // random(3) = 0,1,2, -1 = -1,0,1 + // Resultado: ±0.5 rad aprox + float rand1 = (static_cast(std::rand() % 256) / 512.0f); + int rand2 = (std::rand() % 3) - 1; // -1, 0, o 1 + angle_ += rand1 * static_cast(rand2); + } - // Lògica Pascal: Actualitza X si dins, sinó ajusta angle aleatòriament - // if (dx>marge_esq) and (dx min_x && new_x < max_x) { - centre_.x = new_x; - } else { - float rand1 = (static_cast(std::rand() % 256) / 512.0f); - int rand2 = (std::rand() % 3) - 1; - angle_ += rand1 * static_cast(rand2); - } + // Lògica Pascal: Actualitza X si dins, sinó ajusta angle aleatòriament + // if (dx>marge_esq) and (dx min_x && new_x < max_x) { + centre_.x = new_x; + } else { + float rand1 = (static_cast(std::rand() % 256) / 512.0f); + int rand2 = (std::rand() % 3) - 1; + angle_ += rand1 * static_cast(rand2); + } - // Nota: La rotació visual (rotacio_ += drotacio_) ja es fa a actualitzar() + // Nota: La rotació visual (rotacio_ += drotacio_) ja es fa a actualitzar() } diff --git a/source/game/entities/enemic.hpp b/source/game/entities/enemic.hpp index c1d8316..b7feb86 100644 --- a/source/game/entities/enemic.hpp +++ b/source/game/entities/enemic.hpp @@ -3,39 +3,42 @@ // © 2025 Port a C++20 amb SDL3 #pragma once -#include "core/graphics/shape.hpp" -#include "core/types.hpp" #include + #include +#include "core/graphics/shape.hpp" +#include "core/types.hpp" + class Enemic { -public: - Enemic() : renderer_(nullptr) {} - Enemic(SDL_Renderer *renderer); + public: + Enemic() + : renderer_(nullptr) {} + Enemic(SDL_Renderer* renderer); - void inicialitzar(); - void actualitzar(float delta_time); - void dibuixar() const; + void inicialitzar(); + void actualitzar(float delta_time); + void dibuixar() const; - // Getters (API pública sense canvis) - bool esta_actiu() const { return esta_; } - const Punt &get_centre() const { return centre_; } - const std::shared_ptr &get_forma() const { return forma_; } - void destruir() { esta_ = false; } + // Getters (API pública sense canvis) + bool esta_actiu() const { return esta_; } + const Punt& get_centre() const { return centre_; } + const std::shared_ptr& get_forma() const { return forma_; } + void destruir() { esta_ = false; } -private: - SDL_Renderer *renderer_; + private: + SDL_Renderer* renderer_; - // [NUEVO] Forma vectorial (compartida entre tots els enemics) - std::shared_ptr forma_; + // [NUEVO] Forma vectorial (compartida entre tots els enemics) + std::shared_ptr forma_; - // [NUEVO] Estat de la instància (separat de la geometria) - Punt centre_; - float angle_; // Angle de moviment - float velocitat_; - float drotacio_; // Delta rotació visual (rad/s) - float rotacio_; // Rotació visual acumulada - bool esta_; + // [NUEVO] Estat de la instància (separat de la geometria) + Punt centre_; + float angle_; // Angle de moviment + float velocitat_; + float drotacio_; // Delta rotació visual (rad/s) + float rotacio_; // Rotació visual acumulada + bool esta_; - void mou(float delta_time); + void mou(float delta_time); }; diff --git a/source/game/entities/nau.cpp b/source/game/entities/nau.cpp index d5d7404..82096a1 100644 --- a/source/game/entities/nau.cpp +++ b/source/game/entities/nau.cpp @@ -3,137 +3,142 @@ // © 2025 Port a C++20 amb SDL3 #include "game/entities/nau.hpp" + +#include + +#include +#include + #include "core/defaults.hpp" #include "core/graphics/shape_loader.hpp" #include "core/rendering/shape_renderer.hpp" #include "game/constants.hpp" -#include -#include -#include -Nau::Nau(SDL_Renderer *renderer) - : renderer_(renderer), centre_({0.0f, 0.0f}), angle_(0.0f), - velocitat_(0.0f), esta_tocada_(false) { +Nau::Nau(SDL_Renderer* renderer) + : renderer_(renderer), + centre_({0.0f, 0.0f}), + angle_(0.0f), + velocitat_(0.0f), + esta_tocada_(false) { + // [NUEVO] Carregar forma compartida des de fitxer + forma_ = Graphics::ShapeLoader::load("ship.shp"); - // [NUEVO] Carregar forma compartida des de fitxer - forma_ = Graphics::ShapeLoader::load("ship.shp"); - - if (!forma_ || !forma_->es_valida()) { - std::cerr << "[Nau] Error: no s'ha pogut carregar ship.shp" << std::endl; - } + if (!forma_ || !forma_->es_valida()) { + std::cerr << "[Nau] Error: no s'ha pogut carregar ship.shp" << std::endl; + } } void Nau::inicialitzar() { - // Inicialització de la nau (triangle) - // Basat en el codi Pascal original: lines 380-384 - // Copiat de joc_asteroides.cpp línies 30-44 + // Inicialització de la nau (triangle) + // Basat en el codi Pascal original: lines 380-384 + // Copiat de joc_asteroides.cpp línies 30-44 - // [NUEVO] Ja no cal configurar punts polars - la geometria es carrega del - // fitxer Només inicialitzem l'estat de la instància + // [NUEVO] Ja no cal configurar punts polars - la geometria es carrega del + // fitxer Només inicialitzem l'estat de la instància - // Posició inicial al centre de la pantalla - centre_.x = 320.0f; - centre_.y = 240.0f; + // Posició inicial al centre de la pantalla + centre_.x = 320.0f; + centre_.y = 240.0f; - // Estat inicial - angle_ = 0.0f; - velocitat_ = 0.0f; - esta_tocada_ = false; + // Estat inicial + angle_ = 0.0f; + velocitat_ = 0.0f; + esta_tocada_ = false; } void Nau::processar_input(float delta_time) { - // Processar input continu (com teclapuls() del Pascal original) - // Basat en joc_asteroides.cpp línies 66-85 - // Només processa input si la nau està viva - if (esta_tocada_) - return; + // Processar input continu (com teclapuls() del Pascal original) + // Basat en joc_asteroides.cpp línies 66-85 + // Només processa input si la nau està viva + if (esta_tocada_) + return; - // Obtenir estat actual del teclat (no events, sinó estat continu) - const bool *keyboard_state = SDL_GetKeyboardState(nullptr); + // Obtenir estat actual del teclat (no events, sinó estat continu) + const bool* keyboard_state = SDL_GetKeyboardState(nullptr); - // Rotació - if (keyboard_state[SDL_SCANCODE_RIGHT]) { - angle_ += Defaults::Physics::ROTATION_SPEED * delta_time; - } - - if (keyboard_state[SDL_SCANCODE_LEFT]) { - angle_ -= Defaults::Physics::ROTATION_SPEED * delta_time; - } - - // Acceleració - if (keyboard_state[SDL_SCANCODE_UP]) { - if (velocitat_ < Defaults::Physics::MAX_VELOCITY) { - velocitat_ += Defaults::Physics::ACCELERATION * delta_time; - if (velocitat_ > Defaults::Physics::MAX_VELOCITY) { - velocitat_ = Defaults::Physics::MAX_VELOCITY; - } + // Rotació + if (keyboard_state[SDL_SCANCODE_RIGHT]) { + angle_ += Defaults::Physics::ROTATION_SPEED * delta_time; + } + + if (keyboard_state[SDL_SCANCODE_LEFT]) { + angle_ -= Defaults::Physics::ROTATION_SPEED * delta_time; + } + + // Acceleració + if (keyboard_state[SDL_SCANCODE_UP]) { + if (velocitat_ < Defaults::Physics::MAX_VELOCITY) { + velocitat_ += Defaults::Physics::ACCELERATION * delta_time; + if (velocitat_ > Defaults::Physics::MAX_VELOCITY) { + velocitat_ = Defaults::Physics::MAX_VELOCITY; + } + } } - } } void Nau::actualitzar(float delta_time) { - // Només actualitzar si la nau està viva - if (esta_tocada_) - return; + // Només actualitzar si la nau està viva + if (esta_tocada_) + return; - // Aplicar física (moviment + fricció) - aplicar_fisica(delta_time); + // Aplicar física (moviment + fricció) + aplicar_fisica(delta_time); } void Nau::dibuixar() const { - // Només dibuixar si la nau està viva - if (esta_tocada_) - return; + // Només dibuixar si la nau està viva + if (esta_tocada_) + return; - if (!forma_) - return; + if (!forma_) + return; - // Escalar velocitat per l'efecte visual (200 px/s → ~6 px d'efecte) - // El codi Pascal original sumava velocitat (0-6) al radi per donar - // sensació de "empenta". Ara velocitat està en px/s (0-200). - // Basat en joc_asteroides.cpp línies 127-134 - // - // [NUEVO] Convertir suma de velocitat_visual a escala multiplicativa - // Radio base del ship = 12 px - // velocitat_visual = 0-6 → r = 12-18 → escala = 1.0-1.5 - float velocitat_visual = velocitat_ / 33.33f; - float escala = 1.0f + (velocitat_visual / 12.0f); + // Escalar velocitat per l'efecte visual (200 px/s → ~6 px d'efecte) + // El codi Pascal original sumava velocitat (0-6) al radi per donar + // sensació de "empenta". Ara velocitat està en px/s (0-200). + // Basat en joc_asteroides.cpp línies 127-134 + // + // [NUEVO] Convertir suma de velocitat_visual a escala multiplicativa + // Radio base del ship = 12 px + // velocitat_visual = 0-6 → r = 12-18 → escala = 1.0-1.5 + float velocitat_visual = velocitat_ / 33.33f; + float escala = 1.0f + (velocitat_visual / 12.0f); - Rendering::render_shape(renderer_, forma_, centre_, angle_, escala, true); + Rendering::render_shape(renderer_, forma_, centre_, angle_, escala, true); } void Nau::aplicar_fisica(float delta_time) { - // Aplicar física de moviment - // Basat en joc_asteroides.cpp línies 87-113 + // Aplicar física de moviment + // Basat en joc_asteroides.cpp línies 87-113 - // Calcular nova posició basada en velocitat i angle - // S'usa (angle - PI/2) perquè angle=0 apunta cap amunt, no cap a la dreta - // velocitat_ està en px/s, així que multipliquem per delta_time - float dy = - (velocitat_ * delta_time) * std::sin(angle_ - Constants::PI / 2.0f) + - centre_.y; - float dx = - (velocitat_ * delta_time) * std::cos(angle_ - Constants::PI / 2.0f) + - centre_.x; + // Calcular nova posició basada en velocitat i angle + // S'usa (angle - PI/2) perquè angle=0 apunta cap amunt, no cap a la dreta + // velocitat_ està en px/s, així que multipliquem per delta_time + float dy = + (velocitat_ * delta_time) * std::sin(angle_ - Constants::PI / 2.0f) + + centre_.y; + float dx = + (velocitat_ * delta_time) * std::cos(angle_ - Constants::PI / 2.0f) + + centre_.x; - // Boundary checking - només actualitzar si dins de la zona de joc - // Acumulació directa amb precisió subpíxel - float min_x, max_x, min_y, max_y; - Constants::obtenir_limits_zona(min_x, max_x, min_y, max_y); + // Boundary checking - només actualitzar si dins de la zona de joc + // Acumulació directa amb precisió subpíxel + float min_x, max_x, min_y, max_y; + Constants::obtenir_limits_zona(min_x, max_x, min_y, max_y); - if (dy > min_y && dy < max_y) { - centre_.y = dy; - } - - if (dx > min_x && dx < max_x) { - centre_.x = dx; - } - - // Fricció - desacceleració gradual (time-based) - if (velocitat_ > 0.1f) { - velocitat_ -= Defaults::Physics::FRICTION * delta_time; - if (velocitat_ < 0.0f) { - velocitat_ = 0.0f; + if (dy > min_y && dy < max_y) { + centre_.y = dy; + } + + if (dx > min_x && dx < max_x) { + centre_.x = dx; + } + + // Fricció - desacceleració gradual (time-based) + if (velocitat_ > 0.1f) { + velocitat_ -= Defaults::Physics::FRICTION * delta_time; + if (velocitat_ < 0.0f) { + velocitat_ = 0.0f; + } } - } } diff --git a/source/game/entities/nau.hpp b/source/game/entities/nau.hpp index 9dc431d..26e19dd 100644 --- a/source/game/entities/nau.hpp +++ b/source/game/entities/nau.hpp @@ -3,41 +3,44 @@ // © 2025 Port a C++20 amb SDL3 #pragma once -#include "core/graphics/shape.hpp" -#include "core/types.hpp" #include + #include +#include "core/graphics/shape.hpp" +#include "core/types.hpp" + class Nau { -public: - Nau() : renderer_(nullptr) {} - Nau(SDL_Renderer *renderer); + public: + Nau() + : renderer_(nullptr) {} + Nau(SDL_Renderer* renderer); - void inicialitzar(); - void processar_input(float delta_time); - void actualitzar(float delta_time); - void dibuixar() const; + void inicialitzar(); + void processar_input(float delta_time); + void actualitzar(float delta_time); + void dibuixar() const; - // Getters (API pública sense canvis) - const Punt &get_centre() const { return centre_; } - float get_angle() const { return angle_; } - bool esta_viva() const { return !esta_tocada_; } + // Getters (API pública sense canvis) + const Punt& get_centre() const { return centre_; } + float get_angle() const { return angle_; } + bool esta_viva() const { return !esta_tocada_; } - // Col·lisions (Fase 10) - void marcar_tocada() { esta_tocada_ = true; } + // Col·lisions (Fase 10) + void marcar_tocada() { esta_tocada_ = true; } -private: - SDL_Renderer *renderer_; + private: + SDL_Renderer* renderer_; - // [NUEVO] Forma vectorial (compartida, només 1 instància de Nau però preparat - // per reutilització) - std::shared_ptr forma_; + // [NUEVO] Forma vectorial (compartida, només 1 instància de Nau però preparat + // per reutilització) + std::shared_ptr forma_; - // [NUEVO] Estat de la instància (separat de la geometria) - Punt centre_; - float angle_; // Angle d'orientació - float velocitat_; // Velocitat (px/s) - bool esta_tocada_; + // [NUEVO] Estat de la instància (separat de la geometria) + Punt centre_; + float angle_; // Angle d'orientació + float velocitat_; // Velocitat (px/s) + bool esta_tocada_; - void aplicar_fisica(float delta_time); + void aplicar_fisica(float delta_time); }; diff --git a/source/game/escenes/escena_joc.cpp b/source/game/escenes/escena_joc.cpp index cc55206..59adc8a 100644 --- a/source/game/escenes/escena_joc.cpp +++ b/source/game/escenes/escena_joc.cpp @@ -3,233 +3,238 @@ // © 2025 Port a C++20 amb SDL3 #include "escena_joc.hpp" -#include "../../core/rendering/line_renderer.hpp" -#include "../../core/system/gestor_escenes.hpp" -#include "../../core/system/global_events.hpp" + #include #include #include #include #include -EscenaJoc::EscenaJoc(SDLManager &sdl) - : sdl_(sdl), debris_manager_(sdl.obte_renderer()), - nau_(sdl.obte_renderer()), itocado_(0), text_(sdl.obte_renderer()) { - // Inicialitzar bales amb renderer - for (auto &bala : bales_) { - bala = Bala(sdl.obte_renderer()); - } +#include "../../core/rendering/line_renderer.hpp" +#include "../../core/system/gestor_escenes.hpp" +#include "../../core/system/global_events.hpp" - // Inicialitzar enemics amb renderer - for (auto &enemy : orni_) { - enemy = Enemic(sdl.obte_renderer()); - } +EscenaJoc::EscenaJoc(SDLManager& sdl) + : sdl_(sdl), + debris_manager_(sdl.obte_renderer()), + nau_(sdl.obte_renderer()), + itocado_(0), + text_(sdl.obte_renderer()) { + // Inicialitzar bales amb renderer + for (auto& bala : bales_) { + bala = Bala(sdl.obte_renderer()); + } + + // Inicialitzar enemics amb renderer + for (auto& enemy : orni_) { + enemy = Enemic(sdl.obte_renderer()); + } } void EscenaJoc::executar() { - std::cout << "Escena Joc: Inicialitzant...\n"; + std::cout << "Escena Joc: Inicialitzant...\n"; - // Inicialitzar estat del joc - inicialitzar(); + // Inicialitzar estat del joc + inicialitzar(); - SDL_Event event; - Uint64 last_time = SDL_GetTicks(); + SDL_Event event; + Uint64 last_time = SDL_GetTicks(); - while (GestorEscenes::actual == GestorEscenes::Escena::JOC) { - // Calcular delta_time real - Uint64 current_time = SDL_GetTicks(); - float delta_time = (current_time - last_time) / 1000.0f; - last_time = current_time; + while (GestorEscenes::actual == GestorEscenes::Escena::JOC) { + // Calcular delta_time real + Uint64 current_time = SDL_GetTicks(); + float delta_time = (current_time - last_time) / 1000.0f; + last_time = current_time; - // Limitar delta_time per evitar grans salts - if (delta_time > 0.05f) { - delta_time = 0.05f; + // Limitar delta_time per evitar grans salts + if (delta_time > 0.05f) { + delta_time = 0.05f; + } + + // Processar events SDL + while (SDL_PollEvent(&event)) { + // Manejo de finestra + if (sdl_.handleWindowEvent(event)) { + continue; + } + + // Events globals (F1/F2/F3/ESC/QUIT) + if (GlobalEvents::handle(event, sdl_)) { + continue; + } + + // Processament específic del joc (SPACE per disparar) + processar_input(event); + } + + // Actualitzar física del joc amb delta_time real + actualitzar(delta_time); + + // Actualitzar colors oscil·lats + sdl_.updateColors(delta_time); + + // Netejar pantalla (usa color oscil·lat) + sdl_.neteja(0, 0, 0); + + // Dibuixar joc + dibuixar(); + + // Presentar renderer (swap buffers) + sdl_.presenta(); } - // Processar events SDL - while (SDL_PollEvent(&event)) { - // Manejo de finestra - if (sdl_.handleWindowEvent(event)) { - continue; - } - - // Events globals (F1/F2/F3/ESC/QUIT) - if (GlobalEvents::handle(event, sdl_)) { - continue; - } - - // Processament específic del joc (SPACE per disparar) - processar_input(event); - } - - // Actualitzar física del joc amb delta_time real - actualitzar(delta_time); - - // Actualitzar colors oscil·lats - sdl_.updateColors(delta_time); - - // Netejar pantalla (usa color oscil·lat) - sdl_.neteja(0, 0, 0); - - // Dibuixar joc - dibuixar(); - - // Presentar renderer (swap buffers) - sdl_.presenta(); - } - - std::cout << "Escena Joc: Finalitzant...\n"; + std::cout << "Escena Joc: Finalitzant...\n"; } void EscenaJoc::inicialitzar() { - // Inicialitzar generador de números aleatoris - // Basat en el codi Pascal original: line 376 - std::srand(static_cast(std::time(nullptr))); + // Inicialitzar generador de números aleatoris + // Basat en el codi Pascal original: line 376 + std::srand(static_cast(std::time(nullptr))); - // Inicialitzar estat de col·lisió - itocado_ = 0; + // Inicialitzar estat de col·lisió + itocado_ = 0; - // Inicialitzar nau - nau_.inicialitzar(); + // Inicialitzar nau + nau_.inicialitzar(); - // Inicialitzar enemics (ORNIs) - for (auto &enemy : orni_) { - enemy.inicialitzar(); - } + // Inicialitzar enemics (ORNIs) + for (auto& enemy : orni_) { + enemy.inicialitzar(); + } - // Inicialitzar bales - for (auto &bala : bales_) { - bala.inicialitzar(); - } + // Inicialitzar bales + for (auto& bala : bales_) { + bala.inicialitzar(); + } } void EscenaJoc::actualitzar(float delta_time) { - // Actualitzar nau (input + física) - nau_.processar_input(delta_time); - nau_.actualitzar(delta_time); + // Actualitzar nau (input + física) + nau_.processar_input(delta_time); + nau_.actualitzar(delta_time); - // Actualitzar moviment i rotació dels enemics (ORNIs) - for (auto &enemy : orni_) { - enemy.actualitzar(delta_time); - } + // Actualitzar moviment i rotació dels enemics (ORNIs) + for (auto& enemy : orni_) { + enemy.actualitzar(delta_time); + } - // Actualitzar moviment de bales (Fase 9) - for (auto &bala : bales_) { - bala.actualitzar(delta_time); - } + // Actualitzar moviment de bales (Fase 9) + for (auto& bala : bales_) { + bala.actualitzar(delta_time); + } - // Actualitzar fragments d'explosions - debris_manager_.actualitzar(delta_time); + // Actualitzar fragments d'explosions + debris_manager_.actualitzar(delta_time); } void EscenaJoc::dibuixar() { - // Dibuixar marges de la zona de joc - dibuixar_marges(); + // Dibuixar marges de la zona de joc + dibuixar_marges(); - // Dibuixar nau - nau_.dibuixar(); + // Dibuixar nau + nau_.dibuixar(); - // Dibuixar ORNIs (enemics) - for (const auto &enemy : orni_) { - enemy.dibuixar(); - } + // Dibuixar ORNIs (enemics) + for (const auto& enemy : orni_) { + enemy.dibuixar(); + } - // Dibuixar bales (Fase 9) - for (const auto &bala : bales_) { - bala.dibuixar(); - } + // Dibuixar bales (Fase 9) + for (const auto& bala : bales_) { + bala.dibuixar(); + } - // Dibuixar fragments d'explosions (després d'altres objectes) - debris_manager_.dibuixar(); + // Dibuixar fragments d'explosions (després d'altres objectes) + debris_manager_.dibuixar(); - // Dibuixar marcador - dibuixar_marcador(); + // Dibuixar marcador + dibuixar_marcador(); } -void EscenaJoc::processar_input(const SDL_Event &event) { - // Processament d'input per events puntuals (no continus) - // L'input continu (fletxes) es processa en actualitzar() amb - // SDL_GetKeyboardState() +void EscenaJoc::processar_input(const SDL_Event& event) { + // Processament d'input per events puntuals (no continus) + // L'input continu (fletxes) es processa en actualitzar() amb + // SDL_GetKeyboardState() - if (event.type == SDL_EVENT_KEY_DOWN) { - switch (event.key.key) { - case SDLK_SPACE: { - // TEMPORAL: Explotar enemic aleatori (per testing) - // TODO: Restaurar dispars quan el sistema d'explosions estiga validat + if (event.type == SDL_EVENT_KEY_DOWN) { + switch (event.key.key) { + case SDLK_SPACE: { + // TEMPORAL: Explotar enemic aleatori (per testing) + // TODO: Restaurar dispars quan el sistema d'explosions estiga validat - // Buscar tots els enemics actius - std::vector enemics_actius; - for (int i = 0; i < Constants::MAX_ORNIS; i++) { - if (orni_[i].esta_actiu()) { - enemics_actius.push_back(i); + // Buscar tots els enemics actius + std::vector enemics_actius; + for (int i = 0; i < Constants::MAX_ORNIS; i++) { + if (orni_[i].esta_actiu()) { + enemics_actius.push_back(i); + } + } + + if (!enemics_actius.empty()) { + // Seleccionar enemic aleatori + int idx = enemics_actius[std::rand() % enemics_actius.size()]; + + // Crear explosió + debris_manager_.explotar(orni_[idx].get_forma(), + orni_[idx].get_centre(), + 0.0f, // angle (enemics no roten físicament) + 1.0f, // escala + Defaults::Physics::Debris::VELOCITAT_BASE); + + // Desactivar enemic + orni_[idx].destruir(); + + std::cout << "[TEST] Enemic " << idx << " explotat!\n"; + } + break; + } + + default: + break; } - } - - if (!enemics_actius.empty()) { - // Seleccionar enemic aleatori - int idx = enemics_actius[std::rand() % enemics_actius.size()]; - - // Crear explosió - debris_manager_.explotar(orni_[idx].get_forma(), - orni_[idx].get_centre(), - 0.0f, // angle (enemics no roten físicament) - 1.0f, // escala - Defaults::Physics::Debris::VELOCITAT_BASE); - - // Desactivar enemic - orni_[idx].destruir(); - - std::cout << "[TEST] Enemic " << idx << " explotat!\n"; - } - break; } - - default: - break; - } - } } void EscenaJoc::tocado() { - // TODO: Implementar seqüència de mort + // TODO: Implementar seqüència de mort } void EscenaJoc::dibuixar_marges() const { - // Dibuixar rectangle de la zona de joc - const SDL_FRect &zona = Defaults::Zones::PLAYAREA; + // Dibuixar rectangle de la zona de joc + const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - // Coordenades dels cantons - int x1 = static_cast(zona.x); - int y1 = static_cast(zona.y); - int x2 = static_cast(zona.x + zona.w); - int y2 = static_cast(zona.y + zona.h); + // Coordenades dels cantons + int x1 = static_cast(zona.x); + int y1 = static_cast(zona.y); + int x2 = static_cast(zona.x + zona.w); + int y2 = static_cast(zona.y + zona.h); - // 4 línies per formar el rectangle - Rendering::linea(sdl_.obte_renderer(), x1, y1, x2, y1, true); // Top - Rendering::linea(sdl_.obte_renderer(), x1, y2, x2, y2, true); // Bottom - Rendering::linea(sdl_.obte_renderer(), x1, y1, x1, y2, true); // Left - Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2, true); // Right + // 4 línies per formar el rectangle + Rendering::linea(sdl_.obte_renderer(), x1, y1, x2, y1, true); // Top + Rendering::linea(sdl_.obte_renderer(), x1, y2, x2, y2, true); // Bottom + Rendering::linea(sdl_.obte_renderer(), x1, y1, x1, y2, true); // Left + Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2, true); // Right } void EscenaJoc::dibuixar_marcador() { - // Text estàtic (hardcoded) - const std::string text = "SCORE: 01000 LIFE: 3 LEVEL: 01"; + // Text estàtic (hardcoded) + const std::string text = "SCORE: 01000 LIFE: 3 LEVEL: 01"; - // Paràmetres de renderització - const float escala = 0.85f; - const float spacing = 0.0f; + // Paràmetres de renderització + const float escala = 0.85f; + const float spacing = 0.0f; - // Calcular dimensions del text - float text_width = text_.get_text_width(text, escala, spacing); - float text_height = text_.get_text_height(escala); + // Calcular dimensions del text + float text_width = text_.get_text_width(text, escala, spacing); + float text_height = text_.get_text_height(escala); - // Centrat horitzontal dins de la zona del marcador - float x = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f; + // Centrat horitzontal dins de la zona del marcador + float x = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f; - // Centrat vertical dins de la zona del marcador - float y = Defaults::Zones::SCOREBOARD.y + - (Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f; + // Centrat vertical dins de la zona del marcador + float y = Defaults::Zones::SCOREBOARD.y + + (Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f; - // Renderitzar - text_.render(text, {x, y}, escala, spacing); + // Renderitzar + text_.render(text, {x, y}, escala, spacing); } diff --git a/source/game/escenes/escena_joc.hpp b/source/game/escenes/escena_joc.hpp index fe20920..3142b0b 100644 --- a/source/game/escenes/escena_joc.hpp +++ b/source/game/escenes/escena_joc.hpp @@ -5,50 +5,52 @@ #ifndef ESCENA_JOC_HPP #define ESCENA_JOC_HPP -#include "../../core/graphics/vector_text.hpp" -#include "../../core/rendering/sdl_manager.hpp" -#include "../effects/debris_manager.hpp" -#include "../../core/types.hpp" -#include "../constants.hpp" -#include "../entities/bala.hpp" -#include "../entities/enemic.hpp" -#include "../entities/nau.hpp" #include + #include #include +#include "../../core/graphics/vector_text.hpp" +#include "../../core/rendering/sdl_manager.hpp" +#include "../../core/types.hpp" +#include "../constants.hpp" +#include "../effects/debris_manager.hpp" +#include "../entities/bala.hpp" +#include "../entities/enemic.hpp" +#include "../entities/nau.hpp" + // Classe principal del joc (escena) class EscenaJoc { -public: - explicit EscenaJoc(SDLManager &sdl); - ~EscenaJoc() = default; + public: + explicit EscenaJoc(SDLManager& sdl); + ~EscenaJoc() = default; - void executar(); // Bucle principal de l'escena - void inicialitzar(); - void actualitzar(float delta_time); - void dibuixar(); - void processar_input(const SDL_Event &event); + void executar(); // Bucle principal de l'escena + void inicialitzar(); + void actualitzar(float delta_time); + void dibuixar(); + void processar_input(const SDL_Event& event); -private: - SDLManager &sdl_; + private: + SDLManager& sdl_; - // Efectes visuals - Effects::DebrisManager debris_manager_; + // Efectes visuals + Effects::DebrisManager debris_manager_; - // Estat del joc - Nau nau_; - std::array orni_; - std::array bales_; - Poligon chatarra_cosmica_; - uint16_t itocado_; + // Estat del joc + Nau nau_; + std::array orni_; + std::array bales_; + Poligon chatarra_cosmica_; + uint16_t itocado_; - // Text vectorial - Graphics::VectorText text_; + // Text vectorial + Graphics::VectorText text_; - // Funcions privades - void tocado(); - void dibuixar_marges() const; // Dibuixar vores de la zona de joc - void dibuixar_marcador(); // Dibuixar marcador de puntuació + // Funcions privades + void tocado(); + void dibuixar_marges() const; // Dibuixar vores de la zona de joc + void dibuixar_marcador(); // Dibuixar marcador de puntuació }; -#endif // ESCENA_JOC_HPP +#endif // ESCENA_JOC_HPP diff --git a/source/game/escenes/escena_logo.cpp b/source/game/escenes/escena_logo.cpp index 9a46a6e..8f450e0 100644 --- a/source/game/escenes/escena_logo.cpp +++ b/source/game/escenes/escena_logo.cpp @@ -2,262 +2,340 @@ // © 2025 Port a C++20 #include "escena_logo.hpp" -#include "../../core/graphics/shape_loader.hpp" -#include "../../core/rendering/shape_renderer.hpp" -#include "../../core/system/gestor_escenes.hpp" -#include "../../core/system/global_events.hpp" + #include #include #include +#include "../../core/graphics/shape_loader.hpp" +#include "../../core/rendering/shape_renderer.hpp" +#include "../../core/system/gestor_escenes.hpp" +#include "../../core/system/global_events.hpp" + // Helper: calcular el progrés individual d'una lletra // en funció del progrés global (efecte seqüencial) -static float calcular_progress_letra(size_t letra_index, size_t num_letras, - float global_progress, float threshold) { - if (num_letras == 0) - return 1.0f; +static float calcular_progress_letra(size_t letra_index, size_t num_letras, float global_progress, float threshold) { + if (num_letras == 0) + return 1.0f; - // Calcular temps per lletra - float duration_per_letra = 1.0f / static_cast(num_letras); - float step = threshold * duration_per_letra; - float start = static_cast(letra_index) * step; - float end = start + duration_per_letra; + // Calcular temps per lletra + float duration_per_letra = 1.0f / static_cast(num_letras); + float step = threshold * duration_per_letra; + float start = static_cast(letra_index) * step; + float end = start + duration_per_letra; - // Interpolar progrés - if (global_progress < start) { - return 0.0f; // Encara no ha començat - } else if (global_progress >= end) { - return 1.0f; // Completament apareguda - } else { - return (global_progress - start) / (end - start); - } + // Interpolar progrés + if (global_progress < start) { + return 0.0f; // Encara no ha començat + } else if (global_progress >= end) { + return 1.0f; // Completament apareguda + } else { + return (global_progress - start) / (end - start); + } } -EscenaLogo::EscenaLogo(SDLManager &sdl) - : sdl_(sdl), estat_actual_(EstatAnimacio::PRE_ANIMATION), - temps_estat_actual_(0.0f) { - std::cout << "Escena Logo: Inicialitzant...\n"; - inicialitzar_lletres(); +EscenaLogo::EscenaLogo(SDLManager& sdl) + : sdl_(sdl), + estat_actual_(EstatAnimacio::PRE_ANIMATION), + temps_estat_actual_(0.0f), + debris_manager_(std::make_unique(sdl.obte_renderer())), + lletra_explosio_index_(0), + temps_des_ultima_explosio_(0.0f) { + std::cout << "Escena Logo: Inicialitzant...\n"; + inicialitzar_lletres(); } void EscenaLogo::executar() { - SDL_Event event; - Uint64 last_time = SDL_GetTicks(); + SDL_Event event; + Uint64 last_time = SDL_GetTicks(); - while (GestorEscenes::actual == GestorEscenes::Escena::LOGO) { - // Calcular delta_time real - Uint64 current_time = SDL_GetTicks(); - float delta_time = (current_time - last_time) / 1000.0f; - last_time = current_time; + while (GestorEscenes::actual == GestorEscenes::Escena::LOGO) { + // Calcular delta_time real + Uint64 current_time = SDL_GetTicks(); + float delta_time = (current_time - last_time) / 1000.0f; + last_time = current_time; - // Limitar delta_time per evitar grans salts - if (delta_time > 0.05f) { - delta_time = 0.05f; + // Limitar delta_time per evitar grans salts + if (delta_time > 0.05f) { + delta_time = 0.05f; + } + + // Processar events SDL + while (SDL_PollEvent(&event)) { + // Manejo de finestra + if (sdl_.handleWindowEvent(event)) { + continue; + } + + // Events globals (F1/F2/F3/ESC/QUIT) + if (GlobalEvents::handle(event, sdl_)) { + continue; + } + + // Processar events de l'escena (qualsevol tecla/clic salta al joc) + processar_events(event); + } + + // Actualitzar lògica + actualitzar(delta_time); + + // Actualitzar colors oscil·lats (efecte verd global) + sdl_.updateColors(delta_time); + + // Dibuixar + dibuixar(); } - // Processar events SDL - while (SDL_PollEvent(&event)) { - // Manejo de finestra - if (sdl_.handleWindowEvent(event)) { - continue; - } - - // Events globals (F1/F2/F3/ESC/QUIT) - if (GlobalEvents::handle(event, sdl_)) { - continue; - } - - // Processar events de l'escena (qualsevol tecla/clic salta al joc) - processar_events(event); - } - - // Actualitzar lògica - actualitzar(delta_time); - - // Actualitzar colors oscil·lats (efecte verd global) - sdl_.updateColors(delta_time); - - // Dibuixar - dibuixar(); - } - - std::cout << "Escena Logo: Finalitzant...\n"; + std::cout << "Escena Logo: Finalitzant...\n"; } void EscenaLogo::inicialitzar_lletres() { - using namespace Graphics; + using namespace Graphics; - // Llista de fitxers .shp (A repetida per a les dues A's) - std::vector fitxers = { - "logo/letra_j.shp", "logo/letra_a.shp", "logo/letra_i.shp", - "logo/letra_l.shp", "logo/letra_g.shp", "logo/letra_a.shp", - "logo/letra_m.shp", "logo/letra_e.shp", "logo/letra_s.shp"}; + // Llista de fitxers .shp (A repetida per a les dues A's) + std::vector fitxers = { + "logo/letra_j.shp", + "logo/letra_a.shp", + "logo/letra_i.shp", + "logo/letra_l.shp", + "logo/letra_g.shp", + "logo/letra_a.shp", + "logo/letra_m.shp", + "logo/letra_e.shp", + "logo/letra_s.shp"}; - // Pas 1: Carregar totes les formes i calcular amplades - float ancho_total = 0.0f; + // Pas 1: Carregar totes les formes i calcular amplades + float ancho_total = 0.0f; - for (const auto &fitxer : fitxers) { - auto forma = ShapeLoader::load(fitxer); - if (!forma || !forma->es_valida()) { - std::cerr << "[EscenaLogo] Error carregant " << fitxer << std::endl; - continue; + for (const auto& fitxer : fitxers) { + auto forma = ShapeLoader::load(fitxer); + if (!forma || !forma->es_valida()) { + std::cerr << "[EscenaLogo] Error carregant " << fitxer << std::endl; + continue; + } + + // Calcular bounding box de la forma (trobar ancho) + float min_x = FLT_MAX; + float max_x = -FLT_MAX; + + for (const auto& prim : forma->get_primitives()) { + for (const auto& punt : prim.points) { + min_x = std::min(min_x, punt.x); + max_x = std::max(max_x, punt.x); + } + } + + float ancho_sin_escalar = max_x - min_x; + + // IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL + // per que les posicions finals coincideixin amb la mida real de les lletres + float ancho = ancho_sin_escalar * ESCALA_FINAL; + float offset_centre = (forma->get_centre().x - min_x) * ESCALA_FINAL; + + lletres_.push_back({forma, + {0.0f, 0.0f}, // Posició es calcularà després + ancho, + offset_centre}); + + ancho_total += ancho; } - // Calcular bounding box de la forma (trobar ancho) - float min_x = FLT_MAX; - float max_x = -FLT_MAX; + // Pas 2: Afegir espaiat entre lletres + ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1); - for (const auto &prim : forma->get_primitives()) { - for (const auto &punt : prim.points) { - min_x = std::min(min_x, punt.x); - max_x = std::max(max_x, punt.x); - } + // Pas 3: Calcular posició inicial (centrat horitzontal) + constexpr float PANTALLA_ANCHO = 640.0f; + constexpr float PANTALLA_ALTO = 480.0f; + + float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0f; + float y_centre = PANTALLA_ALTO / 2.0f; + + // Pas 4: Assignar posicions a cada lletra + float x_actual = x_inicial; + + for (auto& lletra : lletres_) { + // Posicionar el centre de la forma (shape_centre) en pantalla + // Usar offset_centre en lloc de ancho/2 perquè shape_centre + // pot no estar exactament al mig del bounding box + lletra.posicio.x = x_actual + lletra.offset_centre; + lletra.posicio.y = y_centre; + + // Avançar per a següent lletra + x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; } - float ancho_sin_escalar = max_x - min_x; - - // IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL - // per que les posicions finals coincideixin amb la mida real de les lletres - float ancho = ancho_sin_escalar * ESCALA_FINAL; - float offset_centre = (forma->get_centre().x - min_x) * ESCALA_FINAL; - - lletres_.push_back({forma, - {0.0f, 0.0f}, // Posició es calcularà després - ancho, - offset_centre}); - - ancho_total += ancho; - } - - // Pas 2: Afegir espaiat entre lletres - ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1); - - // Pas 3: Calcular posició inicial (centrat horitzontal) - constexpr float PANTALLA_ANCHO = 640.0f; - constexpr float PANTALLA_ALTO = 480.0f; - - float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0f; - float y_centre = PANTALLA_ALTO / 2.0f; - - // Pas 4: Assignar posicions a cada lletra - float x_actual = x_inicial; - - for (auto &lletra : lletres_) { - // Posicionar el centre de la forma (shape_centre) en pantalla - // Usar offset_centre en lloc de ancho/2 perquè shape_centre - // pot no estar exactament al mig del bounding box - lletra.posicio.x = x_actual + lletra.offset_centre; - lletra.posicio.y = y_centre; - - // Avançar per a següent lletra - x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES; - } - - std::cout << "[EscenaLogo] " << lletres_.size() - << " lletres carregades, ancho total: " << ancho_total << " px\n"; + std::cout << "[EscenaLogo] " << lletres_.size() + << " lletres carregades, ancho total: " << ancho_total << " px\n"; } void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) { - estat_actual_ = nou_estat; - temps_estat_actual_ = 0.0f; // Reset temps - std::cout << "[EscenaLogo] Canvi a estat: " << static_cast(nou_estat) - << "\n"; + estat_actual_ = nou_estat; + temps_estat_actual_ = 0.0f; // Reset temps + + // Inicialitzar estat d'explosió + if (nou_estat == EstatAnimacio::EXPLOSION) { + lletra_explosio_index_ = 0; + temps_des_ultima_explosio_ = 0.0f; + } + + std::cout << "[EscenaLogo] Canvi a estat: " << static_cast(nou_estat) + << "\n"; } bool EscenaLogo::totes_lletres_completes() const { - // Quan global_progress = 1.0, totes les lletres tenen letra_progress = 1.0 - return temps_estat_actual_ >= DURACIO_ZOOM; + // Quan global_progress = 1.0, totes les lletres tenen letra_progress = 1.0 + return temps_estat_actual_ >= DURACIO_ZOOM; +} + +void EscenaLogo::actualitzar_explosions(float delta_time) { + temps_des_ultima_explosio_ += delta_time; + + // Comprovar si és el moment d'explotar la següent lletra + if (temps_des_ultima_explosio_ >= DELAY_ENTRE_EXPLOSIONS) { + if (lletra_explosio_index_ < lletres_.size()) { + // Explotar lletra actual + const auto& lletra = lletres_[lletra_explosio_index_]; + + debris_manager_->explotar( + lletra.forma, // Forma a explotar + lletra.posicio, // Posició + 0.0f, // Angle (sense rotació) + ESCALA_FINAL, // Escala (lletres a escala final) + VELOCITAT_EXPLOSIO // Velocitat base + ); + + std::cout << "[EscenaLogo] Explota lletra " << lletra_explosio_index_ << "\n"; + + // Passar a la següent lletra + lletra_explosio_index_++; + temps_des_ultima_explosio_ = 0.0f; + } else { + // Totes les lletres han explotat, transició a POST_EXPLOSION + canviar_estat(EstatAnimacio::POST_EXPLOSION); + } + } } void EscenaLogo::actualitzar(float delta_time) { - temps_estat_actual_ += delta_time; + temps_estat_actual_ += delta_time; - switch (estat_actual_) { - case EstatAnimacio::PRE_ANIMATION: - if (temps_estat_actual_ >= DURACIO_PRE) { - canviar_estat(EstatAnimacio::ANIMATION); - } - break; + switch (estat_actual_) { + case EstatAnimacio::PRE_ANIMATION: + if (temps_estat_actual_ >= DURACIO_PRE) { + canviar_estat(EstatAnimacio::ANIMATION); + } + break; - case EstatAnimacio::ANIMATION: - if (totes_lletres_completes()) { - canviar_estat(EstatAnimacio::POST_ANIMATION); - } - break; + case EstatAnimacio::ANIMATION: + if (totes_lletres_completes()) { + canviar_estat(EstatAnimacio::POST_ANIMATION); + } + break; - case EstatAnimacio::POST_ANIMATION: - if (temps_estat_actual_ >= DURACIO_POST) { - GestorEscenes::actual = GestorEscenes::Escena::JOC; + case EstatAnimacio::POST_ANIMATION: + if (temps_estat_actual_ >= DURACIO_POST_ANIMATION) { + canviar_estat(EstatAnimacio::EXPLOSION); + } + break; + + case EstatAnimacio::EXPLOSION: + actualitzar_explosions(delta_time); + break; + + case EstatAnimacio::POST_EXPLOSION: + if (temps_estat_actual_ >= DURACIO_POST_EXPLOSION) { + GestorEscenes::actual = GestorEscenes::Escena::JOC; + } + break; } - break; - } + + // Actualitzar animacions de debris + debris_manager_->actualitzar(delta_time); } void EscenaLogo::dibuixar() { - // Fons negre - sdl_.neteja(0, 0, 0); + // Fons negre + sdl_.neteja(0, 0, 0); - // PRE_ANIMATION: Només pantalla negra - if (estat_actual_ == EstatAnimacio::PRE_ANIMATION) { - sdl_.presenta(); - return; // No renderitzar lletres - } - - // ANIMATION o POST_ANIMATION: Calcular progrés - float global_progress = - (estat_actual_ == EstatAnimacio::ANIMATION) - ? std::min(temps_estat_actual_ / DURACIO_ZOOM, 1.0f) - : 1.0f; // POST: mantenir al 100% - - // Punt inicial del zoom (configurable amb ORIGEN_ZOOM_X/Y) - const Punt ORIGEN_ZOOM = {ORIGEN_ZOOM_X, ORIGEN_ZOOM_Y}; - - // Dibuixar cada lletra amb animació seqüencial - for (size_t i = 0; i < lletres_.size(); i++) { - const auto &lletra = lletres_[i]; - - // Calcular progrés individual d'aquesta lletra (0.0 → 1.0) - float letra_progress = calcular_progress_letra( - i, lletres_.size(), global_progress, THRESHOLD_LETRA); - - // Si la lletra encara no ha començat, saltar-la - if (letra_progress <= 0.0f) { - continue; + // PRE_ANIMATION: Només pantalla negra + if (estat_actual_ == EstatAnimacio::PRE_ANIMATION) { + sdl_.presenta(); + return; // No renderitzar lletres } - // Interpolar posició: des del origen zoom cap a posició final - Punt pos_actual; - pos_actual.x = - ORIGEN_ZOOM.x + (lletra.posicio.x - ORIGEN_ZOOM.x) * letra_progress; - pos_actual.y = - ORIGEN_ZOOM.y + (lletra.posicio.y - ORIGEN_ZOOM.y) * letra_progress; + // ANIMATION o POST_ANIMATION: Dibuixar lletres amb animació + if (estat_actual_ == EstatAnimacio::ANIMATION || + estat_actual_ == EstatAnimacio::POST_ANIMATION) { + float global_progress = + (estat_actual_ == EstatAnimacio::ANIMATION) + ? std::min(temps_estat_actual_ / DURACIO_ZOOM, 1.0f) + : 1.0f; // POST: mantenir al 100% - // Aplicar ease-out quadràtic per suavitat - float t = letra_progress; - float ease_factor = 1.0f - (1.0f - t) * (1.0f - t); + const Punt ORIGEN_ZOOM = {ORIGEN_ZOOM_X, ORIGEN_ZOOM_Y}; - // Interpolar escala amb ease-out: des de ESCALA_INICIAL cap a ESCALA_FINAL - float escala_actual = - ESCALA_INICIAL + (ESCALA_FINAL - ESCALA_INICIAL) * ease_factor; + for (size_t i = 0; i < lletres_.size(); i++) { + const auto& lletra = lletres_[i]; - // Renderitzar la lletra - Rendering::render_shape( - sdl_.obte_renderer(), lletra.forma, - pos_actual, // Posició interpolada - 0.0f, // Sense rotació - escala_actual, // Escala interpolada amb ease-out - true, // Dibuixar - 1.0f // Progress = 1.0 (lletra completa, sense animació de primitives) - ); - } + float letra_progress = calcular_progress_letra( + i, + lletres_.size(), + global_progress, + THRESHOLD_LETRA); - sdl_.presenta(); + if (letra_progress <= 0.0f) { + continue; + } + + Punt pos_actual; + pos_actual.x = + ORIGEN_ZOOM.x + (lletra.posicio.x - ORIGEN_ZOOM.x) * letra_progress; + pos_actual.y = + ORIGEN_ZOOM.y + (lletra.posicio.y - ORIGEN_ZOOM.y) * letra_progress; + + float t = letra_progress; + float ease_factor = 1.0f - (1.0f - t) * (1.0f - t); + float escala_actual = + ESCALA_INICIAL + (ESCALA_FINAL - ESCALA_INICIAL) * ease_factor; + + Rendering::render_shape( + sdl_.obte_renderer(), + lletra.forma, + pos_actual, + 0.0f, + escala_actual, + true, + 1.0f); + } + } + + // EXPLOSION: Dibuixar només lletres que encara no han explotat + if (estat_actual_ == EstatAnimacio::EXPLOSION) { + for (size_t i = lletra_explosio_index_; i < lletres_.size(); i++) { + const auto& lletra = lletres_[i]; + + Rendering::render_shape( + sdl_.obte_renderer(), + lletra.forma, + lletra.posicio, + 0.0f, + ESCALA_FINAL, + true, + 1.0f); + } + } + + // POST_EXPLOSION: No dibuixar lletres, només debris (a baix) + + // Sempre dibuixar debris (si n'hi ha d'actius) + debris_manager_->dibuixar(); + + sdl_.presenta(); } -void EscenaLogo::processar_events(const SDL_Event &event) { - // Qualsevol tecla o clic de ratolí salta al joc - if (event.type == SDL_EVENT_KEY_DOWN || - event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { - GestorEscenes::actual = GestorEscenes::Escena::JOC; - } +void EscenaLogo::processar_events(const SDL_Event& event) { + // Qualsevol tecla o clic de ratolí salta al joc + if (event.type == SDL_EVENT_KEY_DOWN || + event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { + GestorEscenes::actual = GestorEscenes::Escena::JOC; + } } diff --git a/source/game/escenes/escena_logo.hpp b/source/game/escenes/escena_logo.hpp index 36a6ff3..db1e468 100644 --- a/source/game/escenes/escena_logo.hpp +++ b/source/game/escenes/escena_logo.hpp @@ -4,66 +4,77 @@ #pragma once -#include "../../core/graphics/shape.hpp" -#include "../../core/rendering/sdl_manager.hpp" -#include "../../core/types.hpp" #include + #include #include +#include "../../core/graphics/shape.hpp" +#include "../../core/rendering/sdl_manager.hpp" +#include "../../core/types.hpp" +#include "../effects/debris_manager.hpp" + class EscenaLogo { -public: - explicit EscenaLogo(SDLManager &sdl); - void executar(); // Bucle principal de l'escena + public: + explicit EscenaLogo(SDLManager& sdl); + void executar(); // Bucle principal de l'escena -private: - // Màquina d'estats per l'animació - enum class EstatAnimacio { - PRE_ANIMATION, // Pantalla negra inicial - ANIMATION, // Animació de zoom de lletres - POST_ANIMATION // Logo complet visible - }; + private: + // Màquina d'estats per l'animació + enum class EstatAnimacio { + PRE_ANIMATION, // Pantalla negra inicial + ANIMATION, // Animació de zoom de lletres + POST_ANIMATION, // Logo complet visible + EXPLOSION, // Explosió seqüencial de lletres + POST_EXPLOSION // Espera després de l'última explosió + }; - SDLManager &sdl_; - EstatAnimacio estat_actual_; // Estat actual de la màquina - float - temps_estat_actual_; // Temps en l'estat actual (reset en cada transició) + SDLManager& sdl_; + EstatAnimacio estat_actual_; // Estat actual de la màquina + float + temps_estat_actual_; // Temps en l'estat actual (reset en cada transició) - // Estructura per a cada lletra del logo - struct LetraLogo { - std::shared_ptr forma; - Punt posicio; // Posició final en pantalla - float ancho; // Ancho del bounding box - float offset_centre; // Distància de min_x a shape_centre.x - }; + // Gestor de fragments d'explosions + std::unique_ptr debris_manager_; - std::vector lletres_; // 9 lletres: J-A-I-L-G-A-M-E-S + // Seguiment d'explosions seqüencials + size_t lletra_explosio_index_; // Índex de la següent lletra a explotar + float temps_des_ultima_explosio_; // Temps des de l'última explosió - // Constants d'animació - static constexpr float DURACIO_PRE = - 1.5f; // Duració PRE_ANIMATION (pantalla negra) - static constexpr float DURACIO_ZOOM = 4.0f; // Duració del zoom (segons) - static constexpr float DURACIO_POST = - 4.0f; // Duració POST_ANIMATION (logo complet) - static constexpr float ESCALA_INICIAL = 0.1f; // Escala inicial (10%) - static constexpr float ESCALA_FINAL = 0.8f; // Escala final (80%) - static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espaiat entre lletres + // Estructura per a cada lletra del logo + struct LetraLogo { + std::shared_ptr forma; + Punt posicio; // Posició final en pantalla + float ancho; // Ancho del bounding box + float offset_centre; // Distància de min_x a shape_centre.x + }; - // Constants d'animació seqüencial - static constexpr float THRESHOLD_LETRA = - 0.6f; // Umbral per activar següent lletra (0.0-1.0) - static constexpr float ORIGEN_ZOOM_X = - 640.0f / 2.0f; // Punt inicial X del zoom (320) - static constexpr float ORIGEN_ZOOM_Y = - 480.0f * 0.4f; // Punt inicial Y del zoom (240) + std::vector lletres_; // 9 lletres: J-A-I-L-G-A-M-E-S - // Mètodes privats - void inicialitzar_lletres(); - void actualitzar(float delta_time); - void dibuixar(); - void processar_events(const SDL_Event &event); + // Constants d'animació + static constexpr float DURACIO_PRE = 1.5f; // Duració PRE_ANIMATION (pantalla negra) + static constexpr float DURACIO_ZOOM = 4.0f; // Duració del zoom (segons) + static constexpr float DURACIO_POST_ANIMATION = 3.0f; // Duració POST_ANIMATION (logo complet) + static constexpr float DURACIO_POST_EXPLOSION = 3.0f; // Duració POST_EXPLOSION (espera final) + static constexpr float DELAY_ENTRE_EXPLOSIONS = 1.0f; // Temps entre explosions de lletres + static constexpr float VELOCITAT_EXPLOSIO = 80.0f; // Velocitat base fragments (px/s) + static constexpr float ESCALA_INICIAL = 0.1f; // Escala inicial (10%) + static constexpr float ESCALA_FINAL = 0.8f; // Escala final (80%) + static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espaiat entre lletres - // Mètodes de gestió d'estats - void canviar_estat(EstatAnimacio nou_estat); - bool totes_lletres_completes() const; + // Constants d'animació seqüencial + static constexpr float THRESHOLD_LETRA = 0.6f; // Umbral per activar següent lletra (0.0-1.0) + static constexpr float ORIGEN_ZOOM_X = 640.0f / 2.0f; // Punt inicial X del zoom (320) + static constexpr float ORIGEN_ZOOM_Y = 480.0f * 0.4f; // Punt inicial Y del zoom (240) + + // Mètodes privats + void inicialitzar_lletres(); + void actualitzar(float delta_time); + void actualitzar_explosions(float delta_time); + void dibuixar(); + void processar_events(const SDL_Event& event); + + // Mètodes de gestió d'estats + void canviar_estat(EstatAnimacio nou_estat); + bool totes_lletres_completes() const; }; diff --git a/source/game/options.cpp b/source/game/options.cpp index fa72482..ae5cfbb 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -1,298 +1,299 @@ #include "options.hpp" -#include "../core/defaults.hpp" -#include "../external/fkyaml_node.hpp" -#include "project.h" #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; + console = true; #else - console = false; + console = false; #endif - // Window - window.width = Defaults::Window::WIDTH; - window.height = Defaults::Window::HEIGHT; - window.fullscreen = false; - window.size_increment = Defaults::Window::SIZE_INCREMENT; + // Window + window.width = Defaults::Window::WIDTH; + window.height = Defaults::Window::HEIGHT; + window.fullscreen = false; + window.size_increment = Defaults::Window::SIZE_INCREMENT; - // 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; + // 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; + // Gameplay + gameplay.max_enemies = Defaults::Entities::MAX_ORNIS; + gameplay.max_bullets = Defaults::Entities::MAX_BALES; - // Version - version = std::string(Project::VERSION); + // Version + version = std::string(Project::VERSION); } // Establir la ruta del fitxer de configuració -void setConfigFile(const std::string &path) { config_file_path = path; } +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"]; +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("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("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("fullscreen")) { + try { + window.fullscreen = win["fullscreen"].get_value(); + } catch (...) { + window.fullscreen = false; + } + } - if (win.contains("size_increment")) { - try { - auto val = win["size_increment"].get_value(); - window.size_increment = - (val > 0) ? val : Defaults::Window::SIZE_INCREMENT; - } catch (...) { - window.size_increment = Defaults::Window::SIZE_INCREMENT; - } + if (win.contains("size_increment")) { + try { + auto val = win["size_increment"].get_value(); + window.size_increment = + (val > 0) ? val : Defaults::Window::SIZE_INCREMENT; + } catch (...) { + window.size_increment = Defaults::Window::SIZE_INCREMENT; + } + } } - } } -static void loadPhysicsConfigFromYaml(const fkyaml::node &yaml) { - if (yaml.contains("physics")) { - const auto &phys = yaml["physics"]; +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("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("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("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("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("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; - } + 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"]; +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_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; - } + 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; + } + } } - } } // Carregar configuració des del fitxer YAML auto loadFromFile() -> bool { - const std::string CONFIG_VERSION = std::string(Project::VERSION); + 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(); + 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; } - 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; + // 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); + + 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; } - - // Carregar seccions - loadWindowConfigFromYaml(yaml); - loadPhysicsConfigFromYaml(yaml); - loadGameplayConfigFromYaml(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'; + 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; } - 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"; + // 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 << "version: \"" << Project::VERSION << "\"\n\n"; - file << "# FINESTRA\n"; - file << "window:\n"; - file << " width: " << window.width << "\n"; - file << " height: " << window.height << "\n"; - file << " fullscreen: " << (window.fullscreen ? "true" : "false") << "\n"; - file << " size_increment: " << window.size_increment << "\n\n"; + file << "# FINESTRA\n"; + file << "window:\n"; + file << " width: " << window.width << "\n"; + file << " height: " << window.height << "\n"; + file << " fullscreen: " << (window.fullscreen ? "true" : "false") << "\n"; + file << " size_increment: " << window.size_increment << "\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 << "# 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"; + file << "# GAMEPLAY\n"; + file << "gameplay:\n"; + file << " max_enemies: " << gameplay.max_enemies << "\n"; + file << " max_bullets: " << gameplay.max_bullets << "\n"; - file.close(); + file.close(); - if (console) { - std::cout << "Config guardada a: " << config_file_path << '\n'; - } + if (console) { + std::cout << "Config guardada a: " << config_file_path << '\n'; + } - return true; + return true; } -} // namespace Options +} // namespace Options diff --git a/source/game/options.hpp b/source/game/options.hpp index 1d6a1af..c9bbcb8 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -7,42 +7,42 @@ namespace Options { // Estructures de configuració struct Window { - int width{640}; - int height{480}; - bool fullscreen{false}; - int size_increment{100}; // Increment per F1/F2 + int width{640}; + int height{480}; + bool fullscreen{false}; + int size_increment{100}; // Increment per F1/F2 }; 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 + 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}; + int max_enemies{15}; + int max_bullets{3}; }; // Variables globals (inline per evitar ODR violations) -inline std::string version{}; // Versió del config per validació -inline bool console{false}; // Eixida de debug +inline std::string version{}; // Versió del config per validació +inline bool console{false}; // Eixida de debug inline Window window{}; inline Physics physics{}; inline Gameplay gameplay{}; -inline std::string config_file_path{}; // Establert per setConfigFile() +inline std::string config_file_path{}; // Establert per setConfigFile() // Funcions públiques -void init(); // Inicialitzar amb valors per defecte +void init(); // Inicialitzar amb valors per defecte void setConfigFile( - const std::string &path); // Establir ruta del fitxer de config -auto loadFromFile() -> bool; // Carregar config YAML -auto saveToFile() -> bool; // Guardar config YAML + const std::string& path); // Establir ruta del fitxer de config +auto loadFromFile() -> bool; // Carregar config YAML +auto saveToFile() -> bool; // Guardar config YAML -} // namespace Options +} // namespace Options diff --git a/source/legacy/asteroids.cpp b/source/legacy/asteroids.cpp index b77f01a..171bb31 100644 --- a/source/legacy/asteroids.cpp +++ b/source/legacy/asteroids.cpp @@ -1,4 +1,5 @@ #include + #include #include @@ -13,34 +14,34 @@ #define MAX_BALES 3 struct ipunt { - float r; - float angle; + float r; + float angle; }; struct punt { - int x; - int y; + int x; + int y; }; struct triangle { - ipunt p1, p2, p3; - punt centre; - float angle; - float velocitat; + ipunt p1, p2, p3; + punt centre; + float angle; + float velocitat; }; typedef std::vector ivector(); struct poligon { - ivector *ipunts; - ivector ipuntx; - punt centre; - float angl; - float velocitat; - Uint8 n; - float drotacio; - float rotacio; - bool esta; + ivector* ipunts; + ivector ipuntx; + punt centre; + float angl; + float velocitat; + Uint8 n; + float drotacio; + float rotacio; + bool esta; }; std::vector pvirt(38400); @@ -385,7 +386,7 @@ begin instalarkb; repeat { - rota_tri(nau, nau.angle, nau.velocitat, 0);} + rota_tri(nau, nau.angle, nau.velocitat, 0);} clsvirt; if teclapuls(KEYarrowright) then nau.angle:=nau.angle+0.157079632; @@ -413,19 +414,19 @@ begin nau.centre.x:=Dx; if (nau.velocitat>0.1) then nau.velocitat:=nau.velocitat-0.1; { - dist: - = distancia(nau.centre, pol.centre); - diferencia(pol.centre, nau.centre, puntaux); - if dist - < (pol.ipuntx[1].r + 30) then begin nau.centre.x - : = nau.centre.x + round(dist * cos(angle(puntaux) + 0.031415)); - nau.centre.y - : = nau.centre.y + round(dist * sin(angle(puntaux) + 0.031415)); - end;} + dist: + = distancia(nau.centre, pol.centre); + diferencia(pol.centre, nau.centre, puntaux); + if dist + < (pol.ipuntx[1].r + 30) then begin nau.centre.x + : = nau.centre.x + round(dist * cos(angle(puntaux) + 0.031415)); + nau.centre.y + : = nau.centre.y + round(dist * sin(angle(puntaux) + 0.031415)); + end;} { for - i: - = 1 to 5 do begin rota_pol(orni[i], ang, 0); - end;} + i: + = 1 to 5 do begin rota_pol(orni[i], ang, 0); + end;} for i:=1 to MAX_ORNIS do begin mou_orni(orni[i]); rota_pol(orni[i],orni[i].rotacio,1); @@ -440,26 +441,26 @@ begin waitretrace; volca; { - if aux - = 1 then begin { - gotoxy(0, 0); - write('tocado') tocado; - delay(200); - end; - } - gotoxy(50, 24); - write('� Visente i Sergi'); - gotoxy(50, 25); - write('�ETA 2.2 2/6/99'); - until teclapuls(keyesc); - desinstalarkb; - ang: - = 0; - repeat waitretrace; - rota_pol(pol, ang, 0); - ang: - = ang + 0.031415; - rota_pol(pol, ang, 1); - until keypressed; - text; - end. \ No newline at end of file + if aux + = 1 then begin { + gotoxy(0, 0); + write('tocado') tocado; + delay(200); + end; + } + gotoxy(50, 24); + write('� Visente i Sergi'); + gotoxy(50, 25); + write('�ETA 2.2 2/6/99'); + until teclapuls(keyesc); + desinstalarkb; + ang: + = 0; + repeat waitretrace; + rota_pol(pol, ang, 0); + ang: + = ang + 0.031415; + rota_pol(pol, ang, 1); + until keypressed; + text; + end. \ No newline at end of file diff --git a/source/main.cpp b/source/main.cpp index 16abcab..5e24d80 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -2,17 +2,18 @@ // © 1999 Visente i Sergi (versió Pascal) // © 2025 Port a C++20 amb SDL3 -#include "core/system/director.hpp" #include #include -int main(int argc, char *argv[]) { - // Convertir arguments a std::vector - std::vector args(argv, argv + argc); +#include "core/system/director.hpp" - // Crear director (inicialitza sistema, opcions, configuració) - Director director(args); +int main(int argc, char* argv[]) { + // Convertir arguments a std::vector + std::vector args(argv, argv + argc); - // Executar bucle principal del joc - return director.run(); + // Crear director (inicialitza sistema, opcions, configuració) + Director director(args); + + // Executar bucle principal del joc + return director.run(); }