diff --git a/data/fonts/CaesarDressing-Regular.fnt b/data/fonts/CaesarDressing-Regular.fnt new file mode 100644 index 0000000..712fe07 --- /dev/null +++ b/data/fonts/CaesarDressing-Regular.fnt @@ -0,0 +1,132 @@ +# Font: CaesarDressing-Regular — generado desde CaesarDressing-Regular.ttf size 24 +# Generado con font_gen.py + +box_width 18 +box_height 30 +columns 15 + +# codepoint_decimal ancho_visual +32 8 # U+0020 +33 5 # ! +34 6 # " +35 14 # # +36 7 # $ +37 17 # % +38 9 # & +39 3 # ' +40 10 # ( +41 8 # ) +42 10 # * +43 11 # + +44 5 # , +45 10 # - +46 3 # . +47 9 # / +48 12 # 0 +49 6 # 1 +50 10 # 2 +51 9 # 3 +52 12 # 4 +53 11 # 5 +54 13 # 6 +55 11 # 7 +56 13 # 8 +57 13 # 9 +58 5 # : +59 4 # ; +60 11 # < +61 11 # = +62 11 # > +63 11 # ? +64 14 # @ +65 14 # A +66 12 # B +67 11 # C +68 12 # D +69 10 # E +70 10 # F +71 15 # G +72 12 # H +73 9 # I +74 9 # J +75 13 # K +76 10 # L +77 18 # M +78 12 # N +79 13 # O +80 12 # P +81 12 # Q +82 13 # R +83 9 # S +84 12 # T +85 11 # U +86 12 # V +87 17 # W +88 11 # X +89 12 # Y +90 10 # Z +91 8 # [ +92 8 # \ +93 7 # ] +94 11 # ^ +95 13 # _ +96 7 # ` +97 13 # a +98 12 # b +99 11 # c +100 12 # d +101 10 # e +102 10 # f +103 15 # g +104 12 # h +105 5 # i +106 9 # j +107 13 # k +108 10 # l +109 18 # m +110 12 # n +111 13 # o +112 12 # p +113 12 # q +114 12 # r +115 10 # s +116 12 # t +117 11 # u +118 12 # v +119 17 # w +120 11 # x +121 12 # y +122 10 # z +123 9 # { +124 5 # | +125 9 # } +126 14 # ~ +192 14 # À +193 14 # Á +200 10 # È +201 10 # É +204 9 # Ì +205 9 # Í +210 13 # Ò +211 13 # Ó +217 11 # Ù +218 11 # Ú +209 12 # Ñ +199 11 # Ç +224 13 # à +225 13 # á +232 10 # è +233 10 # é +236 7 # ì +237 8 # í +242 13 # ò +243 13 # ó +249 11 # ù +250 11 # ú +241 12 # ñ +231 11 # ç +161 5 # ¡ +191 10 # ¿ +171 11 # « +187 11 # » +183 3 # · diff --git a/data/fonts/CaesarDressing-Regular.gif b/data/fonts/CaesarDressing-Regular.gif new file mode 100644 index 0000000..e849889 Binary files /dev/null and b/data/fonts/CaesarDressing-Regular.gif differ diff --git a/data/ui.yaml b/data/ui.yaml index e50dd6f..62081a3 100644 --- a/data/ui.yaml +++ b/data/ui.yaml @@ -36,6 +36,12 @@ fonts: gif: "data/fonts/ari-w9500-condensed-display.gif" fnt: "data/fonts/ari-w9500-condensed-display.fnt" kerning: 1 + # Font del rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO). + room_text: + name: "CaesarDressing-Regular" + gif: "data/fonts/CaesarDressing-Regular.gif" + fnt: "data/fonts/CaesarDressing-Regular.fnt" + kerning: 1 menu_title: name: "ari-w9500-display" gif: "data/fonts/ari-w9500-display.gif" @@ -48,7 +54,7 @@ fonts: kerning: 0 hud: - bg_enabled: true + bg_enabled: false entry_ms: 800 # durada de l'animació d'entrada dels tres blocs flash_ms: 250 # durada del flash quan un jugador rep un hit shake_ms: 300 # durada del shake que acompanya el flash diff --git a/source/core/system/director.cpp b/source/core/system/director.cpp index 3515074..209a665 100644 --- a/source/core/system/director.cpp +++ b/source/core/system/director.cpp @@ -122,6 +122,14 @@ Director::Director() { const std::string PLACEHOLDER_FONT_GIF_KEY = UiConfig::get().placeholderFont().name + ".gif"; Resource::Cache::get().loadSurface(PLACEHOLDER_FONT_GIF_KEY, UiConfig::get().placeholderFont().gif); Resource::Cache::get().getSurface(PLACEHOLDER_FONT_GIF_KEY)->setPalette(Resource::Cache::get().getPalette("ui")); + // Font del rètol de transició de sala (la consumeix `GameScene` + // tant per la línia "Piràmide N / Sala M" de l'INTRO com pel + // "Fase completada" de l'OUTRO). Es preloda ací per consistència + // amb la resta de fonts d'UI; sense això, la primera entrada a + // GAME aniria a disc. + const std::string ROOM_TEXT_FONT_GIF_KEY = UiConfig::get().roomTextFont().name + ".gif"; + Resource::Cache::get().loadSurface(ROOM_TEXT_FONT_GIF_KEY, UiConfig::get().roomTextFont().gif); + Resource::Cache::get().getSurface(ROOM_TEXT_FONT_GIF_KEY)->setPalette(Resource::Cache::get().getPalette("ui")); // Música de la piràmide 1 (primer i únic track de moment). Carreguem ací // perquè forma part del preload d'assets, com la font i la paleta. diff --git a/source/core/ui/ui_config.cpp b/source/core/ui/ui_config.cpp index f982f0b..faa1a87 100644 --- a/source/core/ui/ui_config.cpp +++ b/source/core/ui/ui_config.cpp @@ -96,6 +96,7 @@ UiConfig::UiConfig() pause_exit_curve_(curveByName(Defaults::UI::PAUSE_EXIT_CURVE)), hud_font_{.name = Defaults::UI::Font::HUD_NAME, .gif = Defaults::UI::Font::HUD_GIF, .fnt = Defaults::UI::Font::HUD_FNT, .kerning = Defaults::UI::Font::HUD_KERNING}, placeholder_font_{.name = Defaults::UI::Font::PLACEHOLDER_NAME, .gif = Defaults::UI::Font::PLACEHOLDER_GIF, .fnt = Defaults::UI::Font::PLACEHOLDER_FNT, .kerning = Defaults::UI::Font::PLACEHOLDER_KERNING}, + room_text_font_{.name = Defaults::UI::Font::ROOM_TEXT_NAME, .gif = Defaults::UI::Font::ROOM_TEXT_GIF, .fnt = Defaults::UI::Font::ROOM_TEXT_FNT, .kerning = Defaults::UI::Font::ROOM_TEXT_KERNING}, menu_title_font_{.name = Defaults::UI::Font::MENU_TITLE_NAME, .gif = Defaults::UI::Font::MENU_TITLE_GIF, .fnt = Defaults::UI::Font::MENU_TITLE_FNT, .kerning = Defaults::UI::Font::MENU_TITLE_KERNING}, menu_items_font_{.name = Defaults::UI::Font::MENU_ITEMS_NAME, .gif = Defaults::UI::Font::MENU_ITEMS_GIF, .fnt = Defaults::UI::Font::MENU_ITEMS_FNT, .kerning = Defaults::UI::Font::MENU_ITEMS_KERNING} { } @@ -159,6 +160,7 @@ void UiConfig::load(const std::string& file_path) { const auto& f = yaml["fonts"]; readFont(f, "hud", hud_font_); readFont(f, "placeholder", placeholder_font_); + readFont(f, "room_text", room_text_font_); readFont(f, "menu_title", menu_title_font_); readFont(f, "menu_items", menu_items_font_); } diff --git a/source/core/ui/ui_config.hpp b/source/core/ui/ui_config.hpp index a856eb8..44df727 100644 --- a/source/core/ui/ui_config.hpp +++ b/source/core/ui/ui_config.hpp @@ -77,6 +77,7 @@ class UiConfig { // Fonts de UI. [[nodiscard]] auto hudFont() const -> const Font& { return hud_font_; } [[nodiscard]] auto placeholderFont() const -> const Font& { return placeholder_font_; } + [[nodiscard]] auto roomTextFont() const -> const Font& { return room_text_font_; } [[nodiscard]] auto menuTitleFont() const -> const Font& { return menu_title_font_; } [[nodiscard]] auto menuItemsFont() const -> const Font& { return menu_items_font_; } @@ -118,6 +119,7 @@ class UiConfig { Font hud_font_; Font placeholder_font_; + Font room_text_font_; Font menu_title_font_; Font menu_items_font_; diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 1f591a7..45fbc1a 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -436,6 +436,13 @@ GameScene::GameScene(Config config) pause_font_ = std::make_shared(pause_surface, UiConfig::get().placeholderFont().fnt); pause_font_->setDefaultKerning(UiConfig::get().placeholderFont().kerning); + // Font del rètol de transició de sala. Mateixa convenció que la pausa + // (Surface preloaded pel Director, només construïm el `Text` ací). + const std::string ROOM_TEXT_FONT_KEY = UiConfig::get().roomTextFont().name + ".gif"; + auto room_text_surface = Resource::Cache::get().getSurface(ROOM_TEXT_FONT_KEY); + room_text_font_ = std::make_shared(room_text_surface, UiConfig::get().roomTextFont().fnt); + room_text_font_->setDefaultKerning(UiConfig::get().roomTextFont().kerning); + // Timings i corbes de la FSM de sala. Llegits del YAML una sola // vegada; canvis requereixen reiniciar el joc. transition_config_ = RoomFsm::loadConfig(Defaults::GameScene::CONFIG_YAML); @@ -627,10 +634,10 @@ void GameScene::renderRoomFade() { } void GameScene::renderRoomText() { - // Text de la transició de sala. Reusa la font cachejada per a la - // pausa (mateix tipus de lletra, mateix índex de color), i fa el - // mateix patró esquerra → centre → dreta que el text de pausa però - // amb les durades pròpies de la transició. + // Text de la transició de sala. Font dedicada (`room_text_font_`, + // CaesarDressing per defecte) — la pausa té la seua i no es barregen. + // Patró esquerra → centre → dreta amb les durades pròpies de la + // transició. // // Mapatge per estat (només els estats amb text actiu hi entren): // ROOM_INTRO: phases 2..4 → enter / hold / exit; dues línies @@ -639,7 +646,7 @@ void GameScene::renderRoomText() { // per deixar la porta oberta a desacoblar-les. // ROOM_OUTRO: phases 0..2 → enter / hold / exit; una línia // ("fase completada"). - if (!pause_font_) { return; } + if (!room_text_font_) { return; } constexpr int KERNING = 0; constexpr Uint8 OUTLINE_WHITE = 255; @@ -671,7 +678,7 @@ void GameScene::renderRoomText() { }; auto draw_line = [&](const std::string& text, int x, int y) { - pause_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE4_FLAG, + room_text_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE4_FLAG, x, y, text, @@ -685,25 +692,28 @@ void GameScene::renderRoomText() { const auto& mgr = Levels::LevelManager::get(); const std::string LINE_PYRAMID = RoomFsm::formatIntroPyramidLine(mgr.currentPyramid()); const std::string LINE_ROOM = RoomFsm::formatIntroRoomLine(mgr.currentRoom()); - const int W_PYRAMID = pause_font_->length(LINE_PYRAMID, KERNING); - const int W_ROOM = pause_font_->length(LINE_ROOM, KERNING); + const int W_PYRAMID = room_text_font_->length(LINE_PYRAMID, KERNING); + const int W_ROOM = room_text_font_->length(LINE_ROOM, KERNING); const int DUR_ENTER = transition_config_.intro_text_enter_ms; const int DUR_EXIT = transition_config_.intro_text_exit_ms; const int X_PYRAMID = compute_x(W_PYRAMID, 2, 4, DUR_ENTER, DUR_EXIT); const int X_ROOM = compute_x(W_ROOM, 2, 4, DUR_ENTER, DUR_EXIT); - // Bloc de dues línies centrat al voltant de CENTER_Y: la - // piràmide ocupa la línia de damunt, la sala la de davall. Si - // un dia el font canvia d'alçada, `getLineHeight` ho reflecteix. - const int LINE_HEIGHT = pause_font_->getLineHeight(); - const int Y_PYRAMID = GameCanvas::CENTER_Y - LINE_HEIGHT; - const int Y_ROOM = GameCanvas::CENTER_Y; + // Bloc de dues línies centrat al voltant de CENTER_Y. Entre + // les dues línies hi posem `intro_text_line_gap_px` extra (a + // més del lineHeight natural) per donar-los aire visual; la + // meitat la traiem cap amunt de la piràmide i la meitat la + // sumem cap avall a la sala, mantenint el centre del bloc fix. + const int LINE_HEIGHT = room_text_font_->getLineHeight(); + const int GAP = transition_config_.intro_text_line_gap_px; + const int Y_PYRAMID = GameCanvas::CENTER_Y - LINE_HEIGHT - (GAP / 2); + const int Y_ROOM = GameCanvas::CENTER_Y + (GAP / 2); draw_line(LINE_PYRAMID, X_PYRAMID, Y_PYRAMID); draw_line(LINE_ROOM, X_ROOM, Y_ROOM); } else if (state_ == RoomFsm::State::ROOM_OUTRO && transition_.phase >= 0 && transition_.phase <= 2) { const std::string TEXT = Locale::get().text("game.room_complete"); - const int W = pause_font_->length(TEXT, KERNING); + const int W = room_text_font_->length(TEXT, KERNING); const int X = compute_x(W, 0, 2, transition_config_.outro_text_enter_ms, transition_config_.outro_text_exit_ms); - const int Y = GameCanvas::CENTER_Y - (pause_font_->getLineHeight() / 2); + const int Y = GameCanvas::CENTER_Y - (room_text_font_->getLineHeight() / 2); draw_line(TEXT, X, Y); } } diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index f975f8e..e17ed67 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -153,6 +153,13 @@ class GameScene : public Scene { // abans — ja cachejada al Director. std::shared_ptr pause_font_; + // Font del rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO). + // Distinta de `pause_font_` perquè el rètol de fase té estètica + // pròpia (CaesarDressing per defecte, configurable via + // `data/ui.yaml > fonts.room_text`). La pausa segueix amb la + // placeholder. + std::shared_ptr room_text_font_; + enum class PauseState : std::uint8_t { IDLE, // Joc corrent; cap transició en marxa. scale == 1. ENTERING, // Frenant cap a la pausa. scale decreix a 0. diff --git a/source/game/scenes/room_fsm.cpp b/source/game/scenes/room_fsm.cpp index ee1c461..0ec5671 100644 --- a/source/game/scenes/room_fsm.cpp +++ b/source/game/scenes/room_fsm.cpp @@ -43,6 +43,7 @@ namespace RoomFsm { cfg.intro_text_hold_ms = readChildInt(n, "text_hold_ms", cfg.intro_text_hold_ms); cfg.intro_text_exit_ms = readChildInt(n, "text_exit_ms", cfg.intro_text_exit_ms); cfg.player_spawn_delay_ms = readChildInt(n, "player_spawn_delay_ms", cfg.player_spawn_delay_ms); + cfg.intro_text_line_gap_px = readChildInt(n, "line_gap_px", cfg.intro_text_line_gap_px); } if (yaml.contains("room_outro")) { const auto& n = yaml["room_outro"]; diff --git a/source/game/scenes/room_fsm.hpp b/source/game/scenes/room_fsm.hpp index a4c8684..7a85e27 100644 --- a/source/game/scenes/room_fsm.hpp +++ b/source/game/scenes/room_fsm.hpp @@ -49,6 +49,13 @@ namespace RoomFsm { // moment en què el fade-in acaba i el text encara no ha entrat. int player_spawn_delay_ms{500}; + // Píxels extra entre les dues línies del text d'INTRO (piràmide + // damunt, sala davall). Es sumen al `lineHeight` natural del + // font per separar visualment les dues línies. 0 = stack + // immediat (com l'antic single-line). El YAML pot afinar-lo + // sense recompilar. + int intro_text_line_gap_px{8}; + UiConfig::CurveFn text_enter_curve{nullptr}; UiConfig::CurveFn text_exit_curve{nullptr}; }; diff --git a/source/utils/defaults.hpp b/source/utils/defaults.hpp index 9392e36..d8e2905 100644 --- a/source/utils/defaults.hpp +++ b/source/utils/defaults.hpp @@ -130,6 +130,15 @@ namespace Defaults { constexpr const char* PLACEHOLDER_FNT = "data/fonts/ari-w9500-condensed-display.fnt"; constexpr int PLACEHOLDER_KERNING = 1; + // Text de transició de sala (ROOM_INTRO "Piràmide N / Sala M" i + // ROOM_OUTRO "Fase completada"). Font temàtica diferenciada de la + // del HUD i de la dels placeholders perquè el rètol de fase tinga + // pes visual propi. La pausa segueix amb la PLACEHOLDER. + constexpr const char* ROOM_TEXT_NAME = "CaesarDressing-Regular"; + constexpr const char* ROOM_TEXT_GIF = "data/fonts/CaesarDressing-Regular.gif"; + constexpr const char* ROOM_TEXT_FNT = "data/fonts/CaesarDressing-Regular.fnt"; + constexpr int ROOM_TEXT_KERNING = 1; + // Menú de servei (F9): títol en display normal, items en la // variant condensed-display (més compacta, perquè caben més // files sense encongir la caixa). Kerning 0 a totes dues perquè