feat(level-manager): HUD i text d'INTRO segueixen el LevelManager
HUD: la GameScene crida hud_->setPyramidRoom amb els valors del
LevelManager al ctor i a cada loadRoom — el marcador deixa de
mostrar 1/1 fix i s'actualitza en avançar de sala, en saltar amb
[/] i en arrencar amb starting_level del debug.yaml.
INTRO a dues línies: substitueix la clau game.room_intro per
game.room_intro_pyramid ("Piramide {p}") i game.room_intro_room
("Sala {r}") als locale ca/en. RoomFsm::formatRoomTitle(int)
desapareix i el reemplacen formatIntroPyramidLine(int) +
formatIntroRoomLine(int). renderRoomText es refactoritza per
dibuixar les dues línies (piràmide damunt, sala davall) amb
writeDX independents — comparteixen animació ara mateix però el
disseny ja les deixa preparades per ser desacoblades en el futur.
Drive-by: elimina el membre room_index_ de GameScene; les tres
ubicacions que el sincronitzaven amb el LevelManager (ctor,
advanceToNextRoom, salt debug) ja no fan falta — renderRoomText
i el HUD pregunten directament al singleton, evitant qualsevol
mirror local desincronitzable.
Test: T9/T10 actualitzats per a les dues noves formatadores.
This commit is contained in:
1 parent
b18bd4d021
commit
e2f6d326aa
7 files changed
+130
-80
No files matched your search
+6
-3
@@ -37,10 +37,13 @@ hud:
|
|||||||
room: "Sala"
|
room: "Sala"
|
||||||
|
|
||||||
# Textos de la transició d'entrada/sortida de sala (FSM de GameScene).
|
# Textos de la transició d'entrada/sortida de sala (FSM de GameScene).
|
||||||
# `room_intro` accepta el placeholder `{n}` que es substitueix amb
|
# Les dues primeres claus es renderitzen com a línies separades durant
|
||||||
# l'índex de sala actual; veure `GameScene::formatRoomTitle`.
|
# 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.
|
||||||
game:
|
game:
|
||||||
room_intro: "Sala {n}"
|
room_intro_pyramid: "Piramide {p}"
|
||||||
|
room_intro_room: "Sala {r}"
|
||||||
room_complete: "Fase completada"
|
room_complete: "Fase completada"
|
||||||
|
|
||||||
menu:
|
menu:
|
||||||
|
|||||||
+7
-4
@@ -36,11 +36,14 @@ hud:
|
|||||||
pyramid: "Pyramid"
|
pyramid: "Pyramid"
|
||||||
room: "Room"
|
room: "Room"
|
||||||
|
|
||||||
# Room transition texts (GameScene FSM intro/outro). The `room_intro`
|
# Room transition texts (GameScene FSM intro/outro). The two pyramid /
|
||||||
# string accepts a `{n}` placeholder that is replaced with the current
|
# room strings render as independent lines during INTRO (so they can
|
||||||
# room index; see `GameScene::formatRoomTitle`.
|
# be animated separately in the future). They accept `{p}` (pyramid)
|
||||||
|
# and `{r}` (room) placeholders, replaced with the current LevelManager
|
||||||
|
# values.
|
||||||
game:
|
game:
|
||||||
room_intro: "Room {n}"
|
room_intro_pyramid: "Pyramid {p}"
|
||||||
|
room_intro_room: "Room {r}"
|
||||||
room_complete: "Stage complete"
|
room_complete: "Stage complete"
|
||||||
|
|
||||||
menu:
|
menu:
|
||||||
|
|||||||
@@ -239,12 +239,11 @@ auto GameScene::defaultConfig() -> Config {
|
|||||||
GameScene::GameScene(Config config)
|
GameScene::GameScene(Config config)
|
||||||
: config_(std::move(config)),
|
: config_(std::move(config)),
|
||||||
tilemap_(makeTilemap(config_.map_yaml)) {
|
tilemap_(makeTilemap(config_.map_yaml)) {
|
||||||
// Sincronitzem l'identificador de la sala amb el LevelManager (la
|
// El text d'INTRO i el HUD demanaran piràmide/sala directament al
|
||||||
// mateixa font que ha donat el `map_yaml`). El text d'intro encara
|
// LevelManager — `GameScene` ja no manté un mirror local. La
|
||||||
// consumeix només `{n}` = número de sala dins de la piràmide; quan
|
// sincronització "una vegada per càrrega de sala" la fa `loadRoom`
|
||||||
// afegim el placeholder de piràmide al locale, ací també caldrà
|
// (per al HUD) i `renderRoomText` (per al text d'INTRO, llegint el
|
||||||
// passar-li `currentPyramid()`.
|
// valor a cada frame de la transició).
|
||||||
room_index_ = Levels::LevelManager::get().currentRoom();
|
|
||||||
|
|
||||||
// Convenció: el Resource::Cache indexa per basename del fitxer, perquè
|
// Convenció: el Resource::Cache indexa per basename del fitxer, perquè
|
||||||
// la mateixa clau l'usa el parseador d'animació (YAML > tileSetFile).
|
// la mateixa clau l'usa el parseador d'animació (YAML > tileSetFile).
|
||||||
@@ -415,6 +414,13 @@ GameScene::GameScene(Config config)
|
|||||||
// només P1). Mentre els events del bus no porten `player_id`, els hits
|
// només P1). Mentre els events del bus no porten `player_id`, els hits
|
||||||
// segueixen aplicant-se a P1 (veure TODO a Hud::onPlayerHit).
|
// segueixen aplicant-se a P1 (veure TODO a Hud::onPlayerHit).
|
||||||
if (USE_P2) { hud_->activatePlayer(2); }
|
if (USE_P2) { hud_->activatePlayer(2); }
|
||||||
|
// Sincronització inicial dels comptadors de piràmide/sala del HUD amb
|
||||||
|
// el LevelManager (després de cada `loadRoom` també es refresca; ací
|
||||||
|
// és el primer ajust per a la sala d'arrencada).
|
||||||
|
{
|
||||||
|
const auto& mgr = Levels::LevelManager::get();
|
||||||
|
hud_->setPyramidRoom(mgr.currentPyramid(), mgr.currentRoom());
|
||||||
|
}
|
||||||
|
|
||||||
// Música de fons en bucle infinit. El track ja està al Resource::Cache
|
// Música de fons en bucle infinit. El track ja està al Resource::Cache
|
||||||
// (preload al Director), així que playMusic només mou el punter cap a
|
// (preload al Director), així que playMusic només mou el punter cap a
|
||||||
@@ -486,6 +492,15 @@ void GameScene::loadRoom(const std::string& yaml_path) {
|
|||||||
// el primer.
|
// el primer.
|
||||||
for (auto& p : players_) { p->disable(); }
|
for (auto& p : players_) { p->disable(); }
|
||||||
|
|
||||||
|
// Refresca el marcador (piràmide / sala) amb el LevelManager. El
|
||||||
|
// GameScene no manté els valors a part: la lectura sempre és a
|
||||||
|
// través del singleton, així no es desincronitzen mai entre
|
||||||
|
// hotkeys de salt, debug.yaml i creuament natural de porta.
|
||||||
|
if (hud_) {
|
||||||
|
const auto& mgr = Levels::LevelManager::get();
|
||||||
|
hud_->setPyramidRoom(mgr.currentPyramid(), mgr.currentRoom());
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "[ROOM] load " << yaml_path << ": entities reloaded, paint cleared, "
|
std::cout << "[ROOM] load " << yaml_path << ": entities reloaded, paint cleared, "
|
||||||
<< ACTIVE_COUNT << " active player(s) disabled (awaiting spawn cinematic)\n";
|
<< ACTIVE_COUNT << " active player(s) disabled (awaiting spawn cinematic)\n";
|
||||||
}
|
}
|
||||||
@@ -498,7 +513,6 @@ void GameScene::advanceToNextRoom() {
|
|||||||
std::cout << "[ROOM] final del levels.yaml — bucle a la primera sala\n";
|
std::cout << "[ROOM] final del levels.yaml — bucle a la primera sala\n";
|
||||||
mgr.restart();
|
mgr.restart();
|
||||||
}
|
}
|
||||||
room_index_ = mgr.currentRoom();
|
|
||||||
loadRoom(mgr.currentPath());
|
loadRoom(mgr.currentPath());
|
||||||
enterRoomIntro();
|
enterRoomIntro();
|
||||||
}
|
}
|
||||||
@@ -615,70 +629,83 @@ void GameScene::renderRoomFade() {
|
|||||||
void GameScene::renderRoomText() {
|
void GameScene::renderRoomText() {
|
||||||
// Text de la transició de sala. Reusa la font cachejada per a la
|
// 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
|
// pausa (mateix tipus de lletra, mateix índex de color), i fa el
|
||||||
// mateix patró esquerra → centre → esquerra que el text de pausa
|
// mateix patró esquerra → centre → dreta que el text de pausa però
|
||||||
// però amb les durades pròpies de la transició.
|
// amb les durades pròpies de la transició.
|
||||||
//
|
//
|
||||||
// Mapatge per estat (només els estats amb text actiu hi entren):
|
// Mapatge per estat (només els estats amb text actiu hi entren):
|
||||||
// ROOM_INTRO: phases 2..4 → enter / hold / exit; text = sala N
|
// ROOM_INTRO: phases 2..4 → enter / hold / exit; dues línies
|
||||||
// ROOM_OUTRO: phases 0..2 → enter / hold / exit; text = "fase completada"
|
// (piràmide damunt, sala davall) que comparteixen
|
||||||
|
// animació però es dibuixen amb writeDX independents
|
||||||
|
// 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 (!pause_font_) { return; }
|
||||||
|
|
||||||
std::string text;
|
|
||||||
int phase_enter = 0;
|
|
||||||
int phase_exit = 0;
|
|
||||||
int dur_enter = 0;
|
|
||||||
int dur_exit = 0;
|
|
||||||
if (state_ == RoomFsm::State::ROOM_INTRO && transition_.phase >= 2 && transition_.phase <= 4) {
|
|
||||||
text = RoomFsm::formatRoomTitle(room_index_);
|
|
||||||
phase_enter = 2;
|
|
||||||
phase_exit = 4;
|
|
||||||
dur_enter = transition_config_.intro_text_enter_ms;
|
|
||||||
dur_exit = transition_config_.intro_text_exit_ms;
|
|
||||||
} else if (state_ == RoomFsm::State::ROOM_OUTRO && transition_.phase >= 0 && transition_.phase <= 2) {
|
|
||||||
text = Locale::get().text("game.room_complete");
|
|
||||||
phase_enter = 0;
|
|
||||||
phase_exit = 2;
|
|
||||||
dur_enter = transition_config_.outro_text_enter_ms;
|
|
||||||
dur_exit = transition_config_.outro_text_exit_ms;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int KERNING = 0;
|
constexpr int KERNING = 0;
|
||||||
const int W = pause_font_->length(text, KERNING);
|
constexpr Uint8 OUTLINE_WHITE = 255;
|
||||||
// El text entra des de l'esquerra (off-screen amb el seu cantó dret
|
constexpr Uint8 STROKE_DIST = 1;
|
||||||
// a x=0) i, després del hold, surt cap a la dreta (off-screen amb
|
|
||||||
// el seu cantó esquerre a x=WIDTH). Així el text "travessa" la
|
|
||||||
// pantalla en una sola direcció en compte de tornar enrere.
|
|
||||||
const int OFF_LEFT = -W;
|
|
||||||
const int OFF_RIGHT = GameCanvas::WIDTH;
|
|
||||||
int x = GameCanvas::CENTER_X;
|
|
||||||
|
|
||||||
|
// Calcula la X animada d'una línia: entra des de l'esquerra
|
||||||
|
// (off-screen amb el seu cantó dret a x=0), s'atura al CENTER_X
|
||||||
|
// durant el hold i surt cap a la dreta (off-screen amb el cantó
|
||||||
|
// esquerre a x=WIDTH). El càlcul depèn de l'amplada de la línia
|
||||||
|
// — per això la passem com a paràmetre.
|
||||||
|
auto compute_x = [&](int width, int phase_enter, int phase_exit, int dur_enter, int dur_exit) -> int {
|
||||||
|
const int OFF_LEFT = -width;
|
||||||
|
const int OFF_RIGHT = GameCanvas::WIDTH;
|
||||||
if (transition_.phase == phase_enter) {
|
if (transition_.phase == phase_enter) {
|
||||||
const float T = (dur_enter > 0)
|
const float T = (dur_enter > 0)
|
||||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_enter), 0.0F, 1.0F)
|
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_enter), 0.0F, 1.0F)
|
||||||
: 1.0F;
|
: 1.0F;
|
||||||
const auto CURVE = (transition_config_.text_enter_curve != nullptr) ? transition_config_.text_enter_curve : &Easing::linear;
|
const auto CURVE = (transition_config_.text_enter_curve != nullptr) ? transition_config_.text_enter_curve : &Easing::linear;
|
||||||
x = Easing::lerpInt(OFF_LEFT, GameCanvas::CENTER_X, CURVE(T));
|
return Easing::lerpInt(OFF_LEFT, GameCanvas::CENTER_X, CURVE(T));
|
||||||
} else if (transition_.phase == phase_exit) {
|
}
|
||||||
|
if (transition_.phase == phase_exit) {
|
||||||
const float T = (dur_exit > 0)
|
const float T = (dur_exit > 0)
|
||||||
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_exit), 0.0F, 1.0F)
|
? std::clamp(static_cast<float>(transition_.elapsed_ms) / static_cast<float>(dur_exit), 0.0F, 1.0F)
|
||||||
: 1.0F;
|
: 1.0F;
|
||||||
const auto CURVE = (transition_config_.text_exit_curve != nullptr) ? transition_config_.text_exit_curve : &Easing::linear;
|
const auto CURVE = (transition_config_.text_exit_curve != nullptr) ? transition_config_.text_exit_curve : &Easing::linear;
|
||||||
x = Easing::lerpInt(GameCanvas::CENTER_X, OFF_RIGHT, CURVE(T));
|
return Easing::lerpInt(GameCanvas::CENTER_X, OFF_RIGHT, CURVE(T));
|
||||||
}
|
}
|
||||||
|
return GameCanvas::CENTER_X;
|
||||||
|
};
|
||||||
|
|
||||||
constexpr Uint8 OUTLINE_WHITE = 255;
|
auto draw_line = [&](const std::string& text, int x, int y) {
|
||||||
constexpr Uint8 STROKE_DIST = 1;
|
|
||||||
const int Y = GameCanvas::CENTER_Y - (pause_font_->getLineHeight() / 2);
|
|
||||||
pause_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE4_FLAG,
|
pause_font_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG | Text::STROKE4_FLAG,
|
||||||
x,
|
x,
|
||||||
Y,
|
y,
|
||||||
text,
|
text,
|
||||||
KERNING,
|
KERNING,
|
||||||
UiConfig::get().notificationText(),
|
UiConfig::get().notificationText(),
|
||||||
STROKE_DIST,
|
STROKE_DIST,
|
||||||
OUTLINE_WHITE);
|
OUTLINE_WHITE);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (state_ == RoomFsm::State::ROOM_INTRO && transition_.phase >= 2 && transition_.phase <= 4) {
|
||||||
|
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 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;
|
||||||
|
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 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);
|
||||||
|
draw_line(TEXT, X, Y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::spawnDust(float feet_x_center, float feet_y, bool is_landing) {
|
void GameScene::spawnDust(float feet_x_center, float feet_y, bool is_landing) {
|
||||||
@@ -833,7 +860,6 @@ void GameScene::update(int delta_ms) {
|
|||||||
if (DELTA != 0) {
|
if (DELTA != 0) {
|
||||||
auto& mgr = Levels::LevelManager::get();
|
auto& mgr = Levels::LevelManager::get();
|
||||||
mgr.setIndex(mgr.index() + DELTA);
|
mgr.setIndex(mgr.index() + DELTA);
|
||||||
room_index_ = mgr.currentRoom();
|
|
||||||
loadRoom(mgr.currentPath());
|
loadRoom(mgr.currentPath());
|
||||||
enterRoomIntro();
|
enterRoomIntro();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,12 +183,6 @@ class GameScene : public Scene {
|
|||||||
// reseteja a {0, 0}.
|
// reseteja a {0, 0}.
|
||||||
RoomFsm::Transition transition_{};
|
RoomFsm::Transition transition_{};
|
||||||
|
|
||||||
// Índex de sala que es passa com a paràmetre {n} al text d'intro.
|
|
||||||
// Avui és sempre 1 (no hi ha itinerari de sales encara). Quan
|
|
||||||
// arribi l'itinerari, és el camp que es modificarà al transicionar
|
|
||||||
// de ROOM_OUTRO → ROOM_INTRO per saltar a la pròxima.
|
|
||||||
int room_index_{1};
|
|
||||||
|
|
||||||
// Píxels del fade negre i del text de la transició de sala.
|
// Píxels del fade negre i del text de la transició de sala.
|
||||||
// Cridats des de `render()` entre el bloc d'actors i el HUD —
|
// Cridats des de `render()` entre el bloc d'actors i el HUD —
|
||||||
// així marcador, notificacions i service menu queden per damunt.
|
// així marcador, notificacions i service menu queden per damunt.
|
||||||
|
|||||||
@@ -107,15 +107,28 @@ namespace RoomFsm {
|
|||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto formatRoomTitle(int n) -> std::string {
|
namespace {
|
||||||
auto tpl = Locale::get().text("game.room_intro");
|
// Substitueix la primera ocurrència de `placeholder` dins de `tpl`
|
||||||
constexpr std::string_view PLACEHOLDER = "{n}";
|
// pel valor d'`n` convertit a string. Utilitzat per les dues
|
||||||
auto pos = tpl.find(PLACEHOLDER);
|
// formatadores d'INTRO; si la clau del Locale no porta el
|
||||||
|
// placeholder (traducció buida o ja substituïda), retorna la
|
||||||
|
// plantilla intacta.
|
||||||
|
auto substitute(std::string tpl, std::string_view placeholder, int n) -> std::string {
|
||||||
|
auto pos = tpl.find(placeholder);
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
tpl.replace(pos, PLACEHOLDER.size(), std::to_string(n));
|
tpl.replace(pos, placeholder.size(), std::to_string(n));
|
||||||
}
|
}
|
||||||
return tpl;
|
return tpl;
|
||||||
}
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
auto formatIntroPyramidLine(int pyramid) -> std::string {
|
||||||
|
return substitute(Locale::get().text("game.room_intro_pyramid"), "{p}", pyramid);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto formatIntroRoomLine(int room) -> std::string {
|
||||||
|
return substitute(Locale::get().text("game.room_intro_room"), "{r}", room);
|
||||||
|
}
|
||||||
|
|
||||||
auto stateName(State s) -> const char* {
|
auto stateName(State s) -> const char* {
|
||||||
switch (s) {
|
switch (s) {
|
||||||
|
|||||||
@@ -66,10 +66,15 @@ namespace RoomFsm {
|
|||||||
// (això ho fa `GameScene::setState`). Funció pura sobre l'argument.
|
// (això ho fa `GameScene::setState`). Funció pura sobre l'argument.
|
||||||
[[nodiscard]] auto advance(State current, Transition& t, const Config& cfg, int delta_ms) -> State;
|
[[nodiscard]] auto advance(State current, Transition& t, const Config& cfg, int delta_ms) -> State;
|
||||||
|
|
||||||
// Substitueix `{n}` de la clau "game.room_intro" del Locale per
|
// Substitueix `{p}` de la clau "game.room_intro_pyramid" del Locale
|
||||||
// l'enter passat. Si la clau no està traduïda, retorna la pròpia
|
// per l'enter passat. Pensat per a la primera línia del text d'INTRO.
|
||||||
// clau (fallback de Locale::text).
|
// Si la clau no està traduïda, retorna la pròpia clau (fallback de
|
||||||
[[nodiscard]] auto formatRoomTitle(int n) -> std::string;
|
// Locale::text).
|
||||||
|
[[nodiscard]] auto formatIntroPyramidLine(int pyramid) -> std::string;
|
||||||
|
|
||||||
|
// Equivalent per a `game.room_intro_room` amb el placeholder `{r}`.
|
||||||
|
// Es renderitza com a segona línia del text d'INTRO.
|
||||||
|
[[nodiscard]] auto formatIntroRoomLine(int room) -> std::string;
|
||||||
|
|
||||||
// Nom textual per a logs i tests. No es fa servir per a lògica.
|
// Nom textual per a logs i tests. No es fa servir per a lògica.
|
||||||
[[nodiscard]] auto stateName(State s) -> const char*;
|
[[nodiscard]] auto stateName(State s) -> const char*;
|
||||||
|
|||||||
+16
-10
@@ -8,8 +8,8 @@
|
|||||||
// T6 advance — PLAYING és sumidor: no avança res ni canvia d'estat.
|
// T6 advance — PLAYING és sumidor: no avança res ni canvia d'estat.
|
||||||
// T7 advance — ROOM_OUTRO consumeix les 4 sub-fases i recau a ROOM_INTRO.
|
// T7 advance — ROOM_OUTRO consumeix les 4 sub-fases i recau a ROOM_INTRO.
|
||||||
// T8 advance — un delta gran salta més d'una sub-fase en un sol pas.
|
// T8 advance — un delta gran salta més d'una sub-fase en un sol pas.
|
||||||
// T9 formatRoomTitle — substitueix `{n}` quan el Locale té la clau.
|
// T9 formatIntroPyramidLine / formatIntroRoomLine — substitució amb Locale.
|
||||||
// T10 formatRoomTitle — sense Locale, retorna la pròpia clau (fallback).
|
// T10 formatIntroPyramidLine / formatIntroRoomLine — fallback sense Locale.
|
||||||
// T11 stateName — strings esperats per a cada State.
|
// T11 stateName — strings esperats per a cada State.
|
||||||
//
|
//
|
||||||
// El render no es testeja: la FSM és pura i no pinta res. Les regles de
|
// El render no es testeja: la FSM és pura i no pinta res. Les regles de
|
||||||
@@ -189,23 +189,29 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void testFormatRoomTitleWithLocale() {
|
void testFormatRoomTitleWithLocale() {
|
||||||
std::cout << "T9 formatRoomTitle — substitució amb Locale\n";
|
std::cout << "T9 formatIntroPyramidLine / formatIntroRoomLine — substitució amb Locale\n";
|
||||||
const auto PATH = (std::filesystem::temp_directory_path() / "aee_test_locale.yaml").string();
|
const auto PATH = (std::filesystem::temp_directory_path() / "aee_test_locale.yaml").string();
|
||||||
writeTemp(PATH, "game:\n room_intro: \"Sala {n}\"\n");
|
writeTemp(PATH,
|
||||||
|
"game:\n"
|
||||||
|
" room_intro_pyramid: \"Piramide {p}\"\n"
|
||||||
|
" room_intro_room: \"Sala {r}\"\n");
|
||||||
Locale::get().load(PATH);
|
Locale::get().load(PATH);
|
||||||
CHECK(RoomFsm::formatRoomTitle(1) == "Sala 1");
|
CHECK(RoomFsm::formatIntroPyramidLine(1) == "Piramide 1");
|
||||||
CHECK(RoomFsm::formatRoomTitle(7) == "Sala 7");
|
CHECK(RoomFsm::formatIntroPyramidLine(5) == "Piramide 5");
|
||||||
|
CHECK(RoomFsm::formatIntroRoomLine(1) == "Sala 1");
|
||||||
|
CHECK(RoomFsm::formatIntroRoomLine(7) == "Sala 7");
|
||||||
std::filesystem::remove(PATH);
|
std::filesystem::remove(PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testFormatRoomTitleNoLocale() {
|
void testFormatRoomTitleNoLocale() {
|
||||||
std::cout << "T10 formatRoomTitle — fallback sense traducció\n";
|
std::cout << "T10 formatIntroPyramidLine / formatIntroRoomLine — fallback sense traducció\n";
|
||||||
const auto PATH = (std::filesystem::temp_directory_path() / "aee_test_locale_empty.yaml").string();
|
const auto PATH = (std::filesystem::temp_directory_path() / "aee_test_locale_empty.yaml").string();
|
||||||
writeTemp(PATH, "other: foo\n");
|
writeTemp(PATH, "other: foo\n");
|
||||||
Locale::get().load(PATH);
|
Locale::get().load(PATH);
|
||||||
// Sense la clau "game.room_intro", Locale retorna la pròpia clau,
|
// Sense les claus, Locale retorna la pròpia clau i com que no
|
||||||
// i com que no porta `{n}`, la substitució no canvia res.
|
// porten `{p}`/`{r}`, la substitució no canvia res.
|
||||||
CHECK(RoomFsm::formatRoomTitle(3) == "game.room_intro");
|
CHECK(RoomFsm::formatIntroPyramidLine(3) == "game.room_intro_pyramid");
|
||||||
|
CHECK(RoomFsm::formatIntroRoomLine(2) == "game.room_intro_room");
|
||||||
std::filesystem::remove(PATH);
|
std::filesystem::remove(PATH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user