afegida colisió nau-enemics i game over
This commit is contained in:
@@ -108,6 +108,14 @@ void EscenaJoc::inicialitzar() {
|
||||
// Inicialitzar estat de col·lisió
|
||||
itocado_ = 0;
|
||||
|
||||
// Initialize lives and game over state
|
||||
num_vides_ = Defaults::Game::STARTING_LIVES;
|
||||
game_over_ = false;
|
||||
game_over_timer_ = 0.0f;
|
||||
|
||||
// Set spawn point to center of play area
|
||||
Constants::obtenir_centre_zona(punt_spawn_.x, punt_spawn_.y);
|
||||
|
||||
// Inicialitzar nau
|
||||
nau_.inicialitzar();
|
||||
|
||||
@@ -123,52 +131,160 @@ void EscenaJoc::inicialitzar() {
|
||||
}
|
||||
|
||||
void EscenaJoc::actualitzar(float delta_time) {
|
||||
// Actualitzar nau (input + física)
|
||||
// Check game over state first
|
||||
if (game_over_) {
|
||||
// Game over: only update timer, enemies, bullets, and debris
|
||||
game_over_timer_ -= delta_time;
|
||||
|
||||
if (game_over_timer_ <= 0.0f) {
|
||||
// Auto-transition to title screen
|
||||
GestorEscenes::actual = GestorEscenes::Escena::TITOL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Enemies and bullets continue moving during game over
|
||||
for (auto& enemy : orni_) {
|
||||
enemy.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
for (auto& bala : bales_) {
|
||||
bala.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
debris_manager_.actualitzar(delta_time);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check death sequence state
|
||||
if (itocado_ > 0.0f) {
|
||||
// Death sequence active: update timer
|
||||
itocado_ += delta_time;
|
||||
|
||||
// Check if death duration completed
|
||||
if (itocado_ >= Defaults::Game::DEATH_DURATION) {
|
||||
// *** PHASE 3: RESPAWN OR GAME OVER ***
|
||||
|
||||
// Decrement lives
|
||||
num_vides_--;
|
||||
|
||||
if (num_vides_ > 0) {
|
||||
// Respawn ship
|
||||
nau_.inicialitzar(&punt_spawn_);
|
||||
itocado_ = 0.0f;
|
||||
} else {
|
||||
// Game over
|
||||
game_over_ = true;
|
||||
game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
|
||||
itocado_ = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Enemies and bullets continue moving during death sequence
|
||||
for (auto& enemy : orni_) {
|
||||
enemy.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
for (auto& bala : bales_) {
|
||||
bala.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
debris_manager_.actualitzar(delta_time);
|
||||
return;
|
||||
}
|
||||
|
||||
// *** NORMAL GAMEPLAY ***
|
||||
|
||||
// Update ship (input + physics)
|
||||
nau_.processar_input(delta_time);
|
||||
nau_.actualitzar(delta_time);
|
||||
|
||||
// Actualitzar moviment i rotació dels enemics (ORNIs)
|
||||
// Update enemy movement and rotation
|
||||
for (auto& enemy : orni_) {
|
||||
enemy.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
// Actualitzar moviment de bales (Fase 9)
|
||||
// Update bullet movement
|
||||
for (auto& bala : bales_) {
|
||||
bala.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
// Detectar col·lisions bala-enemic (Fase 10)
|
||||
// Detect collisions
|
||||
detectar_col·lisions_bales_enemics();
|
||||
detectar_col·lisio_nau_enemics(); // New collision check
|
||||
|
||||
// Actualitzar fragments d'explosions
|
||||
// Update debris
|
||||
debris_manager_.actualitzar(delta_time);
|
||||
}
|
||||
|
||||
void EscenaJoc::dibuixar() {
|
||||
// Dibuixar marges de la zona de joc
|
||||
// Draw borders (always visible)
|
||||
dibuixar_marges();
|
||||
|
||||
// Dibuixar nau
|
||||
nau_.dibuixar();
|
||||
// Check game over state
|
||||
if (game_over_) {
|
||||
// Game over: draw enemies, bullets, debris, and "GAME OVER" text
|
||||
|
||||
// Dibuixar ORNIs (enemics)
|
||||
for (const auto& enemy : orni_) {
|
||||
enemy.dibuixar();
|
||||
}
|
||||
|
||||
for (const auto& bala : bales_) {
|
||||
bala.dibuixar();
|
||||
}
|
||||
|
||||
debris_manager_.dibuixar();
|
||||
|
||||
// Draw centered "GAME OVER" text
|
||||
const std::string game_over_text = "GAME OVER";
|
||||
constexpr float escala = 2.0f;
|
||||
constexpr float spacing = 4.0f;
|
||||
|
||||
float text_width = text_.get_text_width(game_over_text, escala, spacing);
|
||||
float text_height = text_.get_text_height(escala);
|
||||
|
||||
const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
|
||||
float x = play_area.x + (play_area.w - text_width) / 2.0f;
|
||||
float y = play_area.y + (play_area.h - text_height) / 2.0f;
|
||||
|
||||
text_.render(game_over_text, {x, y}, escala, spacing);
|
||||
|
||||
dibuixar_marcador();
|
||||
return;
|
||||
}
|
||||
|
||||
// During death sequence, don't draw ship (debris draws automatically)
|
||||
if (itocado_ == 0.0f) {
|
||||
nau_.dibuixar();
|
||||
}
|
||||
|
||||
// Draw enemies (always)
|
||||
for (const auto& enemy : orni_) {
|
||||
enemy.dibuixar();
|
||||
}
|
||||
|
||||
// Dibuixar bales (Fase 9)
|
||||
// Draw bullets (always)
|
||||
for (const auto& bala : bales_) {
|
||||
bala.dibuixar();
|
||||
}
|
||||
|
||||
// Dibuixar fragments d'explosions (després d'altres objectes)
|
||||
// Draw debris
|
||||
debris_manager_.dibuixar();
|
||||
|
||||
// Dibuixar marcador
|
||||
// Draw scoreboard
|
||||
dibuixar_marcador();
|
||||
}
|
||||
|
||||
void EscenaJoc::processar_input(const SDL_Event& event) {
|
||||
// Ignore ship controls during game over
|
||||
if (game_over_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore ship controls during death sequence
|
||||
if (itocado_ > 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Processament d'input per events puntuals (no continus)
|
||||
// L'input continu (fletxes) es processa en actualitzar() amb
|
||||
// SDL_GetKeyboardState()
|
||||
@@ -217,7 +333,34 @@ void EscenaJoc::processar_input(const SDL_Event& event) {
|
||||
}
|
||||
|
||||
void EscenaJoc::tocado() {
|
||||
// TODO: Implementar seqüència de mort
|
||||
// Death sequence: 3 phases
|
||||
// Phase 1: First call (itocado_ == 0) - trigger explosion
|
||||
// Phase 2: Animation (0 < itocado_ < 3.0s) - debris animation
|
||||
// Phase 3: Respawn or game over (itocado_ >= 3.0s) - handled in actualitzar()
|
||||
|
||||
if (itocado_ == 0.0f) {
|
||||
// *** PHASE 1: TRIGGER DEATH ***
|
||||
|
||||
// Mark ship as dead (stops rendering and input)
|
||||
nau_.marcar_tocada();
|
||||
|
||||
// Create ship explosion
|
||||
const Punt& ship_pos = nau_.get_centre();
|
||||
float ship_angle = nau_.get_angle();
|
||||
|
||||
debris_manager_.explotar(
|
||||
nau_.get_forma(), // Ship shape (3 lines)
|
||||
ship_pos, // Center position
|
||||
ship_angle, // Ship orientation
|
||||
1.0f, // Normal scale
|
||||
Defaults::Physics::Debris::VELOCITAT_BASE // 80 px/s
|
||||
);
|
||||
|
||||
// Start death timer (non-zero to avoid re-triggering)
|
||||
itocado_ = 0.001f;
|
||||
}
|
||||
// Phase 2 is automatic (debris updates in actualitzar())
|
||||
// Phase 3 is handled in actualitzar() when itocado_ >= DEATH_DURATION
|
||||
}
|
||||
|
||||
void EscenaJoc::dibuixar_marges() const {
|
||||
@@ -238,8 +381,8 @@ void EscenaJoc::dibuixar_marges() const {
|
||||
}
|
||||
|
||||
void EscenaJoc::dibuixar_marcador() {
|
||||
// Text estàtic (hardcoded)
|
||||
const std::string text = "SCORE: 01000 LIFE: 3 LEVEL: 01";
|
||||
// Display actual lives count (user requested "LIFES" plural English)
|
||||
std::string text = "SCORE: 01000 LIFES: " + std::to_string(num_vides_) + " LEVEL: 01";
|
||||
|
||||
// Paràmetres de renderització
|
||||
const float escala = 0.85f;
|
||||
@@ -316,3 +459,39 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EscenaJoc::detectar_col·lisio_nau_enemics() {
|
||||
// Only check collisions if ship is alive
|
||||
if (!nau_.esta_viva()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Generous collision detection (80% hitbox)
|
||||
constexpr float RADI_NAU = Defaults::Entities::SHIP_RADIUS;
|
||||
constexpr float RADI_ENEMIC = Defaults::Entities::ENEMY_RADIUS;
|
||||
constexpr float SUMA_RADIS =
|
||||
(RADI_NAU + RADI_ENEMIC) * Defaults::Game::COLLISION_SHIP_ENEMY_AMPLIFIER;
|
||||
constexpr float SUMA_RADIS_QUADRAT = SUMA_RADIS * SUMA_RADIS;
|
||||
|
||||
const Punt& pos_nau = nau_.get_centre();
|
||||
|
||||
// Check collision with all active enemies
|
||||
for (const auto& enemic : orni_) {
|
||||
if (!enemic.esta_actiu()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Punt& pos_enemic = enemic.get_centre();
|
||||
|
||||
// Calculate squared distance (avoid sqrt)
|
||||
float dx = static_cast<float>(pos_nau.x - pos_enemic.x);
|
||||
float dy = static_cast<float>(pos_nau.y - pos_enemic.y);
|
||||
float distancia_quadrada = dx * dx + dy * dy;
|
||||
|
||||
// Check collision
|
||||
if (distancia_quadrada <= SUMA_RADIS_QUADRAT) {
|
||||
tocado(); // Trigger death sequence
|
||||
return; // Only one collision per frame
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,13 @@ class EscenaJoc {
|
||||
std::array<Enemic, Constants::MAX_ORNIS> orni_;
|
||||
std::array<Bala, Constants::MAX_BALES> bales_;
|
||||
Poligon chatarra_cosmica_;
|
||||
uint16_t itocado_;
|
||||
float itocado_; // Death timer (seconds)
|
||||
|
||||
// Lives and game over system
|
||||
int num_vides_; // Current lives count
|
||||
bool game_over_; // Game over state flag
|
||||
float game_over_timer_; // Countdown timer for auto-return (seconds)
|
||||
Punt punt_spawn_; // Configurable spawn point
|
||||
|
||||
// Text vectorial
|
||||
Graphics::VectorText text_;
|
||||
@@ -50,6 +56,7 @@ class EscenaJoc {
|
||||
// Funcions privades
|
||||
void tocado();
|
||||
void detectar_col·lisions_bales_enemics(); // Col·lisions bala-enemic
|
||||
void detectar_col·lisio_nau_enemics(); // Ship-enemy collision detection
|
||||
void dibuixar_marges() const; // Dibuixar vores de la zona de joc
|
||||
void dibuixar_marcador(); // Dibuixar marcador de puntuació
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user