afegida colisió nau-enemics i game over
This commit is contained in:
@@ -73,6 +73,14 @@ constexpr float ENEMY_RADIUS = 20.0f;
|
|||||||
constexpr float BULLET_RADIUS = 5.0f;
|
constexpr float BULLET_RADIUS = 5.0f;
|
||||||
} // namespace Entities
|
} // 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)
|
// Física (valores actuales del juego, sincronizados con joc_asteroides.cpp)
|
||||||
namespace Physics {
|
namespace Physics {
|
||||||
constexpr float ROTATION_SPEED = 3.14f; // rad/s (~180°/s)
|
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 ACCELERACIO = -60.0f; // Fricció/desacceleració (px/s²)
|
||||||
constexpr float ROTACIO_MIN = 0.1f; // Rotació mínima (rad/s ~5.7°/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 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)
|
constexpr float SHRINK_RATE = 0.5f; // Reducció de mida (factor/s)
|
||||||
} // namespace Debris
|
} // namespace Debris
|
||||||
} // namespace Physics
|
} // namespace Physics
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Nau::Nau(SDL_Renderer* renderer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nau::inicialitzar() {
|
void Nau::inicialitzar(const Punt* spawn_point) {
|
||||||
// Inicialització de la nau (triangle)
|
// Inicialització de la nau (triangle)
|
||||||
// Basat en el codi Pascal original: lines 380-384
|
// Basat en el codi Pascal original: lines 380-384
|
||||||
// Copiat de joc_asteroides.cpp línies 30-44
|
// 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
|
// [NUEVO] Ja no cal configurar punts polars - la geometria es carrega del
|
||||||
// fitxer Només inicialitzem l'estat de la instància
|
// fitxer Només inicialitzem l'estat de la instància
|
||||||
|
|
||||||
// Posició inicial al centre de l'àrea de joc
|
// Use custom spawn point if provided, otherwise use center
|
||||||
float centre_x, centre_y;
|
if (spawn_point) {
|
||||||
Constants::obtenir_centre_zona(centre_x, centre_y);
|
centre_.x = spawn_point->x;
|
||||||
centre_.x = centre_x; // 320
|
centre_.y = spawn_point->y;
|
||||||
centre_.y = centre_y; // 213 (not 240!)
|
} 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
|
// Estat inicial
|
||||||
angle_ = 0.0f;
|
angle_ = 0.0f;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class Nau {
|
|||||||
: renderer_(nullptr) {}
|
: renderer_(nullptr) {}
|
||||||
Nau(SDL_Renderer* renderer);
|
Nau(SDL_Renderer* renderer);
|
||||||
|
|
||||||
void inicialitzar();
|
void inicialitzar(const Punt* spawn_point = nullptr);
|
||||||
void processar_input(float delta_time);
|
void processar_input(float delta_time);
|
||||||
void actualitzar(float delta_time);
|
void actualitzar(float delta_time);
|
||||||
void dibuixar() const;
|
void dibuixar() const;
|
||||||
@@ -25,6 +25,7 @@ class Nau {
|
|||||||
const Punt& get_centre() const { return centre_; }
|
const Punt& get_centre() const { return centre_; }
|
||||||
float get_angle() const { return angle_; }
|
float get_angle() const { return angle_; }
|
||||||
bool esta_viva() const { return !esta_tocada_; }
|
bool esta_viva() const { return !esta_tocada_; }
|
||||||
|
const std::shared_ptr<Graphics::Shape>& get_forma() const { return forma_; }
|
||||||
|
|
||||||
// Col·lisions (Fase 10)
|
// Col·lisions (Fase 10)
|
||||||
void marcar_tocada() { esta_tocada_ = true; }
|
void marcar_tocada() { esta_tocada_ = true; }
|
||||||
|
|||||||
@@ -108,6 +108,14 @@ void EscenaJoc::inicialitzar() {
|
|||||||
// Inicialitzar estat de col·lisió
|
// Inicialitzar estat de col·lisió
|
||||||
itocado_ = 0;
|
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
|
// Inicialitzar nau
|
||||||
nau_.inicialitzar();
|
nau_.inicialitzar();
|
||||||
|
|
||||||
@@ -123,52 +131,160 @@ void EscenaJoc::inicialitzar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EscenaJoc::actualitzar(float delta_time) {
|
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_.processar_input(delta_time);
|
||||||
nau_.actualitzar(delta_time);
|
nau_.actualitzar(delta_time);
|
||||||
|
|
||||||
// Actualitzar moviment i rotació dels enemics (ORNIs)
|
// Update enemy movement and rotation
|
||||||
for (auto& enemy : orni_) {
|
for (auto& enemy : orni_) {
|
||||||
enemy.actualitzar(delta_time);
|
enemy.actualitzar(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualitzar moviment de bales (Fase 9)
|
// Update bullet movement
|
||||||
for (auto& bala : bales_) {
|
for (auto& bala : bales_) {
|
||||||
bala.actualitzar(delta_time);
|
bala.actualitzar(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detectar col·lisions bala-enemic (Fase 10)
|
// Detect collisions
|
||||||
detectar_col·lisions_bales_enemics();
|
detectar_col·lisions_bales_enemics();
|
||||||
|
detectar_col·lisio_nau_enemics(); // New collision check
|
||||||
|
|
||||||
// Actualitzar fragments d'explosions
|
// Update debris
|
||||||
debris_manager_.actualitzar(delta_time);
|
debris_manager_.actualitzar(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EscenaJoc::dibuixar() {
|
void EscenaJoc::dibuixar() {
|
||||||
// Dibuixar marges de la zona de joc
|
// Draw borders (always visible)
|
||||||
dibuixar_marges();
|
dibuixar_marges();
|
||||||
|
|
||||||
// Dibuixar nau
|
// Check game over state
|
||||||
nau_.dibuixar();
|
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_) {
|
for (const auto& enemy : orni_) {
|
||||||
enemy.dibuixar();
|
enemy.dibuixar();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibuixar bales (Fase 9)
|
// Draw bullets (always)
|
||||||
for (const auto& bala : bales_) {
|
for (const auto& bala : bales_) {
|
||||||
bala.dibuixar();
|
bala.dibuixar();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dibuixar fragments d'explosions (després d'altres objectes)
|
// Draw debris
|
||||||
debris_manager_.dibuixar();
|
debris_manager_.dibuixar();
|
||||||
|
|
||||||
// Dibuixar marcador
|
// Draw scoreboard
|
||||||
dibuixar_marcador();
|
dibuixar_marcador();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EscenaJoc::processar_input(const SDL_Event& event) {
|
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)
|
// Processament d'input per events puntuals (no continus)
|
||||||
// L'input continu (fletxes) es processa en actualitzar() amb
|
// L'input continu (fletxes) es processa en actualitzar() amb
|
||||||
// SDL_GetKeyboardState()
|
// SDL_GetKeyboardState()
|
||||||
@@ -217,7 +333,34 @@ void EscenaJoc::processar_input(const SDL_Event& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EscenaJoc::tocado() {
|
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 {
|
void EscenaJoc::dibuixar_marges() const {
|
||||||
@@ -238,8 +381,8 @@ void EscenaJoc::dibuixar_marges() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EscenaJoc::dibuixar_marcador() {
|
void EscenaJoc::dibuixar_marcador() {
|
||||||
// Text estàtic (hardcoded)
|
// Display actual lives count (user requested "LIFES" plural English)
|
||||||
const std::string text = "SCORE: 01000 LIFE: 3 LEVEL: 01";
|
std::string text = "SCORE: 01000 LIFES: " + std::to_string(num_vides_) + " LEVEL: 01";
|
||||||
|
|
||||||
// Paràmetres de renderització
|
// Paràmetres de renderització
|
||||||
const float escala = 0.85f;
|
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<Enemic, Constants::MAX_ORNIS> orni_;
|
||||||
std::array<Bala, Constants::MAX_BALES> bales_;
|
std::array<Bala, Constants::MAX_BALES> bales_;
|
||||||
Poligon chatarra_cosmica_;
|
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
|
// Text vectorial
|
||||||
Graphics::VectorText text_;
|
Graphics::VectorText text_;
|
||||||
@@ -50,6 +56,7 @@ class EscenaJoc {
|
|||||||
// Funcions privades
|
// Funcions privades
|
||||||
void tocado();
|
void tocado();
|
||||||
void detectar_col·lisions_bales_enemics(); // Col·lisions bala-enemic
|
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_marges() const; // Dibuixar vores de la zona de joc
|
||||||
void dibuixar_marcador(); // Dibuixar marcador de puntuació
|
void dibuixar_marcador(); // Dibuixar marcador de puntuació
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user