From 94a7a38cddcd38daadfb9610ce089c4a7d0a1bb2 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Tue, 9 Dec 2025 16:56:07 +0100 Subject: [PATCH] afegit sistema de punts --- source/core/defaults.hpp | 18 ++++ source/core/graphics/vector_text.cpp | 4 +- source/core/graphics/vector_text.hpp | 3 +- .../game/effects/gestor_puntuacio_flotant.cpp | 99 +++++++++++++++++++ .../game/effects/gestor_puntuacio_flotant.hpp | 54 ++++++++++ source/game/effects/puntuacio_flotant.hpp | 33 +++++++ source/game/escenes/escena_joc.cpp | 42 +++++++- source/game/escenes/escena_joc.hpp | 3 + 8 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 source/game/effects/gestor_puntuacio_flotant.cpp create mode 100644 source/game/effects/gestor_puntuacio_flotant.hpp create mode 100644 source/game/effects/puntuacio_flotant.hpp diff --git a/source/core/defaults.hpp b/source/core/defaults.hpp index e3ca926..07302b5 100644 --- a/source/core/defaults.hpp +++ b/source/core/defaults.hpp @@ -254,5 +254,23 @@ namespace Spawn { constexpr float INVULNERABILITY_SCALE_END = 1.0f; // Full size } // namespace Spawn +// Scoring system (puntuació per tipus d'enemic) +namespace Scoring { + constexpr int PENTAGON_SCORE = 100; // Pentàgon (esquivador, 35 px/s) + constexpr int QUADRAT_SCORE = 150; // Quadrat (perseguidor, 40 px/s) + constexpr int MOLINILLO_SCORE = 200; // Molinillo (agressiu, 50 px/s) +} // namespace Scoring + } // namespace Enemies + +// Floating score numbers (números flotants de puntuació) +namespace FloatingScore { + constexpr float LIFETIME = 2.0f; // Duració màxima (segons) + constexpr float VELOCITY_Y = -30.0f; // Velocitat vertical (px/s, negatiu = amunt) + constexpr float VELOCITY_X = 0.0f; // Velocitat horizontal (px/s) + constexpr float SCALE = 0.75f; // Escala del text (0.75 = 75% del marcador) + constexpr float SPACING = 0.0f; // Espaiat entre caràcters + constexpr int MAX_CONCURRENT = 15; // Pool size (= MAX_ORNIS) +} // namespace FloatingScore + } // namespace Defaults diff --git a/source/core/graphics/vector_text.cpp b/source/core/graphics/vector_text.cpp index efb7d18..c4a9d58 100644 --- a/source/core/graphics/vector_text.cpp +++ b/source/core/graphics/vector_text.cpp @@ -181,7 +181,7 @@ bool VectorText::is_supported(char c) const { return chars_.find(c) != chars_.end(); } -void VectorText::render(const std::string& text, const Punt& posicio, float escala, float spacing) { +void VectorText::render(const std::string& text, const Punt& posicio, float escala, float spacing, float brightness) { if (!renderer_) { return; } @@ -223,7 +223,7 @@ void VectorText::render(const std::string& text, const Punt& posicio, float esca // Ajustar Y para que posicio represente esquina superior izquierda // (render_shape espera el centro, así que sumamos la mitad de la altura) Punt char_pos = {current_x, posicio.y + char_height_scaled / 2.0f}; - Rendering::render_shape(renderer_, it->second, char_pos, 0.0f, escala, true); + Rendering::render_shape(renderer_, it->second, char_pos, 0.0f, escala, true, 1.0f, brightness); // Avanzar posición current_x += char_width_scaled + spacing_scaled; diff --git a/source/core/graphics/vector_text.hpp b/source/core/graphics/vector_text.hpp index 50c6760..1120c7b 100644 --- a/source/core/graphics/vector_text.hpp +++ b/source/core/graphics/vector_text.hpp @@ -24,7 +24,8 @@ class VectorText { // - posicio: posición inicial (esquina superior izquierda) // - escala: factor de escala (1.0 = 20×40 px por carácter) // - spacing: espacio entre caracteres en píxeles (a escala 1.0) - void render(const std::string& text, const Punt& posicio, float escala = 1.0f, float spacing = 2.0f); + // - brightness: factor de brillantor (0.0-1.0, default 1.0 = màxima brillantor) + void render(const std::string& text, const Punt& posicio, float escala = 1.0f, float spacing = 2.0f, float brightness = 1.0f); // Calcular ancho total de un string (útil para centrado) float get_text_width(const std::string& text, float escala = 1.0f, float spacing = 2.0f) const; diff --git a/source/game/effects/gestor_puntuacio_flotant.cpp b/source/game/effects/gestor_puntuacio_flotant.cpp new file mode 100644 index 0000000..efbbb52 --- /dev/null +++ b/source/game/effects/gestor_puntuacio_flotant.cpp @@ -0,0 +1,99 @@ +// gestor_puntuacio_flotant.cpp - Implementació del gestor de números flotants +// © 2025 Port a C++20 amb SDL3 + +#include "gestor_puntuacio_flotant.hpp" + +#include + +namespace Effects { + +GestorPuntuacioFlotant::GestorPuntuacioFlotant(SDL_Renderer* renderer) + : renderer_(renderer), text_(renderer) { + // Inicialitzar tots els slots com inactius + for (auto& pf : pool_) { + pf.actiu = false; + } +} + +void GestorPuntuacioFlotant::crear(int punts, const Punt& posicio) { + // 1. Trobar slot lliure + PuntuacioFlotant* pf = trobar_slot_lliure(); + if (!pf) + return; // Pool ple (improbable) + + // 2. Inicialitzar puntuació flotant + pf->text = std::to_string(punts); + pf->posicio = posicio; + pf->velocitat = {Defaults::FloatingScore::VELOCITY_X, + Defaults::FloatingScore::VELOCITY_Y}; + pf->temps_vida = 0.0f; + pf->temps_max = Defaults::FloatingScore::LIFETIME; + pf->brightness = 1.0f; + pf->actiu = true; +} + +void GestorPuntuacioFlotant::actualitzar(float delta_time) { + for (auto& pf : pool_) { + if (!pf.actiu) + continue; + + // 1. Actualitzar posició (deriva cap amunt) + pf.posicio.x += pf.velocitat.x * delta_time; + pf.posicio.y += pf.velocitat.y * delta_time; + + // 2. Actualitzar temps de vida + pf.temps_vida += delta_time; + + // 3. Calcular brightness (fade lineal) + float progress = pf.temps_vida / pf.temps_max; // 0.0 → 1.0 + pf.brightness = 1.0f - progress; // 1.0 → 0.0 + + // 4. Desactivar quan acaba el temps + if (pf.temps_vida >= pf.temps_max) { + pf.actiu = false; + } + } +} + +void GestorPuntuacioFlotant::dibuixar() { + for (const auto& pf : pool_) { + if (!pf.actiu) + continue; + + // 1. Calcular dimensions del text per centrar-lo + constexpr float escala = Defaults::FloatingScore::SCALE; + constexpr float spacing = Defaults::FloatingScore::SPACING; + float text_width = text_.get_text_width(pf.text, escala, spacing); + + // 2. Centrar text sobre la posició + Punt render_pos = {pf.posicio.x - text_width / 2.0f, pf.posicio.y}; + + // 3. Renderitzar amb brightness (fade) + text_.render(pf.text, render_pos, escala, spacing, pf.brightness); + } +} + +void GestorPuntuacioFlotant::reiniciar() { + for (auto& pf : pool_) { + pf.actiu = false; + } +} + +int GestorPuntuacioFlotant::get_num_actius() const { + int count = 0; + for (const auto& pf : pool_) { + if (pf.actiu) + count++; + } + return count; +} + +PuntuacioFlotant* GestorPuntuacioFlotant::trobar_slot_lliure() { + for (auto& pf : pool_) { + if (!pf.actiu) + return &pf; + } + return nullptr; // Pool ple +} + +} // namespace Effects diff --git a/source/game/effects/gestor_puntuacio_flotant.hpp b/source/game/effects/gestor_puntuacio_flotant.hpp new file mode 100644 index 0000000..6c5243d --- /dev/null +++ b/source/game/effects/gestor_puntuacio_flotant.hpp @@ -0,0 +1,54 @@ +// gestor_puntuacio_flotant.hpp - Gestor de números de puntuació flotants +// © 2025 Port a C++20 amb SDL3 + +#pragma once + +#include + +#include + +#include "core/defaults.hpp" +#include "core/graphics/vector_text.hpp" +#include "core/types.hpp" +#include "puntuacio_flotant.hpp" + +namespace Effects { + +// Gestor de números de puntuació flotants +// Manté un pool de PuntuacioFlotant i gestiona el seu cicle de vida +class GestorPuntuacioFlotant { + public: + explicit GestorPuntuacioFlotant(SDL_Renderer* renderer); + + // Crear número flotant + // - punts: valor numèric (100, 150, 200) + // - posicio: on apareix (normalment centre d'enemic destruït) + void crear(int punts, const Punt& posicio); + + // Actualitzar tots els números actius + void actualitzar(float delta_time); + + // Dibuixar tots els números actius + void dibuixar(); + + // Reiniciar tots (neteja) + void reiniciar(); + + // Obtenir número actius (debug) + int get_num_actius() const; + + private: + SDL_Renderer* renderer_; + Graphics::VectorText text_; // Sistema de text vectorial + + // Pool de números flotants (màxim concurrent) + // Màxim 15 enemics simultanis = màxim 15 números + static constexpr int MAX_PUNTUACIONS = + Defaults::FloatingScore::MAX_CONCURRENT; + std::array pool_; + + // Trobar primer slot inactiu + PuntuacioFlotant* trobar_slot_lliure(); +}; + +} // namespace Effects diff --git a/source/game/effects/puntuacio_flotant.hpp b/source/game/effects/puntuacio_flotant.hpp new file mode 100644 index 0000000..2063c41 --- /dev/null +++ b/source/game/effects/puntuacio_flotant.hpp @@ -0,0 +1,33 @@ +// puntuacio_flotant.hpp - Número de puntuació que apareix i desapareix +// © 2025 Port a C++20 amb SDL3 + +#pragma once + +#include + +#include "core/types.hpp" + +namespace Effects { + +// PuntuacioFlotant: text animat que mostra punts guanyats +// S'activa quan es destrueix un enemic i s'esvaeix després d'un temps +struct PuntuacioFlotant { + // Text a mostrar (e.g., "100", "150", "200") + std::string text; + + // Posició actual (coordenades mundials) + Punt posicio; + + // Animació de moviment + Punt velocitat; // px/s (normalment cap amunt: {0.0f, -30.0f}) + + // Animació de fade + float temps_vida; // Temps transcorregut (segons) + float temps_max; // Temps de vida màxim (segons) + float brightness; // Brillantor calculada (0.0-1.0) + + // Estat + bool actiu; +}; + +} // namespace Effects diff --git a/source/game/escenes/escena_joc.cpp b/source/game/escenes/escena_joc.cpp index c7096fe..544fcad 100644 --- a/source/game/escenes/escena_joc.cpp +++ b/source/game/escenes/escena_joc.cpp @@ -26,8 +26,10 @@ EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context) : sdl_(sdl), context_(context), debris_manager_(sdl.obte_renderer()), + gestor_puntuacio_(sdl.obte_renderer()), nau_(sdl.obte_renderer()), itocado_(0), + puntuacio_total_(0), text_(sdl.obte_renderer()) { // Consumir opcions (preparació per MODE_DEMO futur) auto opcio = context_.consumir_opcio(); @@ -140,6 +142,10 @@ void EscenaJoc::inicialitzar() { game_over_ = false; game_over_timer_ = 0.0f; + // Initialize score + puntuacio_total_ = 0; + gestor_puntuacio_.reiniciar(); + // Set spawn point to center of play area Constants::obtenir_centre_zona(punt_spawn_.x, punt_spawn_.y); @@ -187,6 +193,7 @@ void EscenaJoc::actualitzar(float delta_time) { } debris_manager_.actualitzar(delta_time); + gestor_puntuacio_.actualitzar(delta_time); return; } @@ -224,6 +231,7 @@ void EscenaJoc::actualitzar(float delta_time) { } debris_manager_.actualitzar(delta_time); + gestor_puntuacio_.actualitzar(delta_time); return; } @@ -279,6 +287,7 @@ void EscenaJoc::actualitzar(float delta_time) { detectar_col·lisions_bales_enemics(); detectar_col·lisio_nau_enemics(); debris_manager_.actualitzar(delta_time); + gestor_puntuacio_.actualitzar(delta_time); break; } @@ -297,6 +306,7 @@ void EscenaJoc::actualitzar(float delta_time) { // [NEW] Update debris (from last destroyed enemies) debris_manager_.actualitzar(delta_time); + gestor_puntuacio_.actualitzar(delta_time); break; } } @@ -318,6 +328,7 @@ void EscenaJoc::dibuixar() { } debris_manager_.dibuixar(); + gestor_puntuacio_.dibuixar(); // Draw centered "GAME OVER" text const std::string game_over_text = "GAME OVER"; @@ -354,6 +365,7 @@ void EscenaJoc::dibuixar() { // [NEW] Draw debris debris_manager_.dibuixar(); + gestor_puntuacio_.dibuixar(); // [EXISTING] Draw intro message and score dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_START); @@ -375,6 +387,7 @@ void EscenaJoc::dibuixar() { } debris_manager_.dibuixar(); + gestor_puntuacio_.dibuixar(); dibuixar_marcador(); break; @@ -391,6 +404,7 @@ void EscenaJoc::dibuixar() { // [NEW] Draw debris (from last destroyed enemies) debris_manager_.dibuixar(); + gestor_puntuacio_.dibuixar(); // [EXISTING] Draw completion message and score dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_COMPLETED); @@ -518,7 +532,11 @@ void EscenaJoc::dibuixar_marcador() { std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num) : std::to_string(stage_num); - std::string text = "SCORE: 01000 LIFES: " + std::to_string(num_vides_) + + // Format score with padding to 5 digits (e.g., 150 → "00150") + std::string score_str = std::to_string(puntuacio_total_); + score_str = std::string(5 - std::min(5, static_cast(score_str.length())), '0') + score_str; + + std::string text = "SCORE: " + score_str + " LIFES: " + std::to_string(num_vides_) + " LEVEL: " + stage_str; // Paràmetres de renderització @@ -580,7 +598,27 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() { if (distancia_quadrada <= SUMA_RADIS_QUADRAT) { // *** COL·LISIÓ DETECTADA *** - // 1. Destruir enemic (marca com inactiu) + // 1. Calculate score for enemy type + int punts = 0; + switch (enemic.get_tipus()) { + case TipusEnemic::PENTAGON: + punts = Defaults::Enemies::Scoring::PENTAGON_SCORE; + break; + case TipusEnemic::QUADRAT: + punts = Defaults::Enemies::Scoring::QUADRAT_SCORE; + break; + case TipusEnemic::MOLINILLO: + punts = Defaults::Enemies::Scoring::MOLINILLO_SCORE; + break; + } + + // 2. Add to total score + puntuacio_total_ += punts; + + // 3. Create floating score number + gestor_puntuacio_.crear(punts, pos_enemic); + + // 4. Destruir enemic (marca com inactiu) enemic.destruir(); // 2. Crear explosió de fragments diff --git a/source/game/escenes/escena_joc.hpp b/source/game/escenes/escena_joc.hpp index c8b69f5..b0fda4e 100644 --- a/source/game/escenes/escena_joc.hpp +++ b/source/game/escenes/escena_joc.hpp @@ -12,6 +12,7 @@ #include "../constants.hpp" #include "../effects/debris_manager.hpp" +#include "../effects/gestor_puntuacio_flotant.hpp" #include "../entities/bala.hpp" #include "../entities/enemic.hpp" #include "../entities/nau.hpp" @@ -41,6 +42,7 @@ class EscenaJoc { // Efectes visuals Effects::DebrisManager debris_manager_; + Effects::GestorPuntuacioFlotant gestor_puntuacio_; // Estat del joc Nau nau_; @@ -54,6 +56,7 @@ class EscenaJoc { bool game_over_; // Game over state flag float game_over_timer_; // Countdown timer for auto-return (seconds) Punt punt_spawn_; // Configurable spawn point + int puntuacio_total_; // Current score // Text vectorial Graphics::VectorText text_;