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:
JailDesigner committed 2026-04-26 15:40:09 +02:00
1 parent f52e7f4682
commit af25a31639
11 files changed
+201 -17

No files matched your search

+26 -16
View File
@@ -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);
}
}
+7
View File
@@ -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.
+1
View File
@@ -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"];
+7
View File
@@ -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};
};