diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index b032462..e043a9c 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -22,6 +22,7 @@ #include "core/system/global_events.hpp" #include "game/stage_system/stage_loader.hpp" #include "game/systems/collision_system.hpp" +#include "game/systems/continue_system.hpp" // Using declarations per simplificar el codi using SceneManager::SceneContext; @@ -296,8 +297,21 @@ void GameScene::update(float delta_time) { // Handle CONTINUE screen if (game_over_state_ == GameOverState::CONTINUE) { - actualitzar_continue(delta_time); - processar_input_continue(); + Systems::ContinueScreen::Context cont_ctx{ + .state = game_over_state_, + .counter = continue_counter_, + .tick_timer = continue_tick_timer_, + .continues_used = continues_used_, + .game_over_timer = game_over_timer_, + .lives_per_player = lives_per_player_, + .score_per_player = score_per_player_, + .hit_timer_per_player = hit_timer_per_player_, + .ships = ships_, + .match_config = match_config_, + .get_spawn_point = [this](uint8_t pid) { return obtenir_punt_spawn(pid); }, + }; + Systems::ContinueScreen::update(cont_ctx, delta_time); + Systems::ContinueScreen::processInput(cont_ctx); // Still update enemies, bullets, and effects during continue screen for (auto& enemy : enemies_) { @@ -1106,116 +1120,6 @@ void GameScene::disparar_bala(uint8_t player_id) { } } -// ==================== CONTINUE & JOIN SYSTEM ==================== - -void GameScene::check_and_apply_continue_timeout() { - if (continue_counter_ < 0) { - game_over_state_ = GameOverState::GAME_OVER; - game_over_timer_ = Defaults::Game::GAME_OVER_DURATION; - } -} - -void GameScene::actualitzar_continue(float delta_time) { - continue_tick_timer_ -= delta_time; - - if (continue_tick_timer_ <= 0.0F) { - continue_counter_--; - continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION; - - // Check if timeout reached (counter < 0) - check_and_apply_continue_timeout(); - - // Play sound only if still in CONTINUE state (not transitioned to GAME_OVER) - if (game_over_state_ == GameOverState::CONTINUE) { - Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME); - } - } -} - -void GameScene::processar_input_continue() { - auto* input = Input::get(); - - // Check START for both players - bool p1_start = input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT); - bool p2_start = input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT); - - if (p1_start || p2_start) { - // Check continue limit (skip if infinite continues) - if (!Defaults::Game::INFINITE_CONTINUES && continues_used_ >= Defaults::Game::MAX_CONTINUES) { - // Max continues reached → final game over - game_over_state_ = GameOverState::GAME_OVER; - game_over_timer_ = Defaults::Game::GAME_OVER_DURATION; - return; - } - - // Only increment if not infinite - if (!Defaults::Game::INFINITE_CONTINUES) { - continues_used_++; - } - - // Determine which player(s) to revive - uint8_t player_to_revive = p1_start ? 0 : 1; - - // Reset score and lives (KEEP level and enemies!) - score_per_player_[player_to_revive] = 0; - lives_per_player_[player_to_revive] = Defaults::Game::STARTING_LIVES; - hit_timer_per_player_[player_to_revive] = 0.0F; - - // Activate player if not already - if (player_to_revive == 0) { - match_config_.jugador1_actiu = true; - } else { - match_config_.jugador2_actiu = true; - } - - // Spawn with invulnerability - Vec2 spawn_pos = obtenir_punt_spawn(player_to_revive); - ships_[player_to_revive].init(&spawn_pos, true); - - // Check if other player wants to continue too - if (p1_start && p2_start) { - uint8_t other_player = 1; - score_per_player_[other_player] = 0; - lives_per_player_[other_player] = Defaults::Game::STARTING_LIVES; - hit_timer_per_player_[other_player] = 0.0F; - match_config_.jugador2_actiu = true; - Vec2 spawn_pos2 = obtenir_punt_spawn(other_player); - ships_[other_player].init(&spawn_pos2, true); - } - - // Resume game - game_over_state_ = GameOverState::NONE; - continue_counter_ = 0; - continue_tick_timer_ = 0.0F; - - // Play continue confirmation sound - Audio::get()->playSound(Defaults::Sound::START, Audio::Group::GAME); - - return; - } - - // Check THRUST/FIRE to accelerate countdown (DO_NOT_ALLOW_REPEAT to avoid spam) - bool thrust_p1 = input->checkActionPlayer1(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT); - bool thrust_p2 = input->checkActionPlayer2(InputAction::THRUST, Input::DO_NOT_ALLOW_REPEAT); - bool fire_p1 = input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT); - bool fire_p2 = input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT); - - if (thrust_p1 || thrust_p2 || fire_p1 || fire_p2) { - continue_counter_--; - - // Check if timeout reached (counter < 0) - check_and_apply_continue_timeout(); - - // Play sound only if still in CONTINUE state - if (game_over_state_ == GameOverState::CONTINUE) { - Audio::get()->playSound(Defaults::Sound::CONTINUE, Audio::Group::GAME); - } - - // Reset timer to prevent double-decrement - continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION; - } -} - void GameScene::dibuixar_continue() { const SDL_FRect& play_area = Defaults::Zones::PLAYAREA; constexpr float spacing = 4.0F; diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index b089d25..e4ae658 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -25,7 +25,7 @@ #include "game/stage_system/stage_manager.hpp" // Game over state machine -enum class GameOverState { +enum class GameOverState : uint8_t { NONE, // Normal gameplay CONTINUE, // Continue countdown screen (9→0) GAME_OVER // Final game over (returning to title) @@ -89,9 +89,6 @@ class GameScene { // [NEW] Continue & Join system void unir_jugador(uint8_t player_id); // Join inactive player mid-game - void processar_input_continue(); // Handle input during continue screen - void actualitzar_continue(float delta_time); // Update continue countdown - void check_and_apply_continue_timeout(); // Check if continue timed out and transition to GAME_OVER void dibuixar_continue(); // Draw continue screen // [NEW] Stage system helpers diff --git a/source/game/systems/continue_system.cpp b/source/game/systems/continue_system.cpp new file mode 100644 index 0000000..f9a6dd4 --- /dev/null +++ b/source/game/systems/continue_system.cpp @@ -0,0 +1,109 @@ +// continue_system.cpp - Implementación de la pantalla de continue + +#include "game/systems/continue_system.hpp" + +#include + +#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 diff --git a/source/game/systems/continue_system.hpp b/source/game/systems/continue_system.hpp new file mode 100644 index 0000000..266a9ec --- /dev/null +++ b/source/game/systems/continue_system.hpp @@ -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 +#include +#include + +#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& lives_per_player; + std::array& score_per_player; + std::array& hit_timer_per_player; + std::array& ships; + GameConfig::MatchConfig& match_config; + // Helper inyectado por GameScene (obtenir_punt_spawn). + std::function 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