#pragma once #include #include #include "core/ui/ui_config.hpp" // FSM de sala (intro/outro) extreta de GameScene per a testabilitat. La // lògica que viu aquí és pura: no toca SDL, ni Audio, ni cap singleton // excepte `Locale::get()` (per a la formatació del text). El consumidor // actual és `GameScene`, que cridarà `RoomFsm::advance` cada frame. namespace RoomFsm { enum class State : std::uint8_t { ROOM_INTRO, // Entrada de sala: fade in + text d'entrada. PLAYING, // Joc normal. ROOM_OUTRO, // Sortida de sala: text de sortida + fade out. }; // Sub-fase i temps acumulat dins de l'estat actual. Es reseteja a // {0, 0} quan es canvia d'estat. Significat de `phase` per estat: // ROOM_INTRO: 0=fade_in, 1=text_delay, 2=text_enter, 3=text_hold, 4=text_exit // ROOM_OUTRO: 0=text_enter, 1=text_hold, 2=text_exit, 3=fade_out // PLAYING: no s'usa. struct Transition { int phase{0}; int elapsed_ms{0}; }; // Timings i corbes carregats de `data/scenes/game.yaml`. Els valors // per defecte coincideixen amb els del YAML versionat: si manca el // fitxer, la FSM funciona igualment amb aquests fallbacks. struct Config { int intro_fade_in_ms{500}; int intro_text_delay_ms{500}; int intro_text_enter_ms{500}; int intro_text_hold_ms{1000}; int intro_text_exit_ms{500}; int outro_text_enter_ms{500}; int outro_text_hold_ms{1000}; int outro_text_exit_ms{500}; int outro_fade_out_ms{500}; UiConfig::CurveFn text_enter_curve{nullptr}; UiConfig::CurveFn text_exit_curve{nullptr}; }; // Llegeix la configuració des del YAML. Si el fitxer manca o falten // claus, els camps no presents conserven els seus fallbacks. Les // corbes es resolen amb `UiConfig::curveByName` (mateix mecanisme // que la pausa); si el nom és desconegut cau a `Easing::linear`. [[nodiscard]] auto loadConfig(const std::string& yaml_path) -> Config; // Avança la transició consumint `delta_ms` des del temps real. // Retorna l'estat resultant (potser el mateix que l'entrant si // encara queden sub-fases). Si retorna un estat diferent, el caller // ha de resetejar la `Transition` a {0, 0} abans del següent frame // (això ho fa `GameScene::setState`). Funció pura sobre l'argument. [[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 // l'enter passat. Si la clau no està traduïda, retorna la pròpia // clau (fallback de Locale::text). [[nodiscard]] auto formatRoomTitle(int n) -> std::string; // Nom textual per a logs i tests. No es fa servir per a lògica. [[nodiscard]] auto stateName(State s) -> const char*; } // namespace RoomFsm