nous sons

This commit is contained in:
2025-12-17 17:05:42 +01:00
parent ec333efe66
commit 8bc259b25a
12 changed files with 105 additions and 96 deletions

View File

@@ -159,25 +159,35 @@ constexpr float P2_SPAWN_X_RATIO = 0.67f; // 67% desde izquierda
constexpr float SPAWN_Y_RATIO = 0.75f; // 75% desde arriba
// Continue system behavior
constexpr int CONTINUE_COUNT_START = 9; // Countdown starts at 9
constexpr float CONTINUE_TICK_DURATION = 1.0f; // Seconds per countdown tick
constexpr int MAX_CONTINUES = 3; // Maximum continues per game
constexpr bool INFINITE_CONTINUES = false; // If true, unlimited continues
constexpr int CONTINUE_COUNT_START = 9; // Countdown starts at 9
constexpr float CONTINUE_TICK_DURATION = 1.0f; // Seconds per countdown tick
constexpr int MAX_CONTINUES = 3; // Maximum continues per game
constexpr bool INFINITE_CONTINUES = false; // If true, unlimited continues
// Continue screen visual configuration
namespace ContinueScreen {
// "CONTINUE" text
constexpr float CONTINUE_TEXT_SCALE = 2.0f; // Text size
constexpr float CONTINUE_TEXT_Y_RATIO = 0.35f; // 35% from top of PLAYAREA
constexpr float CONTINUE_TEXT_SCALE = 2.0f; // Text size
constexpr float CONTINUE_TEXT_Y_RATIO = 0.30f; // 35% from top of PLAYAREA
// Countdown number (9, 8, 7...)
constexpr float COUNTER_TEXT_SCALE = 4.0f; // Text size (large)
constexpr float COUNTER_TEXT_Y_RATIO = 0.50f; // 50% from top of PLAYAREA
// "CONTINUES LEFT: X" text
constexpr float INFO_TEXT_SCALE = 1.0f; // Text size (small)
constexpr float INFO_TEXT_Y_RATIO = 0.65f; // 65% from top of PLAYAREA
constexpr float INFO_TEXT_SCALE = 0.7f; // Text size (small)
constexpr float INFO_TEXT_Y_RATIO = 0.75f; // 65% from top of PLAYAREA
} // namespace ContinueScreen
// Game Over screen visual configuration
namespace GameOverScreen {
constexpr float TEXT_SCALE = 2.0f; // "GAME OVER" text size
constexpr float TEXT_SPACING = 4.0f; // Character spacing
} // namespace GameOverScreen
// Stage message configuration (LEVEL_START, LEVEL_COMPLETED)
constexpr float STAGE_MESSAGE_Y_RATIO = 0.25f; // 25% from top of PLAYAREA
constexpr float STAGE_MESSAGE_MAX_WIDTH_RATIO = 0.9f; // 90% of PLAYAREA width
} // namespace Game
// Física (valores actuales del juego, sincronizados con joc_asteroides.cpp)
@@ -269,19 +279,24 @@ constexpr bool ENABLED = true; // Audio habilitado por defecto
// Música (pistas de fondo)
namespace Music {
constexpr float VOLUME = 0.8F; // Volumen música
constexpr bool ENABLED = true; // Música habilitada
constexpr const char* GAME_TRACK = "game.ogg"; // Pista de juego
constexpr int FADE_DURATION_MS = 1000; // Fade out duration
constexpr float VOLUME = 0.8F; // Volumen música
constexpr bool ENABLED = true; // Música habilitada
constexpr const char* GAME_TRACK = "game.ogg"; // Pista de juego
constexpr const char* TITLE_TRACK = "title.ogg"; // Pista de titulo
constexpr int FADE_DURATION_MS = 1000; // Fade out duration
} // namespace Music
// Efectes de so (sons puntuals)
namespace Sound {
constexpr float VOLUME = 1.0F; // Volumen efectos
constexpr bool ENABLED = true; // Sonidos habilitados
constexpr const char* CONTINUE = "effects/continue.wav"; // Cuenta atras
constexpr const char* EXPLOSION = "effects/explosion.wav"; // Explosión
constexpr const char* EXPLOSION2 = "effects/explosion2.wav"; // Explosión alternativa
constexpr const char* INIT_HUD = "effects/init_hud.wav"; // Para la animación del HUD
constexpr const char* LASER = "effects/laser_shoot.wav"; // Disparo
constexpr const char* LOGO = "effects/logo.wav"; // Logo
constexpr const char* START = "effects/start.wav"; // El jugador pulsa START
constexpr const char* GOOD_JOB_COMMANDER = "voices/good_job_commander.wav"; // Voz: "Good job, commander"
} // namespace Sound
@@ -473,8 +488,8 @@ constexpr float P2_FREQUENCY_MULTIPLIER = 1.12f; // 12% més ràpida
namespace Layout {
// Posicions verticals (anclatges des del TOP de pantalla lògica, 0.0-1.0)
constexpr float LOGO_POS = 0.20f; // Logo "ORNI"
constexpr float PRESS_START_POS = 0.73f; // "PRESS START TO PLAY"
constexpr float COPYRIGHT1_POS = 0.87f; // Primera línia copyright
constexpr float PRESS_START_POS = 0.75f; // "PRESS START TO PLAY"
constexpr float COPYRIGHT1_POS = 0.90f; // Primera línia copyright
// Separacions relatives (proporció respecte Game::HEIGHT = 480px)
constexpr float LOGO_LINE_SPACING = 0.02f; // Entre "ORNI" i "ATTACK!" (10px)

View File

@@ -236,6 +236,22 @@ void VectorText::render(const std::string& text, const Punt& posicio, float esca
}
}
void VectorText::render_centered(const std::string& text, const Punt& centre_punt, float escala, float spacing, float brightness) {
// Calcular dimensions del text
float text_width = get_text_width(text, escala, spacing);
float text_height = get_text_height(escala);
// Calcular posició de l'esquina superior esquerra
// restant la meitat de les dimensions del punt central
Punt posicio_esquerra = {
centre_punt.x - (text_width / 2.0f),
centre_punt.y - (text_height / 2.0f)
};
// Delegar al mètode render() existent
render(text, posicio_esquerra, escala, spacing, brightness);
}
float VectorText::get_text_width(const std::string& text, float escala, float spacing) const {
if (text.empty()) {
return 0.0f;

View File

@@ -27,6 +27,14 @@ class VectorText {
// - brightness: factor de brillantor (0.0-1.0, default 1.0 = màxima brillantor)
void render(const std::string& text, const Punt& posicio, float escala = 1.0f, float spacing = 2.0f, float brightness = 1.0f);
// Renderizar string centrado en un punto
// - text: cadena a renderizar
// - centre_punt: punto central del texto (no esquina superior izquierda)
// - escala: factor de escala (1.0 = 20×40 px por carácter)
// - spacing: espacio entre caracteres en píxeles (a escala 1.0)
// - brightness: factor de brillantor (0.0-1.0, default 1.0 = màxima brillantor)
void render_centered(const std::string& text, const Punt& centre_punt, float escala = 1.0f, float spacing = 2.0f, float brightness = 1.0f);
// Calcular ancho total de un string (útil para centrado)
float get_text_width(const std::string& text, float escala = 1.0f, float spacing = 2.0f) const;

View File

@@ -51,13 +51,14 @@ void DebrisManager::explotar(const std::shared_ptr<Graphics::Shape>& shape,
float brightness,
const Punt& velocitat_objecte,
float velocitat_angular,
float factor_herencia_visual) {
float factor_herencia_visual,
const std::string& sound) {
if (!shape || !shape->es_valida()) {
return;
}
// Reproducir sonido de explosión
Audio::get()->playSound(Defaults::Sound::EXPLOSION, Audio::Group::GAME);
Audio::get()->playSound(sound, Audio::Group::GAME);
// Obtenir centre de la forma per a transformacions
const Punt& shape_centre = shape->get_centre();

View File

@@ -37,7 +37,8 @@ class DebrisManager {
float brightness = 1.0f,
const Punt& velocitat_objecte = {0.0f, 0.0f},
float velocitat_angular = 0.0f,
float factor_herencia_visual = 0.0f);
float factor_herencia_visual = 0.0f,
const std::string& sound = Defaults::Sound::EXPLOSION);
// Actualitzar tots els fragments actius
void actualitzar(float delta_time);

View File

@@ -61,16 +61,11 @@ void GestorPuntuacioFlotant::dibuixar() {
if (!pf.actiu)
continue;
// 1. Calcular dimensions del text per centrar-lo
// Renderitzar centrat amb brightness (fade)
constexpr float escala = Defaults::FloatingScore::SCALE;
constexpr float spacing = Defaults::FloatingScore::SPACING;
float text_width = text_.get_text_width(pf.text, escala, spacing);
// 2. Centrar text sobre la posició
Punt render_pos = {pf.posicio.x - text_width / 2.0f, pf.posicio.y};
// 3. Renderitzar amb brightness (fade)
text_.render(pf.text, render_pos, escala, spacing, pf.brightness);
text_.render_centered(pf.text, pf.posicio, escala, spacing, pf.brightness);
}
}

View File

@@ -526,17 +526,15 @@ void EscenaJoc::dibuixar() {
// Draw centered "GAME OVER" text
const std::string game_over_text = "GAME OVER";
constexpr float escala = 2.0f;
constexpr float spacing = 4.0f;
float text_width = text_.get_text_width(game_over_text, escala, spacing);
float text_height = text_.get_text_height(escala);
constexpr float escala = Defaults::Game::GameOverScreen::TEXT_SCALE;
constexpr float spacing = Defaults::Game::GameOverScreen::TEXT_SPACING;
// Calcular centre de l'àrea de joc usant constants
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
float x = play_area.x + (play_area.w - text_width) / 2.0f;
float y = play_area.y + (play_area.h - text_height) / 2.0f;
float centre_x = play_area.x + play_area.w / 2.0f;
float centre_y = play_area.y + play_area.h / 2.0f;
text_.render(game_over_text, {x, y}, escala, spacing);
text_.render_centered(game_over_text, {centre_x, centre_y}, escala, spacing);
dibuixar_marcador();
return;
@@ -577,7 +575,7 @@ void EscenaJoc::dibuixar() {
if (rect_progress > 0.0f) {
// [NOU] Reproduir so quan comença l'animació del rectangle
if (!init_hud_rect_sound_played_) {
Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME);
Audio::get()->playSound(Defaults::Sound::INIT_HUD, Audio::Group::GAME);
init_hud_rect_sound_played_ = true;
}
@@ -702,11 +700,10 @@ void EscenaJoc::tocado(uint8_t player_id) {
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)
0.0f, // Sin herencia visual (rotación aleatoria)
Defaults::Sound::EXPLOSION2 // Sonido alternativo para la explosión
);
Audio::get()->playSound(Defaults::Sound::EXPLOSION, Audio::Group::GAME);
// Start death timer (non-zero to avoid re-triggering)
itocado_per_jugador_[player_id] = 0.001f;
}
@@ -739,19 +736,13 @@ void EscenaJoc::dibuixar_marcador() {
const float escala = 0.85f;
const float spacing = 0.0f;
// Calcular dimensions del text
float text_width = text_.get_text_width(text, escala, spacing);
float text_height = text_.get_text_height(escala);
// Calcular centre de la zona del marcador
const SDL_FRect& scoreboard = Defaults::Zones::SCOREBOARD;
float centre_x = scoreboard.w / 2.0f;
float centre_y = scoreboard.y + scoreboard.h / 2.0f;
// Centrat horitzontal dins de la zona del marcador
float x = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f;
// Centrat vertical dins de la zona del marcador
float y = Defaults::Zones::SCOREBOARD.y +
(Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f;
// Renderitzar
text_.render(text, {x, y}, escala, spacing);
// Renderitzar centrat
text_.render_centered(text, {centre_x, centre_y}, escala, spacing);
}
void EscenaJoc::dibuixar_marges_animat(float progress) const {
@@ -826,25 +817,19 @@ void EscenaJoc::dibuixar_marcador_animat(float progress) {
const float escala = 0.85f;
const float spacing = 0.0f;
// Calcular dimensions
float text_width = text_.get_text_width(text, escala, spacing);
float text_height = text_.get_text_height(escala);
// Posició X final (centrada horitzontalment)
float x_final = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f;
// Posició Y final (centrada verticalment en la zona de scoreboard)
float y_final = Defaults::Zones::SCOREBOARD.y +
(Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f;
// Calcular centre de la zona del marcador
const SDL_FRect& scoreboard = Defaults::Zones::SCOREBOARD;
float centre_x = scoreboard.w / 2.0f;
float centre_y_final = scoreboard.y + scoreboard.h / 2.0f;
// Posició Y inicial (offscreen, sota de la pantalla)
float y_inicial = static_cast<float>(Defaults::Game::HEIGHT) + text_height;
float centre_y_inicial = static_cast<float>(Defaults::Game::HEIGHT);
// Interpolació amb easing
float y_animada = y_inicial + (y_final - y_inicial) * eased_progress;
float centre_y_animada = centre_y_inicial + (centre_y_final - centre_y_inicial) * eased_progress;
// Renderitzar en posició animada
text_.render(text, {x_final, y_animada}, escala, spacing);
// Renderitzar centrat en posició animada
text_.render_centered(text, {centre_x, centre_y_animada}, escala, spacing);
}
Punt EscenaJoc::calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const {
@@ -1067,10 +1052,9 @@ void EscenaJoc::detectar_col·lisio_naus_enemics() {
void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
constexpr float escala_base = 1.0f;
constexpr float spacing = 2.0f;
constexpr float max_width_ratio = 0.9f; // 90% del ancho disponible
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
const float max_width = play_area.w * max_width_ratio; // 558px
const float max_width = play_area.w * Defaults::Game::STAGE_MESSAGE_MAX_WIDTH_RATIO;
// ========== TYPEWRITER EFFECT (PARAMETRIZED) ==========
// Get state-specific timing configuration
@@ -1123,7 +1107,7 @@ void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
// Calculate position as if FULL text was there (for fixed position typewriter)
float x = play_area.x + (play_area.w - full_text_width) / 2.0f;
float y = play_area.y + (play_area.h * 0.25f) - (text_height / 2.0f);
float y = play_area.y + (play_area.h * Defaults::Game::STAGE_MESSAGE_Y_RATIO) - (text_height / 2.0f);
// Render only the partial message (typewriter effect)
Punt pos = {x, y};
@@ -1190,7 +1174,7 @@ void EscenaJoc::actualitzar_continue(float delta_time) {
continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION;
// Play tick sound
Audio::get()->playSound("continue_tick");
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
if (continue_counter_ <= 0) {
// Timeout → final game over
@@ -1257,7 +1241,7 @@ void EscenaJoc::processar_input_continue() {
continue_tick_timer_ = 0.0f;
// Play continue confirmation sound
Audio::get()->playSound("continue_confirm");
Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME);
return;
}
@@ -1272,7 +1256,7 @@ void EscenaJoc::processar_input_continue() {
continue_counter_--;
// Play tick sound on manual decrement
Audio::get()->playSound("continue_tick");
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
if (continue_counter_ <= 0) {
estat_game_over_ = EstatGameOver::GAME_OVER;
@@ -1293,22 +1277,19 @@ void EscenaJoc::dibuixar_continue() {
float escala_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_SCALE;
float y_ratio_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_Y_RATIO;
float text_width_continue = text_.get_text_width(continue_text, escala_continue, spacing);
float x_continue = play_area.x + (play_area.w - text_width_continue) / 2.0f;
float y_continue = play_area.y + play_area.h * y_ratio_continue;
float centre_x = play_area.x + play_area.w / 2.0f;
float centre_y_continue = play_area.y + play_area.h * y_ratio_continue;
text_.render(continue_text, {x_continue, y_continue}, escala_continue, spacing);
text_.render_centered(continue_text, {centre_x, centre_y_continue}, escala_continue, spacing);
// Countdown number (using constants)
const std::string counter_str = std::to_string(continue_counter_);
float escala_counter = Defaults::Game::ContinueScreen::COUNTER_TEXT_SCALE;
float y_ratio_counter = Defaults::Game::ContinueScreen::COUNTER_TEXT_Y_RATIO;
float text_width_counter = text_.get_text_width(counter_str, escala_counter, spacing);
float x_counter = play_area.x + (play_area.w - text_width_counter) / 2.0f;
float y_counter = play_area.y + play_area.h * y_ratio_counter;
float centre_y_counter = play_area.y + play_area.h * y_ratio_counter;
text_.render(counter_str, {x_counter, y_counter}, escala_counter, spacing);
text_.render_centered(counter_str, {centre_x, centre_y_counter}, escala_counter, spacing);
// "CONTINUES LEFT" (conditional + using constants)
if (!Defaults::Game::INFINITE_CONTINUES) {
@@ -1316,11 +1297,9 @@ void EscenaJoc::dibuixar_continue() {
float escala_info = Defaults::Game::ContinueScreen::INFO_TEXT_SCALE;
float y_ratio_info = Defaults::Game::ContinueScreen::INFO_TEXT_Y_RATIO;
float text_width_info = text_.get_text_width(continues_text, escala_info, spacing);
float x_info = play_area.x + (play_area.w - text_width_info) / 2.0f;
float y_info = play_area.y + play_area.h * y_ratio_info;
float centre_y_info = play_area.y + play_area.h * y_ratio_info;
text_.render(continues_text, {x_info, y_info}, escala_info, spacing);
text_.render_centered(continues_text, {centre_x, centre_y_info}, escala_info, spacing);
}
}

View File

@@ -420,8 +420,8 @@ void EscenaTitol::actualitzar(float delta_time) {
}
}
// Reproducir so de LASER quan el segon jugador s'uneix
Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
// Reproducir so de START quan el segon jugador s'uneix
Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME);
// Reiniciar el timer per allargar el temps de transició
temps_acumulat_ = 0.0f;
@@ -496,7 +496,7 @@ void EscenaTitol::actualitzar(float delta_time) {
}
Audio::get()->fadeOutMusic(MUSIC_FADE);
Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME);
}
}
}
@@ -652,12 +652,10 @@ void EscenaTitol::dibuixar() {
const std::string main_text = "PRESS START TO PLAY";
const float escala_main = Defaults::Title::Layout::PRESS_START_SCALE;
float text_width = text_.get_text_width(main_text, escala_main, spacing);
float centre_x = Defaults::Game::WIDTH / 2.0f;
float centre_y = Defaults::Game::HEIGHT * Defaults::Title::Layout::PRESS_START_POS;
float x_center = (Defaults::Game::WIDTH - text_width) / 2.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);
text_.render_centered(main_text, {centre_x, centre_y}, escala_main, spacing);
}
// === Copyright a la part inferior (centrat horitzontalment, dues línies) ===
@@ -681,15 +679,11 @@ void EscenaTitol::dibuixar() {
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
// 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ínees centrades
float centre_x = Defaults::Game::WIDTH / 2.0f;
// 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);
text_.render_centered(copyright_original, {centre_x, y_line1}, escala_copy, spacing);
text_.render_centered(copyright_port, {centre_x, y_line2}, escala_copy, spacing);
}
}