diff --git a/Makefile b/Makefile index 40c9568..b18c053 100644 --- a/Makefile +++ b/Makefile @@ -318,7 +318,7 @@ endif # Backup to remote server backup: - @echo "Backing up project to maverick:/home/sergio/git-backup/asteroids..." + @echo "Backing up project to maverick:/home/sergio/git-backup/orni..." rsync -a --delete \ --exclude='build/' \ --exclude='*.o' \ @@ -326,7 +326,7 @@ backup: --exclude='orni' \ --exclude='orni_debug' \ --exclude='*_release/' \ - $(DIR_ROOT) maverick:/home/sergio/git-backup/asteroids/ + $(DIR_ROOT) maverick:/home/sergio/git-backup/orni/ @echo "Backup completed successfully" # Help target diff --git a/data/music/game.ogg b/data/music/game.ogg new file mode 100644 index 0000000..2885bea Binary files /dev/null and b/data/music/game.ogg differ diff --git a/data/music/title.ogg b/data/music/title.ogg new file mode 100644 index 0000000..e77a9ae Binary files /dev/null and b/data/music/title.ogg differ diff --git a/source/core/graphics/starfield.cpp b/source/core/graphics/starfield.cpp index 9c0e729..dd99d1d 100644 --- a/source/core/graphics/starfield.cpp +++ b/source/core/graphics/starfield.cpp @@ -103,9 +103,12 @@ float Starfield::calcular_escala(const Estrella& estrella) const { float Starfield::calcular_brightness(const Estrella& estrella) const { // Interpolació lineal: estrelles properes (vora) més brillants // distancia_centre: 0.0 (centre, llunyanes) → 1.0 (vora, properes) - return Defaults::Brightness::STARFIELD_MIN + + float brightness_base = Defaults::Brightness::STARFIELD_MIN + (Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) * estrella.distancia_centre; + + // Aplicar multiplicador i limitar a 1.0 + return std::min(1.0f, brightness_base * multiplicador_brightness_); } // Actualitzar posicions de les estrelles @@ -135,6 +138,11 @@ void Starfield::actualitzar(float delta_time) { } } +// Establir multiplicador de brightness +void Starfield::set_brightness(float multiplier) { + multiplicador_brightness_ = std::max(0.0f, multiplier); // Evitar valors negatius +} + // Dibuixar totes les estrelles void Starfield::dibuixar() { if (!shape_estrella_->es_valida()) { diff --git a/source/core/graphics/starfield.hpp b/source/core/graphics/starfield.hpp index 4109503..2fa3557 100644 --- a/source/core/graphics/starfield.hpp +++ b/source/core/graphics/starfield.hpp @@ -42,6 +42,7 @@ class Starfield { // Setters per ajustar paràmetres en temps real void set_punt_fuga(const Punt& punt) { punt_fuga_ = punt; } + void set_brightness(float multiplier); private: // Estructura interna per cada estrella @@ -75,6 +76,7 @@ class Starfield { SDL_FRect area_; // Àrea activa float radi_max_; // Distància màxima del centre al límit de pantalla int densitat_; // Nombre total d'estrelles + float multiplicador_brightness_{1.0f}; // Multiplicador de brillantor (1.0 = default) }; } // namespace Graphics diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 6afbd53..688ba7c 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -8,6 +8,7 @@ #include #include "core/audio/audio.hpp" +#include "core/audio/audio_cache.hpp" #include "core/defaults.hpp" #include "core/rendering/sdl_manager.hpp" #include "game/escenes/escena_joc.hpp" @@ -161,6 +162,13 @@ auto Director::run() -> int { // Inicialitzar sistema d'audio Audio::init(); + // Precachejar música per evitar lag al començar + AudioCache::getMusic("title.ogg"); + if (Options::console) { + std::cout << "Música precachejada: " + << AudioCache::getMusicCacheSize() << " fitxers\n"; + } + // Bucle principal de gestió d'escenes while (GestorEscenes::actual != GestorEscenes::Escena::EIXIR) { switch (GestorEscenes::actual) { diff --git a/source/game/escenes/escena_joc.cpp b/source/game/escenes/escena_joc.cpp index cea0ca9..39cb454 100644 --- a/source/game/escenes/escena_joc.cpp +++ b/source/game/escenes/escena_joc.cpp @@ -141,6 +141,9 @@ void EscenaJoc::inicialitzar() { for (auto& bala : bales_) { bala.inicialitzar(); } + + // Iniciar música de joc (sense stopMusic, ja s'ha parat en destructor de TITOL) + Audio::get()->playMusic("game.ogg"); } void EscenaJoc::actualitzar(float delta_time) { @@ -150,6 +153,8 @@ void EscenaJoc::actualitzar(float delta_time) { game_over_timer_ -= delta_time; if (game_over_timer_ <= 0.0f) { + // Aturar música de joc abans de tornar al títol + Audio::get()->stopMusic(); // Auto-transition to title screen GestorEscenes::actual = GestorEscenes::Escena::TITOL; return; diff --git a/source/game/escenes/escena_logo.cpp b/source/game/escenes/escena_logo.cpp index 8640b5d..08abc86 100644 --- a/source/game/escenes/escena_logo.cpp +++ b/source/game/escenes/escena_logo.cpp @@ -200,6 +200,10 @@ void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) { std::mt19937 g(rd()); std::shuffle(ordre_explosio_.begin(), ordre_explosio_.end(), g); } + else if (nou_estat == EstatAnimacio::POST_EXPLOSION) + { + Audio::get()->playMusic("title.ogg"); + } std::cout << "[EscenaLogo] Canvi a estat: " << static_cast(nou_estat) << "\n"; @@ -288,6 +292,7 @@ void EscenaLogo::actualitzar(float delta_time) { case EstatAnimacio::POST_EXPLOSION: if (temps_estat_actual_ >= DURACIO_POST_EXPLOSION) { + // Iniciar música de títol abans de la transició GestorEscenes::actual = GestorEscenes::Escena::TITOL; } break; diff --git a/source/game/escenes/escena_logo.hpp b/source/game/escenes/escena_logo.hpp index 345c311..e208072 100644 --- a/source/game/escenes/escena_logo.hpp +++ b/source/game/escenes/escena_logo.hpp @@ -9,7 +9,7 @@ #include #include -#include "../effects/debris_manager.hpp" +#include "game/effects/debris_manager.hpp" #include "core/defaults.hpp" #include "core/graphics/shape.hpp" #include "core/rendering/sdl_manager.hpp" diff --git a/source/game/escenes/escena_titol.cpp b/source/game/escenes/escena_titol.cpp index f6c884f..13c8277 100644 --- a/source/game/escenes/escena_titol.cpp +++ b/source/game/escenes/escena_titol.cpp @@ -4,6 +4,7 @@ #include "escena_titol.hpp" #include +#include #include #include @@ -15,6 +16,11 @@ #include "core/system/global_events.hpp" #include "project.h" +namespace { +// Brightness del starfield (1.0 = default, >1.0 més brillant, <1.0 menys brillant) +constexpr float BRIGHTNESS_STARFIELD = 1.2f; +} // namespace + EscenaTitol::EscenaTitol(SDLManager& sdl) : sdl_(sdl), text_(sdl.obte_renderer()), @@ -40,8 +46,21 @@ EscenaTitol::EscenaTitol(SDLManager& sdl) 150 // densitat: 150 estrelles (50 per capa) ); + // Configurar brightness del starfield + starfield_->set_brightness(BRIGHTNESS_STARFIELD); + // Inicialitzar lletres del títol "ORNI ATTACK!" inicialitzar_titol(); + + // Iniciar música de títol si no està sonant + if (Audio::get()->getMusicState() != Audio::MusicState::PLAYING) { + Audio::get()->playMusic("title.ogg"); + } +} + +EscenaTitol::~EscenaTitol() { + // Aturar música de títol quan es destrueix l'escena + Audio::get()->stopMusic(); } void EscenaTitol::inicialitzar_titol() { @@ -256,9 +275,18 @@ void EscenaTitol::actualitzar(float delta_time) { estat_actual_ = EstatTitol::MAIN; } break; + case EstatTitol::MAIN: // No hi ha lògica d'actualització en l'estat MAIN break; + + case EstatTitol::TRANSITION: + temps_acumulat_ += delta_time; + if (temps_acumulat_ >= DURACIO_TRANSITION) { + // Transició a JOC (la música ja s'ha parat en el fade) + GestorEscenes::actual = GestorEscenes::Escena::JOC; + } + break; } } @@ -273,8 +301,8 @@ void EscenaTitol::dibuixar() { return; } - // Estat MAIN: Dibuixar títol i text (sobre el starfield) - if (estat_actual_ == EstatTitol::MAIN) { + // Estat MAIN i TRANSITION: Dibuixar títol i text (sobre el starfield) + if (estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::TRANSITION) { // === Dibuixar lletres del títol "ORNI ATTACK!" === // Dibuixar "ORNI" (línia 1) @@ -303,19 +331,31 @@ void EscenaTitol::dibuixar() { ); } - // === Text "PRESS BUTTON TO PLAY" (a sota del títol) === - const std::string main_text = "PRESS BUTTON TO PLAY"; - const float escala_main = 1.0f; - const float spacing = 2.0f; + // === Text "PRESS BUTTON TO PLAY" === + // En estat MAIN: sempre visible + // En estat TRANSITION: parpellejant (blink amb sinusoide) - float text_width = text_.get_text_width(main_text, escala_main, spacing); + const float spacing = 2.0f; // Espai entre caràcters (usat també per copyright) - float x_center = (Defaults::Game::WIDTH - text_width) / 2.0f; - // Usar posició dinàmica: ATTACK + altura lletres + separació - float altura_attack = lletres_attack_.empty() ? 50.0f : lletres_attack_[0].altura; - float y_center = y_attack_dinamica_ + altura_attack + 70.0f; // 70px sota "ATTACK!" + bool mostrar_text = true; + if (estat_actual_ == EstatTitol::TRANSITION) { + // Parpelleig: sin oscil·la entre -1 i 1, volem ON quan > 0 + float fase = temps_acumulat_ * BLINK_FREQUENCY * 2.0f * 3.14159f; // 2π × freq × temps + mostrar_text = (std::sin(fase) > 0.0f); + } - text_.render(main_text, Punt{x_center, y_center}, escala_main, spacing); + if (mostrar_text) { + const std::string main_text = "PRESS BUTTON TO PLAY"; + const float escala_main = 1.0f; + + float text_width = text_.get_text_width(main_text, escala_main, spacing); + + float x_center = (Defaults::Game::WIDTH - text_width) / 2.0f; + float altura_attack = lletres_attack_.empty() ? 50.0f : lletres_attack_[0].altura; + float y_center = y_attack_dinamica_ + altura_attack + 70.0f; + + text_.render(main_text, Punt{x_center, y_center}, escala_main, spacing); + } // === Copyright a la part inferior (centrat horitzontalment) === // Convert to uppercase since VectorText only supports A-Z @@ -346,9 +386,16 @@ void EscenaTitol::processar_events(const SDL_Event& event) { // Saltar a MAIN estat_actual_ = EstatTitol::MAIN; break; + case EstatTitol::MAIN: - // Anar al joc - GestorEscenes::actual = GestorEscenes::Escena::JOC; + // Iniciar transició amb fade-out de música + estat_actual_ = EstatTitol::TRANSITION; + temps_acumulat_ = 0.0f; // Reset del comptador + Audio::get()->fadeOutMusic(MUSIC_FADE); // Fade de 300ms + break; + + case EstatTitol::TRANSITION: + // Ignorar inputs durant la transició break; } } diff --git a/source/game/escenes/escena_titol.hpp b/source/game/escenes/escena_titol.hpp index a1ad2fe..0fef599 100644 --- a/source/game/escenes/escena_titol.hpp +++ b/source/game/escenes/escena_titol.hpp @@ -19,22 +19,24 @@ class EscenaTitol { public: explicit EscenaTitol(SDLManager& sdl); + ~EscenaTitol(); // Destructor per aturar música void executar(); // Bucle principal de l'escena private: // Màquina d'estats per la pantalla de títol enum class EstatTitol { - INIT, // Pantalla negra inicial (2 segons) - MAIN // Pantalla de títol amb text + INIT, // Pantalla negra inicial (2 segons) + MAIN, // Pantalla de títol amb text + TRANSITION // Transició amb fade-out de música i text parpellejant }; // Estructura per emmagatzemar informació de cada lletra del títol struct LetraLogo { - std::shared_ptr forma; // Forma vectorial de la lletra - Punt posicio; // Posició en pantalla - float ancho; // Amplada escalada - float altura; // Altura escalada - float offset_centre; // Offset del centre per posicionament + std::shared_ptr forma; // Forma vectorial de la lletra + Punt posicio; // Posició en pantalla + float ancho; // Amplada escalada + float altura; // Altura escalada + float offset_centre; // Offset del centre per posicionament }; SDLManager& sdl_; @@ -49,11 +51,14 @@ class EscenaTitol { float y_attack_dinamica_; // Posició Y calculada dinàmicament per "ATTACK!" // Constants - static constexpr float DURACIO_INIT = 2.0f; // Duració de l'estat INIT (2 segons) - static constexpr float ESCALA_TITULO = 0.6f; // Escala per les lletres del títol (50%) + static constexpr float DURACIO_INIT = 4.0f; // Duració de l'estat INIT (2 segons) + static constexpr float DURACIO_TRANSITION = 1.5f; // Duració de la transició (1.5 segons) + static constexpr float ESCALA_TITULO = 0.6f; // Escala per les lletres del títol (50%) static constexpr float ESPAI_ENTRE_LLETRES = 10.0f; // Espai entre lletres - static constexpr float Y_ORNI = 150.0f; // Posició Y de "ORNI" - static constexpr float SEPARACION_LINEAS = 10.0f; // Separació entre "ORNI" i "ATTACK!" (0.0f = pegades) + static constexpr float Y_ORNI = 150.0f; // Posició Y de "ORNI" + static constexpr float SEPARACION_LINEAS = 10.0f; // Separació entre "ORNI" i "ATTACK!" (0.0f = pegades) + static constexpr float BLINK_FREQUENCY = 3.0f; // Freqüència de parpelleig (3 Hz) + static constexpr int MUSIC_FADE = 1000; // Duracio del fade de la musica del titol al començar a jugar // Mètodes privats void actualitzar(float delta_time);