feat(intro): font dedicada (CaesarDressing) i separació vertical entre línies
Nova font del rètol de transició de sala (ROOM_INTRO i ROOM_OUTRO): CaesarDressing-Regular, vista com a fonts.room_text al data/ui.yaml i exposada per UiConfig::roomTextFont(). Director la preloda al costat de la del HUD i la del placeholder; GameScene construeix room_text_font_ paral·lel a pause_font_ i renderRoomText l'usa per a les dues línies de l'INTRO i el text de l'OUTRO. La pausa segueix amb la font placeholder antiga — no es barregen. Espai vertical configurable: nova clau room_intro.line_gap_px al data/scenes/game.yaml (default 8 px). El gap es suma al lineHeight natural del font i es reparteix simètricament cap amunt i cap avall del CENTER_Y, deixant el bloc de dues línies sempre centrat. Es poden afinar des del YAML sense recompilar.
This commit is contained in:
1 parent
f52e7f4682
commit
af25a31639
11 files changed
+201
-17
No files matched your search
@@ -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 # ·
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
+7
-1
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -436,6 +436,13 @@ GameScene::GameScene(Config config)
|
||||
pause_font_ = std::make_shared<Text>(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<Text>(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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,13 @@ class GameScene : public Scene {
|
||||
// abans — ja cachejada al Director.
|
||||
std::shared_ptr<Text> 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<Text> 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.
|
||||
|
||||
@@ -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"];
|
||||
|
||||
@@ -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};
|
||||
};
|
||||
|
||||
@@ -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è
|
||||
|
||||
Reference in new issue
Block a user