afegida logica de continues

fix: el text no centrava correctament en horitzontal
This commit is contained in:
2025-12-17 13:31:32 +01:00
parent 3b432e6580
commit ec333efe66
4 changed files with 291 additions and 23 deletions

View File

@@ -153,7 +153,10 @@ void EscenaJoc::inicialitzar() {
// Initialize lives and game over state (independent lives per player)
vides_per_jugador_[0] = Defaults::Game::STARTING_LIVES;
vides_per_jugador_[1] = Defaults::Game::STARTING_LIVES;
game_over_ = false;
estat_game_over_ = EstatGameOver::NONE;
continue_counter_ = 0;
continue_tick_timer_ = 0.0f;
continues_usados_ = 0;
game_over_timer_ = 0.0f;
// Initialize scores (separate per player)
@@ -206,7 +209,7 @@ void EscenaJoc::inicialitzar() {
void EscenaJoc::actualitzar(float delta_time) {
// Processar disparos (state-based, no event-based)
if (!game_over_) {
if (estat_game_over_ == EstatGameOver::NONE) {
auto* input = Input::get();
// Jugador 1 dispara (només si està actiu)
@@ -222,10 +225,42 @@ void EscenaJoc::actualitzar(float delta_time) {
disparar_bala(1);
}
}
// [NEW] Allow mid-game join: inactive player presses START
// P2 can join if only P1 is active
if (config_partida_.jugador1_actiu && !config_partida_.jugador2_actiu) {
if (input->checkActionPlayer2(InputAction::START, Input::DO_NOT_ALLOW_REPEAT)) {
unir_jugador(1);
}
}
// P1 can join if only P2 is active
if (!config_partida_.jugador1_actiu && config_partida_.jugador2_actiu) {
if (input->checkActionPlayer1(InputAction::START, Input::DO_NOT_ALLOW_REPEAT)) {
unir_jugador(0);
}
}
}
// Check game over state first
if (game_over_) {
// Handle CONTINUE screen
if (estat_game_over_ == EstatGameOver::CONTINUE) {
actualitzar_continue(delta_time);
processar_input_continue();
// Still update enemies, bullets, and effects during continue screen
for (auto& enemy : orni_) {
enemy.actualitzar(delta_time);
}
for (auto& bala : bales_) {
bala.actualitzar(delta_time);
}
debris_manager_.actualitzar(delta_time);
gestor_puntuacio_.actualitzar(delta_time);
return;
}
// Handle final GAME OVER state
if (estat_game_over_ == EstatGameOver::GAME_OVER) {
// Game over: only update timer, enemies, bullets, and debris
game_over_timer_ -= delta_time;
@@ -277,13 +312,14 @@ void EscenaJoc::actualitzar(float delta_time) {
// Set sentinel value to prevent re-entering this block
itocado_per_jugador_[i] = 999.0f;
// Check if ALL ACTIVE players are dead (game over)
// Check if ALL ACTIVE players are dead (trigger continue screen)
bool p1_dead = !config_partida_.jugador1_actiu || vides_per_jugador_[0] <= 0;
bool p2_dead = !config_partida_.jugador2_actiu || vides_per_jugador_[1] <= 0;
if (p1_dead && p2_dead) {
game_over_ = true;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
estat_game_over_ = EstatGameOver::CONTINUE;
continue_counter_ = Defaults::Game::CONTINUE_COUNT_START;
continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION;
}
}
}
@@ -450,8 +486,30 @@ void EscenaJoc::actualitzar(float delta_time) {
}
void EscenaJoc::dibuixar() {
// Check game over state
if (game_over_) {
// Handle CONTINUE screen
if (estat_game_over_ == EstatGameOver::CONTINUE) {
// Draw game background elements first
dibuixar_marges();
for (const auto& enemy : orni_) {
enemy.dibuixar();
}
for (const auto& bala : bales_) {
bala.dibuixar();
}
debris_manager_.dibuixar();
gestor_puntuacio_.dibuixar();
dibuixar_marcador();
// Draw CONTINUE screen overlay
dibuixar_continue();
return;
}
// Handle final GAME OVER state
if (estat_game_over_ == EstatGameOver::GAME_OVER) {
// Game over: draw enemies, bullets, debris, and "GAME OVER" text
dibuixar_marges();
@@ -1121,3 +1179,169 @@ void EscenaJoc::disparar_bala(uint8_t player_id) {
}
}
}
// ==================== CONTINUE & JOIN SYSTEM ====================
void EscenaJoc::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;
// Play tick sound
Audio::get()->playSound("continue_tick");
if (continue_counter_ <= 0) {
// Timeout → final game over
estat_game_over_ = EstatGameOver::GAME_OVER;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
}
}
}
void EscenaJoc::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_usados_ >= Defaults::Game::MAX_CONTINUES) {
// Max continues reached → final game over
estat_game_over_ = EstatGameOver::GAME_OVER;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
return;
}
// Only increment if not infinite
if (!Defaults::Game::INFINITE_CONTINUES) {
continues_usados_++;
}
// Determine which player(s) to revive
uint8_t player_to_revive = p1_start ? 0 : 1;
// Reset score and lives (KEEP level and enemies!)
puntuacio_per_jugador_[player_to_revive] = 0;
vides_per_jugador_[player_to_revive] = Defaults::Game::STARTING_LIVES;
itocado_per_jugador_[player_to_revive] = 0.0f;
// Activate player if not already
if (player_to_revive == 0) {
config_partida_.jugador1_actiu = true;
} else {
config_partida_.jugador2_actiu = true;
}
// Spawn with invulnerability
Punt spawn_pos = obtenir_punt_spawn(player_to_revive);
naus_[player_to_revive].inicialitzar(&spawn_pos, true);
// Check if other player wants to continue too
if (p1_start && p2_start) {
uint8_t other_player = 1;
puntuacio_per_jugador_[other_player] = 0;
vides_per_jugador_[other_player] = Defaults::Game::STARTING_LIVES;
itocado_per_jugador_[other_player] = 0.0f;
config_partida_.jugador2_actiu = true;
Punt spawn_pos2 = obtenir_punt_spawn(other_player);
naus_[other_player].inicialitzar(&spawn_pos2, true);
}
// Resume game
estat_game_over_ = EstatGameOver::NONE;
continue_counter_ = 0;
continue_tick_timer_ = 0.0f;
// Play continue confirmation sound
Audio::get()->playSound("continue_confirm");
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_--;
// Play tick sound on manual decrement
Audio::get()->playSound("continue_tick");
if (continue_counter_ <= 0) {
estat_game_over_ = EstatGameOver::GAME_OVER;
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
}
// Reset timer to prevent double-decrement
continue_tick_timer_ = Defaults::Game::CONTINUE_TICK_DURATION;
}
}
void EscenaJoc::dibuixar_continue() {
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
constexpr float spacing = 4.0f;
// "CONTINUE" text (using constants)
const std::string continue_text = "CONTINUE";
float escala_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_SCALE;
float y_ratio_continue = Defaults::Game::ContinueScreen::CONTINUE_TEXT_Y_RATIO;
float text_width_continue = text_.get_text_width(continue_text, escala_continue, spacing);
float x_continue = play_area.x + (play_area.w - text_width_continue) / 2.0f;
float y_continue = play_area.y + play_area.h * y_ratio_continue;
text_.render(continue_text, {x_continue, y_continue}, escala_continue, spacing);
// Countdown number (using constants)
const std::string counter_str = std::to_string(continue_counter_);
float escala_counter = Defaults::Game::ContinueScreen::COUNTER_TEXT_SCALE;
float y_ratio_counter = Defaults::Game::ContinueScreen::COUNTER_TEXT_Y_RATIO;
float text_width_counter = text_.get_text_width(counter_str, escala_counter, spacing);
float x_counter = play_area.x + (play_area.w - text_width_counter) / 2.0f;
float y_counter = play_area.y + play_area.h * y_ratio_counter;
text_.render(counter_str, {x_counter, y_counter}, escala_counter, spacing);
// "CONTINUES LEFT" (conditional + using constants)
if (!Defaults::Game::INFINITE_CONTINUES) {
const std::string continues_text = "CONTINUES LEFT: " + std::to_string(Defaults::Game::MAX_CONTINUES - continues_usados_);
float escala_info = Defaults::Game::ContinueScreen::INFO_TEXT_SCALE;
float y_ratio_info = Defaults::Game::ContinueScreen::INFO_TEXT_Y_RATIO;
float text_width_info = text_.get_text_width(continues_text, escala_info, spacing);
float x_info = play_area.x + (play_area.w - text_width_info) / 2.0f;
float y_info = play_area.y + play_area.h * y_ratio_info;
text_.render(continues_text, {x_info, y_info}, escala_info, spacing);
}
}
void EscenaJoc::unir_jugador(uint8_t player_id) {
// Activate player
if (player_id == 0) {
config_partida_.jugador1_actiu = true;
} else {
config_partida_.jugador2_actiu = true;
}
// Reset stats
vides_per_jugador_[player_id] = Defaults::Game::STARTING_LIVES;
puntuacio_per_jugador_[player_id] = 0;
itocado_per_jugador_[player_id] = 0.0f;
// Spawn with invulnerability
Punt spawn_pos = obtenir_punt_spawn(player_id);
naus_[player_id].inicialitzar(&spawn_pos, true);
// No visual message, just spawn (per user requirement)
std::cout << "[EscenaJoc] Jugador " << (int)(player_id + 1) << " s'ha unit a la partida!" << std::endl;
}