feat(hud): famílies de color per bloc + ombra del text
This commit is contained in:
1 parent
932d195eb0
commit
629baaaec3
6 files changed
+131
-33
No files matched your search
@@ -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_);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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,
|
||||
|
||||
+47
-13
@@ -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<std::size_t>(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: "<label>-000000" centrada ---
|
||||
const std::string LBL1 = label + SEPARATOR;
|
||||
@@ -289,9 +318,9 @@ void Hud::renderIdleSide(int x, int y, int w, const Block& blk, int player_index
|
||||
const int SC_W = font_->lengthMonoDigits(SCORE, MONO_DIGIT_W);
|
||||
const int LINE1_W = LBL_W + SC_W;
|
||||
int xx = x + ((w - LINE1_W) / 2);
|
||||
font_->writeColored(xx, y + LINE1_Y, LBL1, UiConfig::get().hudLabel());
|
||||
drawShadowed(*font_, xx, y + LINE1_Y, LBL1, LBL_C, SHD_C);
|
||||
xx += LBL_W;
|
||||
font_->writeMonoDigits(xx, y + LINE1_Y, SCORE, SCORE_C, MONO_DIGIT_W);
|
||||
drawMonoShadowed(*font_, xx, y + LINE1_Y, SCORE, SCORE_C, SHD_C, MONO_DIGIT_W);
|
||||
|
||||
// --- Línia 2: "<vides>-<n> <tresors>-<n>" centrada ---
|
||||
const std::string V_LBL = Locale::get().text("hud.lives") + SEPARATOR;
|
||||
@@ -304,16 +333,21 @@ void Hud::renderIdleSide(int x, int y, int w, const Block& blk, int player_index
|
||||
const int T_VW = font_->lengthMonoDigits(T_VAL, MONO_DIGIT_W);
|
||||
const int LINE2_W = V_LW + V_VW + FIELD_GAP + T_LW + T_VW;
|
||||
xx = x + ((w - LINE2_W) / 2);
|
||||
font_->writeColored(xx, y + LINE2_Y, V_LBL, UiConfig::get().hudLabel());
|
||||
drawShadowed(*font_, xx, y + LINE2_Y, V_LBL, LBL_C, SHD_C);
|
||||
xx += V_LW;
|
||||
font_->writeMonoDigits(xx, y + LINE2_Y, V_VAL, UiConfig::get().hudValue(), MONO_DIGIT_W);
|
||||
drawMonoShadowed(*font_, xx, y + LINE2_Y, V_VAL, VAL_C, SHD_C, MONO_DIGIT_W);
|
||||
xx += V_VW + FIELD_GAP;
|
||||
font_->writeColored(xx, y + LINE2_Y, T_LBL, UiConfig::get().hudLabel());
|
||||
drawShadowed(*font_, xx, y + LINE2_Y, T_LBL, LBL_C, SHD_C);
|
||||
xx += T_LW;
|
||||
font_->writeMonoDigits(xx, y + LINE2_Y, T_VAL, UiConfig::get().hudValue(), MONO_DIGIT_W);
|
||||
drawMonoShadowed(*font_, xx, y + LINE2_Y, T_VAL, VAL_C, SHD_C, MONO_DIGIT_W);
|
||||
}
|
||||
|
||||
void Hud::renderIdleCenter(int x, int y, int w) const {
|
||||
const auto& ui = UiConfig::get();
|
||||
const Uint8 LBL_C = ui.hudCenterLabel();
|
||||
const Uint8 VAL_C = ui.hudCenterValue();
|
||||
const Uint8 SHD_C = ui.hudCenterShadow();
|
||||
|
||||
// --- Línia 1: "<piramide>-<n>" centrada ---
|
||||
const std::string PIR_LBL = Locale::get().text("hud.pyramid") + SEPARATOR;
|
||||
const std::string PIR_VAL = std::to_string(pyramid_);
|
||||
@@ -321,9 +355,9 @@ void Hud::renderIdleCenter(int x, int y, int w) const {
|
||||
const int PV_W = font_->lengthMonoDigits(PIR_VAL, MONO_DIGIT_W);
|
||||
const int LINE1_W = PL_W + PV_W;
|
||||
int xx = x + ((w - LINE1_W) / 2);
|
||||
font_->writeColored(xx, y + LINE1_Y, PIR_LBL, UiConfig::get().hudLabel());
|
||||
drawShadowed(*font_, xx, y + LINE1_Y, PIR_LBL, LBL_C, SHD_C);
|
||||
xx += PL_W;
|
||||
font_->writeMonoDigits(xx, y + LINE1_Y, PIR_VAL, UiConfig::get().hudValue(), MONO_DIGIT_W);
|
||||
drawMonoShadowed(*font_, xx, y + LINE1_Y, PIR_VAL, VAL_C, SHD_C, MONO_DIGIT_W);
|
||||
|
||||
// --- Línia 2: "<sala>-<r>" centrada ---
|
||||
const std::string SALA_LBL = Locale::get().text("hud.room") + SEPARATOR;
|
||||
@@ -332,7 +366,7 @@ void Hud::renderIdleCenter(int x, int y, int w) const {
|
||||
const int SV_W = font_->lengthMonoDigits(SALA_VAL, MONO_DIGIT_W);
|
||||
const int LINE2_W = SL_W + SV_W;
|
||||
xx = x + ((w - LINE2_W) / 2);
|
||||
font_->writeColored(xx, y + LINE2_Y, SALA_LBL, UiConfig::get().hudLabel());
|
||||
drawShadowed(*font_, xx, y + LINE2_Y, SALA_LBL, LBL_C, SHD_C);
|
||||
xx += SL_W;
|
||||
font_->writeMonoDigits(xx, y + LINE2_Y, SALA_VAL, UiConfig::get().hudValue(), MONO_DIGIT_W);
|
||||
drawMonoShadowed(*font_, xx, y + LINE2_Y, SALA_VAL, VAL_C, SHD_C, MONO_DIGIT_W);
|
||||
}
|
||||
@@ -217,13 +217,21 @@ namespace Defaults {
|
||||
constexpr Uint8 MENU_DISABLED = 16; // Label en gris quan no aplica.
|
||||
|
||||
// Índexs de paleta per al marcador de joc (HUD de la GameScene).
|
||||
// Arranca reciclant els colors del menú per coherència visual;
|
||||
// separant els noms a constants pròpies, tocar-los a posteriori
|
||||
// no afecta el menú.
|
||||
constexpr Uint8 HUD_BG = 0; // Fons de cada bloc.
|
||||
constexpr Uint8 HUD_LABEL = 58; // Etiquetes ("P1", "V", "T").
|
||||
constexpr Uint8 HUD_VALUE = 187; // Valors (score, vides, tresors).
|
||||
constexpr Uint8 HUD_FLASH = 237; // Color temporal del valor en flash.
|
||||
// Tres "famílies" de color — un parell (label, value) + sombra per
|
||||
// cada bloc — donen jerarquia visual: P1 càlid (vermell+crema),
|
||||
// CENTER ambiental (ambre+or), P2 fred (cian+blau pàl·lid). La
|
||||
// sombra és un to més fosc del mateix matís per donar volum.
|
||||
constexpr Uint8 HUD_BG = 0; // Fons de cada bloc.
|
||||
constexpr Uint8 HUD_P1_LABEL = 7; // P1 label: vermell pur (#ff0000).
|
||||
constexpr Uint8 HUD_P1_VALUE = 183; // P1 value: crema-préssec (#ffdaaa).
|
||||
constexpr Uint8 HUD_P1_SHADOW = 2; // P1 ombra: vermell fosc (#480000).
|
||||
constexpr Uint8 HUD_CENTER_LABEL = 29; // CENTER label: ambre cremat (#b66d00).
|
||||
constexpr Uint8 HUD_CENTER_VALUE = 55; // CENTER value: or brillant (#ffda00).
|
||||
constexpr Uint8 HUD_CENTER_SHADOW = 10; // CENTER ombra: marró cremat (#482400).
|
||||
constexpr Uint8 HUD_P2_LABEL = 248; // P2 label: cian pur (#00ffff).
|
||||
constexpr Uint8 HUD_P2_VALUE = 253; // P2 value: blau pàl·lid (#b6ffff).
|
||||
constexpr Uint8 HUD_P2_SHADOW = 64; // P2 ombra: blau marí (#000055).
|
||||
constexpr Uint8 HUD_FLASH = 63; // Flash (groc neó, comú a totes les famílies).
|
||||
|
||||
// Rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO).
|
||||
// Independent dels colors de notificació perquè el rètol de
|
||||
|
||||
Reference in new issue
Block a user