afegida colisió nau-enemics i game over

This commit is contained in:
2025-12-03 12:04:44 +01:00
parent 0500dce7aa
commit 1441134aea
5 changed files with 226 additions and 24 deletions

View File

@@ -73,6 +73,14 @@ constexpr float ENEMY_RADIUS = 20.0f;
constexpr float BULLET_RADIUS = 5.0f;
} // namespace Entities
// Game rules (lives, respawn, game over)
namespace Game {
constexpr int STARTING_LIVES = 3; // Initial lives
constexpr float DEATH_DURATION = 3.0f; // Seconds of death animation
constexpr float GAME_OVER_DURATION = 5.0f; // Seconds to display game over
constexpr float COLLISION_SHIP_ENEMY_AMPLIFIER = 0.80f; // 80% hitbox (generous)
} // namespace Game
// Física (valores actuales del juego, sincronizados con joc_asteroides.cpp)
namespace Physics {
constexpr float ROTATION_SPEED = 3.14f; // rad/s (~180°/s)
@@ -90,7 +98,8 @@ constexpr float VARIACIO_VELOCITAT = 40.0f; // ±variació aleatòria (px/s)
constexpr float ACCELERACIO = -60.0f; // Fricció/desacceleració (px/s²)
constexpr float ROTACIO_MIN = 0.1f; // Rotació mínima (rad/s ~5.7°/s)
constexpr float ROTACIO_MAX = 0.3f; // Rotació màxima (rad/s ~17.2°/s)
constexpr float TEMPS_VIDA = 2.0f; // Duració màxima (segons)
constexpr float TEMPS_VIDA = 2.0f; // Duració màxima (segons) - enemy/bullet debris
constexpr float TEMPS_VIDA_NAU = 3.0f; // Ship debris lifetime (matches DEATH_DURATION)
constexpr float SHRINK_RATE = 0.5f; // Reducció de mida (factor/s)
} // namespace Debris
} // namespace Physics

View File

@@ -29,7 +29,7 @@ Nau::Nau(SDL_Renderer* renderer)
}
}
void Nau::inicialitzar() {
void Nau::inicialitzar(const Punt* spawn_point) {
// Inicialització de la nau (triangle)
// Basat en el codi Pascal original: lines 380-384
// Copiat de joc_asteroides.cpp línies 30-44
@@ -37,11 +37,17 @@ void Nau::inicialitzar() {
// [NUEVO] Ja no cal configurar punts polars - la geometria es carrega del
// fitxer Només inicialitzem l'estat de la instància
// Posició inicial al centre de l'àrea de joc
float centre_x, centre_y;
Constants::obtenir_centre_zona(centre_x, centre_y);
centre_.x = centre_x; // 320
centre_.y = centre_y; // 213 (not 240!)
// Use custom spawn point if provided, otherwise use center
if (spawn_point) {
centre_.x = spawn_point->x;
centre_.y = spawn_point->y;
} else {
// Default: center of play area
float centre_x, centre_y;
Constants::obtenir_centre_zona(centre_x, centre_y);
centre_.x = static_cast<int>(centre_x);
centre_.y = static_cast<int>(centre_y);
}
// Estat inicial
angle_ = 0.0f;

View File

@@ -16,7 +16,7 @@ class Nau {
: renderer_(nullptr) {}
Nau(SDL_Renderer* renderer);
void inicialitzar();
void inicialitzar(const Punt* spawn_point = nullptr);
void processar_input(float delta_time);
void actualitzar(float delta_time);
void dibuixar() const;
@@ -25,6 +25,7 @@ class Nau {
const Punt& get_centre() const { return centre_; }
float get_angle() const { return angle_; }
bool esta_viva() const { return !esta_tocada_; }
const std::shared_ptr<Graphics::Shape>& get_forma() const { return forma_; }
// Col·lisions (Fase 10)
void marcar_tocada() { esta_tocada_ = true; }

View File

@@ -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
}
}
}

View File

@@ -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ó
};