diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 08caf57..bd09ba0 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -814,7 +814,7 @@ void GameScene::drawInitHudState() { } if (score_progress > 0.0F) { - Systems::InitHud::drawScoreboardAnimated(text_, buildScoreboardData(), score_progress); + Systems::InitHud::drawScoreboardAnimated(sdl_.getRenderer(), text_, buildScoreboardData(), score_progress); } if (ship1_progress > 0.0F && match_config_.player1_active && ships_[0].isActive()) { @@ -933,7 +933,7 @@ void GameScene::drawScoreboard() { text_.renderCentered(Locale::get().text("demo.banner"), CENTER, SCALE, SPACING); return; } - Systems::InitHud::drawScoreboardAt(text_, buildScoreboardData(), CENTER.y, SCALE, SPACING); + Systems::InitHud::drawScoreboardAt(sdl_.getRenderer(), text_, buildScoreboardData(), CENTER.y, SCALE, SPACING); } auto GameScene::buildScoreboardData() const -> Systems::InitHud::ScoreboardData { @@ -950,6 +950,11 @@ auto GameScene::buildScoreboardData() const -> Systems::InitHud::ScoreboardData out.score_p2 = match_config_.player2_active ? FORMAT_SCORE(score_per_player_[1]) : "000000"; out.lives_p2 = match_config_.player2_active ? lives_per_player_[1] : 0; + // Shapes de les naus per a les icones de vides (reutilitza la geometria ja + // carregada de cada Ship). + out.shape_p1 = ships_[0].getShape(); + out.shape_p2 = ships_[1].getShape(); + // Nivell: etiqueta localitzada + número a 2 dígits (separats per pintar-los // amb tonalitats distintes). const uint8_t STAGE_NUM = stage_manager_->getCurrentStage(); diff --git a/source/game/systems/init_hud_animator.cpp b/source/game/systems/init_hud_animator.cpp index 7d6274b..d46b865 100644 --- a/source/game/systems/init_hud_animator.cpp +++ b/source/game/systems/init_hud_animator.cpp @@ -11,6 +11,7 @@ #include "core/defaults/hud.hpp" #include "core/math/easing.hpp" #include "core/rendering/line_renderer.hpp" +#include "core/rendering/shape_renderer.hpp" namespace Systems::InitHud { @@ -80,10 +81,50 @@ namespace Systems::InitHud { namespace { - // Vides com a text (2 dígits) — provisional fins a migrar-les a icones. - auto livesText(int lives) -> std::string { - const std::string S = std::to_string(lives); - return (lives < 10) ? "0" + S : S; + // Alçada de la icona de vida (px lògics) derivada de l'alçada del marcador. + auto lifeIconHeight() -> float { + return Defaults::Hud::Lives::ICON_HEIGHT_RATIO * Defaults::Zones::SCOREBOARD_BOTTOM_H; + } + + // Nombre d'icones de vida a dibuixar (acotat a MAX_ICONS, mai negatiu). + auto lifeIconCount(int lives) -> int { + return std::clamp(lives, 0, Defaults::Hud::Lives::MAX_ICONS); + } + + // Ample del bloc de vides (0 si no hi ha vides). N icones = (N-1) passos + // de separació + l'amplada d'una icona. + auto livesBlockWidth(int lives) -> float { + const int N = lifeIconCount(lives); + if (N <= 0) { + return 0.0F; + } + const float ICON_H = lifeIconHeight(); + const float STEP = ICON_H * Defaults::Hud::Lives::ICON_SPACING_FACTOR; + return (static_cast(N - 1) * STEP) + ICON_H; + } + + // Dibuixa les vides com a icones de la nau (apuntant amunt, color del + // jugador). El glow el posa el shader. x_left = vora esquerra del bloc. + void drawLives(Rendering::Renderer* renderer, + const std::shared_ptr& shape, + int lives, + SDL_Color color, + float x_left, + float center_y) { + const int N = lifeIconCount(lives); + if (N <= 0 || !shape) { + return; + } + const float ICON_H = lifeIconHeight(); + const float STEP = ICON_H * Defaults::Hud::Lives::ICON_SPACING_FACTOR; + // Escala que ajusta el cercle circumscrit de la shape a l'alçada + // objectiu (mida predictible independent del .shp). + const float RADIUS = shape->getBoundingRadius(); + const float SCALE = (RADIUS > 0.001F) ? (ICON_H / (2.0F * RADIUS)) : 1.0F; + for (int i = 0; i < N; i++) { + const Vec2 POS = {.x = x_left + (ICON_H / 2.0F) + (static_cast(i) * STEP), .y = center_y}; + Rendering::renderShape(renderer, shape, POS, 0.0F, SCALE, 1.0F, 1.0F, color); + } } // Pinta la puntuació amb els zeros de farciment previs al primer dígit @@ -112,27 +153,30 @@ namespace Systems::InitHud { } // Pinta el bloc d'un jugador "punts vides" amb el seu color (punts amb - // zeros atenuats, vides en brillant). Si right_align, el bloc acaba a - // anchor_x (ancorat a la dreta); si no, comença a anchor_x (esquerra). - void drawPlayerBlock(const Graphics::VectorText& text, + // zeros atenuats, vides com a icones de nau en brillant). Si right_align, + // el bloc acaba a anchor_x (ancorat a la dreta); si no, comença a + // anchor_x (esquerra). + void drawPlayerBlock(Rendering::Renderer* renderer, + const Graphics::VectorText& text, + const std::shared_ptr& shape, const std::string& score, int lives, SDL_Color bright, SDL_Color dim, float anchor_x, - float top_y, + float center_y, float scale, float spacing, bool right_align) { - const std::string LIVES_STR = livesText(lives); + const float TOP_Y = center_y - (Graphics::VectorText::getTextHeight(scale) / 2.0F); const float W_SCORE = Graphics::VectorText::getTextWidth(score, scale, spacing); - const float W_LIVES = Graphics::VectorText::getTextWidth(LIVES_STR, scale, spacing); + const float W_LIVES = livesBlockWidth(lives); const float BLOCK_W = W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP + W_LIVES; float x = right_align ? (anchor_x - BLOCK_W) : anchor_x; - drawScore(text, score, bright, dim, x, top_y, scale, spacing); + drawScore(text, score, bright, dim, x, TOP_Y, scale, spacing); x += W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP; - text.render(LIVES_STR, {.x = x, .y = top_y}, scale, spacing, 1.0F, bright); + drawLives(renderer, shape, lives, bright, x, center_y); } // Pinta el nivell centrat: etiqueta "NIVELL" en verd atenuat i el número @@ -154,7 +198,8 @@ namespace Systems::InitHud { } // namespace - void drawScoreboardAt(const Graphics::VectorText& text, + void drawScoreboardAt(Rendering::Renderer* renderer, + const Graphics::VectorText& text, const ScoreboardData& data, float center_y, float scale, @@ -165,12 +210,13 @@ namespace Systems::InitHud { const float RIGHT = play.x + play.w; const float TOP_Y = center_y - (Graphics::VectorText::getTextHeight(scale) / 2.0F); - drawPlayerBlock(text, data.score_p1, data.lives_p1, Defaults::Hud::Colors::P1_BRIGHT, Defaults::Hud::Colors::P1_DIM, LEFT, TOP_Y, scale, spacing, false); - drawPlayerBlock(text, data.score_p2, data.lives_p2, Defaults::Hud::Colors::P2_BRIGHT, Defaults::Hud::Colors::P2_DIM, RIGHT, TOP_Y, scale, spacing, true); + drawPlayerBlock(renderer, text, data.shape_p1, data.score_p1, data.lives_p1, Defaults::Hud::Colors::P1_BRIGHT, Defaults::Hud::Colors::P1_DIM, LEFT, center_y, scale, spacing, false); + drawPlayerBlock(renderer, text, data.shape_p2, data.score_p2, data.lives_p2, Defaults::Hud::Colors::P2_BRIGHT, Defaults::Hud::Colors::P2_DIM, RIGHT, center_y, scale, spacing, true); drawLevel(text, data.level_label, data.level_value, TOP_Y, scale, spacing); } - void drawScoreboardAnimated(const Graphics::VectorText& text, + void drawScoreboardAnimated(Rendering::Renderer* renderer, + const Graphics::VectorText& text, const ScoreboardData& data, float progress) { const float EASED = Easing::easeOutQuad(progress); @@ -183,7 +229,7 @@ namespace Systems::InitHud { const auto Y_INI = static_cast(Defaults::Game::HEIGHT); const float Y_ANIM = Y_INI + ((Y_FINAL - Y_INI) * EASED); - drawScoreboardAt(text, data, Y_ANIM, SCALE, SPACING); + drawScoreboardAt(renderer, text, data, Y_ANIM, SCALE, SPACING); } } // namespace Systems::InitHud diff --git a/source/game/systems/init_hud_animator.hpp b/source/game/systems/init_hud_animator.hpp index eb9413e..84cb337 100644 --- a/source/game/systems/init_hud_animator.hpp +++ b/source/game/systems/init_hud_animator.hpp @@ -13,8 +13,10 @@ #pragma once +#include #include +#include "core/graphics/shape.hpp" #include "core/graphics/vector_text.hpp" #include "core/rendering/render_context.hpp" #include "core/types.hpp" @@ -32,6 +34,9 @@ namespace Systems::InitHud { int lives_p2{0}; // vides P2 std::string level_label; // ex: "NIVELL " std::string level_value; // ex: "01" + // Shapes de les naus per dibuixar les vides com a icones en miniatura. + std::shared_ptr shape_p1; + std::shared_ptr shape_p2; }; // Convierte un progreso global 0..1 al sub-progreso de un elemento que solo @@ -55,8 +60,10 @@ namespace Systems::InitHud { // Dibuixa el marcador en tres blocs ancorats a la fila d'alçada `center_y`: // bloc P1 a l'esquerra, bloc P2 a la dreta i nivell centrat. Cada bloc amb - // el seu color (Defaults::Hud::Colors). - void drawScoreboardAt(const Graphics::VectorText& text, + // el seu color (Defaults::Hud::Colors). El renderer cal per dibuixar les + // icones de vides (shapes de nau). + void drawScoreboardAt(Rendering::Renderer* renderer, + const Graphics::VectorText& text, const ScoreboardData& data, float center_y, float scale, @@ -64,7 +71,8 @@ namespace Systems::InitHud { // Dibuixa el marcador pujant des de fora de la pantalla fins a la seva // posició final amb easing. Delega a drawScoreboardAt. - void drawScoreboardAnimated(const Graphics::VectorText& text, + void drawScoreboardAnimated(Rendering::Renderer* renderer, + const Graphics::VectorText& text, const ScoreboardData& data, float progress);