layout de TITOL

This commit is contained in:
2025-12-17 11:32:37 +01:00
parent 886ec8ab1d
commit 3b432e6580
45 changed files with 880 additions and 745 deletions

View File

@@ -18,9 +18,9 @@ struct Debris {
float acceleracio; // Acceleració negativa (fricció) en px/s²
// Rotació
float angle_rotacio; // Angle de rotació acumulat (radians)
float velocitat_rot; // Velocitat de rotació de TRAYECTORIA (rad/s)
float velocitat_rot_visual; // Velocitat de rotació VISUAL del segment (rad/s)
float angle_rotacio; // Angle de rotació acumulat (radians)
float velocitat_rot; // Velocitat de rotació de TRAYECTORIA (rad/s)
float velocitat_rot_visual; // Velocitat de rotació VISUAL del segment (rad/s)
// Estat de vida
float temps_vida; // Temps transcorregut (segons)

View File

@@ -8,7 +8,8 @@
namespace Effects {
GestorPuntuacioFlotant::GestorPuntuacioFlotant(SDL_Renderer* renderer)
: renderer_(renderer), text_(renderer) {
: renderer_(renderer),
text_(renderer) {
// Inicialitzar tots els slots com inactius
for (auto& pf : pool_) {
pf.actiu = false;
@@ -25,7 +26,7 @@ void GestorPuntuacioFlotant::crear(int punts, const Punt& posicio) {
pf->text = std::to_string(punts);
pf->posicio = posicio;
pf->velocitat = {Defaults::FloatingScore::VELOCITY_X,
Defaults::FloatingScore::VELOCITY_Y};
Defaults::FloatingScore::VELOCITY_Y};
pf->temps_vida = 0.0f;
pf->temps_max = Defaults::FloatingScore::LIFETIME;
pf->brightness = 1.0f;

View File

@@ -17,38 +17,38 @@ 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);
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);
// 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);
// Actualitzar tots els números actius
void actualitzar(float delta_time);
// Dibuixar tots els números actius
void dibuixar();
// Dibuixar tots els números actius
void dibuixar();
// Reiniciar tots (neteja)
void reiniciar();
// Reiniciar tots (neteja)
void reiniciar();
// Obtenir número actius (debug)
int get_num_actius() const;
// Obtenir número actius (debug)
int get_num_actius() const;
private:
SDL_Renderer* renderer_;
Graphics::VectorText text_; // Sistema de text vectorial
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_;
// 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();
// Trobar primer slot inactiu
PuntuacioFlotant* trobar_slot_lliure();
};
} // namespace Effects

View File

@@ -12,22 +12,22 @@ 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;
// Text a mostrar (e.g., "100", "150", "200")
std::string text;
// Posició actual (coordenades mundials)
Punt posicio;
// Posició actual (coordenades mundials)
Punt posicio;
// Animació de moviment
Punt velocitat; // px/s (normalment cap amunt: {0.0f, -30.0f})
// 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)
// 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;
// Estat
bool actiu;
};
} // namespace Effects

View File

