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