Fase 9d: descomponer GameScene::update en sub-pasos privados
update() pasa de 339 LOC monolitico (cognitive complexity ~137) a
18 LOC orquestadores. Cada seccion logica vive en su propio metodo
privado con responsabilidad unica:
- stepPhysics(dt): physics_world.update + postUpdate.
- stepShootingInput(): SHOOT de P1/P2.
- stepMidGameJoin(): START de jugador inactivo o muerto sin vidas.
- stepContinueScreen(dt): wrapping del Systems::ContinueScreen +
update de fondo. Devuelve true si frame debe terminar.
- stepGameOver(dt): timer final + transicion a TITLE. Devuelve true
si frame debe terminar.
- stepDeathSequence(dt): death timer/respawn/transicion a CONTINUE.
- stepStageStateMachine(dt): despacha a runStage{InitHud,LevelStart,
Playing,LevelCompleted} segun el estado actual.
- runCollisionDetections(): construye el Systems::Collision::Context
y llama detectAll.
GameScene.cpp acumulado tras Fase 9 (a+b+c+d): 1429 -> 1015 LOC.
update() solo: 339 -> 18 LOC. Smoke test xvfb OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+156
-184
@@ -226,10 +226,28 @@ void GameScene::init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GameScene::update(float delta_time) {
|
void GameScene::update(float delta_time) {
|
||||||
// === FÍSICA: integrar bodies del frame anterior y resolver colisiones ===
|
// Orquestador delgado: cada paso vive en su propia función para
|
||||||
// Se ejecuta al inicio del frame: las fuerzas aplicadas en el frame N-1
|
// mantener update() legible y reducir complejidad cognitiva.
|
||||||
// por processInput/AI se integran ahora, y postUpdate sincroniza los
|
stepPhysics(delta_time);
|
||||||
// mirrors (center_/angle_) antes de la lógica de juego que los lee.
|
|
||||||
|
if (game_over_state_ == GameOverState::NONE) {
|
||||||
|
stepShootingInput();
|
||||||
|
stepMidGameJoin();
|
||||||
|
}
|
||||||
|
if (stepContinueScreen(delta_time)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stepGameOver(delta_time)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stepDeathSequence(delta_time);
|
||||||
|
stepStageStateMachine(delta_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::stepPhysics(float delta_time) {
|
||||||
|
// Las fuerzas aplicadas en el frame N-1 por processInput/AI se integran
|
||||||
|
// ahora; postUpdate sincroniza los mirrors (center_/angle_) antes de la
|
||||||
|
// lógica de juego que los lee.
|
||||||
physics_world_.update(delta_time);
|
physics_world_.update(delta_time);
|
||||||
for (auto& ship : ships_) {
|
for (auto& ship : ships_) {
|
||||||
ship.postUpdate(delta_time);
|
ship.postUpdate(delta_time);
|
||||||
@@ -240,64 +258,57 @@ void GameScene::update(float delta_time) {
|
|||||||
for (auto& bullet : bullets_) {
|
for (auto& bullet : bullets_) {
|
||||||
bullet.postUpdate(delta_time);
|
bullet.postUpdate(delta_time);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Processar disparos (state-based, no event-based)
|
void GameScene::stepShootingInput() {
|
||||||
if (game_over_state_ == GameOverState::NONE) {
|
|
||||||
auto* input = Input::get();
|
auto* input = Input::get();
|
||||||
|
if (match_config_.jugador1_actiu &&
|
||||||
// Jugador 1 dispara (solo si está active)
|
input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
if (match_config_.jugador1_actiu) {
|
|
||||||
if (input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
|
||||||
disparar_bala(0);
|
disparar_bala(0);
|
||||||
}
|
}
|
||||||
}
|
if (match_config_.jugador2_actiu &&
|
||||||
|
input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
||||||
// Jugador 2 dispara (solo si está active)
|
|
||||||
if (match_config_.jugador2_actiu) {
|
|
||||||
if (input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
|
|
||||||
disparar_bala(1);
|
disparar_bala(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [FIXED] Allow mid-game join: inactive or dead player presses START
|
void GameScene::stepMidGameJoin() {
|
||||||
// Only during PLAYING state (not INIT_HUD, CONTINUE, GAME_OVER)
|
// Permitir join solo durante PLAYING.
|
||||||
if (stage_manager_->get_estat() == StageSystem::EstatStage::PLAYING) {
|
if (stage_manager_->get_estat() != StageSystem::EstatStage::PLAYING) {
|
||||||
// Check if at least one player is alive and playing (game in progress)
|
return;
|
||||||
bool algun_jugador_viu = false;
|
|
||||||
if (match_config_.jugador1_actiu && hit_timer_per_player_[0] != 999.0F) {
|
|
||||||
algun_jugador_viu = true;
|
|
||||||
}
|
|
||||||
if (match_config_.jugador2_actiu && hit_timer_per_player_[1] != 999.0F) {
|
|
||||||
algun_jugador_viu = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only allow join if there's an active game
|
// Solo se permite join si hay al menos un jugador vivo (no se puede
|
||||||
if (algun_jugador_viu) {
|
// hacer join en pantalla vacía).
|
||||||
// P2 can join if not currently playing (never joined OR dead without lives)
|
const bool ALGU_VIU =
|
||||||
bool p2_no_juga = !match_config_.jugador2_actiu || // Never joined
|
(match_config_.jugador1_actiu && hit_timer_per_player_[0] != 999.0F) ||
|
||||||
hit_timer_per_player_[1] == 999.0F; // Dead without lives
|
(match_config_.jugador2_actiu && hit_timer_per_player_[1] != 999.0F);
|
||||||
|
if (!ALGU_VIU) {
|
||||||
if (p2_no_juga) {
|
return;
|
||||||
if (input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT)) {
|
|
||||||
unir_jugador(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// P1 can join if not currently playing (never joined OR dead without lives)
|
auto* input = Input::get();
|
||||||
bool p1_no_juga = !match_config_.jugador1_actiu || // Never joined
|
for (uint8_t pid = 0; pid < 2; pid++) {
|
||||||
hit_timer_per_player_[0] == 999.0F; // Dead without lives
|
const bool ACTIU = (pid == 0) ? match_config_.jugador1_actiu
|
||||||
|
: match_config_.jugador2_actiu;
|
||||||
if (p1_no_juga) {
|
const bool MUERTO_SIN_VIDAS = hit_timer_per_player_[pid] == 999.0F;
|
||||||
if (input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT)) {
|
if (ACTIU && !MUERTO_SIN_VIDAS) {
|
||||||
unir_jugador(0);
|
continue; // jugador ya está jugando
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const bool START_PRESSED = (pid == 0)
|
||||||
|
? input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT)
|
||||||
|
: input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT);
|
||||||
|
if (START_PRESSED) {
|
||||||
|
unir_jugador(pid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle CONTINUE screen
|
auto GameScene::stepContinueScreen(float delta_time) -> bool {
|
||||||
if (game_over_state_ == GameOverState::CONTINUE) {
|
if (game_over_state_ != GameOverState::CONTINUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Systems::ContinueScreen::Context cont_ctx{
|
Systems::ContinueScreen::Context cont_ctx{
|
||||||
.state = game_over_state_,
|
.state = game_over_state_,
|
||||||
.counter = continue_counter_,
|
.counter = continue_counter_,
|
||||||
@@ -314,7 +325,7 @@ void GameScene::update(float delta_time) {
|
|||||||
Systems::ContinueScreen::update(cont_ctx, delta_time);
|
Systems::ContinueScreen::update(cont_ctx, delta_time);
|
||||||
Systems::ContinueScreen::processInput(cont_ctx);
|
Systems::ContinueScreen::processInput(cont_ctx);
|
||||||
|
|
||||||
// Still update enemies, bullets, and effects during continue screen
|
// Enemies, bullets y efectos siguen moviéndose en background.
|
||||||
for (auto& enemy : enemies_) {
|
for (auto& enemy : enemies_) {
|
||||||
enemy.update(delta_time);
|
enemy.update(delta_time);
|
||||||
}
|
}
|
||||||
@@ -323,204 +334,194 @@ void GameScene::update(float delta_time) {
|
|||||||
}
|
}
|
||||||
debris_manager_.update(delta_time);
|
debris_manager_.update(delta_time);
|
||||||
floating_score_manager_.update(delta_time);
|
floating_score_manager_.update(delta_time);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle final GAME OVER state
|
auto GameScene::stepGameOver(float delta_time) -> bool {
|
||||||
if (game_over_state_ == GameOverState::GAME_OVER) {
|
if (game_over_state_ != GameOverState::GAME_OVER) {
|
||||||
// Game over: only update timer, enemies, bullets, and debris
|
return false;
|
||||||
game_over_timer_ -= delta_time;
|
}
|
||||||
|
|
||||||
|
game_over_timer_ -= delta_time;
|
||||||
if (game_over_timer_ <= 0.0F) {
|
if (game_over_timer_ <= 0.0F) {
|
||||||
// Aturar música de juego antes de tornar al título
|
|
||||||
Audio::get()->stopMusic();
|
Audio::get()->stopMusic();
|
||||||
// Transición a pantalla de título
|
|
||||||
context_.setNextScene(SceneType::TITLE);
|
context_.setNextScene(SceneType::TITLE);
|
||||||
SceneManager::actual = SceneType::TITLE;
|
SceneManager::actual = SceneType::TITLE;
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enemies and bullets continue moving during game over
|
// Enemies, bullets y efectos siguen moviéndose como fondo.
|
||||||
for (auto& enemy : enemies_) {
|
for (auto& enemy : enemies_) {
|
||||||
enemy.update(delta_time);
|
enemy.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& bullet : bullets_) {
|
for (auto& bullet : bullets_) {
|
||||||
bullet.update(delta_time);
|
bullet.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
debris_manager_.update(delta_time);
|
debris_manager_.update(delta_time);
|
||||||
floating_score_manager_.update(delta_time);
|
floating_score_manager_.update(delta_time);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check death sequence state for BOTH players
|
auto GameScene::stepDeathSequence(float delta_time) -> bool {
|
||||||
bool algun_jugador_mort = false;
|
bool algun_mort = false;
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
if (hit_timer_per_player_[i] > 0.0F && hit_timer_per_player_[i] < 999.0F) {
|
if (hit_timer_per_player_[i] <= 0.0F || hit_timer_per_player_[i] >= 999.0F) {
|
||||||
algun_jugador_mort = true;
|
continue;
|
||||||
// Death sequence active: update timer
|
}
|
||||||
|
algun_mort = true;
|
||||||
hit_timer_per_player_[i] += delta_time;
|
hit_timer_per_player_[i] += delta_time;
|
||||||
|
|
||||||
// Check if death duration completed (only trigger ONCE using sentinel value)
|
if (hit_timer_per_player_[i] < Defaults::Game::DEATH_DURATION) {
|
||||||
if (hit_timer_per_player_[i] >= Defaults::Game::DEATH_DURATION) {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// *** PHASE 3: RESPAWN OR GAME OVER ***
|
// *** PHASE 3: RESPAWN OR GAME OVER ***
|
||||||
|
|
||||||
// Decrement lives for this player (only once)
|
|
||||||
lives_per_player_[i]--;
|
lives_per_player_[i]--;
|
||||||
|
|
||||||
if (lives_per_player_[i] > 0) {
|
if (lives_per_player_[i] > 0) {
|
||||||
// Respawn ship en spawn position con invulnerabilidad
|
|
||||||
Vec2 spawn_pos = obtenir_punt_spawn(i);
|
Vec2 spawn_pos = obtenir_punt_spawn(i);
|
||||||
ships_[i].init(&spawn_pos, true);
|
ships_[i].init(&spawn_pos, /*activar_invulnerabilitat=*/true);
|
||||||
hit_timer_per_player_[i] = 0.0F;
|
hit_timer_per_player_[i] = 0.0F;
|
||||||
} else {
|
continue;
|
||||||
// Player is permanently dead (out of lives)
|
}
|
||||||
// Set sentinel value to prevent re-entering this block
|
|
||||||
|
// Sin vidas: marcar definitivamente muerto y comprobar transición a CONTINUE.
|
||||||
hit_timer_per_player_[i] = 999.0F;
|
hit_timer_per_player_[i] = 999.0F;
|
||||||
|
const bool P1_DEAD = !match_config_.jugador1_actiu || lives_per_player_[0] <= 0;
|
||||||
// Check if ALL ACTIVE players are dead (trigger continue screen)
|
const bool P2_DEAD = !match_config_.jugador2_actiu || lives_per_player_[1] <= 0;
|
||||||
bool p1_dead = !match_config_.jugador1_actiu || lives_per_player_[0] <= 0;
|
if (P1_DEAD && P2_DEAD) {
|
||||||
bool p2_dead = !match_config_.jugador2_actiu || lives_per_player_[1] <= 0;
|
|
||||||
|
|
||||||
if (p1_dead && p2_dead) {
|
|
||||||
game_over_state_ = GameOverState::CONTINUE;
|
game_over_state_ = GameOverState::CONTINUE;
|
||||||
continue_counter_ = Defaults::Game::CONTINUE_COUNT_START;
|
continue_counter_ = Defaults::Game::CONTINUE_COUNT_START;
|
||||||
continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION;
|
continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any player is dead, still update enemies/bullets/effects
|
// Si hay algún muerto, los enemigos/balas/efectos siguen actualizándose
|
||||||
if (algun_jugador_mort) {
|
// aunque otros jugadores aún jueguen.
|
||||||
// Enemies and bullets continue moving during death sequence
|
if (algun_mort) {
|
||||||
for (auto& enemy : enemies_) {
|
for (auto& enemy : enemies_) {
|
||||||
enemy.update(delta_time);
|
enemy.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& bullet : bullets_) {
|
for (auto& bullet : bullets_) {
|
||||||
bullet.update(delta_time);
|
bullet.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
debris_manager_.update(delta_time);
|
debris_manager_.update(delta_time);
|
||||||
floating_score_manager_.update(delta_time);
|
floating_score_manager_.update(delta_time);
|
||||||
|
}
|
||||||
// Don't return - allow alive players to continue playing
|
return algun_mort;
|
||||||
}
|
}
|
||||||
|
|
||||||
// *** STAGE SYSTEM STATE MACHINE ***
|
void GameScene::stepStageStateMachine(float delta_time) {
|
||||||
|
const StageSystem::EstatStage STATE = stage_manager_->get_estat();
|
||||||
StageSystem::EstatStage state = stage_manager_->get_estat();
|
switch (STATE) {
|
||||||
|
case StageSystem::EstatStage::INIT_HUD:
|
||||||
switch (state) {
|
runStageInitHud(delta_time);
|
||||||
case StageSystem::EstatStage::INIT_HUD: {
|
break;
|
||||||
// Update stage manager timer (pot canviar l'state!)
|
case StageSystem::EstatStage::LEVEL_START:
|
||||||
stage_manager_->update(delta_time);
|
runStageLevelStart(delta_time);
|
||||||
|
break;
|
||||||
// [FIX] Si l'state ha canviat durante update(), salir immediatament
|
case StageSystem::EstatStage::PLAYING:
|
||||||
// per evitar recalcular la posición de la ship con el nuevo timer
|
runStagePlaying(delta_time);
|
||||||
if (stage_manager_->get_estat() != StageSystem::EstatStage::INIT_HUD) {
|
break;
|
||||||
|
case StageSystem::EstatStage::LEVEL_COMPLETED:
|
||||||
|
runStageLevelCompleted(delta_time);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::runStageInitHud(float delta_time) {
|
||||||
|
// Update stage manager timer (puede cambiar el state).
|
||||||
|
stage_manager_->update(delta_time);
|
||||||
|
// Si el state cambió, salir para no usar el timer del nuevo state.
|
||||||
|
if (stage_manager_->get_estat() != StageSystem::EstatStage::INIT_HUD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Calcular global progress (0.0 al inicio → 1.0 al final)
|
|
||||||
float global_progress = 1.0F - (stage_manager_->get_timer_transicio() / Defaults::Game::INIT_HUD_DURATION);
|
float global_progress = 1.0F - (stage_manager_->get_timer_transicio() / Defaults::Game::INIT_HUD_DURATION);
|
||||||
global_progress = std::min(1.0F, global_progress);
|
global_progress = std::min(1.0F, global_progress);
|
||||||
|
|
||||||
// [NEW] Calcular progress independiente para cada nave
|
const float SHIP1_P = Systems::InitHud::computeRangeProgress(
|
||||||
float ship1_progress = Systems::InitHud::computeRangeProgress(
|
|
||||||
global_progress,
|
global_progress,
|
||||||
Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
|
Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
|
||||||
Defaults::Game::INIT_HUD_SHIP1_RATIO_END);
|
Defaults::Game::INIT_HUD_SHIP1_RATIO_END);
|
||||||
|
const float SHIP2_P = Systems::InitHud::computeRangeProgress(
|
||||||
float ship2_progress = Systems::InitHud::computeRangeProgress(
|
|
||||||
global_progress,
|
global_progress,
|
||||||
Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
|
Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
|
||||||
Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
|
Defaults::Game::INIT_HUD_SHIP2_RATIO_END);
|
||||||
|
|
||||||
// [MODIFICAT] Animar AMBAS naves con sus progress respectivos
|
if (match_config_.jugador1_actiu && SHIP1_P < 1.0F) {
|
||||||
if (match_config_.jugador1_actiu && ship1_progress < 1.0F) {
|
ships_[0].setCenter(Systems::InitHud::computeShipPosition(SHIP1_P, obtenir_punt_spawn(0)));
|
||||||
Vec2 pos_p1 = Systems::InitHud::computeShipPosition(ship1_progress, obtenir_punt_spawn(0));
|
}
|
||||||
ships_[0].setCenter(pos_p1);
|
if (match_config_.jugador2_actiu && SHIP2_P < 1.0F) {
|
||||||
|
ships_[1].setCenter(Systems::InitHud::computeShipPosition(SHIP2_P, obtenir_punt_spawn(1)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match_config_.jugador2_actiu && ship2_progress < 1.0F) {
|
void GameScene::runStageLevelStart(float delta_time) {
|
||||||
Vec2 pos_p2 = Systems::InitHud::computeShipPosition(ship2_progress, obtenir_punt_spawn(1));
|
|
||||||
ships_[1].setCenter(pos_p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Una vez l'animación acaba, permetre control normal
|
|
||||||
// pero mantenir la posición inicial especial hasta LEVEL_START
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case StageSystem::EstatStage::LEVEL_START: {
|
|
||||||
// [DEBUG] Log entrada a LEVEL_START
|
|
||||||
static bool first_entry = true;
|
|
||||||
if (first_entry) {
|
|
||||||
std::cout << "[LEVEL_START] ENTERED with P1 pos.y=" << ships_[0].getCenter().y << '\n';
|
|
||||||
first_entry = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update countdown timer
|
|
||||||
stage_manager_->update(delta_time);
|
stage_manager_->update(delta_time);
|
||||||
|
|
||||||
// [NEW] Allow both ships movement and shooting during intro
|
// Ambas naves pueden moverse y disparar durante el intro.
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
|
const bool ACTIU = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
|
||||||
if (jugador_actiu && hit_timer_per_player_[i] == 0.0F) { // Only active, alive players
|
if (ACTIU && hit_timer_per_player_[i] == 0.0F) {
|
||||||
ships_[i].processInput(delta_time, i);
|
ships_[i].processInput(delta_time, i);
|
||||||
ships_[i].update(delta_time);
|
ships_[i].update(delta_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [NEW] Update bullets
|
|
||||||
for (auto& bullet : bullets_) {
|
for (auto& bullet : bullets_) {
|
||||||
bullet.update(delta_time);
|
bullet.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [NEW] Update debris
|
|
||||||
debris_manager_.update(delta_time);
|
debris_manager_.update(delta_time);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case StageSystem::EstatStage::PLAYING: {
|
void GameScene::runStagePlaying(float delta_time) {
|
||||||
// [NEW] Update stage manager (spawns enemies, pause if BOTH dead)
|
const bool PAUSE_SPAWN = (hit_timer_per_player_[0] > 0.0F && hit_timer_per_player_[1] > 0.0F);
|
||||||
bool pause_spawn = (hit_timer_per_player_[0] > 0.0F && hit_timer_per_player_[1] > 0.0F);
|
stage_manager_->getSpawnController().update(delta_time, enemies_, PAUSE_SPAWN);
|
||||||
stage_manager_->getSpawnController().update(delta_time, enemies_, pause_spawn);
|
|
||||||
|
|
||||||
// [NEW] Check stage completion (only when at least one player alive)
|
// Stage completado: cuando al menos un jugador está vivo y todos los enemies muertos.
|
||||||
bool algun_jugador_viu = (hit_timer_per_player_[0] == 0.0F || hit_timer_per_player_[1] == 0.0F);
|
const bool ALGU_VIU = (hit_timer_per_player_[0] == 0.0F || hit_timer_per_player_[1] == 0.0F);
|
||||||
if (algun_jugador_viu) {
|
if (ALGU_VIU && stage_manager_->getSpawnController().tots_enemics_destruits(enemies_)) {
|
||||||
auto& spawn_ctrl = stage_manager_->getSpawnController();
|
|
||||||
if (spawn_ctrl.tots_enemics_destruits(enemies_)) {
|
|
||||||
stage_manager_->stage_completat();
|
stage_manager_->stage_completat();
|
||||||
Audio::get()->playSound(Defaults::Sound::GOOD_JOB_COMMANDER, Audio::Group::GAME);
|
Audio::get()->playSound(Defaults::Sound::GOOD_JOB_COMMANDER, Audio::Group::GAME);
|
||||||
break;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// [EXISTING] Normal gameplay - update active players
|
// Gameplay normal: ships activos + entidades + colisiones + efectos.
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
|
const bool ACTIU = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
|
||||||
if (jugador_actiu && hit_timer_per_player_[i] == 0.0F) { // Only active, alive players
|
if (ACTIU && hit_timer_per_player_[i] == 0.0F) {
|
||||||
ships_[i].processInput(delta_time, i);
|
ships_[i].processInput(delta_time, i);
|
||||||
ships_[i].update(delta_time);
|
ships_[i].update(delta_time);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& enemy : enemies_) {
|
for (auto& enemy : enemies_) {
|
||||||
enemy.update(delta_time);
|
enemy.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& bullet : bullets_) {
|
for (auto& bullet : bullets_) {
|
||||||
bullet.update(delta_time);
|
bullet.update(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
runCollisionDetections();
|
||||||
|
debris_manager_.update(delta_time);
|
||||||
|
floating_score_manager_.update(delta_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::runStageLevelCompleted(float delta_time) {
|
||||||
|
stage_manager_->update(delta_time);
|
||||||
|
for (uint8_t i = 0; i < 2; i++) {
|
||||||
|
const bool ACTIU = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
|
||||||
|
if (ACTIU && hit_timer_per_player_[i] == 0.0F) {
|
||||||
|
ships_[i].processInput(delta_time, i);
|
||||||
|
ships_[i].update(delta_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& bullet : bullets_) {
|
||||||
|
bullet.update(delta_time);
|
||||||
|
}
|
||||||
|
debris_manager_.update(delta_time);
|
||||||
|
floating_score_manager_.update(delta_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameScene::runCollisionDetections() {
|
||||||
Systems::Collision::Context col_ctx{
|
Systems::Collision::Context col_ctx{
|
||||||
.ships = ships_,
|
.ships = ships_,
|
||||||
.enemies = enemies_,
|
.enemies = enemies_,
|
||||||
@@ -535,35 +536,6 @@ void GameScene::update(float delta_time) {
|
|||||||
};
|
};
|
||||||
Systems::Collision::detectAll(col_ctx);
|
Systems::Collision::detectAll(col_ctx);
|
||||||
}
|
}
|
||||||
debris_manager_.update(delta_time);
|
|
||||||
floating_score_manager_.update(delta_time);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case StageSystem::EstatStage::LEVEL_COMPLETED:
|
|
||||||
// Update countdown timer
|
|
||||||
stage_manager_->update(delta_time);
|
|
||||||
|
|
||||||
// [NEW] Allow both ships movement and shooting during outro
|
|
||||||
for (uint8_t i = 0; i < 2; i++) {
|
|
||||||
bool jugador_actiu = (i == 0) ? match_config_.jugador1_actiu : match_config_.jugador2_actiu;
|
|
||||||
if (jugador_actiu && hit_timer_per_player_[i] == 0.0F) { // Only active, alive players
|
|
||||||
ships_[i].processInput(delta_time, i);
|
|
||||||
ships_[i].update(delta_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [NEW] Update bullets (allow last shots to continue)
|
|
||||||
for (auto& bullet : bullets_) {
|
|
||||||
bullet.update(delta_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
// [NEW] Update debris (from last destroyed enemies)
|
|
||||||
debris_manager_.update(delta_time);
|
|
||||||
floating_score_manager_.update(delta_time);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GameScene::draw() {
|
void GameScene::draw() {
|
||||||
// Handle CONTINUE screen
|
// Handle CONTINUE screen
|
||||||
|
|||||||
@@ -96,4 +96,24 @@ class GameScene {
|
|||||||
|
|
||||||
// [NEW] Función helper del marcador
|
// [NEW] Función helper del marcador
|
||||||
[[nodiscard]] std::string buildScoreboard() const;
|
[[nodiscard]] std::string buildScoreboard() const;
|
||||||
|
|
||||||
|
// 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 / transition a CONTINUE. Devuelve
|
||||||
|
// true si algun jugador está en secuencia de muerte (para que el
|
||||||
|
// caller actualice efectos sin gameplay).
|
||||||
|
[[nodiscard]] auto stepDeathSequence(float delta_time) -> bool;
|
||||||
|
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();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user