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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -168,16 +168,26 @@ constexpr bool INFINITE_CONTINUES = false; // If true, unlimited continue
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_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)
@@ -272,6 +282,7 @@ 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 const char* TITLE_TRACK = "title.ogg"; // Pista de titulo
constexpr int FADE_DURATION_MS = 1000; // Fade out duration
} // namespace Music
@@ -279,9 +290,13 @@ constexpr int FADE_DURATION_MS = 1000; // Fade out duration
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);
}
}