#pragma once #include #include // FSM secundària de la GameScene que governa el flux "tots els jugadors // fora de partida" — CONTINUE? amb countdown → GAME OVER → fade-to-black // → transició a LOGO. Viu separada de la `RoomFsm` (que gestiona // intro/outro de cada sala) perquè els seus disparadors són ortogonals // i la composició entre les dues queda al GameScene. // // Lògica pura: no toca SDL ni Audio, només temporitzadors. La part // visual (text + panel + fade) viu al GameScene reaprofitant la mateixa // infraestructura del room text. namespace GameOverFsm { // Estats canònics. Phase indices per estat: // OFF — joc normal; el GameScene transita a // CONTINUE_PROMPT quan detecta 0 jugadors actius. // CONTINUE_PROMPT — phases: 0=text_enter, 1=text_hold (countdown // visible), 2=text_exit (només si l'usuari // cancel·la pulsant START). Si la fase 1 acaba // sense cancel·lació → salt a GAME_OVER amb el // panel encara amunt (sense exit anim). // GAME_OVER — phases: 0=text_hold (apareix sense enter // anim, panel ja amunt), 1=text_exit, 2=wait // (otro tiempo más abans del fade). // FADE_OUT — phases: 0=fade, 1=black_hold (perquè el // negre quedi un instant abans de cedir el // pas a la LogoScene). // DONE — sentinel: el caller (GameScene) ha de // demanar la transició cap a LOGO. enum class State : std::uint8_t { OFF, CONTINUE_PROMPT, GAME_OVER, FADE_OUT, DONE, }; // Sub-fase i temps acumulat dins de l'estat actual. Es reseteja a // {0, 0} quan es canvia d'estat (`GameScene::setGameOverState`). struct Transition { int phase{0}; int elapsed_ms{0}; }; // Timings llegits de `data/scenes/game/game.yaml` (secció // `game_over:`). Si el fitxer no porta cap d'aquests camps, els // valors per defecte coincideixen amb els del .yaml versionat. struct Config { // CONTINUE? — animació d'entrada/sortida idèntica al room text. int continue_text_enter_ms{500}; // Hold = duració total del countdown. Es divideix en // (continue_count_start + 1) graons iguals; el cas canònic és // 10000 ms / 10 graons d'1 s cadascun. int continue_text_hold_ms{10000}; int continue_text_exit_ms{500}; // Primer número visible al countdown. La sèrie és // [continue_count_start, ..., 1, 0] — sempre acaba en 0 i el // valor 0 es manté visible durant l'últim graó abans del salt // a GAME_OVER. 9 → "9, 8, ..., 0" (10 graons). int continue_count_start{9}; // GAME OVER — apareix sense enter anim (el panel ja és amunt // del CONTINUE?) i s'esvaeix amb la mateixa exit anim que el // room text. `wait_ms` és el "otro tiempo más" abans del fade. int game_over_text_hold_ms{2000}; int game_over_text_exit_ms{500}; int game_over_wait_ms{500}; // Fade final cap a LOGO. `black_hold_ms` és el temps amb el // canvas negre opac abans que el GameScene demani la transició. int fade_out_ms{500}; int black_hold_ms{500}; }; // Llegeix la configuració des del YAML. Si el fitxer manca o falta // alguna clau, els camps no presents conserven els seus fallbacks. [[nodiscard]] auto loadConfig(const std::string& yaml_path) -> Config; // Avança la transició consumint `delta_ms` des del temps real. // Retorna l'estat resultant. Si retorna un estat diferent, el // caller ha de resetejar la `Transition` a {0, 0} abans del següent // frame. Funció pura sobre l'argument. // // Particularitats: // - CONTINUE_PROMPT phase 1 (hold) arribar a la fi → GAME_OVER // (NO reprodueix exit anim; el caller ja té el panel amunt). // - CONTINUE_PROMPT phase 2 (exit) arribar a la fi → OFF (cas // "cancel·lació via START": el GameScene ha forçat la fase 2 // amb `cancelContinuePrompt`). // - GAME_OVER phase 2 (wait) arribar a la fi → FADE_OUT. // - FADE_OUT phase 1 (black_hold) arribar a la fi → DONE. // - DONE i OFF no avancen ni canvien d'estat (sumiders). [[nodiscard]] auto advance(State current, Transition& t, const Config& cfg, int delta_ms) -> State; // Salta a la fase 2 (text_exit) del CONTINUE_PROMPT. No fa res si // l'estat no és CONTINUE_PROMPT o ja som a la fase 2 (idempotent). // Disparat pel GameScene quan un jugador pulsa START dins del // countdown — el join real també el fa GameScene; aquesta funció // només dispara la sortida visual del rètol. void cancelContinuePrompt(State current, Transition& t); // Quants segons "queden" del countdown de CONTINUE?. Retorna un // enter en el rang [0, continue_count_steps] perquè es renderitzi // com a segona línia del rètol durant la fase 1 (hold). Fora de // CONTINUE_PROMPT phase 1, retorna 0 (no s'hauria de pintar). [[nodiscard]] auto countdownSecondsRemaining(State current, const Transition& t, const Config& cfg) -> int; // Nom textual per a logs i tests. [[nodiscard]] auto stateName(State s) -> const char*; } // namespace GameOverFsm