@@ -25,7 +25,7 @@ Enemic::Enemic(SDL_Renderer* renderer)
tipus_(TipusEnemic::PENTAGON),
tracking_timer_(0.0f),
ship_position_(nullptr),
tracking_strength_(0.5f), // Default tracking strength
tracking_strength_(0.5f), // Default tracking strength
timer_invulnerabilitat_(0.0f) { // Start vulnerable
// [NUEVO] Forma es carrega a inicialitzar() segons el tipus
// Constructor no carrega forma per permetre tipus diferents
@@ -126,7 +126,7 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Punt* ship_pos) {
// [NEW] Inicialitzar invulnerabilitat
timer_invulnerabilitat_ = Defaults::Enemies::Spawn::INVULNERABILITY_DURATION; // 3.0s
brightness_ = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; // 0.3f
brightness_ = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START; // 0.3f
// Activar
esta_ = true;
@@ -144,7 +144,7 @@ void Enemic::actualitzar(float delta_time) {
// [NEW] Update brightness with LERP during invulnerability
float t_inv = timer_invulnerabilitat_ / Defaults::Enemies::Spawn::INVULNERABILITY_DURATION;
float t = 1.0f - t_inv; // 0.0 → 1.0
float t = 1.0f - t_inv; // 0.0 → 1.0
float smooth_t = t * t * (3.0f - 2.0f * t); // smoothstep
constexpr float START = Defaults::Enemies::Spawn::INVULNERABILITY_BRIGHTNESS_START;

View File

@@ -54,8 +54,7 @@ class Enemic {
Punt get_velocitat_vector() const {
return {
velocitat_ * std::cos(angle_ - Constants::PI / 2.0f),
velocitat_ * std::sin(angle_ - Constants::PI / 2.0f)
};
velocitat_ * std::sin(angle_ - Constants::PI / 2.0f)};
}
// Set ship position reference for tracking behavior
@@ -68,7 +67,10 @@ class Enemic {
// [NEW] Setters for difficulty multipliers (stage system)
void set_velocity(float vel) { velocitat_ = vel; }
void set_rotation(float rot) { drotacio_ = rot; animacio_.drotacio_base = rot; }
void set_rotation(float rot) {
drotacio_ = rot;
animacio_.drotacio_base = rot;
}
void set_tracking_strength(float strength);
// [NEW] Invulnerability queries

View File

@@ -140,7 +140,7 @@ void Nau::dibuixar() const {
if (es_invulnerable()) {
// Calcular ciclo de parpadeo
float blink_cycle = Defaults::Ship::BLINK_VISIBLE_TIME +
Defaults::Ship::BLINK_INVISIBLE_TIME;
Defaults::Ship::BLINK_INVISIBLE_TIME;
float time_in_cycle = std::fmod(invulnerable_timer_, blink_cycle);
// Si estamos en fase invisible, no dibujar

View File

@@ -33,8 +33,7 @@ class Nau {
Punt get_velocitat_vector() const {
return {
velocitat_ * std::cos(angle_ - Constants::PI / 2.0f),
velocitat_ * std::sin(angle_ - Constants::PI / 2.0f)
};
velocitat_ * std::sin(angle_ - Constants::PI / 2.0f)};
}
// Setters
@@ -55,7 +54,7 @@ class Nau {
float angle_; // Angle d'orientació
float velocitat_; // Velocitat (px/s)
bool esta_tocada_;
float brightness_; // Factor de brillantor (0.0-1.0)
float brightness_; // Factor de brillantor (0.0-1.0)
float invulnerable_timer_; // 0.0f = vulnerable, >0.0f = invulnerable
void aplicar_fisica(float delta_time);

View File

@@ -323,22 +323,19 @@ void EscenaJoc::actualitzar(float delta_time) {
}
// Calcular global progress (0.0 al inicio → 1.0 al final)
float global_progress = 1.0f - (stage_manager_->get_timer_transicio() /
Defaults::Game::INIT_HUD_DURATION);
float global_progress = 1.0f - (stage_manager_->get_timer_transicio() / Defaults::Game::INIT_HUD_DURATION);
global_progress = std::min(1.0f, global_progress);
// [NEW] Calcular progress independiente para cada nave
float ship1_progress = calcular_progress_rango(
global_progress,
Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP1_RATIO_END
);
Defaults::Game::INIT_HUD_SHIP1_RATIO_END);
float ship2_progress = calcular_progress_rango(
global_progress,
Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP2_RATIO_END
);
Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
// [MODIFICAT] Animar AMBAS naus con sus progress respectivos
if (config_partida_.jugador1_actiu && ship1_progress < 1.0f) {
@@ -501,26 +498,22 @@ void EscenaJoc::dibuixar() {
float rect_progress = calcular_progress_rango(
global_progress,
Defaults::Game::INIT_HUD_RECT_RATIO_INIT,
Defaults::Game::INIT_HUD_RECT_RATIO_END
);
Defaults::Game::INIT_HUD_RECT_RATIO_END);
float score_progress = calcular_progress_rango(
global_progress,
Defaults::Game::INIT_HUD_SCORE_RATIO_INIT,
Defaults::Game::INIT_HUD_SCORE_RATIO_END
);
Defaults::Game::INIT_HUD_SCORE_RATIO_END);
float ship1_progress = calcular_progress_rango(
global_progress,
Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP1_RATIO_END
);
Defaults::Game::INIT_HUD_SHIP1_RATIO_END);
float ship2_progress = calcular_progress_rango(
global_progress,
Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
Defaults::Game::INIT_HUD_SHIP2_RATIO_END
);
Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
// Dibuixar elements animats
if (rect_progress > 0.0f) {
@@ -643,15 +636,15 @@ void EscenaJoc::tocado(uint8_t player_id) {
Punt vel_nau_80 = {vel_nau.x * 0.8f, vel_nau.y * 0.8f};
debris_manager_.explotar(
naus_[player_id].get_forma(), // Ship shape (3 lines)
ship_pos, // Center position
ship_angle, // Ship orientation
1.0f, // Normal scale
Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s
naus_[player_id].get_brightness(), // Heredar brightness
vel_nau_80, // Heredar 80% velocitat
0.0f, // Nave: trayectorias rectas (sin drotacio)
0.0f // Sin herencia visual (rotación aleatoria)
naus_[player_id].get_forma(), // Ship shape (3 lines)
ship_pos, // Center position
ship_angle, // Ship orientation
1.0f, // Normal scale
Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s
naus_[player_id].get_brightness(), // Heredar brightness
vel_nau_80, // Heredar 80% velocitat
0.0f, // Nave: trayectorias rectas (sin drotacio)
0.0f // Sin herencia visual (rotación aleatoria)
);
Audio::get()->playSound(Defaults::Sound::EXPLOSION, Audio::Group::GAME);
@@ -863,7 +856,7 @@ std::string EscenaJoc::construir_marcador() const {
// Nivell (2 dígits)
uint8_t stage_num = stage_manager_->get_stage_actual();
std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num)
: std::to_string(stage_num);
: std::to_string(stage_num);
// Puntuació P2 (6 dígits) - mostrar zeros si inactiu
std::string score_p2;
@@ -946,15 +939,15 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() {
// 2. Crear explosió de fragments
Punt vel_enemic = enemic.get_velocitat_vector();
debris_manager_.explotar(
enemic.get_forma(), // Forma vectorial del pentàgon
pos_enemic, // Posició central
0.0f, // Angle (enemic té rotació interna)
1.0f, // Escala normal
VELOCITAT_EXPLOSIO, // 50 px/s (explosió suau)
enemic.get_brightness(), // Heredar brightness
vel_enemic, // Heredar velocitat
enemic.get_drotacio(), // Heredar velocitat angular (trayectorias curvas)
0.0f // Sin herencia visual (rotación aleatoria)
enemic.get_forma(), // Forma vectorial del pentàgon
pos_enemic, // Posició central
0.0f, // Angle (enemic té rotació interna)
1.0f, // Escala normal
VELOCITAT_EXPLOSIO, // 50 px/s (explosió suau)
enemic.get_brightness(), // Heredar brightness
vel_enemic, // Heredar velocitat
enemic.get_drotacio(), // Heredar velocitat angular (trayectorias curvas)
0.0f // Sin herencia visual (rotación aleatoria)
);
// 3. Desactivar bala
@@ -1063,8 +1056,8 @@ void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
// Auto-scale if text exceeds max width
float escala = (text_width_at_base <= max_width)
? escala_base
: max_width / text_width_at_base;
? escala_base
: max_width / text_width_at_base;
// Recalculate dimensions with final scale (using FULL message for centering)
float full_text_width = text_.get_text_width(missatge, escala, spacing);
@@ -1099,8 +1092,7 @@ Punt EscenaJoc::obtenir_punt_spawn(uint8_t player_id) const {
return {
zona.x + zona.w * x_ratio,
zona.y + zona.h * Defaults::Game::SPAWN_Y_RATIO
};
zona.y + zona.h * Defaults::Game::SPAWN_Y_RATIO};
}
void EscenaJoc::disparar_bala(uint8_t player_id) {

View File

@@ -9,6 +9,7 @@
#include <array>
#include <cstdint>
#include <memory>
#include "../constants.hpp"
#include "../effects/debris_manager.hpp"
@@ -23,8 +24,6 @@
#include "core/system/game_config.hpp"
#include "core/types.hpp"
#include <memory>
// Classe principal del joc (escena)
class EscenaJoc {
public:
@@ -57,7 +56,7 @@ class EscenaJoc {
bool game_over_; // Game over state flag
float game_over_timer_; // Countdown timer for auto-return (seconds)
// Punt punt_spawn_; // DEPRECATED: usar obtenir_punt_spawn(player_id)
Punt punt_mort_; // Death position (for respawn, legacy)
Punt punt_mort_; // Death position (for respawn, legacy)
std::array<int, 2> puntuacio_per_jugador_; // [0]=P1, [1]=P2
// Text vectorial
@@ -72,20 +71,20 @@ class EscenaJoc {
// Funcions privades
void tocado(uint8_t player_id);
void detectar_col·lisions_bales_enemics(); // Col·lisions bala-enemic
void detectar_col·lisio_naus_enemics(); // Ship-enemy collision detection (plural)
void dibuixar_marges() const; // Dibuixar vores de la zona de joc
void dibuixar_marcador(); // Dibuixar marcador de puntuació
void disparar_bala(uint8_t player_id); // Shoot bullet from player
void detectar_col·lisions_bales_enemics(); // Col·lisions bala-enemic
void detectar_col·lisio_naus_enemics(); // Ship-enemy collision detection (plural)
void dibuixar_marges() const; // Dibuixar vores de la zona de joc
void dibuixar_marcador(); // Dibuixar marcador de puntuació
void disparar_bala(uint8_t player_id); // Shoot bullet from player
Punt obtenir_punt_spawn(uint8_t player_id) const; // Get spawn position for player
// [NEW] Stage system helpers
void dibuixar_missatge_stage(const std::string& missatge);
// [NEW] Funcions d'animació per INIT_HUD
void dibuixar_marges_animat(float progress) const; // Rectangle amb creixement uniforme
void dibuixar_marcador_animat(float progress); // Marcador que puja des de baix
Punt calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const; // Posició animada de la nau
void dibuixar_marges_animat(float progress) const; // Rectangle amb creixement uniforme
void dibuixar_marcador_animat(float progress); // Marcador que puja des de baix
Punt calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const; // Posició animada de la nau
// [NEW] Función helper del sistema de animación INIT_HUD
float calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const;

View File

@@ -220,10 +220,8 @@ void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) {
std::random_device rd;
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");
} else if (nou_estat == EstatAnimacio::POST_EXPLOSION) {
Audio::get()->playMusic("title.ogg");
}
std::cout << "[EscenaLogo] Canvi a estat: " << static_cast<int>(nou_estat)
@@ -246,13 +244,13 @@ void EscenaLogo::actualitzar_explosions(float delta_time) {
const auto& lletra = lletres_[index_actual];
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
1.0f, // Brightness màxim (per defecte)
{0.0f, 0.0f} // Sense velocitat (per defecte)
lletra.forma, // Forma a explotar
lletra.posicio, // Posició
0.0f, // Angle (sense rotació)
ESCALA_FINAL, // Escala (lletres a escala final)
VELOCITAT_EXPLOSIO, // Velocitat base
1.0f, // Brightness màxim (per defecte)
{0.0f, 0.0f} // Sense velocitat (per defecte)
);
std::cout << "[EscenaLogo] Explota lletra " << lletra_explosio_index_ << "\n";

View File

@@ -10,18 +10,18 @@
#include <memory>
#include <vector>
#include "game/effects/debris_manager.hpp"
#include "core/defaults.hpp"
#include "core/graphics/shape.hpp"
#include "core/input/input_types.hpp"
#include "core/rendering/sdl_manager.hpp"
#include "core/system/context_escenes.hpp"
#include "core/types.hpp"
#include "game/effects/debris_manager.hpp"
class EscenaLogo {
public:
explicit EscenaLogo(SDLManager& sdl, GestorEscenes::ContextEscenes& context);
~EscenaLogo(); // Destructor per aturar sons
~EscenaLogo(); // Destructor per aturar sons
void executar(); // Bucle principal de l'escena
private:

View File

@@ -140,10 +140,10 @@ void EscenaTitol::inicialitzar_titol() {
float ancho_sin_escalar = max_x - min_x;
float altura_sin_escalar = max_y - min_y;
// Escalar ancho, altura i offset amb ESCALA_TITULO
float ancho = ancho_sin_escalar * ESCALA_TITULO;
float altura = altura_sin_escalar * ESCALA_TITULO;
float offset_centre = (forma->get_centre().x - min_x) * ESCALA_TITULO;
// Escalar ancho, altura i offset amb LOGO_SCALE
float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float offset_centre = (forma->get_centre().x - min_x) * Defaults::Title::Layout::LOGO_SCALE;
lletres_orni_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre});
@@ -159,7 +159,7 @@ void EscenaTitol::inicialitzar_titol() {
for (auto& lletra : lletres_orni_) {
lletra.posicio.x = x_actual + lletra.offset_centre;
lletra.posicio.y = Y_ORNI;
lletra.posicio.y = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS;
x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
}
@@ -169,7 +169,9 @@ void EscenaTitol::inicialitzar_titol() {
// === Calcular posició Y dinàmica per "ATTACK!" ===
// Totes les lletres ORNI tenen la mateixa altura, utilitzem la primera
float altura_orni = lletres_orni_.empty() ? 50.0f : lletres_orni_[0].altura;
y_attack_dinamica_ = Y_ORNI + altura_orni + SEPARACION_LINEAS;
float y_orni = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_POS;
float separacion_lineas = Defaults::Game::HEIGHT * Defaults::Title::Layout::LOGO_LINE_SPACING;
y_attack_dinamica_ = y_orni + altura_orni + separacion_lineas;
std::cout << "[EscenaTitol] Altura ORNI: " << altura_orni
<< " px, Y_ATTACK dinàmica: " << y_attack_dinamica_ << " px\n";
@@ -212,10 +214,10 @@ void EscenaTitol::inicialitzar_titol() {
float ancho_sin_escalar = max_x - min_x;
float altura_sin_escalar = max_y - min_y;
// Escalar ancho, altura i offset amb ESCALA_TITULO
float ancho = ancho_sin_escalar * ESCALA_TITULO;
float altura = altura_sin_escalar * ESCALA_TITULO;
float offset_centre = (forma->get_centre().x - min_x) * ESCALA_TITULO;
// Escalar ancho, altura i offset amb LOGO_SCALE
float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float offset_centre = (forma->get_centre().x - min_x) * Defaults::Title::Layout::LOGO_SCALE;
lletres_attack_.push_back({forma, {0.0f, 0.0f}, ancho, altura, offset_centre});
@@ -326,9 +328,9 @@ void EscenaTitol::actualitzar(float delta_time) {
// Actualitzar naus (quan visibles)
if (ship_animator_ &&
(estat_actual_ == EstatTitol::STARFIELD_FADE_IN ||
estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
ship_animator_->actualitzar(delta_time);
}
@@ -346,7 +348,7 @@ void EscenaTitol::actualitzar(float delta_time) {
// Transició a STARFIELD quan el fade es completa
if (temps_acumulat_ >= DURACIO_FADE_IN) {
estat_actual_ = EstatTitol::STARFIELD;
temps_acumulat_ = 0.0f; // Reset timer per al següent estat
temps_acumulat_ = 0.0f; // Reset timer per al següent estat
starfield_->set_brightness(BRIGHTNESS_STARFIELD); // Assegurar valor final
}
break;
@@ -538,9 +540,9 @@ void EscenaTitol::dibuixar() {
// Dibuixar naus (després starfield, abans logo)
if (ship_animator_ &&
(estat_actual_ == EstatTitol::STARFIELD_FADE_IN ||
estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
ship_animator_->dibuixar();
}
@@ -580,7 +582,7 @@ void EscenaTitol::dibuixar() {
lletres_orni_[i].forma,
pos_shadow,
0.0f,
ESCALA_TITULO,
Defaults::Title::Layout::LOGO_SCALE,
true,
1.0f, // progress = 1.0 (totalment visible)
SHADOW_BRIGHTNESS // brightness = 0.4 (brillantor reduïda)
@@ -598,7 +600,7 @@ void EscenaTitol::dibuixar() {
lletres_attack_[i].forma,
pos_shadow,
0.0f,
ESCALA_TITULO,
Defaults::Title::Layout::LOGO_SCALE,
true,
1.0f, // progress = 1.0 (totalment visible)
SHADOW_BRIGHTNESS);
@@ -614,7 +616,7 @@ void EscenaTitol::dibuixar() {
lletra.forma,
lletra.posicio,
0.0f,
ESCALA_TITULO,
Defaults::Title::Layout::LOGO_SCALE,
true,
1.0f // Brillantor completa
);
@@ -627,7 +629,7 @@ void EscenaTitol::dibuixar() {
lletra.forma,
lletra.posicio,
0.0f,
ESCALA_TITULO,
Defaults::Title::Layout::LOGO_SCALE,
true,
1.0f // Brillantor completa
);
@@ -637,7 +639,7 @@ void EscenaTitol::dibuixar() {
// En estat MAIN: sempre visible
// En estat TRANSITION: parpellejant (blink amb sinusoide)
const float spacing = 2.0f; // Espai entre caràcters (usat també per copyright)
const float spacing = Defaults::Title::Layout::TEXT_SPACING;
bool mostrar_text = true;
if (estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) {
@@ -648,34 +650,46 @@ void EscenaTitol::dibuixar() {
if (mostrar_text) {
const std::string main_text = "PRESS START TO PLAY";
const float escala_main = 1.0f;
const float escala_main = Defaults::Title::Layout::PRESS_START_SCALE;
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;
float y_center = Defaults::Game::HEIGHT * Defaults::Title::Layout::PRESS_START_POS;
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
}
// === Copyright a la part inferior (centrat horitzontalment, dues línies) ===
const float escala_copy = Defaults::Title::Layout::COPYRIGHT_SCALE;
const float copy_height = text_.get_text_height(escala_copy);
const float line_spacing = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT_LINE_SPACING;
// Línea 1: Original (© 1999 Visente i Sergi)
std::string copyright_original = Project::COPYRIGHT_ORIGINAL;
for (char& c : copyright_original) {
if (c >= 'a' && c <= 'z') c = c - 32; // 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);
// Línea 2: Port (© 2025 jaildesigner)
std::string copyright_port = Project::COPYRIGHT_PORT;
for (char& c : copyright_port) {
if (c >= 'a' && c <= 'z') c = c - 32; // Uppercase
}
float x_copy = (Defaults::Game::WIDTH - copy_width) / 2.0f;
float y_copy = Defaults::Game::HEIGHT - copy_height - 20.0f; // 20px des del fons
// Calcular posicions (anclatge des del top + separació)
float y_line1 = Defaults::Game::HEIGHT * Defaults::Title::Layout::COPYRIGHT1_POS;
float y_line2 = y_line1 + copy_height + line_spacing; // Línea 2 debajo de línea 1
text_.render(copyright, Punt{x_copy, y_copy}, escala_copy, spacing);
// Renderitzar línea 1 (original)
float width_line1 = text_.get_text_width(copyright_original, escala_copy, spacing);
float x_line1 = (Defaults::Game::WIDTH - width_line1) / 2.0f;
text_.render(copyright_original, Punt{x_line1, y_line1}, escala_copy, spacing);
// Renderitzar línea 2 (port)
float width_line2 = text_.get_text_width(copyright_port, escala_copy, spacing);
float x_line2 = (Defaults::Game::WIDTH - width_line2) / 2.0f;
text_.render(copyright_port, Punt{x_line2, y_line2}, escala_copy, spacing);
}
}

View File

@@ -23,8 +23,7 @@
// Botones para INICIAR PARTIDA desde MAIN (solo START)
static constexpr std::array<InputAction, 1> START_GAME_BUTTONS = {
InputAction::START
};
InputAction::START};
class EscenaTitol {
public:
@@ -53,12 +52,12 @@ class EscenaTitol {
SDLManager& sdl_;
GestorEscenes::ContextEscenes& context_;
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
GameConfig::ConfigPartida config_partida_; // Configuració de jugadors actius
Graphics::VectorText text_; // Sistema de text vectorial
std::unique_ptr<Graphics::Starfield> starfield_; // Camp d'estrelles de fons
std::unique_ptr<Graphics::Starfield> starfield_; // Camp d'estrelles de fons
std::unique_ptr<Title::ShipAnimator> ship_animator_; // Naus 3D flotants
EstatTitol estat_actual_; // Estat actual de la màquina
float temps_acumulat_; // Temps acumulat per l'estat INIT
EstatTitol estat_actual_; // Estat actual de la màquina
float temps_acumulat_; // Temps acumulat per l'estat INIT
// Lletres del títol "ORNI ATTACK!"
std::vector<LetraLogo> lletres_orni_; // Lletres de "ORNI" (línia 1)
@@ -80,10 +79,7 @@ class EscenaTitol {
static constexpr float DURACIO_FADE_IN = 3.0f; // Duració del fade-in del starfield (1.5 segons)
static constexpr float DURACIO_INIT = 4.0f; // Duració de l'estat INIT (2 segons)
static constexpr float DURACIO_TRANSITION = 2.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 BLINK_FREQUENCY = 3.0f; // Freqüència de parpelleig (3 Hz)
static constexpr float DURACIO_BLACK_SCREEN = 2.0f; // Duració pantalla negra (2 segons)
static constexpr int MUSIC_FADE = 1500; // Duracio del fade de la musica del titol al començar a jugar

View File

@@ -15,107 +15,169 @@ namespace Options {
// Mapa de SDL_Scancode a string
static const std::unordered_map<SDL_Scancode, std::string> SCANCODE_TO_STRING = {
{SDL_SCANCODE_A, "A"}, {SDL_SCANCODE_B, "B"}, {SDL_SCANCODE_C, "C"}, {SDL_SCANCODE_D, "D"},
{SDL_SCANCODE_E, "E"}, {SDL_SCANCODE_F, "F"}, {SDL_SCANCODE_G, "G"}, {SDL_SCANCODE_H, "H"},
{SDL_SCANCODE_I, "I"}, {SDL_SCANCODE_J, "J"}, {SDL_SCANCODE_K, "K"}, {SDL_SCANCODE_L, "L"},
{SDL_SCANCODE_M, "M"}, {SDL_SCANCODE_N, "N"}, {SDL_SCANCODE_O, "O"}, {SDL_SCANCODE_P, "P"},
{SDL_SCANCODE_Q, "Q"}, {SDL_SCANCODE_R, "R"}, {SDL_SCANCODE_S, "S"}, {SDL_SCANCODE_T, "T"},
{SDL_SCANCODE_U, "U"}, {SDL_SCANCODE_V, "V"}, {SDL_SCANCODE_W, "W"}, {SDL_SCANCODE_X, "X"},
{SDL_SCANCODE_Y, "Y"}, {SDL_SCANCODE_Z, "Z"},
{SDL_SCANCODE_1, "1"}, {SDL_SCANCODE_2, "2"}, {SDL_SCANCODE_3, "3"}, {SDL_SCANCODE_4, "4"},
{SDL_SCANCODE_5, "5"}, {SDL_SCANCODE_6, "6"}, {SDL_SCANCODE_7, "7"}, {SDL_SCANCODE_8, "8"},
{SDL_SCANCODE_9, "9"}, {SDL_SCANCODE_0, "0"},
{SDL_SCANCODE_RETURN, "RETURN"}, {SDL_SCANCODE_ESCAPE, "ESCAPE"},
{SDL_SCANCODE_BACKSPACE, "BACKSPACE"}, {SDL_SCANCODE_TAB, "TAB"},
{SDL_SCANCODE_SPACE, "SPACE"},
{SDL_SCANCODE_UP, "UP"}, {SDL_SCANCODE_DOWN, "DOWN"},
{SDL_SCANCODE_LEFT, "LEFT"}, {SDL_SCANCODE_RIGHT, "RIGHT"},
{SDL_SCANCODE_LSHIFT, "LSHIFT"}, {SDL_SCANCODE_RSHIFT, "RSHIFT"},
{SDL_SCANCODE_LCTRL, "LCTRL"}, {SDL_SCANCODE_RCTRL, "RCTRL"},
{SDL_SCANCODE_LALT, "LALT"}, {SDL_SCANCODE_RALT, "RALT"}
};
{SDL_SCANCODE_A, "A"},
{SDL_SCANCODE_B, "B"},
{SDL_SCANCODE_C, "C"},
{SDL_SCANCODE_D, "D"},
{SDL_SCANCODE_E, "E"},
{SDL_SCANCODE_F, "F"},
{SDL_SCANCODE_G, "G"},
{SDL_SCANCODE_H, "H"},
{SDL_SCANCODE_I, "I"},
{SDL_SCANCODE_J, "J"},
{SDL_SCANCODE_K, "K"},
{SDL_SCANCODE_L, "L"},
{SDL_SCANCODE_M, "M"},
{SDL_SCANCODE_N, "N"},
{SDL_SCANCODE_O, "O"},
{SDL_SCANCODE_P, "P"},
{SDL_SCANCODE_Q, "Q"},
{SDL_SCANCODE_R, "R"},
{SDL_SCANCODE_S, "S"},
{SDL_SCANCODE_T, "T"},
{SDL_SCANCODE_U, "U"},
{SDL_SCANCODE_V, "V"},
{SDL_SCANCODE_W, "W"},
{SDL_SCANCODE_X, "X"},
{SDL_SCANCODE_Y, "Y"},
{SDL_SCANCODE_Z, "Z"},
{SDL_SCANCODE_1, "1"},
{SDL_SCANCODE_2, "2"},
{SDL_SCANCODE_3, "3"},
{SDL_SCANCODE_4, "4"},
{SDL_SCANCODE_5, "5"},
{SDL_SCANCODE_6, "6"},
{SDL_SCANCODE_7, "7"},
{SDL_SCANCODE_8, "8"},
{SDL_SCANCODE_9, "9"},
{SDL_SCANCODE_0, "0"},
{SDL_SCANCODE_RETURN, "RETURN"},
{SDL_SCANCODE_ESCAPE, "ESCAPE"},
{SDL_SCANCODE_BACKSPACE, "BACKSPACE"},
{SDL_SCANCODE_TAB, "TAB"},
{SDL_SCANCODE_SPACE, "SPACE"},
{SDL_SCANCODE_UP, "UP"},
{SDL_SCANCODE_DOWN, "DOWN"},
{SDL_SCANCODE_LEFT, "LEFT"},
{SDL_SCANCODE_RIGHT, "RIGHT"},
{SDL_SCANCODE_LSHIFT, "LSHIFT"},
{SDL_SCANCODE_RSHIFT, "RSHIFT"},
{SDL_SCANCODE_LCTRL, "LCTRL"},
{SDL_SCANCODE_RCTRL, "RCTRL"},
{SDL_SCANCODE_LALT, "LALT"},
{SDL_SCANCODE_RALT, "RALT"}};
// Mapa invers: string a SDL_Scancode
static const std::unordered_map<std::string, SDL_Scancode> STRING_TO_SCANCODE = {
{"A", SDL_SCANCODE_A}, {"B", SDL_SCANCODE_B}, {"C", SDL_SCANCODE_C}, {"D", SDL_SCANCODE_D},
{"E", SDL_SCANCODE_E}, {"F", SDL_SCANCODE_F}, {"G", SDL_SCANCODE_G}, {"H", SDL_SCANCODE_H},
{"I", SDL_SCANCODE_I}, {"J", SDL_SCANCODE_J}, {"K", SDL_SCANCODE_K}, {"L", SDL_SCANCODE_L},
{"M", SDL_SCANCODE_M}, {"N", SDL_SCANCODE_N}, {"O", SDL_SCANCODE_O}, {"P", SDL_SCANCODE_P},
{"Q", SDL_SCANCODE_Q}, {"R", SDL_SCANCODE_R}, {"S", SDL_SCANCODE_S}, {"T", SDL_SCANCODE_T},
{"U", SDL_SCANCODE_U}, {"V", SDL_SCANCODE_V}, {"W", SDL_SCANCODE_W}, {"X", SDL_SCANCODE_X},
{"Y", SDL_SCANCODE_Y}, {"Z", SDL_SCANCODE_Z},
{"1", SDL_SCANCODE_1}, {"2", SDL_SCANCODE_2}, {"3", SDL_SCANCODE_3}, {"4", SDL_SCANCODE_4},
{"5", SDL_SCANCODE_5}, {"6", SDL_SCANCODE_6}, {"7", SDL_SCANCODE_7}, {"8", SDL_SCANCODE_8},
{"9", SDL_SCANCODE_9}, {"0", SDL_SCANCODE_0},
{"RETURN", SDL_SCANCODE_RETURN}, {"ESCAPE", SDL_SCANCODE_ESCAPE},
{"BACKSPACE", SDL_SCANCODE_BACKSPACE}, {"TAB", SDL_SCANCODE_TAB},
{"SPACE", SDL_SCANCODE_SPACE},
{"UP", SDL_SCANCODE_UP}, {"DOWN", SDL_SCANCODE_DOWN},
{"LEFT", SDL_SCANCODE_LEFT}, {"RIGHT", SDL_SCANCODE_RIGHT},
{"LSHIFT", SDL_SCANCODE_LSHIFT}, {"RSHIFT", SDL_SCANCODE_RSHIFT},
{"LCTRL", SDL_SCANCODE_LCTRL}, {"RCTRL", SDL_SCANCODE_RCTRL},
{"LALT", SDL_SCANCODE_LALT}, {"RALT", SDL_SCANCODE_RALT}
};
{"A", SDL_SCANCODE_A},
{"B", SDL_SCANCODE_B},
{"C", SDL_SCANCODE_C},
{"D", SDL_SCANCODE_D},
{"E", SDL_SCANCODE_E},
{"F", SDL_SCANCODE_F},
{"G", SDL_SCANCODE_G},
{"H", SDL_SCANCODE_H},
{"I", SDL_SCANCODE_I},
{"J", SDL_SCANCODE_J},
{"K", SDL_SCANCODE_K},
{"L", SDL_SCANCODE_L},
{"M", SDL_SCANCODE_M},
{"N", SDL_SCANCODE_N},
{"O", SDL_SCANCODE_O},
{"P", SDL_SCANCODE_P},
{"Q", SDL_SCANCODE_Q},
{"R", SDL_SCANCODE_R},
{"S", SDL_SCANCODE_S},
{"T", SDL_SCANCODE_T},
{"U", SDL_SCANCODE_U},
{"V", SDL_SCANCODE_V},
{"W", SDL_SCANCODE_W},
{"X", SDL_SCANCODE_X},
{"Y", SDL_SCANCODE_Y},
{"Z", SDL_SCANCODE_Z},
{"1", SDL_SCANCODE_1},
{"2", SDL_SCANCODE_2},
{"3", SDL_SCANCODE_3},
{"4", SDL_SCANCODE_4},
{"5", SDL_SCANCODE_5},
{"6", SDL_SCANCODE_6},
{"7", SDL_SCANCODE_7},
{"8", SDL_SCANCODE_8},
{"9", SDL_SCANCODE_9},
{"0", SDL_SCANCODE_0},
{"RETURN", SDL_SCANCODE_RETURN},
{"ESCAPE", SDL_SCANCODE_ESCAPE},
{"BACKSPACE", SDL_SCANCODE_BACKSPACE},
{"TAB", SDL_SCANCODE_TAB},
{"SPACE", SDL_SCANCODE_SPACE},
{"UP", SDL_SCANCODE_UP},
{"DOWN", SDL_SCANCODE_DOWN},
{"LEFT", SDL_SCANCODE_LEFT},
{"RIGHT", SDL_SCANCODE_RIGHT},
{"LSHIFT", SDL_SCANCODE_LSHIFT},
{"RSHIFT", SDL_SCANCODE_RSHIFT},
{"LCTRL", SDL_SCANCODE_LCTRL},
{"RCTRL", SDL_SCANCODE_RCTRL},
{"LALT", SDL_SCANCODE_LALT},
{"RALT", SDL_SCANCODE_RALT}};
// Mapa de botó de gamepad (int) a string
static const std::unordered_map<int, std::string> BUTTON_TO_STRING = {
{SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, // A (Xbox), Cross (PS)
{SDL_GAMEPAD_BUTTON_EAST, "EAST"}, // B (Xbox), Circle (PS)
{SDL_GAMEPAD_BUTTON_WEST, "WEST"}, // X (Xbox), Square (PS)
{SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, // Y (Xbox), Triangle (PS)
{SDL_GAMEPAD_BUTTON_BACK, "BACK"},
{SDL_GAMEPAD_BUTTON_START, "START"},
{SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"},
{SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"},
{SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"},
{SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"},
{100, "L2_AS_BUTTON"}, // Trigger L2 com a botó digital
{101, "R2_AS_BUTTON"} // Trigger R2 com a botó digital
{SDL_GAMEPAD_BUTTON_SOUTH, "SOUTH"}, // A (Xbox), Cross (PS)
{SDL_GAMEPAD_BUTTON_EAST, "EAST"}, // B (Xbox), Circle (PS)
{SDL_GAMEPAD_BUTTON_WEST, "WEST"}, // X (Xbox), Square (PS)
{SDL_GAMEPAD_BUTTON_NORTH, "NORTH"}, // Y (Xbox), Triangle (PS)
{SDL_GAMEPAD_BUTTON_BACK, "BACK"},
{SDL_GAMEPAD_BUTTON_START, "START"},
{SDL_GAMEPAD_BUTTON_LEFT_SHOULDER, "LEFT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, "RIGHT_SHOULDER"},
{SDL_GAMEPAD_BUTTON_DPAD_UP, "DPAD_UP"},
{SDL_GAMEPAD_BUTTON_DPAD_DOWN, "DPAD_DOWN"},
{SDL_GAMEPAD_BUTTON_DPAD_LEFT, "DPAD_LEFT"},
{SDL_GAMEPAD_BUTTON_DPAD_RIGHT, "DPAD_RIGHT"},
{100, "L2_AS_BUTTON"}, // Trigger L2 com a botó digital
{101, "R2_AS_BUTTON"} // Trigger R2 com a botó digital
};
// Mapa invers: string a botó de gamepad
static const std::unordered_map<std::string, int> STRING_TO_BUTTON = {
{"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH},
{"EAST", SDL_GAMEPAD_BUTTON_EAST},
{"WEST", SDL_GAMEPAD_BUTTON_WEST},
{"NORTH", SDL_GAMEPAD_BUTTON_NORTH},
{"BACK", SDL_GAMEPAD_BUTTON_BACK},
{"START", SDL_GAMEPAD_BUTTON_START},
{"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
{"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
{"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP},
{"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN},
{"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT},
{"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
{"L2_AS_BUTTON", 100},
{"R2_AS_BUTTON", 101}
};
{"SOUTH", SDL_GAMEPAD_BUTTON_SOUTH},
{"EAST", SDL_GAMEPAD_BUTTON_EAST},
{"WEST", SDL_GAMEPAD_BUTTON_WEST},
{"NORTH", SDL_GAMEPAD_BUTTON_NORTH},
{"BACK", SDL_GAMEPAD_BUTTON_BACK},
{"START", SDL_GAMEPAD_BUTTON_START},
{"LEFT_SHOULDER", SDL_GAMEPAD_BUTTON_LEFT_SHOULDER},
{"RIGHT_SHOULDER", SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER},
{"DPAD_UP", SDL_GAMEPAD_BUTTON_DPAD_UP},
{"DPAD_DOWN", SDL_GAMEPAD_BUTTON_DPAD_DOWN},
{"DPAD_LEFT", SDL_GAMEPAD_BUTTON_DPAD_LEFT},
{"DPAD_RIGHT", SDL_GAMEPAD_BUTTON_DPAD_RIGHT},
{"L2_AS_BUTTON", 100},
{"R2_AS_BUTTON", 101}};
static auto scancodeToString(SDL_Scancode code) -> std::string {
auto it = SCANCODE_TO_STRING.find(code);
return (it != SCANCODE_TO_STRING.end()) ? it->second : "UNKNOWN";
auto it = SCANCODE_TO_STRING.find(code);
return (it != SCANCODE_TO_STRING.end()) ? it->second : "UNKNOWN";
}
static auto stringToScancode(const std::string& str) -> SDL_Scancode {
auto it = STRING_TO_SCANCODE.find(str);
return (it != STRING_TO_SCANCODE.end()) ? it->second : SDL_SCANCODE_UNKNOWN;
auto it = STRING_TO_SCANCODE.find(str);
return (it != STRING_TO_SCANCODE.end()) ? it->second : SDL_SCANCODE_UNKNOWN;
}
static auto buttonToString(int button) -> std::string {
auto it = BUTTON_TO_STRING.find(button);
return (it != BUTTON_TO_STRING.end()) ? it->second : "UNKNOWN";
auto it = BUTTON_TO_STRING.find(button);
return (it != BUTTON_TO_STRING.end()) ? it->second : "UNKNOWN";
}
static auto stringToButton(const std::string& str) -> int {
auto it = STRING_TO_BUTTON.find(str);
return (it != STRING_TO_BUTTON.end()) ? it->second : SDL_GAMEPAD_BUTTON_INVALID;
auto it = STRING_TO_BUTTON.find(str);
return (it != STRING_TO_BUTTON.end()) ? it->second : SDL_GAMEPAD_BUTTON_INVALID;
}
// ========== FI FUNCIONS AUXILIARS ==========
// Inicialitzar opcions amb valors per defecte de Defaults::
void init() {
#ifdef _DEBUG

View File

@@ -63,8 +63,8 @@ struct KeyboardControls {
struct GamepadControls {
int button_left{SDL_GAMEPAD_BUTTON_DPAD_LEFT};
int button_right{SDL_GAMEPAD_BUTTON_DPAD_RIGHT};
int button_thrust{SDL_GAMEPAD_BUTTON_WEST}; // X button
int button_shoot{SDL_GAMEPAD_BUTTON_SOUTH}; // A button
int button_thrust{SDL_GAMEPAD_BUTTON_WEST}; // X button
int button_shoot{SDL_GAMEPAD_BUTTON_SOUTH}; // A button
};
struct PlayerControls {
@@ -85,23 +85,23 @@ inline Audio audio{};
// Controles per jugador
inline PlayerControls player1{
.keyboard =
{.key_left = SDL_SCANCODE_LEFT,
.key_right = SDL_SCANCODE_RIGHT,
.key_thrust = SDL_SCANCODE_UP,
.key_shoot = SDL_SCANCODE_SPACE,
.key_start = SDL_SCANCODE_1},
.gamepad_name = "" // Primer gamepad disponible
.keyboard =
{.key_left = SDL_SCANCODE_LEFT,
.key_right = SDL_SCANCODE_RIGHT,
.key_thrust = SDL_SCANCODE_UP,
.key_shoot = SDL_SCANCODE_SPACE,
.key_start = SDL_SCANCODE_1},
.gamepad_name = "" // Primer gamepad disponible
};
inline PlayerControls player2{
.keyboard =
{.key_left = SDL_SCANCODE_A,
.key_right = SDL_SCANCODE_D,
.key_thrust = SDL_SCANCODE_W,
.key_shoot = SDL_SCANCODE_LSHIFT,
.key_start = SDL_SCANCODE_2},
.gamepad_name = "" // Segon gamepad disponible
.keyboard =
{.key_left = SDL_SCANCODE_A,
.key_right = SDL_SCANCODE_D,
.key_thrust = SDL_SCANCODE_W,
.key_shoot = SDL_SCANCODE_LSHIFT,
.key_start = SDL_SCANCODE_2},
.gamepad_name = "" // Segon gamepad disponible
};
// Per compatibilitat amb pollo (no utilitzat en orni, però necessari per Input)

View File

@@ -9,7 +9,10 @@
namespace StageSystem {
SpawnController::SpawnController()
: config_(nullptr), temps_transcorregut_(0.0f), index_spawn_actual_(0), ship_position_(nullptr) {}
: config_(nullptr),
temps_transcorregut_(0.0f),
index_spawn_actual_(0),
ship_position_(nullptr) {}
void SpawnController::configurar(const ConfigStage* config) {
config_ = config;
@@ -114,7 +117,7 @@ void SpawnController::generar_spawn_events() {
for (uint8_t i = 0; i < config_->total_enemics; i++) {
float spawn_time = config_->config_spawn.delay_inicial +
(i * config_->config_spawn.interval_spawn);
(i * config_->config_spawn.interval_spawn);
TipusEnemic tipus = seleccionar_tipus_aleatori();

View File

@@ -15,44 +15,44 @@ namespace StageSystem {
// Informació de spawn planificat
struct SpawnEvent {
float temps_spawn; // Temps absolut (segons) per spawnejar
TipusEnemic tipus; // Tipus d'enemic
bool spawnejat; // Ja s'ha processat?
float temps_spawn; // Temps absolut (segons) per spawnejar
TipusEnemic tipus; // Tipus d'enemic
bool spawnejat; // Ja s'ha processat?
};
class SpawnController {
public:
SpawnController();
public:
SpawnController();
// Configuration
void configurar(const ConfigStage* config); // Set stage config
void iniciar(); // Generate spawn schedule
void reset(); // Clear all pending spawns
// Configuration
void configurar(const ConfigStage* config); // Set stage config
void iniciar(); // Generate spawn schedule
void reset(); // Clear all pending spawns
// Update
void actualitzar(float delta_time, std::array<Enemic, 15>& orni_array, bool pausar = false);
// Update
void actualitzar(float delta_time, std::array<Enemic, 15>& orni_array, bool pausar = false);
// Status queries
bool tots_enemics_spawnejats() const;
bool tots_enemics_destruits(const std::array<Enemic, 15>& orni_array) const;
uint8_t get_enemics_vius(const std::array<Enemic, 15>& orni_array) const;
uint8_t get_enemics_spawnejats() const;
// Status queries
bool tots_enemics_spawnejats() const;
bool tots_enemics_destruits(const std::array<Enemic, 15>& orni_array) const;
uint8_t get_enemics_vius(const std::array<Enemic, 15>& orni_array) const;
uint8_t get_enemics_spawnejats() const;
// [NEW] Set ship position reference for safe spawn
void set_ship_position(const Punt* ship_pos) { ship_position_ = ship_pos; }
// [NEW] Set ship position reference for safe spawn
void set_ship_position(const Punt* ship_pos) { ship_position_ = ship_pos; }
private:
const ConfigStage* config_; // Non-owning pointer to current stage config
std::vector<SpawnEvent> spawn_queue_;
float temps_transcorregut_; // Elapsed time since stage start
uint8_t index_spawn_actual_; // Next spawn to process
private:
const ConfigStage* config_; // Non-owning pointer to current stage config
std::vector<SpawnEvent> spawn_queue_;
float temps_transcorregut_; // Elapsed time since stage start
uint8_t index_spawn_actual_; // Next spawn to process
// Spawn generation
void generar_spawn_events();
TipusEnemic seleccionar_tipus_aleatori() const;
void spawn_enemic(Enemic& enemic, TipusEnemic tipus, const Punt* ship_pos = nullptr);
void aplicar_multiplicadors(Enemic& enemic) const;
const Punt* ship_position_; // [NEW] Non-owning pointer to ship position
// Spawn generation
void generar_spawn_events();
TipusEnemic seleccionar_tipus_aleatori() const;
void spawn_enemic(Enemic& enemic, TipusEnemic tipus, const Punt* ship_pos = nullptr);
void aplicar_multiplicadors(Enemic& enemic) const;
const Punt* ship_position_; // [NEW] Non-owning pointer to ship position
};
} // namespace StageSystem

View File

@@ -19,82 +19,81 @@ enum class ModeSpawn {
// Configuració de spawn
struct ConfigSpawn {
ModeSpawn mode;
float delay_inicial; // Segons abans del primer spawn
float interval_spawn; // Segons entre spawns consecutius
ModeSpawn mode;
float delay_inicial; // Segons abans del primer spawn
float interval_spawn; // Segons entre spawns consecutius
};
// Distribució de tipus d'enemics (percentatges)
struct DistribucioEnemics {
uint8_t pentagon; // 0-100
uint8_t quadrat; // 0-100
uint8_t molinillo; // 0-100
// Suma ha de ser 100, validat en StageLoader
uint8_t pentagon; // 0-100
uint8_t quadrat; // 0-100
uint8_t molinillo; // 0-100
// Suma ha de ser 100, validat en StageLoader
};
// Multiplicadors de dificultat
struct MultiplicadorsDificultat {
float velocitat; // 0.5-2.0 típic
float rotacio; // 0.5-2.0 típic
float tracking_strength; // 0.0-1.5 (aplicat a Quadrat)
float velocitat; // 0.5-2.0 típic
float rotacio; // 0.5-2.0 típic
float tracking_strength; // 0.0-1.5 (aplicat a Quadrat)
};
// Metadades del fitxer YAML
struct MetadataStages {
std::string version;
uint8_t total_stages;
std::string descripcio;
std::string version;
uint8_t total_stages;
std::string descripcio;
};
// Configuració completa d'un stage
struct ConfigStage {
uint8_t stage_id; // 1-10
uint8_t total_enemics; // 5-15
ConfigSpawn config_spawn;
DistribucioEnemics distribucio;
MultiplicadorsDificultat multiplicadors;
uint8_t stage_id; // 1-10
uint8_t total_enemics; // 5-15
ConfigSpawn config_spawn;
DistribucioEnemics distribucio;
MultiplicadorsDificultat multiplicadors;
// Validació
bool es_valid() const {
return stage_id >= 1 && stage_id <= 255 &&
total_enemics > 0 && total_enemics <= 15 &&
distribucio.pentagon + distribucio.quadrat + distribucio.molinillo == 100;
}
// Validació
bool es_valid() const {
return stage_id >= 1 && stage_id <= 255 &&
total_enemics > 0 && total_enemics <= 15 &&
distribucio.pentagon + distribucio.quadrat + distribucio.molinillo == 100;
}
};
// Configuració completa del sistema (carregada des de YAML)
struct ConfigSistemaStages {
MetadataStages metadata;
std::vector<ConfigStage> stages; // Índex [0] = stage 1
MetadataStages metadata;
std::vector<ConfigStage> stages; // Índex [0] = stage 1
// Obtenir configuració d'un stage específic
const ConfigStage* obte_stage(uint8_t stage_id) const {
if (stage_id < 1 || stage_id > stages.size()) {
return nullptr;
// Obtenir configuració d'un stage específic
const ConfigStage* obte_stage(uint8_t stage_id) const {
if (stage_id < 1 || stage_id > stages.size()) {
return nullptr;
}
return &stages[stage_id - 1];
}
return &stages[stage_id - 1];
}
};
// Constants per missatges de transició
namespace Constants {
// Pool de missatges per inici de level (selecció aleatòria)
inline constexpr std::array<const char*, 12> MISSATGES_LEVEL_START = {
"ORNI ALERT!",
"INCOMING ORNIS!",
"ROLLING THREAT!",
"ENEMY WAVE!",
"WAVE OF ORNIS DETECTED!",
"NEXT SWARM APPROACHING!",
"BRACE FOR THE NEXT WAVE!",
"ANOTHER ATTACK INCOMING!",
"SENSORS DETECT HOSTILE ORNIS...",
"UNIDENTIFIED ROLLING OBJECTS INBOUND!",
"ENEMY FORCES MOBILIZING!",
"PREPARE FOR IMPACT!"
};
// Pool de missatges per inici de level (selecció aleatòria)
inline constexpr std::array<const char*, 12> MISSATGES_LEVEL_START = {
"ORNI ALERT!",
"INCOMING ORNIS!",
"ROLLING THREAT!",
"ENEMY WAVE!",
"WAVE OF ORNIS DETECTED!",
"NEXT SWARM APPROACHING!",
"BRACE FOR THE NEXT WAVE!",
"ANOTHER ATTACK INCOMING!",
"SENSORS DETECT HOSTILE ORNIS...",
"UNIDENTIFIED ROLLING OBJECTS INBOUND!",
"ENEMY FORCES MOBILIZING!",
"PREPARE FOR IMPACT!"};
constexpr const char* MISSATGE_LEVEL_COMPLETED = "GOOD JOB COMMANDER!";
}
constexpr const char* MISSATGE_LEVEL_COMPLETED = "GOOD JOB COMMANDER!";
} // namespace Constants
} // namespace StageSystem

View File

@@ -3,13 +3,13 @@
#include "stage_loader.hpp"
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
#include <fstream>
#include <iostream>
#include <sstream>
#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
namespace StageSystem {
std::unique_ptr<ConfigSistemaStages> StageLoader::carregar(const std::string& path) {
@@ -88,8 +88,8 @@ bool StageLoader::parse_metadata(const fkyaml::node& yaml, MetadataStages& meta)
meta.version = yaml["version"].get_value<std::string>();
meta.total_stages = yaml["total_stages"].get_value<uint8_t>();
meta.descripcio = yaml.contains("description")
? yaml["description"].get_value<std::string>()
: "";
? yaml["description"].get_value<std::string>()
: "";
return true;
} catch (const std::exception& e) {

View File

@@ -5,28 +5,29 @@
#include <memory>
#include <string>
#include "external/fkyaml_node.hpp"
#include "stage_config.hpp"
namespace StageSystem {
class StageLoader {
public:
// Carregar configuració des de fitxer YAML
// Retorna nullptr si hi ha errors
static std::unique_ptr<ConfigSistemaStages> carregar(const std::string& path);
public:
// Carregar configuració des de fitxer YAML
// Retorna nullptr si hi ha errors
static std::unique_ptr<ConfigSistemaStages> carregar(const std::string& path);
private:
// Parsing helpers (implementats en .cpp)
static bool parse_metadata(const fkyaml::node& yaml, MetadataStages& meta);
static bool parse_stage(const fkyaml::node& yaml, ConfigStage& stage);
static bool parse_spawn_config(const fkyaml::node& yaml, ConfigSpawn& config);
static bool parse_distribution(const fkyaml::node& yaml, DistribucioEnemics& dist);
static bool parse_multipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult);
static ModeSpawn parse_spawn_mode(const std::string& mode_str);
private:
// Parsing helpers (implementats en .cpp)
static bool parse_metadata(const fkyaml::node& yaml, MetadataStages& meta);
static bool parse_stage(const fkyaml::node& yaml, ConfigStage& stage);
static bool parse_spawn_config(const fkyaml::node& yaml, ConfigSpawn& config);
static bool parse_distribution(const fkyaml::node& yaml, DistribucioEnemics& dist);
static bool parse_multipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult);
static ModeSpawn parse_spawn_mode(const std::string& mode_str);
// Validació
static bool validar_config(const ConfigSistemaStages& config);
// Validació
static bool validar_config(const ConfigSistemaStages& config);
};
} // namespace StageSystem

View File

@@ -57,8 +57,8 @@ void StageManager::stage_completat() {
bool StageManager::tot_completat() const {
return stage_actual_ >= config_->metadata.total_stages &&
estat_ == EstatStage::LEVEL_COMPLETED &&
timer_transicio_ <= 0.0f;
estat_ == EstatStage::LEVEL_COMPLETED &&
timer_transicio_ <= 0.0f;
}
const ConfigStage* StageManager::get_config_actual() const {
@@ -127,8 +127,8 @@ void StageManager::processar_playing(float delta_time, bool pausar_spawn) {
// Update spawn controller (pauses when pausar_spawn = true)
// Note: The actual enemy array update happens in EscenaJoc::actualitzar()
// This is just for internal timekeeping
(void)delta_time; // Spawn controller is updated externally
(void)pausar_spawn; // Passed to spawn_controller_.actualitzar() by EscenaJoc
(void)delta_time; // Spawn controller is updated externally
(void)pausar_spawn; // Passed to spawn_controller_.actualitzar() by EscenaJoc
}
void StageManager::processar_level_completed(float delta_time) {

View File

@@ -13,51 +13,51 @@ namespace StageSystem {
// Estats del stage system
enum class EstatStage {
INIT_HUD, // Animació inicial del HUD (3s)
LEVEL_START, // Pantalla "ENEMY INCOMING" (3s)
PLAYING, // Gameplay normal
LEVEL_COMPLETED // Pantalla "GOOD JOB COMMANDER!" (3s)
INIT_HUD, // Animació inicial del HUD (3s)
LEVEL_START, // Pantalla "ENEMY INCOMING" (3s)
PLAYING, // Gameplay normal
LEVEL_COMPLETED // Pantalla "GOOD JOB COMMANDER!" (3s)
};
class StageManager {
public:
explicit StageManager(const ConfigSistemaStages* config);
public:
explicit StageManager(const ConfigSistemaStages* config);
// Lifecycle
void inicialitzar(); // Reset to stage 1
void actualitzar(float delta_time, bool pausar_spawn = false);
// Lifecycle
void inicialitzar(); // Reset to stage 1
void actualitzar(float delta_time, bool pausar_spawn = false);
// Stage progression
void stage_completat(); // Call when all enemies destroyed
bool tot_completat() const; // All 10 stages done?
// Stage progression
void stage_completat(); // Call when all enemies destroyed
bool tot_completat() const; // All 10 stages done?
// Current state queries
EstatStage get_estat() const { return estat_; }
uint8_t get_stage_actual() const { return stage_actual_; }
const ConfigStage* get_config_actual() const;
float get_timer_transicio() const { return timer_transicio_; }
const std::string& get_missatge_level_start() const { return missatge_level_start_actual_; }
// Current state queries
EstatStage get_estat() const { return estat_; }
uint8_t get_stage_actual() const { return stage_actual_; }
const ConfigStage* get_config_actual() const;
float get_timer_transicio() const { return timer_transicio_; }
const std::string& get_missatge_level_start() const { return missatge_level_start_actual_; }
// Spawn control (delegate to SpawnController)
SpawnController& get_spawn_controller() { return spawn_controller_; }
const SpawnController& get_spawn_controller() const { return spawn_controller_; }
// Spawn control (delegate to SpawnController)
SpawnController& get_spawn_controller() { return spawn_controller_; }
const SpawnController& get_spawn_controller() const { return spawn_controller_; }
private:
const ConfigSistemaStages* config_; // Non-owning pointer
SpawnController spawn_controller_;
private:
const ConfigSistemaStages* config_; // Non-owning pointer
SpawnController spawn_controller_;
EstatStage estat_;
uint8_t stage_actual_; // 1-10
float timer_transicio_; // Timer for LEVEL_START/LEVEL_COMPLETED (3.0s → 0.0s)
std::string missatge_level_start_actual_; // Missatge seleccionat per al level actual
EstatStage estat_;
uint8_t stage_actual_; // 1-10
float timer_transicio_; // Timer for LEVEL_START/LEVEL_COMPLETED (3.0s → 0.0s)
std::string missatge_level_start_actual_; // Missatge seleccionat per al level actual
// State transitions
void canviar_estat(EstatStage nou_estat);
void processar_init_hud(float delta_time);
void processar_level_start(float delta_time);
void processar_playing(float delta_time, bool pausar_spawn);
void processar_level_completed(float delta_time);
void carregar_stage(uint8_t stage_id);
// State transitions
void canviar_estat(EstatStage nou_estat);
void processar_init_hud(float delta_time);
void processar_level_start(float delta_time);
void processar_playing(float delta_time, bool pausar_spawn);
void processar_level_completed(float delta_time);
void carregar_stage(uint8_t stage_id);
};
} // namespace StageSystem

View File

@@ -17,8 +17,8 @@ ShipAnimator::ShipAnimator(SDL_Renderer* renderer)
void ShipAnimator::inicialitzar() {
// Carregar formes de naus amb perspectiva pre-calculada
auto forma_p1 = Graphics::ShapeLoader::load("ship_p1.shp"); // Perspectiva esquerra
auto forma_p2 = Graphics::ShapeLoader::load("ship2_p2.shp"); // Perspectiva dreta
auto forma_p1 = Graphics::ShapeLoader::load("ship_p1.shp"); // Perspectiva esquerra
auto forma_p2 = Graphics::ShapeLoader::load("ship2_p2.shp"); // Perspectiva dreta
// Configurar nau P1
naus_[0].jugador_id = 1;
@@ -59,11 +59,11 @@ void ShipAnimator::dibuixar() const {
renderer_,
nau.forma,
nau.posicio_actual,
0.0f, // angle (rotació 2D no utilitzada)
0.0f, // angle (rotació 2D no utilitzada)
nau.escala_actual,
true, // dibuixar
1.0f, // progress (sempre visible)
1.0f // brightness (brillantor màxima)
true, // dibuixar
1.0f, // progress (sempre visible)
1.0f // brightness (brillantor màxima)
);
}
}
@@ -227,7 +227,7 @@ void ShipAnimator::configurar_nau_p1(NauTitol& nau) {
nau.temps_estat = 0.0f;
// Posicions (clock 8, bottom-left)
nau.posicio_objectiu = {P1_TARGET_X, P1_TARGET_Y};
nau.posicio_objectiu = {P1_TARGET_X(), P1_TARGET_Y()};
// Calcular posició inicial (fora de pantalla)
nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_8_ANGLE);
@@ -262,7 +262,7 @@ void ShipAnimator::configurar_nau_p2(NauTitol& nau) {
nau.temps_estat = 0.0f;
// Posicions (clock 4, bottom-right)
nau.posicio_objectiu = {P2_TARGET_X, P2_TARGET_Y};
nau.posicio_objectiu = {P2_TARGET_X(), P2_TARGET_Y()};
// Calcular posició inicial (fora de pantalla)
nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_4_ANGLE);
@@ -293,7 +293,8 @@ Punt ShipAnimator::calcular_posicio_fora_pantalla(float angle_rellotge) const {
using namespace Defaults::Title::Ships;
// Convertir angle del rellotge a radians (per exemple: 240° per clock 8)
// Calcular posició en direcció radial des del centre, però més lluny (+ ENTRY_OFFSET)
// Calcular posició en direcció radial des del centre, però més lluny
// ENTRY_OFFSET es calcula automàticament: (SHIP_MAX_RADIUS * ENTRY_SCALE_START) + ENTRY_OFFSET_MARGIN
float extended_radius = CLOCK_RADIUS + ENTRY_OFFSET;
float x = Defaults::Game::WIDTH / 2.0f + extended_radius * std::cos(angle_rellotge);

View File

@@ -23,74 +23,74 @@ enum class EstatNau {
// Dades d'una nau individual al títol
struct NauTitol {
// Identificació
int jugador_id; // 1 o 2
// Identificació
int jugador_id; // 1 o 2
// Estat
EstatNau estat;
float temps_estat; // Temps acumulat en l'estat actual
// Estat
EstatNau estat;
float temps_estat; // Temps acumulat en l'estat actual
// Posicions
Punt posicio_inicial; // Posició d'inici (fora de pantalla per ENTERING)
Punt posicio_objectiu; // Posició objectiu (rellotge 8 o 4)
Punt posicio_actual; // Posició interpolada actual
// Posicions
Punt posicio_inicial; // Posició d'inici (fora de pantalla per ENTERING)
Punt posicio_objectiu; // Posició objectiu (rellotge 8 o 4)
Punt posicio_actual; // Posició interpolada actual
// Escales (simulació eix Z)
float escala_inicial; // Escala d'inici (més gran = més a prop)
float escala_objectiu; // Escala objectiu (mida flotació)
float escala_actual; // Escala interpolada actual
// Escales (simulació eix Z)
float escala_inicial; // Escala d'inici (més gran = més a prop)
float escala_objectiu; // Escala objectiu (mida flotació)
float escala_actual; // Escala interpolada actual
// Flotació
float fase_oscilacio; // Acumulador de fase per moviment sinusoïdal
// Flotació
float fase_oscilacio; // Acumulador de fase per moviment sinusoïdal
// Paràmetres d'entrada
float entry_delay; // Delay abans d'entrar (0.0 per P1, 0.5 per P2)
// Paràmetres d'entrada
float entry_delay; // Delay abans d'entrar (0.0 per P1, 0.5 per P2)
// Paràmetres d'oscil·lació per nau
float amplitude_x;
float amplitude_y;
float frequency_x;
float frequency_y;
// Paràmetres d'oscil·lació per nau
float amplitude_x;
float amplitude_y;
float frequency_x;
float frequency_y;
// Forma
std::shared_ptr<Graphics::Shape> forma;
// Forma
std::shared_ptr<Graphics::Shape> forma;
// Visibilitat
bool visible;
// Visibilitat
bool visible;
};
// Gestor d'animació de naus per a l'escena de títol
class ShipAnimator {
public:
explicit ShipAnimator(SDL_Renderer* renderer);
public:
explicit ShipAnimator(SDL_Renderer* renderer);
// Cicle de vida
void inicialitzar();
void actualitzar(float delta_time);
void dibuixar() const;
// Cicle de vida
void inicialitzar();
void actualitzar(float delta_time);
void dibuixar() const;
// Control d'estat (cridat per EscenaTitol)
void start_entry_animation();
void trigger_exit_animation(); // Anima totes les naus
void trigger_exit_animation_for_player(int jugador_id); // Anima només una nau (P1=1, P2=2)
// Control d'estat (cridat per EscenaTitol)
void start_entry_animation();
void trigger_exit_animation(); // Anima totes les naus
void trigger_exit_animation_for_player(int jugador_id); // Anima només una nau (P1=1, P2=2)
// Control de visibilitat
void set_visible(bool visible);
bool is_animation_complete() const;
// Control de visibilitat
void set_visible(bool visible);
bool is_animation_complete() const;
private:
SDL_Renderer* renderer_;
std::array<NauTitol, 2> naus_; // Naus P1 i P2
private:
SDL_Renderer* renderer_;
std::array<NauTitol, 2> naus_; // Naus P1 i P2
// Mètodes d'animació
void actualitzar_entering(NauTitol& nau, float delta_time);
void actualitzar_floating(NauTitol& nau, float delta_time);
void actualitzar_exiting(NauTitol& nau, float delta_time);
// Mètodes d'animació
void actualitzar_entering(NauTitol& nau, float delta_time);
void actualitzar_floating(NauTitol& nau, float delta_time);
void actualitzar_exiting(NauTitol& nau, float delta_time);
// Configuració
void configurar_nau_p1(NauTitol& nau);
void configurar_nau_p2(NauTitol& nau);
Punt calcular_posicio_fora_pantalla(float angle_rellotge) const;
// Configuració
void configurar_nau_p1(NauTitol& nau);
void configurar_nau_p2(NauTitol& nau);
Punt calcular_posicio_fora_pantalla(float angle_rellotge) const;
};
} // namespace Title