Fase 9b: extraer ContinueSystem de GameScene

GameScene::actualitzar_continue, processar_input_continue y el
helper check_and_apply_continue_timeout (3 funciones, ~140 LOC) salen
a Systems::ContinueScreen en source/game/systems/continue_system.{hpp,cpp}.

API:
- struct Systems::ContinueScreen::Context: agrupa el estado mutable
  (state, counter, tick_timer, continues_used, game_over_timer,
  lives/score/hit_timer arrays, ships, match_config) y un callback
  get_spawn_point inyectado por GameScene.
- update(ctx, dt): avanza countdown automatico y transiciona a
  GAME_OVER si timeout.
- processInput(ctx): START revive jugador(es), THRUST/SHOOT acelera
  countdown.

Helpers privados (revivePlayer, checkAndApplyTimeout) en anonymous
namespace del .cpp para evitar contaminar el header.

GameOverState ahora con underlying type explicito (uint8_t) para
permitir forward-declaration limpia en continue_system.hpp.

dibuixar_continue y unir_jugador se quedan en GameScene (render y
gameplay normal, no parte del state machine).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
JailDesignerandClaude Opus 4.7 committed 2026-05-20 07:50:43 +02:00
1 parent 896a899b0f
commit 816bc02d9d
4 files changed
+175 -116

No files matched your search

+109
View File
@@ -0,0 +1,109 @@
// continue_system.cpp - Implementación de la pantalla de continue
#include "game/systems/continue_system.hpp"
#include <cstdint>
#include "core/audio/audio.hpp"
#include "core/defaults.hpp"
#include "core/input/input.hpp"
#include "core/input/input_types.hpp"
#include "game/scenes/game_scene.hpp" // GameOverState (definición completa)
namespace Systems::ContinueScreen {
namespace {
// Si el countdown ha bajado de 0, transiciona a GAME_OVER con su timer.
void checkAndApplyTimeout(Context& ctx) {
if (ctx.counter < 0) {
ctx.state = GameOverState::GAME_OVER;
ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION;
}
}
void revivePlayer(Context& ctx, uint8_t player_id) {
ctx.score_per_player[player_id] = 0;
ctx.lives_per_player[player_id] = Defaults::Game::STARTING_LIVES;
ctx.hit_timer_per_player[player_id] = 0.0F;
if (player_id == 0) {
ctx.match_config.jugador1_actiu = true;
} else {
ctx.match_config.jugador2_actiu = true;
}
const Vec2 SPAWN = ctx.get_spawn_point(player_id);
ctx.ships[player_id].init(&SPAWN, /*activar_invulnerabilitat=*/true);
}
} // namespace
void update(Context& ctx, float delta_time) {
ctx.tick_timer -= delta_time;
if (ctx.tick_timer > 0.0F) {
return;
}
ctx.counter--;
ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION;
checkAndApplyTimeout(ctx);
// Solo pita el tick si seguimos en CONTINUE (no transitamos a GAME_OVER)
if (ctx.state == GameOverState::CONTINUE) {
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
}
}
void processInput(Context& ctx) {
auto* input = Input::get();
const bool P1_START = input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT);
const bool P2_START = input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT);
if (P1_START || P2_START) {
// ¿Quedan continues?
if (!Defaults::Game::INFINITE_CONTINUES &&
ctx.continues_used >= Defaults::Game::MAX_CONTINUES) {
ctx.state = GameOverState::GAME_OVER;
ctx.game_over_timer = Defaults::Game::GAME_OVER_DURATION;
return;
}
if (!Defaults::Game::INFINITE_CONTINUES) {
ctx.continues_used++;
}
const uint8_t PRIMARY = P1_START ? 0 : 1;
revivePlayer(ctx, PRIMARY);
// Si ambos pulsan START, revivimos a los dos.
if (P1_START && P2_START) {
revivePlayer(ctx, 1);
}
// Reanudar partida.
ctx.state = GameOverState::NONE;
ctx.counter = 0;
ctx.tick_timer = 0.0F;
Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME);
return;
}
// THRUST/SHOOT acelera el countdown manualmente.
const bool ACCEL = input->checkActionPlayer1(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT) ||
input->checkActionPlayer2(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT) ||
input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT) ||
input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT);
if (ACCEL) {
ctx.counter--;
checkAndApplyTimeout(ctx);
if (ctx.state == GameOverState::CONTINUE) {
Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME);
}
ctx.tick_timer = Defaults::Game::CONTINUE_TICK_DURATION;
}
}
} // namespace Systems::ContinueScreen
+49
View File
@@ -0,0 +1,49 @@
// continue_system.hpp - Pantalla de continue y máquina de estados de game over
// © 2025 Orni Attack
//
// Gestiona la transición CONTINUE → GAME_OVER, el countdown, los inputs de
// los jugadores para continuar la partida y la revivificación. Vive como
// estado en GameScene; este módulo solo opera sobre referencias a ese estado.
#pragma once
#include <array>
#include <cstdint>
#include <functional>
#include "core/system/game_config.hpp"
#include "core/types.hpp"
#include "game/entities/ship.hpp"
// Forward declaration: GameOverState es un enum class definido en game_scene.hpp.
// Para no traer toda la cabecera, lo declaramos aquí.
enum class GameOverState : uint8_t;
namespace Systems::ContinueScreen {
// Todo lo que el ContinueSystem lee/modifica.
struct Context {
GameOverState& state;
int& counter;
float& tick_timer;
int& continues_used;
float& game_over_timer;
std::array<int, 2>& lives_per_player;
std::array<int, 2>& score_per_player;
std::array<float, 2>& hit_timer_per_player;
std::array<Ship, 2>& ships;
GameConfig::MatchConfig& match_config;
// Helper inyectado por GameScene (obtenir_punt_spawn).
std::function<Vec2(uint8_t /*player_id*/)> get_spawn_point;
};
// Avanza el countdown automático (tick interno). Si el contador cae bajo 0,
// transiciona a GAME_OVER y arranca el timer final.
void update(Context& ctx, float delta_time);
// Procesa input durante la pantalla CONTINUE:
// START → revive al jugador (1 o 2, según quién pulsó).
// THRUST/SHOOT → acelera el countdown.
void processInput(Context& ctx);
} // namespace Systems::ContinueScreen