From e1d6cd1bb9c4357c9986ad70a022223c7198cb8e Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Wed, 20 May 2026 17:25:02 +0200 Subject: [PATCH] refactor: fusionar GameScene::init() al ctor (coherent amb Scene) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'interfície Scene només declara handleEvent/update/draw/isFinished. GameScene::init() era un mètode públic addicional que ningú (ni el Director) cridava externament: només el propi ctor el cridava al final. El comentari del header ("llamado por Director tras crear la escena") era fals: el Director mai l'invoca. TitleScene i LogoScene ja inicialitzen tot al ctor sense exposar init(). Aquesta diferència trencava l'expectativa del lifecycle. Movem tot el cos de init() al ctor i esborrem la declaració i la definició. Aprofitem per: - Eliminar el guard "if (!stage_config_)" que pressuposava re-init, cas que mai s'arribava a donar. - Treure el comentari DEPRECATED sobre spawn_position_ (residu). Hallazgo #1 de CODE_REVIEW.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- source/game/scenes/game_scene.cpp | 57 ++++------ source/game/scenes/game_scene.hpp | 171 +++++++++++++++--------------- 2 files changed, 102 insertions(+), 126 deletions(-) diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index 0ba0bf9..1a83e4a 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -53,46 +53,26 @@ GameScene::GameScene(SDLManager& sdl, SceneContext& context) // Inicialitzar enemigos con renderer std::ranges::fill(enemies_, Enemy(sdl.getRenderer())); - // El resto del estado del juego (física, stages, naves, vidas, puntuación) - // se inicializa en init(), que se llama al final del constructor para que - // la escena esté lista en cuanto el Director la haya construido. - init(); -} - -auto GameScene::isFinished() const -> bool { - return context_.nextScene() != SceneType::GAME; -} - -void GameScene::handleEvent(const SDL_Event& event) { - // GameScene no procesa eventos puntuales SDL: la lógica de input se - // resuelve en update() consultando Input::checkAction. - (void)event; -} - -void GameScene::init() { // Inicialitzar generador de números aleatoris // Basat en el codi Pascal original: line 376 std::srand(static_cast(std::time(nullptr))); // Configurar el mundo físico con los límites de la zona de juego. - // Las entidades se registrarán cada una al inicializarse (Fase 6c-e). physics_world_.clear(); physics_world_.setBounds(Defaults::Zones::PLAYAREA); - // [NEW] Load stage configuration (only once) + // Load stage configuration + stage_config_ = StageSystem::StageLoader::load("data/stages/stages.yaml"); if (!stage_config_) { - stage_config_ = StageSystem::StageLoader::load("data/stages/stages.yaml"); - if (!stage_config_) { - std::cerr << "[GameScene] Error: no s'ha pogut load stages.yaml" << '\n'; - // Continue without stage system (will crash, but helps debugging) - } + std::cerr << "[GameScene] Error: no s'ha pogut load stages.yaml" << '\n'; + // Continue without stage system (will crash, but helps debugging) } - // [NEW] Initialize stage manager + // Initialize stage manager stage_manager_ = std::make_unique(stage_config_.get()); stage_manager_->init(); - // [NEW] Set ship position reference for safe spawn (P1 for now, TODO: dual tracking) + // Set ship position reference for safe spawn (P1 for now, TODO: dual tracking) stage_manager_->getSpawnController().setShipPosition(&ships_[0].getCenter()); // Inicialitzar timers de muerte per player @@ -113,11 +93,6 @@ void GameScene::init() { score_per_player_[1] = 0; floating_score_manager_.reset(); - // DEPRECATED: spawn_position_ ya no s'usa, es calcula dinàmicament con getSpawnPoint(player_id) - // const SDL_FRect& zona = Defaults::Zones::PLAYAREA; - // spawn_position_.x = zona.x + zona.w * 0.5f; - // spawn_position_.y = zona.y + zona.h * Defaults::Game::INIT_HUD_SHIP_START_Y_RATIO; - // Inicialitzar naves segons configuración (solo jugadors active) for (uint8_t i = 0; i < 2; i++) { bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu; @@ -126,7 +101,7 @@ void GameScene::init() { // Jugador active: init normalment Vec2 spawn_pos = getSpawnPoint(i); ships_[i].init(&spawn_pos, false); // No invulnerability at start - // Registrar el cuerpo físico de la nave en el mundo (Fase 6c) + // Registrar el cuerpo físico de la nave en el mundo physics_world_.addBody(&ships_[i].getBody()); std::cout << "[GameScene] Jugador " << (i + 1) << " inicialitzat\n"; } else { @@ -141,15 +116,13 @@ void GameScene::init() { // Initialize enemies as inactive (stage system will spawn them). // Registramos el body al world incluso inactivo: con radius=0 no colisiona // ni se mueve, y al init() del stage system se activa sin re-registrar. - // La construcció dels Enemy ja s'ha fet al ctor de GameScene; ací només - // configurem la referència a la nau i registrem el body. for (auto& enemy : enemies_) { enemy.setShipPosition(&ships_[0].getCenter()); // Set ship reference (P1 for now) physics_world_.addBody(&enemy.getBody()); // DON'T call enemy.init() here - stage system handles spawning } - // Inicialitzar balas (now 6 instead of 3). + // Inicialitzar balas. // Se registran en el physics_world para integración cinemática. // Como su body_.radius=0, no colisionan físicamente con nadie (las // colisiones de gameplay se gestionan en detectar_col·lisions_*). @@ -158,14 +131,20 @@ void GameScene::init() { physics_world_.addBody(&bullet.getBody()); } - // [ELIMINAT] Iniciar música de juego (ara es gestiona en stage_manager) - // La música s'inicia cuando es transiciona de INIT_HUD a LEVEL_START - // Audio::get()->playMusic("game.ogg"); - // Reset flag de sons de animación init_hud_rect_sound_played_ = false; } +auto GameScene::isFinished() const -> bool { + return context_.nextScene() != SceneType::GAME; +} + +void GameScene::handleEvent(const SDL_Event& event) { + // GameScene no procesa eventos puntuales SDL: la lógica de input se + // resuelve en update() consultando Input::checkAction. + (void)event; +} + void GameScene::update(float delta_time) { // Orquestador delgado: cada paso vive en su propia función para // mantener update() legible y reducir complejidad cognitiva. diff --git a/source/game/scenes/game_scene.hpp b/source/game/scenes/game_scene.hpp index 4223b1c..32d9e2a 100644 --- a/source/game/scenes/game_scene.hpp +++ b/source/game/scenes/game_scene.hpp @@ -11,9 +11,9 @@ #include "core/graphics/vector_text.hpp" #include "core/physics/physics_world.hpp" #include "core/rendering/sdl_manager.hpp" +#include "core/system/game_config.hpp" #include "core/system/scene.hpp" #include "core/system/scene_context.hpp" -#include "core/system/game_config.hpp" #include "core/types.hpp" #include "game/constants.hpp" #include "game/effects/debris_manager.hpp" @@ -33,105 +33,102 @@ enum class GameOverState : uint8_t { // Clase principal del juego (escena) class GameScene final : public Scene { - public: - explicit GameScene(SDLManager& sdl, SceneManager::SceneContext& context); - ~GameScene() override = default; + public: + explicit GameScene(SDLManager& sdl, SceneManager::SceneContext& context); + ~GameScene() override = default; - // Scene interface - void handleEvent(const SDL_Event& event) override; - void update(float delta_time) override; - void draw() override; - [[nodiscard]] auto isFinished() const -> bool override; + // Scene interface + void handleEvent(const SDL_Event& event) override; + void update(float delta_time) override; + void draw() override; + [[nodiscard]] auto isFinished() const -> bool override; - // Inicialización del estado del juego (llamado por Director tras crear la escena). - void init(); + private: + SDLManager& sdl_; + SceneManager::SceneContext& context_; + GameConfig::MatchConfig match_config_; // Configuración de jugadors active - private: - SDLManager& sdl_; - SceneManager::SceneContext& context_; - GameConfig::MatchConfig match_config_; // Configuración de jugadors active + // Mundo físico (Fase 5) — integración cinemática + colisiones + Physics::PhysicsWorld physics_world_; - // Mundo físico (Fase 5) — integración cinemática + colisiones - Physics::PhysicsWorld physics_world_; + // Efectes visuals + Effects::DebrisManager debris_manager_; + Effects::FloatingScoreManager floating_score_manager_; - // Efectes visuals - Effects::DebrisManager debris_manager_; - Effects::FloatingScoreManager floating_score_manager_; + // Estat del juego + std::array ships_; // [0]=P1, [1]=P2 + std::array enemies_; + // 6 balas: P1=[0,1,2], P2=[3,4,5]. El cast a size_t evita la + // widening conversion implícita que detecta clang-tidy. + std::array(Constants::MAX_BALES) * 2> bullets_; + std::array hit_timer_per_player_; // Death timers per player (seconds) - // Estat del juego - std::array ships_; // [0]=P1, [1]=P2 - std::array enemies_; - // 6 balas: P1=[0,1,2], P2=[3,4,5]. El cast a size_t evita la - // widening conversion implícita que detecta clang-tidy. - std::array(Constants::MAX_BALES) * 2> bullets_; - std::array hit_timer_per_player_; // Death timers per player (seconds) + // Lives and game over system + std::array lives_per_player_; // [0]=P1, [1]=P2 + GameOverState game_over_state_; // Game over state machine (NONE, CONTINUE, GAME_OVER) + int continue_counter_; // Continue countdown (9→0) + float continue_tick_timer_; // Timer for countdown tick (1.0s) + int continues_used_; // Continues used this game (0-3 max) + float game_over_timer_; // Final GAME OVER timer before title screen + Vec2 death_position_; // Death position (for respawn) + std::array score_per_player_; // [0]=P1, [1]=P2 - // Lives and game over system - std::array lives_per_player_; // [0]=P1, [1]=P2 - GameOverState game_over_state_; // Game over state machine (NONE, CONTINUE, GAME_OVER) - int continue_counter_; // Continue countdown (9→0) - float continue_tick_timer_; // Timer for countdown tick (1.0s) - int continues_used_; // Continues used this game (0-3 max) - float game_over_timer_; // Final GAME OVER timer before title screen - Vec2 death_position_; // Death position (for respawn) - std::array score_per_player_; // [0]=P1, [1]=P2 + // Text vectorial + Graphics::VectorText text_; - // Text vectorial - Graphics::VectorText text_; + // [NEW] Stage system + std::unique_ptr stage_config_; + std::unique_ptr stage_manager_; - // [NEW] Stage system - std::unique_ptr stage_config_; - std::unique_ptr stage_manager_; + // Control de sons de animación INIT_HUD + bool init_hud_rect_sound_played_{false}; // Flag para evitar repetir sonido del rectángulo - // Control de sons de animación INIT_HUD - bool init_hud_rect_sound_played_{false}; // Flag para evitar repetir sonido del rectángulo + // Funciones privades + void tocado(uint8_t player_id); + void drawMargins() const; // Dibuixar vores de la zona de juego + void drawScoreboard(); // Dibuixar marcador de puntuación + void fireBullet(uint8_t player_id); // Shoot bullet from player + [[nodiscard]] auto getSpawnPoint(uint8_t player_id) const -> Vec2; // Get spawn position for player - // Funciones privades - void tocado(uint8_t player_id); - void drawMargins() const; // Dibuixar vores de la zona de juego - void drawScoreboard(); // Dibuixar marcador de puntuación - void fireBullet(uint8_t player_id); // Shoot bullet from player - [[nodiscard]] auto getSpawnPoint(uint8_t player_id) const -> Vec2; // Get spawn position for player + // [NEW] Continue & Join system + void joinPlayer(uint8_t player_id); // Join inactive player mid-game + void drawContinue(); // Draw continue screen - // [NEW] Continue & Join system - void joinPlayer(uint8_t player_id); // Join inactive player mid-game - void drawContinue(); // Draw continue screen + // [NEW] Stage system helpers + void drawStageMessage(const std::string& message); - // [NEW] Stage system helpers - void drawStageMessage(const std::string& message); + // Helpers de renderitzat (extracció de draw() per reduir complexitat). + // Cadascun gestiona un bloc concret; draw() només despatxa segons l'estat. + void drawEnemies() const; + void drawBullets() const; + void drawActiveShipsAlive() const; + void drawContinueState(); + void drawGameOverState(); + void drawInitHudState(); + void drawLevelStartState(); + void drawPlayingState(); + void drawLevelCompletedState(); - // Helpers de renderitzat (extracció de draw() per reduir complexitat). - // Cadascun gestiona un bloc concret; draw() només despatxa segons l'estat. - void drawEnemies() const; - void drawBullets() const; - void drawActiveShipsAlive() const; - void drawContinueState(); - void drawGameOverState(); - void drawInitHudState(); - void drawLevelStartState(); - void drawPlayingState(); - void drawLevelCompletedState(); + // [NEW] Función helper del marcador + [[nodiscard]] auto buildScoreboard() const -> std::string; - // [NEW] Función helper del marcador - [[nodiscard]] auto buildScoreboard() const -> std::string; - - // Sub-pasos de update() (descompuestos en Fase 9d para reducir - // complejidad cognitiva; cada uno es responsable de una sección). - void stepPhysics(float delta_time); - void stepShootingInput(); - void stepMidGameJoin(); - // Devuelven true si el frame debe salir tras esta sección. - [[nodiscard]] auto stepContinueScreen(float delta_time) -> bool; - [[nodiscard]] auto stepGameOver(float delta_time) -> bool; - // Avanza el death timer / respawn / transición a CONTINUE. Si algún - // jugador está en secuencia de muerte, también actualiza efectos - // (enemigos, balas, debris) que siguen vivos en el escenario. - void stepDeathSequence(float delta_time); - void stepStageStateMachine(float delta_time); - void runStageInitHud(float delta_time); - void runStageLevelStart(float delta_time); - void runStagePlaying(float delta_time); - void runStageLevelCompleted(float delta_time); - // Helper: ejecuta colisiones de gameplay con el Context preparado. - void runCollisionDetections(); + // Sub-pasos de update() (descompuestos en Fase 9d para reducir + // complejidad cognitiva; cada uno es responsable de una sección). + void stepPhysics(float delta_time); + void stepShootingInput(); + void stepMidGameJoin(); + // Devuelven true si el frame debe salir tras esta sección. + [[nodiscard]] auto stepContinueScreen(float delta_time) -> bool; + [[nodiscard]] auto stepGameOver(float delta_time) -> bool; + // Avanza el death timer / respawn / transición a CONTINUE. Si algún + // jugador está en secuencia de muerte, también actualiza efectos + // (enemigos, balas, debris) que siguen vivos en el escenario. + void stepDeathSequence(float delta_time); + void stepStageStateMachine(float delta_time); + void runStageInitHud(float delta_time); + void runStageLevelStart(float delta_time); + void runStagePlaying(float delta_time); + void runStageLevelCompleted(float delta_time); + // Helper: ejecuta colisiones de gameplay con el Context preparado. + void runCollisionDetections(); };