feat(game): CONTINUE? + GAME OVER + fade-to-LOGO (Fase 2)
Sub-FSM nova `GameOverFsm` (CONTINUE_PROMPT → GAME_OVER → FADE_OUT → DONE) que viu en paral·lel a la `RoomFsm`. Es dispara quan tots els jugadors queden DISABLED durant PLAYING, mostra el rètol "Continuar?" amb countdown reusant el panel del room text, i si ningú pulsa START acaba en "Fi del Joc" + fade negre i sol·licita la transició a LOGO. Pulsar START durant CONTINUE? cancel·la el countdown i fa join (revive) del jugador. Després de GAME OVER, el HUD passa a "ESPERA / PER JUGAR" i el START queda bloquejat fins a la transició d'escena. Els helpers d'animació del room text (X i altura del panel) ara reben phase + elapsed_ms per paràmetre per poder ser reaprofitats per les dues sub-FSMs sense duplicar la lògica visual. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
c13b11c501
commit
78e164fce4
8 files changed
+562
-43
No files matched your search
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// 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_steps` graons d'1 segon visualment).
|
||||
int continue_text_hold_ms{5000};
|
||||
int continue_text_exit_ms{500};
|
||||
// Quants "tics" mostra el countdown de CONTINUE?. 5 → "5,4,3,2,1".
|
||||
int continue_count_steps{5};
|
||||
|
||||
// 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
|
||||
Reference in new issue
Block a user