afegit sistema de punts

This commit is contained in:
2025-12-09 16:56:07 +01:00
parent 76165e4345
commit 94a7a38cdd
8 changed files with 251 additions and 5 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 <string>
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

View File

@@ -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 <SDL3/SDL.h>
#include <array>
#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<PuntuacioFlotant, MAX_PUNTUACIONS> pool_;
// Trobar primer slot inactiu
PuntuacioFlotant* trobar_slot_lliure();
};
} // namespace Effects

View File

@@ -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 <string>
#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

View File

@@ -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<int>(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

View File

@@ -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_;