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:
JailDesignerandClaude Opus 4.7 committed 2026-05-08 20:30:37 +02:00
1 parent c13b11c501
commit 78e164fce4
8 files changed
+562 -43

No files matched your search

+46 -9
View File
@@ -13,6 +13,7 @@
#include "game/entities/entity_manager.hpp"
#include "game/map/paint_layer.hpp"
#include "game/map/tilemap.hpp"
#include "game/scenes/game_over_fsm.hpp"
#include "game/scenes/room_fsm.hpp"
#ifdef _DEBUG
@@ -291,15 +292,51 @@ class GameScene : public Scene {
void renderRoomFade();
void renderRoomText();
// Helpers de `renderRoomText`. Tots consulten `transition_` i
// `transition_config_` directament a través de `this` — així les
// signatures es queden netes i la funció pública s'orquestra amb
// poques línies. Tres tasques separades:
// - X animada d'una línia (entra per un costat, surt per l'altre).
// - Altura animada del panel translúcid (creix, manté, decreix).
// - Pintat efectiu del panel i d'una línia de text.
[[nodiscard]] auto computeRoomTextX(int width, int phase_enter, int phase_exit, int dur_enter, int dur_exit, bool from_right) const -> int;
[[nodiscard]] auto computeRoomTextPanelHeight(int phase_enter, int phase_hold, int phase_exit, int dur_enter, int dur_exit) const -> int;
// Configuració, estat i transició de la sub-FSM de game over
// (CONTINUE? + GAME OVER). Lligada al mateix `game.yaml` que la
// RoomFsm via `GameOverFsm::loadConfig`.
GameOverFsm::Config game_over_config_{};
GameOverFsm::State game_over_state_{GameOverFsm::State::OFF};
GameOverFsm::Transition game_over_transition_{};
// Fade final cap a LOGO. Independent del `outro_fade_` perquè el
// game over passa per damunt de la RoomFsm i pot disparar-se durant
// PLAYING sense haver d'arrencar un OUTRO de sala. Comença a la
// fase 0 de FADE_OUT i es manté pintant alpha=255 durant el
// `black_hold` final (mateix patró que `outro_fade_`).
Transitions::FadeToBlack game_over_fade_;
bool game_over_fade_started_{false};
// Avança la sub-FSM de game over consumint `delta_ms`. Detecta la
// transició de "tots els jugadors fora de partida" durant PLAYING
// (entrada a CONTINUE_PROMPT), arma el fade de FADE_OUT i, en
// arribar a DONE, demana la transició a `LOGO`.
void updateGameOverFlow(int delta_ms);
// Substitueix `setState` per a la sub-FSM de game over: log
// d'entrada/sortida i reset de la `Transition`. També arma el
// `game_over_fade_` quan entrem a FADE_OUT, en analogia amb
// `outro_fade_`.
void setGameOverState(GameOverFsm::State new_state);
// Pintat del rètol de game over (CONTINUE? + countdown durant
// CONTINUE_PROMPT; "GAME OVER" durant GAME_OVER). Reaprofita el
// panel translúcid i la font del room text.
void renderGameOverText();
// Pintat del fade negre final (FADE_OUT i black hold). Cridat
// després del HUD perquè cobreixi tota la pantalla, inclòs el
// marcador.
void renderGameOverFade();
// Helpers d'animació del rètol de transició. Reutilitzats pel
// rètol de sala (`renderRoomText`) i pel de game over
// (`renderGameOverText`). Reben `phase` i `elapsed_ms` per
// paràmetre perquè la mateixa lògica visual serveix les dues
// sub-FSMs sense saber a quina pertanyen — només es lliguen al
// `transition_config_` per a corbes i geometria del panel.
[[nodiscard]] auto computeRoomTextX(int width, int phase, int elapsed_ms, int phase_enter, int phase_exit, int dur_enter, int dur_exit, bool from_right) const -> int;
[[nodiscard]] auto computeRoomTextPanelHeight(int phase, int elapsed_ms, int phase_enter, int phase_hold, int phase_exit, int dur_enter, int dur_exit) const -> int;
void drawRoomTextPanel(int center_y, int panel_height) const;
void drawRoomTextLine(const std::string& text, int x, int y, std::uint8_t fill_color, std::uint8_t outline_color) const;