From 629baaaec32c020a2138bc0b6000ba05195812fc Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Mon, 11 May 2026 22:18:56 +0200 Subject: [PATCH] =?UTF-8?q?feat(hud):=20fam=C3=ADlies=20de=20color=20per?= =?UTF-8?q?=20bloc=20+=20ombra=20del=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/config/ui.yaml | 22 +++++++++++-- source/core/ui/ui_config.cpp | 28 +++++++++++++--- source/core/ui/ui_config.hpp | 26 ++++++++++++--- source/game/editor/editor.cpp | 6 ++-- source/game/ui/hud.cpp | 60 +++++++++++++++++++++++++++-------- source/utils/defaults.hpp | 22 +++++++++---- 6 files changed, 131 insertions(+), 33 deletions(-) diff --git a/data/config/ui.yaml b/data/config/ui.yaml index d5875a7..13ecc13 100644 --- a/data/config/ui.yaml +++ b/data/config/ui.yaml @@ -17,9 +17,7 @@ colors: menu_cursor: 71 menu_disabled: 16 hud_bg: 9 - hud_label: 58 - hud_value: 187 - hud_flash: 237 + hud_flash: 63 # Rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO). Independents # de notification_* perquè la fase i les notificacions són dos canals # visuals distints: ací pots canviar el color del rètol sense tocar @@ -74,6 +72,24 @@ hud: flash_ms: 250 # durada del flash quan un jugador rep un hit shake_ms: 300 # durada del shake que acompanya el flash +# Famílies de color del HUD per bloc. Cada bloc té `label` (to apagat +# per etiquetes), `value` (to viu per a la xifra) i `shadow` (to fosc +# del mateix matís — efecte embossing 1 px a baix-dreta). Si manca un +# camp, cau al default compilat a `Defaults::UI`. El `colors.hud_flash` +# és comú a totes tres famílies. +hud_p1: + label: 7 # vermell pur + value: 183 # crema-préssec + shadow: 2 # vermell fosc +hud_center: + label: 29 # ambre cremat + value: 55 # or brillant + shadow: 10 # marró cremat +hud_p2: + label: 248 # cian pur + value: 253 # blau pàl·lid + shadow: 64 # blau marí + # Geometria del popup superior (Notifier). En píxels del canvas, llevat de # slide_ms (durada en ms de l'animació d'entrada i d'eixida; constant # independentment de la mida de la caixa). El padding vertical és asimètric a diff --git a/source/core/ui/ui_config.cpp b/source/core/ui/ui_config.cpp index e2ed476..5d55ace 100644 --- a/source/core/ui/ui_config.cpp +++ b/source/core/ui/ui_config.cpp @@ -82,8 +82,15 @@ UiConfig::UiConfig() menu_cursor_(Defaults::UI::MENU_CURSOR), menu_disabled_(Defaults::UI::MENU_DISABLED), hud_bg_(Defaults::UI::HUD_BG), - hud_label_(Defaults::UI::HUD_LABEL), - hud_value_(Defaults::UI::HUD_VALUE), + hud_p1_label_(Defaults::UI::HUD_P1_LABEL), + hud_p1_value_(Defaults::UI::HUD_P1_VALUE), + hud_p1_shadow_(Defaults::UI::HUD_P1_SHADOW), + hud_center_label_(Defaults::UI::HUD_CENTER_LABEL), + hud_center_value_(Defaults::UI::HUD_CENTER_VALUE), + hud_center_shadow_(Defaults::UI::HUD_CENTER_SHADOW), + hud_p2_label_(Defaults::UI::HUD_P2_LABEL), + hud_p2_value_(Defaults::UI::HUD_P2_VALUE), + hud_p2_shadow_(Defaults::UI::HUD_P2_SHADOW), hud_flash_(Defaults::UI::HUD_FLASH), room_text_(Defaults::UI::ROOM_TEXT), room_text_outline_(Defaults::UI::ROOM_TEXT_OUTLINE), @@ -218,13 +225,26 @@ void UiConfig::load(const std::string& file_path) { readColorIndex(c, "menu_cursor", menu_cursor_); readColorIndex(c, "menu_disabled", menu_disabled_); readColorIndex(c, "hud_bg", hud_bg_); - readColorIndex(c, "hud_label", hud_label_); - readColorIndex(c, "hud_value", hud_value_); readColorIndex(c, "hud_flash", hud_flash_); readColorIndex(c, "room_text", room_text_); readColorIndex(c, "room_text_outline", room_text_outline_); } + // Famílies de color del HUD: blocs `hud_p1`, `hud_center`, + // `hud_p2` amb claus `label`, `value`, `shadow`. Cada bloc i + // cada clau són opcionals — el que manqui cau al default + // compilat a `Defaults::UI`. + auto read_hud_family = [&](const std::string& key, Uint8& label, Uint8& value, Uint8& shadow) { + if (!yaml.contains(key)) { return; } + const auto& f = yaml[key]; + readColorIndex(f, "label", label); + readColorIndex(f, "value", value); + readColorIndex(f, "shadow", shadow); + }; + read_hud_family("hud_p1", hud_p1_label_, hud_p1_value_, hud_p1_shadow_); + read_hud_family("hud_center", hud_center_label_, hud_center_value_, hud_center_shadow_); + read_hud_family("hud_p2", hud_p2_label_, hud_p2_value_, hud_p2_shadow_); + if (yaml.contains("timings")) { const auto& t = yaml["timings"]; readField(t, "notification_ms", notification_ms_); diff --git a/source/core/ui/ui_config.hpp b/source/core/ui/ui_config.hpp index a6e78b8..1cb687b 100644 --- a/source/core/ui/ui_config.hpp +++ b/source/core/ui/ui_config.hpp @@ -66,8 +66,19 @@ class UiConfig { [[nodiscard]] auto menuCursor() const -> Uint8 { return menu_cursor_; } [[nodiscard]] auto menuDisabled() const -> Uint8 { return menu_disabled_; } [[nodiscard]] auto hudBg() const -> Uint8 { return hud_bg_; } - [[nodiscard]] auto hudLabel() const -> Uint8 { return hud_label_; } - [[nodiscard]] auto hudValue() const -> Uint8 { return hud_value_; } + // Famílies de color del HUD per bloc (LEFT/CENTER/RIGHT). Cada + // família té label (etiqueta apagada), value (valor destacat) i + // shadow (to fosc per a la sombra-embossing). El flash és comú a + // les tres famílies (groc neó al guanyar punts). + [[nodiscard]] auto hudP1Label() const -> Uint8 { return hud_p1_label_; } + [[nodiscard]] auto hudP1Value() const -> Uint8 { return hud_p1_value_; } + [[nodiscard]] auto hudP1Shadow() const -> Uint8 { return hud_p1_shadow_; } + [[nodiscard]] auto hudCenterLabel() const -> Uint8 { return hud_center_label_; } + [[nodiscard]] auto hudCenterValue() const -> Uint8 { return hud_center_value_; } + [[nodiscard]] auto hudCenterShadow() const -> Uint8 { return hud_center_shadow_; } + [[nodiscard]] auto hudP2Label() const -> Uint8 { return hud_p2_label_; } + [[nodiscard]] auto hudP2Value() const -> Uint8 { return hud_p2_value_; } + [[nodiscard]] auto hudP2Shadow() const -> Uint8 { return hud_p2_shadow_; } [[nodiscard]] auto hudFlash() const -> Uint8 { return hud_flash_; } [[nodiscard]] auto roomText() const -> Uint8 { return room_text_; } [[nodiscard]] auto roomTextOutline() const -> Uint8 { return room_text_outline_; } @@ -209,8 +220,15 @@ class UiConfig { Uint8 menu_cursor_; Uint8 menu_disabled_; Uint8 hud_bg_; - Uint8 hud_label_; - Uint8 hud_value_; + Uint8 hud_p1_label_; + Uint8 hud_p1_value_; + Uint8 hud_p1_shadow_; + Uint8 hud_center_label_; + Uint8 hud_center_value_; + Uint8 hud_center_shadow_; + Uint8 hud_p2_label_; + Uint8 hud_p2_value_; + Uint8 hud_p2_shadow_; Uint8 hud_flash_; Uint8 room_text_; Uint8 room_text_outline_; diff --git a/source/game/editor/editor.cpp b/source/game/editor/editor.cpp index a5adaac..9d2155c 100644 --- a/source/game/editor/editor.cpp +++ b/source/game/editor/editor.cpp @@ -739,8 +739,10 @@ void Editor::renderEditorHud(const EntityManager& entities) const { ? "(" + std::to_string(cursor_col_) + "," + std::to_string(cursor_row_) + ")" : "(--,--)"; - const auto LBL = UiConfig::get().hudLabel(); - const auto VAL = UiConfig::get().hudValue(); + // El HUD ara té tres famílies (P1/CENTER/P2); l'editor n'agafa la + // del CENTER (ambre/or) per ser neutra i contrastar amb el mapa. + const auto LBL = UiConfig::get().hudCenterLabel(); + const auto VAL = UiConfig::get().hudCenterValue(); const auto FLASH = UiConfig::get().hudFlash(); const int KERN = UiConfig::get().hudFont().kerning; // Mateixos Y que el HUD del joc per simetria visual: línia 1 = 4, diff --git a/source/game/ui/hud.cpp b/source/game/ui/hud.cpp index 455858c..cf9a286 100644 --- a/source/game/ui/hud.cpp +++ b/source/game/ui/hud.cpp @@ -40,6 +40,25 @@ namespace { // Separació extra entre camps dins d'una mateixa línia. constexpr int FIELD_GAP = 6; // px entre "Vides 3" i "Tres. 00" + // Desplaçament de l'ombra del text en píxels (sota-dreta). Petit + // perquè la font del HUD és 8 px d'alçada — qualsevol valor més + // gran tapa el següent caràcter. + constexpr int SHADOW_DX = 1; + constexpr int SHADOW_DY = 1; + + // Helpers per pintar text amb ombra. Primer la sombra desplaçada, + // després el text principal al damunt — l'ordre garanteix que la + // sombra mai cobreix el text. El kerning queda al default del + // Text (1 px), igual que la resta del HUD. + void drawShadowed(Text& font, int x, int y, const std::string& s, Uint8 color, Uint8 shadow) { + font.writeColored(x + SHADOW_DX, y + SHADOW_DY, s, shadow); + font.writeColored(x, y, s, color); + } + void drawMonoShadowed(Text& font, int x, int y, const std::string& s, Uint8 color, Uint8 shadow, int cell_w) { + font.writeMonoDigits(x + SHADOW_DX, y + SHADOW_DY, s, shadow, cell_w); + font.writeMonoDigits(x, y, s, color, cell_w); + } + // Format zero-padded a 6 dígits per a la puntuació. auto formatScore(int v) -> std::string { const int CLAMPED = std::clamp(v, 0, 999999); @@ -261,6 +280,16 @@ void Hud::renderBlock(BlockId id) const { void Hud::renderIdleSide(int x, int y, int w, const Block& blk, int player_index, const std::string& label) const { const auto& p = players_[static_cast(player_index)]; + // Família de color del bloc: P1 (vermell-crema-vermell-fosc) per al + // jugador 0, P2 (cian-blau-blau-marí) per al jugador 1. Mantenir el + // mateix matís a label/value/shadow dóna jerarquia visual coherent + // entre línies sense empastar (la sombra és un to fosc del mateix + // matís → efecte embossing). + const auto& ui = UiConfig::get(); + const Uint8 LBL_C = (player_index == 0) ? ui.hudP1Label() : ui.hudP2Label(); + const Uint8 VAL_C = (player_index == 0) ? ui.hudP1Value() : ui.hudP2Value(); + const Uint8 SHD_C = (player_index == 0) ? ui.hudP1Shadow() : ui.hudP2Shadow(); + // Slot inactiu amb prompt actiu: substituïm el render de zeros pel // missatge "PULSA START / PARA JUGAR" o "ESPERA / PARA JUGAR" segons // el mode fixat per la GameScene cada frame. El render de zeros @@ -275,12 +304,12 @@ void Hud::renderIdleSide(int x, int y, int w, const Block& blk, int player_index const int L2_W = font_->length(L2); const int X1 = x + ((w - L1_W) / 2); const int X2 = x + ((w - L2_W) / 2); - font_->writeColored(X1, y + LINE1_Y, L1, UiConfig::get().hudLabel()); - font_->writeColored(X2, y + LINE2_Y, L2, UiConfig::get().hudValue()); + drawShadowed(*font_, X1, y + LINE1_Y, L1, LBL_C, SHD_C); + drawShadowed(*font_, X2, y + LINE2_Y, L2, VAL_C, SHD_C); return; } - const Uint8 SCORE_C = (blk.flash_ms_left > 0) ? UiConfig::get().hudFlash() : UiConfig::get().hudValue(); + const Uint8 SCORE_C = (blk.flash_ms_left > 0) ? ui.hudFlash() : VAL_C; // --- Línia 1: "