layout de TITOL
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user