From 17e9206d2664115210e5e87138c5a28e1a620086 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 29 May 2026 20:43:26 +0200 Subject: [PATCH] tweak(hud): vides com a slots fixos (NUM_SLOTS = MAX_VIDES-1) que s'encenen/atenuen --- source/core/defaults/hud.hpp | 18 +----- source/game/systems/init_hud_animator.cpp | 75 +++++++++++++---------- 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/source/core/defaults/hud.hpp b/source/core/defaults/hud.hpp index 5e329b9..c734e49 100644 --- a/source/core/defaults/hud.hpp +++ b/source/core/defaults/hud.hpp @@ -30,21 +30,9 @@ namespace Defaults::Hud { constexpr SDL_Color LEVEL_DIM = {.r = 29, .g = 107, .b = 44, .a = 255}; // #1D6B2C } // namespace Colors - // Vides representades com a icones de la nau (reutilitza la shape de la nau - // escalada, no un número). Mides derivades de l'alçada del marcador - // (Defaults::Zones::SCOREBOARD_BOTTOM_H) per encaixar-hi sempre. - namespace Lives { - constexpr float ICON_HEIGHT_RATIO = 0.45F; // alçada de la icona com a fracció de l'alçada del marcador - constexpr float ICON_SPACING_FACTOR = 1.35F; // separació centre-a-centre = alçada_icona × factor - constexpr int MAX_ICONS = 5; // límit d'icones dibuixades (acota l'ample del bloc) - } // namespace Lives - - // Disposició del marcador: bloc P1 ancorat a l'esquerra, bloc P2 a la dreta - // (mateix ordre intern "punts vides", no mirrored) i nivell centrat. Els - // blocs s'alineen amb les verticals del PLAYAREA. - namespace Layout { - constexpr float BLOCK_INNER_GAP = 24.0F; // separació punts↔vides dins d'un bloc (px lògics) - } // namespace Layout + // Les vides es dibuixen com a slots fixos de naus en miniatura (NUM_SLOTS = + // MAX_VIDES − 1). Mida i pas dels slots es deriven de la mètrica del glif del + // dígit a init_hud_animator, no de constants soltes. // Animación de entrada del HUD (init_hud_animator). namespace InitAnim { diff --git a/source/game/systems/init_hud_animator.cpp b/source/game/systems/init_hud_animator.cpp index 385a075..c3cb25d 100644 --- a/source/game/systems/init_hud_animator.cpp +++ b/source/game/systems/init_hud_animator.cpp @@ -81,49 +81,57 @@ namespace Systems::InitHud { namespace { - // 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 de slots de vides: una nau menys que el màxim (la nau en joc + // no es dibuixa; els slots són repuestos). Deriva de MAX_VIDES. + constexpr int NUM_SLOTS = Defaults::Game::MAX_VIDES - 1; + + // Pas d'un dígit (amplada + tracking, escalat): és la diferència entre + // l'ample de dos caràcters i el d'un. Marca el ritme de tot el bloc. + auto digitPitch(float scale, float spacing) -> float { + return Graphics::VectorText::getTextWidth("00", scale, spacing) - + Graphics::VectorText::getTextWidth("0", scale, spacing); } - // 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); + // Mida d'un slot = alçada de la majúscula del dígit (mètrica del glif). + auto slotSize(float scale) -> float { + return Graphics::VectorText::getTextHeight(scale); } - // 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) { + // Ample del bloc de slots: constant, independent de les vides. NUM_SLOTS + // slots al pas del dígit (l'últim slot ocupa la seva pròpia mida). + auto slotsBlockWidth(float scale, float spacing) -> float { + if (NUM_SLOTS <= 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; + return (static_cast(NUM_SLOTS - 1) * digitPitch(scale, spacing)) + slotSize(scale); } - // 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, + // Dibuixa els slots de vides com a naus en miniatura en posicions FIXES. + // Slot amb vida disponible (repuesto) → color encès; slot buit → atenuat. + // Repuestos = vides − 1 (la nau en joc no compta com a slot). + void drawSlots(Rendering::Renderer* renderer, const std::shared_ptr& shape, int lives, - SDL_Color color, + SDL_Color bright, + SDL_Color dim, float x_left, - float center_y) { - const int N = lifeIconCount(lives); - if (N <= 0 || !shape) { + float center_y, + float scale, + float spacing) { + if (NUM_SLOTS <= 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 SIZE = slotSize(scale); + const float PITCH = digitPitch(scale, spacing); + // Escala que ajusta el cercle circumscrit de la shape a la mida del + // slot (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); + const float ICON_SCALE = (RADIUS > 0.001F) ? (SIZE / (2.0F * RADIUS)) : 1.0F; + const int FILLED = std::clamp(lives - 1, 0, NUM_SLOTS); + for (int i = 0; i < NUM_SLOTS; i++) { + const SDL_Color COLOR = (i < FILLED) ? bright : dim; + const Vec2 POS = {.x = x_left + (SIZE / 2.0F) + (static_cast(i) * PITCH), .y = center_y}; + Rendering::renderShape(renderer, shape, POS, 0.0F, ICON_SCALE, 1.0F, 1.0F, COLOR); } } @@ -176,13 +184,14 @@ namespace Systems::InitHud { bool right_align) { 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 = livesBlockWidth(lives); - const float BLOCK_W = W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP + W_LIVES; + const float GAP = digitPitch(scale, spacing); // separació punts↔slots = un pas de dígit + const float W_LIVES = slotsBlockWidth(scale, spacing); + const float BLOCK_W = W_SCORE + GAP + W_LIVES; float x = right_align ? (anchor_x - BLOCK_W) : anchor_x; drawScore(text, score, bright, dim, x, TOP_Y, scale, spacing); - x += W_SCORE + Defaults::Hud::Layout::BLOCK_INNER_GAP; - drawLives(renderer, shape, lives, bright, x, center_y); + x += W_SCORE + GAP; + drawSlots(renderer, shape, lives, bright, dim, x, center_y, scale, spacing); } // Pinta el nivell centrat: etiqueta "NIVELL" en verd atenuat i el número