diff --git a/CMakeLists.txt b/CMakeLists.txt index 734095f..a2ec0dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ set(APP_SOURCES source/core/audio/audio_cache.cpp source/game/options.cpp source/game/escenes/escena_logo.cpp + source/game/escenes/escena_titol.cpp source/game/escenes/escena_joc.cpp source/game/entities/nau.cpp source/game/entities/bala.cpp diff --git a/Makefile b/Makefile index 6355d8c..8bbf43b 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ APP_SOURCES := \ source/core/graphics/vector_text.cpp \ source/game/options.cpp \ source/game/escenes/escena_logo.cpp \ + source/game/escenes/escena_titol.cpp \ source/game/escenes/escena_joc.cpp \ source/game/entities/nau.cpp \ source/game/entities/bala.cpp \ diff --git a/data/shapes/font/char_copyright.shp b/data/shapes/font/char_copyright.shp new file mode 100644 index 0000000..c048e82 --- /dev/null +++ b/data/shapes/font/char_copyright.shp @@ -0,0 +1,12 @@ +# char_copyright.shp - Símbolo © (copyright) +# Dimensiones: 20×40 (blocky display) + +name: char_copyright +scale: 1.0 +center: 10, 20 + +# Círculo exterior (aproximado con 12 puntos) +polyline: 10,8 13,9 15,11 17,14 18,17 18,23 17,26 15,29 13,31 10,32 7,31 5,29 3,26 2,23 2,17 3,14 5,11 7,9 10,8 + +# Letra C interior +polyline: 13,16 9,14 7,16 6,20 7,24 9,26 13,24 diff --git a/source/game/escenes/escena_logo.cpp b/source/game/escenes/escena_logo.cpp index 28d3c5b..3409dd0 100644 --- a/source/game/escenes/escena_logo.cpp +++ b/source/game/escenes/escena_logo.cpp @@ -281,7 +281,7 @@ void EscenaLogo::actualitzar(float delta_time) { case EstatAnimacio::POST_EXPLOSION: if (temps_estat_actual_ >= DURACIO_POST_EXPLOSION) { - GestorEscenes::actual = GestorEscenes::Escena::JOC; + GestorEscenes::actual = GestorEscenes::Escena::TITOL; } break; } @@ -379,9 +379,9 @@ void EscenaLogo::dibuixar() { } void EscenaLogo::processar_events(const SDL_Event& event) { - // Qualsevol tecla o clic de ratolí salta al joc + // Qualsevol tecla o clic de ratolí salta a la pantalla de títol if (event.type == SDL_EVENT_KEY_DOWN || event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { - GestorEscenes::actual = GestorEscenes::Escena::JOC; + GestorEscenes::actual = GestorEscenes::Escena::TITOL; } } diff --git a/source/game/escenes/escena_titol.cpp b/source/game/escenes/escena_titol.cpp new file mode 100644 index 0000000..6029706 --- /dev/null +++ b/source/game/escenes/escena_titol.cpp @@ -0,0 +1,149 @@ +// escena_titol.cpp - Implementació de l'escena de títol +// © 2025 Port a C++20 + +#include "escena_titol.hpp" + +#include +#include + +#include "../../core/audio/audio.hpp" +#include "../../core/system/gestor_escenes.hpp" +#include "../../core/system/global_events.hpp" +#include "build/project.h" + +EscenaTitol::EscenaTitol(SDLManager& sdl) + : sdl_(sdl), + text_(sdl.obte_renderer()), + estat_actual_(EstatTitol::INIT), + temps_acumulat_(0.0f) { + std::cout << "Escena Titol: Inicialitzant...\n"; +} + +void EscenaTitol::executar() { + SDL_Event event; + Uint64 last_time = SDL_GetTicks(); + + while (GestorEscenes::actual == GestorEscenes::Escena::TITOL) { + // 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; + } + + // Actualitzar comptador de FPS + sdl_.updateFPS(delta_time); + + // Processar events SDL + while (SDL_PollEvent(&event)) { + // Manejo de finestra + if (sdl_.handleWindowEvent(event)) { + continue; + } + + // Events globals (F1/F2/F3/F4/ESC/QUIT) + if (GlobalEvents::handle(event, sdl_)) { + continue; + } + + // Processar events de l'escena + processar_events(event); + } + + // Actualitzar lògica + actualitzar(delta_time); + + // Actualitzar sistema d'audio + Audio::update(); + + // Actualitzar colors oscil·lats + sdl_.updateColors(delta_time); + + // Netejar pantalla + sdl_.neteja(0, 0, 0); + + // Dibuixar + dibuixar(); + + // Presentar renderer (swap buffers) + sdl_.presenta(); + } + + std::cout << "Escena Titol: Finalitzant...\n"; +} + +void EscenaTitol::actualitzar(float delta_time) { + switch (estat_actual_) { + case EstatTitol::INIT: + temps_acumulat_ += delta_time; + if (temps_acumulat_ >= DURACIO_INIT) { + estat_actual_ = EstatTitol::MAIN; + } + break; + case EstatTitol::MAIN: + // No hi ha lògica d'actualització en l'estat MAIN + break; + } +} + +void EscenaTitol::dibuixar() { + // En l'estat INIT, no dibuixar res (pantalla negra) + if (estat_actual_ == EstatTitol::INIT) { + return; + } + + // Estat MAIN: Dibuixar text de títol i copyright + if (estat_actual_ == EstatTitol::MAIN) { + // Text principal centrat (vertical i horitzontalment) + const std::string main_text = "PRESS BUTTON TO PLAY"; + const float escala_main = 1.0f; + const float spacing = 2.0f; + + float text_width = text_.get_text_width(main_text, escala_main, spacing); + float text_height = text_.get_text_height(escala_main); + + float x_center = (Defaults::Game::WIDTH - text_width) / 2.0f; + float y_center = (Defaults::Game::HEIGHT - text_height) / 2.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 + std::string copyright = Project::COPYRIGHT; + for (char& c : copyright) { + if (c >= 'a' && c <= 'z') { + c = c - 32; // Convert to uppercase + } + } + const float escala_copy = 0.6f; + + float copy_width = text_.get_text_width(copyright, escala_copy, spacing); + float copy_height = text_.get_text_height(escala_copy); + + float x_copy = (Defaults::Game::WIDTH - copy_width) / 2.0f; + float y_copy = Defaults::Game::HEIGHT - copy_height - 20.0f; // 20px des del fons + + text_.render(copyright, Punt{x_copy, y_copy}, escala_copy, spacing); + } +} + +void EscenaTitol::processar_events(const SDL_Event& event) { + // Qualsevol tecla o clic de ratolí + if (event.type == SDL_EVENT_KEY_DOWN || + event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { + + switch (estat_actual_) { + case EstatTitol::INIT: + // Saltar a MAIN + estat_actual_ = EstatTitol::MAIN; + break; + case EstatTitol::MAIN: + // Anar al joc + GestorEscenes::actual = GestorEscenes::Escena::JOC; + break; + } + } +} diff --git a/source/game/escenes/escena_titol.hpp b/source/game/escenes/escena_titol.hpp new file mode 100644 index 0000000..f010a58 --- /dev/null +++ b/source/game/escenes/escena_titol.hpp @@ -0,0 +1,37 @@ +// escena_titol.hpp - Pantalla de títol del joc +// Mostra missatge "PRESS BUTTON TO PLAY" i copyright +// © 2025 Port a C++20 + +#pragma once + +#include + +#include "../../core/graphics/vector_text.hpp" +#include "../../core/rendering/sdl_manager.hpp" +#include "core/defaults.hpp" + +class EscenaTitol { + public: + explicit EscenaTitol(SDLManager& sdl); + 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 + }; + + SDLManager& sdl_; + Graphics::VectorText text_; // Sistema de text vectorial + EstatTitol estat_actual_; // Estat actual de la màquina + float temps_acumulat_; // Temps acumulat per l'estat INIT + + // Constants + static constexpr float DURACIO_INIT = 2.0f; // Duració de l'estat INIT (2 segons) + + // Mètodes privats + void actualitzar(float delta_time); + void dibuixar(); + void processar_events(const SDL_Event& event); +};