Fase 1b: rename d'entitats i metodes virtuals a CamelCase/camelBack
Tots els tipus d'entitat passen del catala a l'angles seguint el
.clang-tidy del projecte (tipus en CamelCase, metodes en camelBack,
membres en lower_case amb sufix _).
Renames de tipus:
- Entitat -> Entity (core/entities/entity.hpp)
- Nau -> Ship (game/entities/ship.{hpp,cpp})
- Enemic -> Enemy (game/entities/enemy.{hpp,cpp})
- Bala -> Bullet (game/entities/bullet.{hpp,cpp})
- TipusEnemic -> EnemyType
- AnimacioEnemic -> EnemyAnimation
Metodes virtuals (s'aplica a tot el codi, no nomes a entitats):
- actualitzar -> update
- dibuixar -> draw
- inicialitzar -> init
- processar_input -> processInput
- esta_actiu -> isActive
- es_collidable -> isCollidable
- get_collision_radius -> getCollisionRadius
Getters comuns:
- get_centre -> getCenter
- get_angle -> getAngle
- get_brightness -> getBrightness
- get_forma -> getShape
Metodes especifics:
- esta_viva -> isAlive
- esta_tocada -> isHit
- es_invulnerable -> isInvulnerable
- get_velocitat_vector -> getVelocityVector
- set_centre -> setCenter
- marcar_tocada -> markHit
- aplicar_fisica -> applyPhysics
- get_tipus -> getType
Camps privats:
- centre_ -> center_
- velocitat_ -> velocity_
- forma_ -> shape_
- esta_tocada_ -> is_hit_
- tipus_ -> type_
L'import d'audio/input d'AEEA quedara coherent (mateix estil).
Diff net: 30 fitxers, +437/-437 (la majoria es renames simetrics).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
// © 1999 Visente i Sergi (versió Pascal)
|
||||
// © 2025 Port a C++20 amb SDL3
|
||||
|
||||
#include "game/entities/bala.hpp"
|
||||
#include "game/entities/bullet.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -11,15 +11,15 @@
|
||||
|
||||
#include "core/audio/audio.hpp"
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entitat.hpp"
|
||||
#include "core/entities/entity.hpp"
|
||||
#include "core/graphics/shape_loader.hpp"
|
||||
#include "core/rendering/shape_renderer.hpp"
|
||||
#include "core/types.hpp"
|
||||
#include "game/constants.hpp"
|
||||
|
||||
Bala::Bala(SDL_Renderer* renderer)
|
||||
: Entitat(renderer),
|
||||
velocitat_(0.0F),
|
||||
Bullet::Bullet(SDL_Renderer* renderer)
|
||||
: Entity(renderer),
|
||||
velocity_(0.0F),
|
||||
esta_(false),
|
||||
owner_id_(0),
|
||||
grace_timer_(0.0F) {
|
||||
@@ -27,23 +27,23 @@ Bala::Bala(SDL_Renderer* renderer)
|
||||
brightness_ = Defaults::Brightness::BALA;
|
||||
|
||||
// [NUEVO] Carregar forma compartida des de fitxer
|
||||
forma_ = Graphics::ShapeLoader::load("bullet.shp");
|
||||
shape_ = Graphics::ShapeLoader::load("bullet.shp");
|
||||
|
||||
if (!forma_ || !forma_->es_valida()) {
|
||||
std::cerr << "[Bala] Error: no s'ha pogut carregar bullet.shp" << '\n';
|
||||
if (!shape_ || !shape_->es_valida()) {
|
||||
std::cerr << "[Bullet] Error: no s'ha pogut carregar bullet.shp" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void Bala::inicialitzar() {
|
||||
void Bullet::init() {
|
||||
// Inicialment inactiva
|
||||
esta_ = false;
|
||||
centre_ = {.x = 0.0F, .y = 0.0F};
|
||||
center_ = {.x = 0.0F, .y = 0.0F};
|
||||
angle_ = 0.0F;
|
||||
velocitat_ = 0.0F;
|
||||
velocity_ = 0.0F;
|
||||
grace_timer_ = 0.0F;
|
||||
}
|
||||
|
||||
void Bala::disparar(const Vec2& posicio, float angle, uint8_t owner_id) {
|
||||
void Bullet::disparar(const Vec2& posicio, float angle, uint8_t owner_id) {
|
||||
// Activar bala i posicionar-la a la nau
|
||||
// Basat en joc_asteroides.cpp línies 188-200
|
||||
|
||||
@@ -51,8 +51,8 @@ void Bala::disparar(const Vec2& posicio, float angle, uint8_t owner_id) {
|
||||
esta_ = true;
|
||||
|
||||
// Posició inicial = centre de la nau
|
||||
centre_.x = posicio.x;
|
||||
centre_.y = posicio.y;
|
||||
center_.x = posicio.x;
|
||||
center_.y = posicio.y;
|
||||
|
||||
// Angle = angle de la nau (dispara en la direcció que apunta)
|
||||
angle_ = angle;
|
||||
@@ -62,7 +62,7 @@ void Bala::disparar(const Vec2& posicio, float angle, uint8_t owner_id) {
|
||||
|
||||
// Velocitat alta (el joc Pascal original usava 7 px/frame)
|
||||
// 7 px/frame × 20 FPS = 140 px/s
|
||||
velocitat_ = 140.0F;
|
||||
velocity_ = 140.0F;
|
||||
|
||||
// Activar grace period (prevents instant self-collision)
|
||||
grace_timer_ = Defaults::Game::BULLET_GRACE_PERIOD;
|
||||
@@ -71,7 +71,7 @@ void Bala::disparar(const Vec2& posicio, float angle, uint8_t owner_id) {
|
||||
Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
|
||||
}
|
||||
|
||||
void Bala::actualitzar(float delta_time) {
|
||||
void Bullet::update(float delta_time) {
|
||||
if (esta_) {
|
||||
// Decrementar grace timer
|
||||
if (grace_timer_ > 0.0F) {
|
||||
@@ -83,30 +83,30 @@ void Bala::actualitzar(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
void Bala::dibuixar() const {
|
||||
if (esta_ && forma_) {
|
||||
void Bullet::draw() const {
|
||||
if (esta_ && shape_) {
|
||||
// [NUEVO] Usar render_shape en lloc de rota_pol
|
||||
// Les bales roten segons l'angle de trajectòria
|
||||
Rendering::render_shape(renderer_, forma_, centre_, angle_, 1.0F, 1.0F, brightness_);
|
||||
Rendering::render_shape(renderer_, shape_, center_, angle_, 1.0F, 1.0F, brightness_);
|
||||
}
|
||||
}
|
||||
|
||||
void Bala::mou(float delta_time) {
|
||||
void Bullet::mou(float delta_time) {
|
||||
// Moviment rectilini de la bala
|
||||
// Basat en el codi Pascal original: procedure mou_bales
|
||||
// Copiat EXACTAMENT de joc_asteroides.cpp línies 396-419
|
||||
|
||||
// Calcular nova posició (moviment polar time-based)
|
||||
// velocitat ja està en px/s (140 px/s), només cal multiplicar per delta_time
|
||||
float velocitat_efectiva = velocitat_ * delta_time;
|
||||
float velocitat_efectiva = velocity_ * delta_time;
|
||||
|
||||
// Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt)
|
||||
float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
|
||||
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
|
||||
|
||||
// Acumulació directa amb precisió subpíxel
|
||||
centre_.y += dy;
|
||||
centre_.x += dx;
|
||||
center_.y += dy;
|
||||
center_.x += dx;
|
||||
|
||||
// Desactivar si surt de la zona de joc (no rebota com els ORNIs)
|
||||
// CORRECCIÓ: Usar límits segurs amb radi de la bala
|
||||
@@ -120,8 +120,8 @@ void Bala::mou(float delta_time) {
|
||||
min_y,
|
||||
max_y);
|
||||
|
||||
if (centre_.x < min_x || centre_.x > max_x ||
|
||||
centre_.y < min_y || centre_.y > max_y) {
|
||||
if (center_.x < min_x || center_.x > max_x ||
|
||||
center_.y < min_y || center_.y > max_y) {
|
||||
esta_ = false;
|
||||
}
|
||||
}
|
||||
@@ -8,28 +8,28 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entitat.hpp"
|
||||
#include "core/entities/entity.hpp"
|
||||
#include "core/types.hpp"
|
||||
|
||||
class Bala : public Entities::Entitat {
|
||||
class Bullet : public Entities::Entity {
|
||||
public:
|
||||
Bala()
|
||||
: Entitat(nullptr) {}
|
||||
Bala(SDL_Renderer* renderer);
|
||||
Bullet()
|
||||
: Entity(nullptr) {}
|
||||
Bullet(SDL_Renderer* renderer);
|
||||
|
||||
void inicialitzar() override;
|
||||
void init() override;
|
||||
void disparar(const Vec2& posicio, float angle, uint8_t owner_id);
|
||||
void actualitzar(float delta_time) override;
|
||||
void dibuixar() const override;
|
||||
void update(float delta_time) override;
|
||||
void draw() const override;
|
||||
|
||||
// Override: Interfície d'Entitat
|
||||
[[nodiscard]] bool esta_actiu() const override { return esta_; }
|
||||
// Override: Interfície d'Entity
|
||||
[[nodiscard]] bool isActive() const override { return esta_; }
|
||||
|
||||
// Override: Interfície de col·lisió
|
||||
[[nodiscard]] float get_collision_radius() const override {
|
||||
[[nodiscard]] float getCollisionRadius() const override {
|
||||
return Defaults::Entities::BULLET_RADIUS;
|
||||
}
|
||||
[[nodiscard]] bool es_collidable() const override {
|
||||
[[nodiscard]] bool isCollidable() const override {
|
||||
return esta_ && grace_timer_ <= 0.0F;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ class Bala : public Entities::Entitat {
|
||||
void desactivar() { esta_ = false; }
|
||||
|
||||
private:
|
||||
// Membres específics de Bala (heretats: renderer_, forma_, centre_, angle_, brightness_)
|
||||
float velocitat_;
|
||||
// Membres específics de Bullet (heretats: renderer_, shape_, center_, angle_, brightness_)
|
||||
float velocity_;
|
||||
bool esta_;
|
||||
uint8_t owner_id_; // 0=P1, 1=P2
|
||||
float grace_timer_; // Grace period timer (0.0 = vulnerable)
|
||||
@@ -2,7 +2,7 @@
|
||||
// © 1999 Visente i Sergi (versió Pascal)
|
||||
// © 2025 Port a C++20 amb SDL3
|
||||
|
||||
#include "game/entities/enemic.hpp"
|
||||
#include "game/entities/enemy.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -10,19 +10,19 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entitat.hpp"
|
||||
#include "core/entities/entity.hpp"
|
||||
#include "core/graphics/shape_loader.hpp"
|
||||
#include "core/rendering/shape_renderer.hpp"
|
||||
#include "core/types.hpp"
|
||||
#include "game/constants.hpp"
|
||||
|
||||
Enemic::Enemic(SDL_Renderer* renderer)
|
||||
: Entitat(renderer),
|
||||
velocitat_(0.0F),
|
||||
Enemy::Enemy(SDL_Renderer* renderer)
|
||||
: Entity(renderer),
|
||||
velocity_(0.0F),
|
||||
drotacio_(0.0F),
|
||||
rotacio_(0.0F),
|
||||
esta_(false),
|
||||
tipus_(TipusEnemic::PENTAGON),
|
||||
type_(EnemyType::PENTAGON),
|
||||
tracking_timer_(0.0F),
|
||||
ship_position_(nullptr),
|
||||
tracking_strength_(0.5F), // Default tracking strength
|
||||
@@ -30,57 +30,57 @@ Enemic::Enemic(SDL_Renderer* renderer)
|
||||
// [NUEVO] Brightness específic per enemics
|
||||
brightness_ = Defaults::Brightness::ENEMIC;
|
||||
|
||||
// [NUEVO] Forma es carrega a inicialitzar() segons el tipus
|
||||
// [NUEVO] Forma es carrega a init() segons el tipus
|
||||
// Constructor no carrega forma per permetre tipus diferents
|
||||
}
|
||||
|
||||
void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
|
||||
void Enemy::init(EnemyType tipus, const Vec2* ship_pos) {
|
||||
// Guardar tipus
|
||||
tipus_ = tipus;
|
||||
type_ = tipus;
|
||||
|
||||
// Carregar forma segons el tipus
|
||||
const char* shape_file;
|
||||
float drotacio_min;
|
||||
float drotacio_max;
|
||||
|
||||
switch (tipus_) {
|
||||
case TipusEnemic::PENTAGON:
|
||||
switch (type_) {
|
||||
case EnemyType::PENTAGON:
|
||||
shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE;
|
||||
velocitat_ = Defaults::Enemies::Pentagon::VELOCITAT;
|
||||
velocity_ = Defaults::Enemies::Pentagon::VELOCITAT;
|
||||
drotacio_min = Defaults::Enemies::Pentagon::DROTACIO_MIN;
|
||||
drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX;
|
||||
break;
|
||||
|
||||
case TipusEnemic::QUADRAT:
|
||||
case EnemyType::QUADRAT:
|
||||
shape_file = Defaults::Enemies::Quadrat::SHAPE_FILE;
|
||||
velocitat_ = Defaults::Enemies::Quadrat::VELOCITAT;
|
||||
velocity_ = Defaults::Enemies::Quadrat::VELOCITAT;
|
||||
drotacio_min = Defaults::Enemies::Quadrat::DROTACIO_MIN;
|
||||
drotacio_max = Defaults::Enemies::Quadrat::DROTACIO_MAX;
|
||||
tracking_timer_ = 0.0F;
|
||||
break;
|
||||
|
||||
case TipusEnemic::MOLINILLO:
|
||||
case EnemyType::MOLINILLO:
|
||||
shape_file = Defaults::Enemies::Molinillo::SHAPE_FILE;
|
||||
velocitat_ = Defaults::Enemies::Molinillo::VELOCITAT;
|
||||
velocity_ = Defaults::Enemies::Molinillo::VELOCITAT;
|
||||
drotacio_min = Defaults::Enemies::Molinillo::DROTACIO_MIN;
|
||||
drotacio_max = Defaults::Enemies::Molinillo::DROTACIO_MAX;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Fallback segur: usar valors de PENTAGON
|
||||
std::cerr << "[Enemic] Error: tipus desconegut ("
|
||||
<< static_cast<int>(tipus_) << "), utilitzant PENTAGON\n";
|
||||
std::cerr << "[Enemy] Error: tipus desconegut ("
|
||||
<< static_cast<int>(type_) << "), utilitzant PENTAGON\n";
|
||||
shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE;
|
||||
velocitat_ = Defaults::Enemies::Pentagon::VELOCITAT;
|
||||
velocity_ = Defaults::Enemies::Pentagon::VELOCITAT;
|
||||
drotacio_min = Defaults::Enemies::Pentagon::DROTACIO_MIN;
|
||||
drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
// Carregar forma
|
||||
forma_ = Graphics::ShapeLoader::load(shape_file);
|
||||
if (!forma_ || !forma_->es_valida()) {
|
||||
std::cerr << "[Enemic] Error: no s'ha pogut carregar " << shape_file << '\n';
|
||||
shape_ = Graphics::ShapeLoader::load(shape_file);
|
||||
if (!shape_ || !shape_->es_valida()) {
|
||||
std::cerr << "[Enemy] Error: no s'ha pogut carregar " << shape_file << '\n';
|
||||
}
|
||||
|
||||
// [MODIFIED] Posició aleatòria amb comprovació de seguretat
|
||||
@@ -103,8 +103,8 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
|
||||
float candidate_y;
|
||||
|
||||
if (intent_spawn_safe(*ship_pos, candidate_x, candidate_y)) {
|
||||
centre_.x = candidate_x;
|
||||
centre_.y = candidate_y;
|
||||
center_.x = candidate_x;
|
||||
center_.y = candidate_y;
|
||||
found_safe_position = true;
|
||||
break;
|
||||
}
|
||||
@@ -114,18 +114,18 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
|
||||
// Fallback: spawn anywhere (user's preference)
|
||||
int range_x = static_cast<int>(max_x - min_x);
|
||||
int range_y = static_cast<int>(max_y - min_y);
|
||||
centre_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
|
||||
centre_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));
|
||||
center_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
|
||||
center_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));
|
||||
|
||||
std::cout << "[Enemic] Advertència: spawn sense zona segura després de "
|
||||
std::cout << "[Enemy] Advertència: spawn sense zona segura després de "
|
||||
<< Defaults::Enemies::Spawn::MAX_SPAWN_ATTEMPTS << " intents" << '\n';
|
||||
}
|
||||
} else {
|
||||
// [EXISTING] No ship position: spawn anywhere (backward compatibility)
|
||||
int range_x = static_cast<int>(max_x - min_x);
|
||||
int range_y = static_cast<int>(max_y - min_y);
|
||||
centre_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
|
||||
centre_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));
|
||||
center_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x));
|
||||
center_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));
|
||||
}
|
||||
|
||||
// Angle aleatori de moviment
|
||||
@@ -137,7 +137,7 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
|
||||
rotacio_ = 0.0F;
|
||||
|
||||
// Inicialitzar estat d'animació
|
||||
animacio_ = AnimacioEnemic(); // Reset to defaults
|
||||
animacio_ = EnemyAnimation(); // Reset to defaults
|
||||
animacio_.drotacio_base = drotacio_;
|
||||
animacio_.drotacio_objetivo = drotacio_;
|
||||
animacio_.drotacio_t = 1.0F; // Start without interpolating
|
||||
@@ -150,7 +150,7 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
|
||||
esta_ = true;
|
||||
}
|
||||
|
||||
void Enemic::actualitzar(float delta_time) {
|
||||
void Enemy::update(float delta_time) {
|
||||
if (esta_) {
|
||||
// [NEW] Update invulnerability timer and brightness
|
||||
if (timer_invulnerabilitat_ > 0.0F) {
|
||||
@@ -179,26 +179,26 @@ void Enemic::actualitzar(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::dibuixar() const {
|
||||
if (esta_ && forma_) {
|
||||
void Enemy::draw() const {
|
||||
if (esta_ && shape_) {
|
||||
// Calculate animated scale (includes invulnerability LERP)
|
||||
float escala = calcular_escala_actual();
|
||||
|
||||
// brightness_ is already updated in actualitzar()
|
||||
Rendering::render_shape(renderer_, forma_, centre_, rotacio_, escala, 1.0F, brightness_);
|
||||
// brightness_ is already updated in update()
|
||||
Rendering::render_shape(renderer_, shape_, center_, rotacio_, escala, 1.0F, brightness_);
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::mou(float delta_time) {
|
||||
void Enemy::mou(float delta_time) {
|
||||
// Dispatcher: crida el comportament específic segons el tipus
|
||||
switch (tipus_) {
|
||||
case TipusEnemic::PENTAGON:
|
||||
switch (type_) {
|
||||
case EnemyType::PENTAGON:
|
||||
comportament_pentagon(delta_time);
|
||||
break;
|
||||
case TipusEnemic::QUADRAT:
|
||||
case EnemyType::QUADRAT:
|
||||
comportament_quadrat(delta_time);
|
||||
break;
|
||||
case TipusEnemic::MOLINILLO:
|
||||
case EnemyType::MOLINILLO:
|
||||
comportament_molinillo(delta_time);
|
||||
break;
|
||||
default:
|
||||
@@ -208,18 +208,18 @@ void Enemic::mou(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::comportament_pentagon(float delta_time) {
|
||||
void Enemy::comportament_pentagon(float delta_time) {
|
||||
// Pentagon: zigzag esquivador (frequent direction changes)
|
||||
// Similar a comportament original però amb probabilitat més alta
|
||||
|
||||
float velocitat_efectiva = velocitat_ * delta_time;
|
||||
float velocitat_efectiva = velocity_ * delta_time;
|
||||
|
||||
// Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt)
|
||||
float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
|
||||
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
|
||||
|
||||
float new_y = centre_.y + dy;
|
||||
float new_x = centre_.x + dx;
|
||||
float new_y = center_.y + dy;
|
||||
float new_x = center_.x + dx;
|
||||
|
||||
// Obtenir límits segurs
|
||||
float min_x;
|
||||
@@ -234,7 +234,7 @@ void Enemic::comportament_pentagon(float delta_time) {
|
||||
|
||||
// Zigzag: canvi d'angle més freqüent en tocar límits
|
||||
if (new_y >= min_y && new_y <= max_y) {
|
||||
centre_.y = new_y;
|
||||
center_.y = new_y;
|
||||
} else {
|
||||
// Probabilitat més alta de canvi d'angle
|
||||
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) {
|
||||
@@ -245,7 +245,7 @@ void Enemic::comportament_pentagon(float delta_time) {
|
||||
}
|
||||
|
||||
if (new_x >= min_x && new_x <= max_x) {
|
||||
centre_.x = new_x;
|
||||
center_.x = new_x;
|
||||
} else {
|
||||
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) {
|
||||
float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
|
||||
@@ -255,7 +255,7 @@ void Enemic::comportament_pentagon(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::comportament_quadrat(float delta_time) {
|
||||
void Enemy::comportament_quadrat(float delta_time) {
|
||||
// Quadrat: perseguidor (tracks player position)
|
||||
|
||||
// Update tracking timer
|
||||
@@ -267,8 +267,8 @@ void Enemic::comportament_quadrat(float delta_time) {
|
||||
|
||||
if (ship_position_ != nullptr) {
|
||||
// Calculate angle to ship
|
||||
float dx = ship_position_->x - centre_.x;
|
||||
float dy = ship_position_->y - centre_.y;
|
||||
float dx = ship_position_->x - center_.x;
|
||||
float dy = ship_position_->y - center_.y;
|
||||
float target_angle = std::atan2(dy, dx) + (Constants::PI / 2.0F);
|
||||
|
||||
// Interpolate toward target angle
|
||||
@@ -288,12 +288,12 @@ void Enemic::comportament_quadrat(float delta_time) {
|
||||
}
|
||||
|
||||
// Move in current direction
|
||||
float velocitat_efectiva = velocitat_ * delta_time;
|
||||
float velocitat_efectiva = velocity_ * delta_time;
|
||||
float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
|
||||
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
|
||||
|
||||
float new_y = centre_.y + dy;
|
||||
float new_x = centre_.x + dx;
|
||||
float new_y = center_.y + dy;
|
||||
float new_x = center_.x + dx;
|
||||
|
||||
// Obtenir límits segurs
|
||||
float min_x;
|
||||
@@ -308,25 +308,25 @@ void Enemic::comportament_quadrat(float delta_time) {
|
||||
|
||||
// Bounce on walls (simple reflection)
|
||||
if (new_y >= min_y && new_y <= max_y) {
|
||||
centre_.y = new_y;
|
||||
center_.y = new_y;
|
||||
} else {
|
||||
angle_ = -angle_; // Vertical reflection
|
||||
}
|
||||
|
||||
if (new_x >= min_x && new_x <= max_x) {
|
||||
centre_.x = new_x;
|
||||
center_.x = new_x;
|
||||
} else {
|
||||
angle_ = Constants::PI - angle_; // Horizontal reflection
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::comportament_molinillo(float delta_time) {
|
||||
void Enemy::comportament_molinillo(float delta_time) {
|
||||
// Molinillo: agressiu (fast, straight lines, proximity spin-up)
|
||||
|
||||
// Check proximity to ship for spin-up effect
|
||||
if (ship_position_ != nullptr) {
|
||||
float dx = ship_position_->x - centre_.x;
|
||||
float dy = ship_position_->y - centre_.y;
|
||||
float dx = ship_position_->x - center_.x;
|
||||
float dy = ship_position_->y - center_.y;
|
||||
float distance = std::sqrt((dx * dx) + (dy * dy));
|
||||
|
||||
if (distance < Defaults::Enemies::Molinillo::PROXIMITY_DISTANCE) {
|
||||
@@ -340,12 +340,12 @@ void Enemic::comportament_molinillo(float delta_time) {
|
||||
}
|
||||
|
||||
// Fast straight-line movement
|
||||
float velocitat_efectiva = velocitat_ * delta_time;
|
||||
float velocitat_efectiva = velocity_ * delta_time;
|
||||
float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
|
||||
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
|
||||
|
||||
float new_y = centre_.y + dy;
|
||||
float new_x = centre_.x + dx;
|
||||
float new_y = center_.y + dy;
|
||||
float new_x = center_.x + dx;
|
||||
|
||||
// Obtenir límits segurs
|
||||
float min_x;
|
||||
@@ -360,7 +360,7 @@ void Enemic::comportament_molinillo(float delta_time) {
|
||||
|
||||
// Rare angle changes on wall hits
|
||||
if (new_y >= min_y && new_y <= max_y) {
|
||||
centre_.y = new_y;
|
||||
center_.y = new_y;
|
||||
} else {
|
||||
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) {
|
||||
float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
|
||||
@@ -370,7 +370,7 @@ void Enemic::comportament_molinillo(float delta_time) {
|
||||
}
|
||||
|
||||
if (new_x >= min_x && new_x <= max_x) {
|
||||
centre_.x = new_x;
|
||||
center_.x = new_x;
|
||||
} else {
|
||||
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) {
|
||||
float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) *
|
||||
@@ -380,12 +380,12 @@ void Enemic::comportament_molinillo(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::actualitzar_animacio(float delta_time) {
|
||||
void Enemy::actualitzar_animacio(float delta_time) {
|
||||
actualitzar_palpitacio(delta_time);
|
||||
actualitzar_rotacio_accelerada(delta_time);
|
||||
}
|
||||
|
||||
void Enemic::actualitzar_palpitacio(float delta_time) {
|
||||
void Enemy::actualitzar_palpitacio(float delta_time) {
|
||||
if (animacio_.palpitacio_activa) {
|
||||
// Advance phase (2π * frequency * dt)
|
||||
animacio_.palpitacio_fase += 2.0F * Constants::PI * animacio_.palpitacio_frequencia * delta_time;
|
||||
@@ -426,7 +426,7 @@ void Enemic::actualitzar_palpitacio(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
void Enemic::actualitzar_rotacio_accelerada(float delta_time) {
|
||||
void Enemy::actualitzar_rotacio_accelerada(float delta_time) {
|
||||
if (animacio_.drotacio_t < 1.0F) {
|
||||
// Transitioning to new target
|
||||
animacio_.drotacio_t += delta_time / animacio_.drotacio_duracio;
|
||||
@@ -471,7 +471,7 @@ void Enemic::actualitzar_rotacio_accelerada(float delta_time) {
|
||||
}
|
||||
}
|
||||
|
||||
float Enemic::calcular_escala_actual() const {
|
||||
float Enemy::calcular_escala_actual() const {
|
||||
float escala = 1.0F;
|
||||
|
||||
// [NEW] Invulnerability LERP prioritza sobre palpitació
|
||||
@@ -497,33 +497,33 @@ float Enemic::calcular_escala_actual() const {
|
||||
|
||||
// [NEW] Stage system API implementations
|
||||
|
||||
float Enemic::get_base_velocity() const {
|
||||
switch (tipus_) {
|
||||
case TipusEnemic::PENTAGON:
|
||||
float Enemy::get_base_velocity() const {
|
||||
switch (type_) {
|
||||
case EnemyType::PENTAGON:
|
||||
return Defaults::Enemies::Pentagon::VELOCITAT;
|
||||
case TipusEnemic::QUADRAT:
|
||||
case EnemyType::QUADRAT:
|
||||
return Defaults::Enemies::Quadrat::VELOCITAT;
|
||||
case TipusEnemic::MOLINILLO:
|
||||
case EnemyType::MOLINILLO:
|
||||
return Defaults::Enemies::Molinillo::VELOCITAT;
|
||||
default:
|
||||
return Defaults::Enemies::Pentagon::VELOCITAT; // Fallback segur
|
||||
}
|
||||
}
|
||||
|
||||
float Enemic::get_base_rotation() const {
|
||||
float Enemy::get_base_rotation() const {
|
||||
// Return the base rotation speed (drotacio_base if available, otherwise current drotacio_)
|
||||
return animacio_.drotacio_base != 0.0F ? animacio_.drotacio_base : drotacio_;
|
||||
}
|
||||
|
||||
void Enemic::set_tracking_strength(float strength) {
|
||||
void Enemy::set_tracking_strength(float strength) {
|
||||
// Only applies to QUADRAT type
|
||||
if (tipus_ == TipusEnemic::QUADRAT) {
|
||||
if (type_ == EnemyType::QUADRAT) {
|
||||
tracking_strength_ = strength;
|
||||
}
|
||||
}
|
||||
|
||||
// [NEW] Safe spawn helper - checks if position is away from ship
|
||||
bool Enemic::intent_spawn_safe(const Vec2& ship_pos, float& out_x, float& out_y) {
|
||||
bool Enemy::intent_spawn_safe(const Vec2& ship_pos, float& out_x, float& out_y) {
|
||||
// Generate random position within safe bounds
|
||||
float min_x;
|
||||
float max_x;
|
||||
@@ -9,19 +9,19 @@
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entitat.hpp"
|
||||
#include "core/entities/entity.hpp"
|
||||
#include "core/types.hpp"
|
||||
#include "game/constants.hpp"
|
||||
|
||||
// Tipus d'enemic
|
||||
enum class TipusEnemic : uint8_t {
|
||||
enum class EnemyType : uint8_t {
|
||||
PENTAGON = 0, // Pentàgon esquivador (zigzag)
|
||||
QUADRAT = 1, // Quadrat perseguidor (tracks ship)
|
||||
MOLINILLO = 2 // Molinillo agressiu (fast, spinning)
|
||||
};
|
||||
|
||||
// Estat d'animació (palpitació i rotació accelerada)
|
||||
struct AnimacioEnemic {
|
||||
struct EnemyAnimation {
|
||||
// Palpitation (breathing effect)
|
||||
bool palpitacio_activa = false;
|
||||
float palpitacio_fase = 0.0F; // Phase in cycle (0.0-2π)
|
||||
@@ -36,35 +36,35 @@ struct AnimacioEnemic {
|
||||
float drotacio_duracio = 0.0F; // Duration of transition (seconds)
|
||||
};
|
||||
|
||||
class Enemic : public Entities::Entitat {
|
||||
class Enemy : public Entities::Entity {
|
||||
public:
|
||||
Enemic()
|
||||
: Entitat(nullptr) {}
|
||||
Enemic(SDL_Renderer* renderer);
|
||||
Enemy()
|
||||
: Entity(nullptr) {}
|
||||
Enemy(SDL_Renderer* renderer);
|
||||
|
||||
void inicialitzar() override { inicialitzar(TipusEnemic::PENTAGON, nullptr); }
|
||||
void inicialitzar(TipusEnemic tipus, const Vec2* ship_pos = nullptr);
|
||||
void actualitzar(float delta_time) override;
|
||||
void dibuixar() const override;
|
||||
void init() override { init(EnemyType::PENTAGON, nullptr); }
|
||||
void init(EnemyType tipus, const Vec2* ship_pos = nullptr);
|
||||
void update(float delta_time) override;
|
||||
void draw() const override;
|
||||
|
||||
// Override: Interfície d'Entitat
|
||||
[[nodiscard]] bool esta_actiu() const override { return esta_; }
|
||||
// Override: Interfície d'Entity
|
||||
[[nodiscard]] bool isActive() const override { return esta_; }
|
||||
|
||||
// Override: Interfície de col·lisió
|
||||
[[nodiscard]] float get_collision_radius() const override {
|
||||
[[nodiscard]] float getCollisionRadius() const override {
|
||||
return Defaults::Entities::ENEMY_RADIUS;
|
||||
}
|
||||
[[nodiscard]] bool es_collidable() const override {
|
||||
[[nodiscard]] bool isCollidable() const override {
|
||||
return esta_ && timer_invulnerabilitat_ <= 0.0F;
|
||||
}
|
||||
|
||||
// Getters (API pública sense canvis)
|
||||
void destruir() { esta_ = false; }
|
||||
[[nodiscard]] float get_drotacio() const { return drotacio_; }
|
||||
[[nodiscard]] Vec2 get_velocitat_vector() const {
|
||||
[[nodiscard]] Vec2 getVelocityVector() const {
|
||||
return {
|
||||
.x = velocitat_ * std::cos(angle_ - (Constants::PI / 2.0F)),
|
||||
.y = velocitat_ * std::sin(angle_ - (Constants::PI / 2.0F))};
|
||||
.x = velocity_ * std::cos(angle_ - (Constants::PI / 2.0F)),
|
||||
.y = velocity_ * std::sin(angle_ - (Constants::PI / 2.0F))};
|
||||
}
|
||||
|
||||
// Set ship position reference for tracking behavior
|
||||
@@ -73,10 +73,10 @@ class Enemic : public Entities::Entitat {
|
||||
// [NEW] Getters for stage system (base stats)
|
||||
[[nodiscard]] float get_base_velocity() const;
|
||||
[[nodiscard]] float get_base_rotation() const;
|
||||
[[nodiscard]] TipusEnemic get_tipus() const { return tipus_; }
|
||||
[[nodiscard]] EnemyType getType() const { return type_; }
|
||||
|
||||
// [NEW] Setters for difficulty multipliers (stage system)
|
||||
void set_velocity(float vel) { velocitat_ = vel; }
|
||||
void set_velocity(float vel) { velocity_ = vel; }
|
||||
void set_rotation(float rot) {
|
||||
drotacio_ = rot;
|
||||
animacio_.drotacio_base = rot;
|
||||
@@ -84,21 +84,21 @@ class Enemic : public Entities::Entitat {
|
||||
void set_tracking_strength(float strength);
|
||||
|
||||
// [NEW] Invulnerability queries
|
||||
[[nodiscard]] bool es_invulnerable() const { return timer_invulnerabilitat_ > 0.0F; }
|
||||
[[nodiscard]] bool isInvulnerable() const { return timer_invulnerabilitat_ > 0.0F; }
|
||||
[[nodiscard]] float get_temps_invulnerabilitat() const { return timer_invulnerabilitat_; }
|
||||
|
||||
private:
|
||||
// Membres específics d'Enemic (heretats: renderer_, forma_, centre_, angle_, brightness_)
|
||||
float velocitat_;
|
||||
// Membres específics d'Enemy (heretats: renderer_, shape_, center_, angle_, brightness_)
|
||||
float velocity_;
|
||||
float drotacio_; // Delta rotació visual (rad/s)
|
||||
float rotacio_; // Rotació visual acumulada
|
||||
bool esta_;
|
||||
|
||||
// [NEW] Enemy type and configuration
|
||||
TipusEnemic tipus_;
|
||||
EnemyType type_;
|
||||
|
||||
// [NEW] Animation state
|
||||
AnimacioEnemic animacio_;
|
||||
EnemyAnimation animacio_;
|
||||
|
||||
// [NEW] Behavior state (type-specific)
|
||||
float tracking_timer_; // For Quadrat: time since last angle update
|
||||
@@ -1,62 +0,0 @@
|
||||
// nau.hpp - Classe per a la nave del jugador
|
||||
// © 1999 Visente i Sergi (versió Pascal)
|
||||
// © 2025 Port a C++20 amb SDL3
|
||||
|
||||
#pragma once
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entitat.hpp"
|
||||
#include "core/types.hpp"
|
||||
#include "game/constants.hpp"
|
||||
|
||||
class Nau : public Entities::Entitat {
|
||||
public:
|
||||
Nau()
|
||||
: Entitat(nullptr) {}
|
||||
Nau(SDL_Renderer* renderer, const char* shape_file = "ship.shp");
|
||||
|
||||
void inicialitzar() override { inicialitzar(nullptr, false); }
|
||||
void inicialitzar(const Vec2* spawn_point, bool activar_invulnerabilitat = false);
|
||||
void processar_input(float delta_time, uint8_t player_id);
|
||||
void actualitzar(float delta_time) override;
|
||||
void dibuixar() const override;
|
||||
|
||||
// Override: Interfície d'Entitat
|
||||
[[nodiscard]] bool esta_actiu() const override { return !esta_tocada_; }
|
||||
|
||||
// Override: Interfície de col·lisió
|
||||
[[nodiscard]] float get_collision_radius() const override {
|
||||
return Defaults::Entities::SHIP_RADIUS;
|
||||
}
|
||||
[[nodiscard]] bool es_collidable() const override {
|
||||
return !esta_tocada_ && invulnerable_timer_ <= 0.0F;
|
||||
}
|
||||
|
||||
// Getters (API pública sense canvis)
|
||||
[[nodiscard]] bool esta_viva() const { return !esta_tocada_; }
|
||||
[[nodiscard]] bool esta_tocada() const { return esta_tocada_; }
|
||||
[[nodiscard]] bool es_invulnerable() const { return invulnerable_timer_ > 0.0F; }
|
||||
[[nodiscard]] Vec2 get_velocitat_vector() const {
|
||||
return {
|
||||
.x = velocitat_ * std::cos(angle_ - (Constants::PI / 2.0F)),
|
||||
.y = velocitat_ * std::sin(angle_ - (Constants::PI / 2.0F))};
|
||||
}
|
||||
|
||||
// Setters
|
||||
void set_centre(const Vec2& nou_centre) { centre_ = nou_centre; }
|
||||
|
||||
// Col·lisions (Fase 10)
|
||||
void marcar_tocada() { esta_tocada_ = true; }
|
||||
|
||||
private:
|
||||
// Membres específics de Nau (heretats: renderer_, forma_, centre_, angle_, brightness_)
|
||||
float velocitat_; // Velocitat (px/s)
|
||||
bool esta_tocada_;
|
||||
float invulnerable_timer_; // 0.0f = vulnerable, >0.0f = invulnerable
|
||||
|
||||
void aplicar_fisica(float delta_time);
|
||||
};
|
||||
@@ -2,7 +2,7 @@
|
||||
// © 1999 Visente i Sergi (versió Pascal)
|
||||
// © 2025 Port a C++20 amb SDL3
|
||||
|
||||
#include "game/entities/nau.hpp"
|
||||
#include "game/entities/ship.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entitat.hpp"
|
||||
#include "core/entities/entity.hpp"
|
||||
#include "core/graphics/shape_loader.hpp"
|
||||
#include "core/input/input.hpp"
|
||||
#include "core/input/input_types.hpp"
|
||||
@@ -20,23 +20,23 @@
|
||||
#include "core/types.hpp"
|
||||
#include "game/constants.hpp"
|
||||
|
||||
Nau::Nau(SDL_Renderer* renderer, const char* shape_file)
|
||||
: Entitat(renderer),
|
||||
velocitat_(0.0F),
|
||||
esta_tocada_(false),
|
||||
Ship::Ship(SDL_Renderer* renderer, const char* shape_file)
|
||||
: Entity(renderer),
|
||||
velocity_(0.0F),
|
||||
is_hit_(false),
|
||||
invulnerable_timer_(0.0F) {
|
||||
// [NUEVO] Brightness específic per naus
|
||||
brightness_ = Defaults::Brightness::NAU;
|
||||
|
||||
// [NUEVO] Carregar forma compartida des de fitxer
|
||||
forma_ = Graphics::ShapeLoader::load(shape_file);
|
||||
shape_ = Graphics::ShapeLoader::load(shape_file);
|
||||
|
||||
if (!forma_ || !forma_->es_valida()) {
|
||||
std::cerr << "[Nau] Error: no s'ha pogut carregar " << shape_file << '\n';
|
||||
if (!shape_ || !shape_->es_valida()) {
|
||||
std::cerr << "[Ship] Error: no s'ha pogut carregar " << shape_file << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void Nau::inicialitzar(const Vec2* spawn_point, bool activar_invulnerabilitat) {
|
||||
void Ship::init(const Vec2* spawn_point, bool activar_invulnerabilitat) {
|
||||
// Inicialització de la nau (triangle)
|
||||
// Basat en el codi Pascal original: lines 380-384
|
||||
// Copiat de joc_asteroides.cpp línies 30-44
|
||||
@@ -46,20 +46,20 @@ void Nau::inicialitzar(const Vec2* spawn_point, bool activar_invulnerabilitat) {
|
||||
|
||||
// Use custom spawn point if provided, otherwise use center
|
||||
if (spawn_point != nullptr) {
|
||||
centre_.x = spawn_point->x;
|
||||
centre_.y = spawn_point->y;
|
||||
center_.x = spawn_point->x;
|
||||
center_.y = spawn_point->y;
|
||||
} else {
|
||||
// Default: center of play area
|
||||
float centre_x;
|
||||
float centre_y;
|
||||
Constants::obtenir_centre_zona(centre_x, centre_y);
|
||||
centre_.x = static_cast<int>(centre_x);
|
||||
centre_.y = static_cast<int>(centre_y);
|
||||
center_.x = static_cast<int>(centre_x);
|
||||
center_.y = static_cast<int>(centre_y);
|
||||
}
|
||||
|
||||
// Estat inicial
|
||||
angle_ = 0.0F;
|
||||
velocitat_ = 0.0F;
|
||||
velocity_ = 0.0F;
|
||||
|
||||
// Activar invulnerabilidad solo si es respawn
|
||||
if (activar_invulnerabilitat) {
|
||||
@@ -68,14 +68,14 @@ void Nau::inicialitzar(const Vec2* spawn_point, bool activar_invulnerabilitat) {
|
||||
invulnerable_timer_ = 0.0F;
|
||||
}
|
||||
|
||||
esta_tocada_ = false;
|
||||
is_hit_ = false;
|
||||
}
|
||||
|
||||
void Nau::processar_input(float delta_time, uint8_t player_id) {
|
||||
void Ship::processInput(float delta_time, uint8_t player_id) {
|
||||
// Processar input continu (com teclapuls() del Pascal original)
|
||||
// Basat en joc_asteroides.cpp línies 66-85
|
||||
// Només processa input si la nau està viva
|
||||
if (esta_tocada_) {
|
||||
if (is_hit_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,9 +93,9 @@ void Nau::processar_input(float delta_time, uint8_t player_id) {
|
||||
}
|
||||
|
||||
if (input->checkActionPlayer1(InputAction::THRUST, Input::ALLOW_REPEAT)) {
|
||||
if (velocitat_ < Defaults::Physics::MAX_VELOCITY) {
|
||||
velocitat_ += Defaults::Physics::ACCELERATION * delta_time;
|
||||
velocitat_ = std::min(velocitat_, Defaults::Physics::MAX_VELOCITY);
|
||||
if (velocity_ < Defaults::Physics::MAX_VELOCITY) {
|
||||
velocity_ += Defaults::Physics::ACCELERATION * delta_time;
|
||||
velocity_ = std::min(velocity_, Defaults::Physics::MAX_VELOCITY);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -109,17 +109,17 @@ void Nau::processar_input(float delta_time, uint8_t player_id) {
|
||||
}
|
||||
|
||||
if (input->checkActionPlayer2(InputAction::THRUST, Input::ALLOW_REPEAT)) {
|
||||
if (velocitat_ < Defaults::Physics::MAX_VELOCITY) {
|
||||
velocitat_ += Defaults::Physics::ACCELERATION * delta_time;
|
||||
velocitat_ = std::min(velocitat_, Defaults::Physics::MAX_VELOCITY);
|
||||
if (velocity_ < Defaults::Physics::MAX_VELOCITY) {
|
||||
velocity_ += Defaults::Physics::ACCELERATION * delta_time;
|
||||
velocity_ = std::min(velocity_, Defaults::Physics::MAX_VELOCITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Nau::actualitzar(float delta_time) {
|
||||
// Només actualitzar si la nau està viva
|
||||
if (esta_tocada_) {
|
||||
void Ship::update(float delta_time) {
|
||||
// Només update si la nau està viva
|
||||
if (is_hit_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -130,17 +130,17 @@ void Nau::actualitzar(float delta_time) {
|
||||
}
|
||||
|
||||
// Aplicar física (moviment + fricció)
|
||||
aplicar_fisica(delta_time);
|
||||
applyPhysics(delta_time);
|
||||
}
|
||||
|
||||
void Nau::dibuixar() const {
|
||||
// Només dibuixar si la nau està viva
|
||||
if (esta_tocada_) {
|
||||
void Ship::draw() const {
|
||||
// Només draw si la nau està viva
|
||||
if (is_hit_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Si invulnerable, parpadear (toggle on/off)
|
||||
if (es_invulnerable()) {
|
||||
if (isInvulnerable()) {
|
||||
// Calcular ciclo de parpadeo
|
||||
float blink_cycle = Defaults::Ship::BLINK_VISIBLE_TIME +
|
||||
Defaults::Ship::BLINK_INVISIBLE_TIME;
|
||||
@@ -152,7 +152,7 @@ void Nau::dibuixar() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (!forma_) {
|
||||
if (!shape_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -164,25 +164,25 @@ void Nau::dibuixar() const {
|
||||
// [NUEVO] Convertir suma de velocitat_visual a escala multiplicativa
|
||||
// Radio base del ship = 12 px
|
||||
// velocitat_visual = 0-6 → r = 12-18 → escala = 1.0-1.5
|
||||
float velocitat_visual = velocitat_ / 33.33F;
|
||||
float velocitat_visual = velocity_ / 33.33F;
|
||||
float escala = 1.0F + (velocitat_visual / 12.0F);
|
||||
|
||||
Rendering::render_shape(renderer_, forma_, centre_, angle_, escala, 1.0F, brightness_);
|
||||
Rendering::render_shape(renderer_, shape_, center_, angle_, escala, 1.0F, brightness_);
|
||||
}
|
||||
|
||||
void Nau::aplicar_fisica(float delta_time) {
|
||||
void Ship::applyPhysics(float delta_time) {
|
||||
// Aplicar física de moviment
|
||||
// Basat en joc_asteroides.cpp línies 87-113
|
||||
|
||||
// Calcular nova posició basada en velocitat i angle
|
||||
// S'usa (angle - PI/2) perquè angle=0 apunta cap amunt, no cap a la dreta
|
||||
// velocitat_ està en px/s, així que multipliquem per delta_time
|
||||
// velocity_ està en px/s, així que multipliquem per delta_time
|
||||
float dy =
|
||||
((velocitat_ * delta_time) * std::sin(angle_ - (Constants::PI / 2.0F))) +
|
||||
centre_.y;
|
||||
((velocity_ * delta_time) * std::sin(angle_ - (Constants::PI / 2.0F))) +
|
||||
center_.y;
|
||||
float dx =
|
||||
((velocitat_ * delta_time) * std::cos(angle_ - (Constants::PI / 2.0F))) +
|
||||
centre_.x;
|
||||
((velocity_ * delta_time) * std::cos(angle_ - (Constants::PI / 2.0F))) +
|
||||
center_.x;
|
||||
|
||||
// Boundary checking amb radi de la nau
|
||||
// CORRECCIÓ: Usar límits segurs i inequalitats inclusives
|
||||
@@ -198,16 +198,16 @@ void Nau::aplicar_fisica(float delta_time) {
|
||||
|
||||
// Inequalitats inclusives (>= i <=)
|
||||
if (dy >= min_y && dy <= max_y) {
|
||||
centre_.y = dy;
|
||||
center_.y = dy;
|
||||
}
|
||||
|
||||
if (dx >= min_x && dx <= max_x) {
|
||||
centre_.x = dx;
|
||||
center_.x = dx;
|
||||
}
|
||||
|
||||
// Fricció - desacceleració gradual (time-based)
|
||||
if (velocitat_ > 0.1F) {
|
||||
velocitat_ -= Defaults::Physics::FRICTION * delta_time;
|
||||
velocitat_ = std::max(velocitat_, 0.0F);
|
||||
if (velocity_ > 0.1F) {
|
||||
velocity_ -= Defaults::Physics::FRICTION * delta_time;
|
||||
velocity_ = std::max(velocity_, 0.0F);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// nau.hpp - Classe per a la nave del jugador
|
||||
// © 1999 Visente i Sergi (versió Pascal)
|
||||
// © 2025 Port a C++20 amb SDL3
|
||||
|
||||
#pragma once
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#include "core/defaults.hpp"
|
||||
#include "core/entities/entity.hpp"
|
||||
#include "core/types.hpp"
|
||||
#include "game/constants.hpp"
|
||||
|
||||
class Ship : public Entities::Entity {
|
||||
public:
|
||||
Ship()
|
||||
: Entity(nullptr) {}
|
||||
Ship(SDL_Renderer* renderer, const char* shape_file = "ship.shp");
|
||||
|
||||
void init() override { init(nullptr, false); }
|
||||
void init(const Vec2* spawn_point, bool activar_invulnerabilitat = false);
|
||||
void processInput(float delta_time, uint8_t player_id);
|
||||
void update(float delta_time) override;
|
||||
void draw() const override;
|
||||
|
||||
// Override: Interfície d'Entity
|
||||
[[nodiscard]] bool isActive() const override { return !is_hit_; }
|
||||
|
||||
// Override: Interfície de col·lisió
|
||||
[[nodiscard]] float getCollisionRadius() const override {
|
||||
return Defaults::Entities::SHIP_RADIUS;
|
||||
}
|
||||
[[nodiscard]] bool isCollidable() const override {
|
||||
return !is_hit_ && invulnerable_timer_ <= 0.0F;
|
||||
}
|
||||
|
||||
// Getters (API pública sense canvis)
|
||||
[[nodiscard]] bool isAlive() const { return !is_hit_; }
|
||||
[[nodiscard]] bool isHit() const { return is_hit_; }
|
||||
[[nodiscard]] bool isInvulnerable() const { return invulnerable_timer_ > 0.0F; }
|
||||
[[nodiscard]] Vec2 getVelocityVector() const {
|
||||
return {
|
||||
.x = velocity_ * std::cos(angle_ - (Constants::PI / 2.0F)),
|
||||
.y = velocity_ * std::sin(angle_ - (Constants::PI / 2.0F))};
|
||||
}
|
||||
|
||||
// Setters
|
||||
void setCenter(const Vec2& nou_centre) { center_ = nou_centre; }
|
||||
|
||||
// Col·lisions (Fase 10)
|
||||
void markHit() { is_hit_ = true; }
|
||||
|
||||
private:
|
||||
// Membres específics de Ship (heretats: renderer_, shape_, center_, angle_, brightness_)
|
||||
float velocity_; // Velocitat (px/s)
|
||||
bool is_hit_;
|
||||
float invulnerable_timer_; // 0.0f = vulnerable, >0.0f = invulnerable
|
||||
|
||||
void applyPhysics(float delta_time);
|
||||
};
|
||||
Reference in New Issue
Block a user