feat(outro): rètol "fase completada" en dues línies oposades

Mateixa tècnica que l'INTRO: la línia 1 ("Fase") entra per
l'esquerra, la línia 2 ("Completada") per la dreta, i surten cap
al costat oposat al d'entrada. Centrades verticalment al voltant
del CENTER_Y amb el gap configurable ( a
data/scenes/game.yaml; 0 per defecte, paral·lel a la del room_intro).

Locale: la clau game.room_complete es divideix en
game.room_complete_line1 / room_complete_line2 als ca.yaml i en.yaml
("Fase"/"Completada" i "Stage"/"Complete"). Cada línia es
tradueix per separat — útil si demà voleu paraules de longitud o
ordre distint en altres idiomes.
This commit is contained in:
JailDesigner committed 2026-04-26 17:28:07 +02:00
1 parent 8da18a6a86
commit 7982df922b
6 files changed
+40 -20

No files matched your search

+6 -5
View File
@@ -37,14 +37,15 @@ hud:
room: "Sala"
# Textos de la transició d'entrada/sortida de sala (FSM de GameScene).
# Les dues primeres claus es renderitzen com a línies separades durant
# l'INTRO (preparades per ser animades de manera independent en el
# futur). Accepten els placeholders `{p}` (piràmide) i `{r}` (sala) que
# es substitueixen amb els valors actuals del LevelManager.
# Tant l'INTRO com l'OUTRO es renderitzen com a dues línies separades
# (cadascuna amb la seua animació). L'INTRO accepta els placeholders
# `{p}` (piràmide) i `{r}` (sala); l'OUTRO no en té cap — són les
# dues paraules del rètol "fase completada".
game:
room_intro_pyramid: "Piramide {p}"
room_intro_room: "Sala {r}"
room_complete: "Fase completada"
room_complete_line1: "Fase"
room_complete_line2: "Completada"
menu:
titles:
+6 -6
View File
@@ -36,15 +36,15 @@ hud:
pyramid: "Pyramid"
room: "Room"
# Room transition texts (GameScene FSM intro/outro). The two pyramid /
# room strings render as independent lines during INTRO (so they can
# be animated separately in the future). They accept `{p}` (pyramid)
# and `{r}` (room) placeholders, replaced with the current LevelManager
# values.
# Room transition texts (GameScene FSM intro/outro). Both INTRO and
# OUTRO render as two independent lines with their own animation.
# INTRO uses `{p}` (pyramid) and `{r}` (room) placeholders; OUTRO has
# no placeholders — it's the two words of the "stage complete" label.
game:
room_intro_pyramid: "Pyramid {p}"
room_intro_room: "Room {r}"
room_complete: "Stage complete"
room_complete_line1: "Stage"
room_complete_line2: "Complete"
menu:
titles:
+5 -4
View File
@@ -10,13 +10,14 @@ room_intro:
text_enter_ms: 400 # text esquerra → centre
text_hold_ms: 1500 # text quiet al centre
text_exit_ms: 400 # text centre → fora per l'esquerra
line_gap_px: 8 # px extra entre les dues línies (piràmide / sala)
line_gap_px: 0 # px extra entre les dues línies (piràmide / sala)
room_outro:
text_enter_ms: 500
text_hold_ms: 1000
text_exit_ms: 500
text_enter_ms: 400
text_hold_ms: 1500
text_exit_ms: 400
fade_out_ms: 500 # món visible → negre opac
line_gap_px: 0 # px extra entre les dues línies (fase / completada)
text:
# Noms d'easing tal com els resol UiConfig::curveByName (vegeu
+17 -5
View File
@@ -721,11 +721,23 @@ void GameScene::renderRoomText() {
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 = 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, /*from_right=*/false);
const int Y = GameCanvas::CENTER_Y - (room_text_font_->getLineHeight() / 2);
draw_line(TEXT, X, Y);
// Mateix patró que l'INTRO: dues línies en direccions oposades.
// Línia 1 ("Fase") per l'esquerra, línia 2 ("Completada") per
// la dreta; surten cap al costat oposat del que han entrat.
const std::string LINE_1 = Locale::get().text("game.room_complete_line1");
const std::string LINE_2 = Locale::get().text("game.room_complete_line2");
const int W_1 = room_text_font_->length(LINE_1, KERNING);
const int W_2 = room_text_font_->length(LINE_2, KERNING);
const int DUR_ENTER = transition_config_.outro_text_enter_ms;
const int DUR_EXIT = transition_config_.outro_text_exit_ms;
const int X_1 = compute_x(W_1, 0, 2, DUR_ENTER, DUR_EXIT, /*from_right=*/false);
const int X_2 = compute_x(W_2, 0, 2, DUR_ENTER, DUR_EXIT, /*from_right=*/true);
const int LINE_HEIGHT = room_text_font_->getLineHeight();
const int GAP = transition_config_.outro_text_line_gap_px;
const int Y_1 = GameCanvas::CENTER_Y - LINE_HEIGHT - (GAP / 2);
const int Y_2 = GameCanvas::CENTER_Y + (GAP / 2);
draw_line(LINE_1, X_1, Y_1);
draw_line(LINE_2, X_2, Y_2);
}
}
+1
View File
@@ -51,6 +51,7 @@ namespace RoomFsm {
cfg.outro_text_hold_ms = readChildInt(n, "text_hold_ms", cfg.outro_text_hold_ms);
cfg.outro_text_exit_ms = readChildInt(n, "text_exit_ms", cfg.outro_text_exit_ms);
cfg.outro_fade_out_ms = readChildInt(n, "fade_out_ms", cfg.outro_fade_out_ms);
cfg.outro_text_line_gap_px = readChildInt(n, "line_gap_px", cfg.outro_text_line_gap_px);
}
if (yaml.contains("text")) {
const auto& n = yaml["text"];
+5
View File
@@ -56,6 +56,11 @@ namespace RoomFsm {
// sense recompilar.
int intro_text_line_gap_px{8};
// Equivalent per a l'OUTRO ("fase" damunt, "completada" davall).
// Es manté independent del de l'INTRO per si el ritme visual
// demana un espaiat distint en cada banda de la transició.
int outro_text_line_gap_px{8};
UiConfig::CurveFn text_enter_curve{nullptr};
UiConfig::CurveFn text_exit_curve{nullptr};
};