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:
2026-05-19 11:37:18 +02:00
parent cd38101f99
commit ae5cc1cfb4
32 changed files with 505 additions and 505 deletions
-49
View File
@@ -1,49 +0,0 @@
// entitat.hpp - Classe base abstracta per a totes les entitats del joc
// © 2025 Orni Attack - Arquitectura d'entitats
#pragma once
#include <SDL3/SDL.h>
#include <memory>
#include "core/graphics/shape.hpp"
#include "core/types.hpp"
namespace Entities {
class Entitat {
public:
virtual ~Entitat() = default;
// Interfície principal (virtual pur)
virtual void inicialitzar() = 0;
virtual void actualitzar(float delta_time) = 0;
virtual void dibuixar() const = 0;
[[nodiscard]] virtual bool esta_actiu() const = 0;
// Interfície de col·lisió (override opcional)
[[nodiscard]] virtual float get_collision_radius() const { return 0.0F; }
[[nodiscard]] virtual bool es_collidable() const { return false; }
// Getters comuns (inline, sense overhead)
[[nodiscard]] const Vec2& get_centre() const { return centre_; }
[[nodiscard]] float get_angle() const { return angle_; }
[[nodiscard]] float get_brightness() const { return brightness_; }
[[nodiscard]] const std::shared_ptr<Graphics::Shape>& get_forma() const { return forma_; }
protected:
// Estat comú (accés directe, sense overhead)
SDL_Renderer* renderer_;
std::shared_ptr<Graphics::Shape> forma_;
Vec2 centre_;
float angle_{0.0F};
float brightness_{1.0F};
// Constructor protegit (classe abstracta)
Entitat(SDL_Renderer* renderer = nullptr)
: renderer_(renderer),
centre_({.x = 0.0F, .y = 0.0F}) {}
};
} // namespace Entities
+49
View File
@@ -0,0 +1,49 @@
// entitat.hpp - Classe base abstracta per a totes les entitats del joc
// © 2025 Orni Attack - Arquitectura d'entitats
#pragma once
#include <SDL3/SDL.h>
#include <memory>
#include "core/graphics/shape.hpp"
#include "core/types.hpp"
namespace Entities {
class Entity {
public:
virtual ~Entity() = default;
// Interfície principal (virtual pur)
virtual void init() = 0;
virtual void update(float delta_time) = 0;
virtual void draw() const = 0;
[[nodiscard]] virtual bool isActive() const = 0;
// Interfície de col·lisió (override opcional)
[[nodiscard]] virtual float getCollisionRadius() const { return 0.0F; }
[[nodiscard]] virtual bool isCollidable() const { return false; }
// Getters comuns (inline, sense overhead)
[[nodiscard]] const Vec2& getCenter() const { return center_; }
[[nodiscard]] float getAngle() const { return angle_; }
[[nodiscard]] float getBrightness() const { return brightness_; }
[[nodiscard]] const std::shared_ptr<Graphics::Shape>& getShape() const { return shape_; }
protected:
// Estat comú (accés directe, sense overhead)
SDL_Renderer* renderer_;
std::shared_ptr<Graphics::Shape> shape_;
Vec2 center_;
float angle_{0.0F};
float brightness_{1.0F};
// Constructor protegit (classe abstracta)
Entity(SDL_Renderer* renderer = nullptr)
: renderer_(renderer),
center_({.x = 0.0F, .y = 0.0F}) {}
};
} // namespace Entities
+4 -4
View File
@@ -11,7 +11,7 @@
namespace Graphics { namespace Graphics {
Shape::Shape(const std::string& filepath) Shape::Shape(const std::string& filepath)
: centre_({.x = 0.0F, .y = 0.0F}), : center_({.x = 0.0F, .y = 0.0F}),
escala_defecte_(1.0F), escala_defecte_(1.0F),
nom_("unnamed") { nom_("unnamed") {
carregar(filepath); carregar(filepath);
@@ -124,11 +124,11 @@ void Shape::parse_center(const std::string& value) {
size_t comma = val.find(','); size_t comma = val.find(',');
if (comma != std::string::npos) { if (comma != std::string::npos) {
try { try {
centre_.x = std::stof(trim(val.substr(0, comma))); center_.x = std::stof(trim(val.substr(0, comma)));
centre_.y = std::stof(trim(val.substr(comma + 1))); center_.y = std::stof(trim(val.substr(comma + 1)));
} catch (...) { } catch (...) {
std::cerr << "[Shape] Warning: centre invàlid, usant (0,0)" << '\n'; std::cerr << "[Shape] Warning: centre invàlid, usant (0,0)" << '\n';
centre_ = {.x = 0.0F, .y = 0.0F}; center_ = {.x = 0.0F, .y = 0.0F};
} }
} }
} }
+2 -2
View File
@@ -39,7 +39,7 @@ class Shape {
[[nodiscard]] const std::vector<ShapePrimitive>& get_primitives() const { [[nodiscard]] const std::vector<ShapePrimitive>& get_primitives() const {
return primitives_; return primitives_;
} }
[[nodiscard]] const Vec2& get_centre() const { return centre_; } [[nodiscard]] const Vec2& getCenter() const { return center_; }
[[nodiscard]] float get_escala_defecte() const { return escala_defecte_; } [[nodiscard]] float get_escala_defecte() const { return escala_defecte_; }
[[nodiscard]] bool es_valida() const { return !primitives_.empty(); } [[nodiscard]] bool es_valida() const { return !primitives_.empty(); }
@@ -49,7 +49,7 @@ class Shape {
private: private:
std::vector<ShapePrimitive> primitives_; std::vector<ShapePrimitive> primitives_;
Vec2 centre_; // Centre/origen de la forma Vec2 center_; // Centre/origen de la forma
float escala_defecte_; // Escala per defecte (normalment 1.0) float escala_defecte_; // Escala per defecte (normalment 1.0)
std::string nom_; // Nom de la forma (per depuració) std::string nom_; // Nom de la forma (per depuració)
+2 -2
View File
@@ -113,7 +113,7 @@ float Starfield::calcular_brightness(const Estrella& estrella) const {
} }
// Actualitzar posicions de les estrelles // Actualitzar posicions de les estrelles
void Starfield::actualitzar(float delta_time) { void Starfield::update(float delta_time) {
for (auto& estrella : estrelles_) { for (auto& estrella : estrelles_) {
// Obtenir configuració de la capa // Obtenir configuració de la capa
const CapaConfig& capa = capes_[estrella.capa]; const CapaConfig& capa = capes_[estrella.capa];
@@ -145,7 +145,7 @@ void Starfield::set_brightness(float multiplier) {
} }
// Dibuixar totes les estrelles // Dibuixar totes les estrelles
void Starfield::dibuixar() { void Starfield::draw() {
if (!shape_estrella_->es_valida()) { if (!shape_estrella_->es_valida()) {
return; return;
} }
+2 -2
View File
@@ -35,10 +35,10 @@ class Starfield {
int densitat = 150); int densitat = 150);
// Actualitzar posicions de les estrelles // Actualitzar posicions de les estrelles
void actualitzar(float delta_time); void update(float delta_time);
// Dibuixar totes les estrelles // Dibuixar totes les estrelles
void dibuixar(); void draw();
// Setters per ajustar paràmetres en temps real // Setters per ajustar paràmetres en temps real
void set_punt_fuga(const Vec2& punt) { punt_fuga_ = punt; } void set_punt_fuga(const Vec2& punt) { punt_fuga_ = punt; }
+6 -6
View File
@@ -3,25 +3,25 @@
#pragma once #pragma once
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/types.hpp" #include "core/types.hpp"
namespace Physics { namespace Physics {
// Comprovació genèrica de col·lisió entre dues entitats // Comprovació genèrica de col·lisió entre dues entitats
inline bool check_collision(const Entities::Entitat& a, const Entities::Entitat& b, float amplifier = 1.0F) { inline bool check_collision(const Entities::Entity& a, const Entities::Entity& b, float amplifier = 1.0F) {
// Comprovar si ambdós són col·lisionables // Comprovar si ambdós són col·lisionables
if (!a.es_collidable() || !b.es_collidable()) { if (!a.isCollidable() || !b.isCollidable()) {
return false; return false;
} }
// Calcular radi combinat (amb amplificador per hitbox generós) // Calcular radi combinat (amb amplificador per hitbox generós)
float suma_radis = (a.get_collision_radius() + b.get_collision_radius()) * amplifier; float suma_radis = (a.getCollisionRadius() + b.getCollisionRadius()) * amplifier;
float suma_radis_sq = suma_radis * suma_radis; float suma_radis_sq = suma_radis * suma_radis;
// Comprovació distància al quadrat (sense sqrt) // Comprovació distància al quadrat (sense sqrt)
const Vec2& pos_a = a.get_centre(); const Vec2& pos_a = a.getCenter();
const Vec2& pos_b = b.get_centre(); const Vec2& pos_b = b.getCenter();
float dx = pos_a.x - pos_b.x; float dx = pos_a.x - pos_b.x;
float dy = pos_a.y - pos_b.y; float dy = pos_a.y - pos_b.y;
float dist_sq = (dx * dx) + (dy * dy); float dist_sq = (dx * dx) + (dy * dy);
+2 -2
View File
@@ -85,13 +85,13 @@ void render_shape(SDL_Renderer* renderer,
return; return;
} }
// Si progress < 1.0, no dibuixar (tot o res) // Si progress < 1.0, no draw (tot o res)
if (progress < 1.0F) { if (progress < 1.0F) {
return; return;
} }
// Obtenir el centre de la forma per a transformacions // Obtenir el centre de la forma per a transformacions
const Vec2& shape_centre = shape->get_centre(); const Vec2& shape_centre = shape->getCenter();
// Iterar sobre totes les primitives // Iterar sobre totes les primitives
for (const auto& primitive : shape->get_primitives()) { for (const auto& primitive : shape->get_primitives()) {
+1 -1
View File
@@ -34,7 +34,7 @@ struct Rotation3D {
// Renderitzar forma amb transformacions // Renderitzar forma amb transformacions
// - renderer: SDL renderer // - renderer: SDL renderer
// - shape: forma vectorial a dibuixar // - shape: forma vectorial a draw
// - posicio: posició del centre en coordenades mundials // - posicio: posició del centre en coordenades mundials
// - angle: rotació en radians (0 = amunt, sentit horari) // - angle: rotació en radians (0 = amunt, sentit horari)
// - escala: factor d'escala (1.0 = mida original) // - escala: factor d'escala (1.0 = mida original)
+3 -3
View File
@@ -62,7 +62,7 @@ void DebrisManager::explotar(const std::shared_ptr<Graphics::Shape>& shape,
Audio::get()->playSound(sound, Audio::Group::GAME); Audio::get()->playSound(sound, Audio::Group::GAME);
// Obtenir centre de la forma per a transformacions // Obtenir centre de la forma per a transformacions
const Vec2& shape_centre = shape->get_centre(); const Vec2& shape_centre = shape->getCenter();
// Iterar sobre totes les primitives de la forma // Iterar sobre totes les primitives de la forma
for (const auto& primitive : shape->get_primitives()) { for (const auto& primitive : shape->get_primitives()) {
@@ -204,7 +204,7 @@ void DebrisManager::explotar(const std::shared_ptr<Graphics::Shape>& shape,
} }
} }
void DebrisManager::actualitzar(float delta_time) { void DebrisManager::update(float delta_time) {
for (auto& debris : debris_pool_) { for (auto& debris : debris_pool_) {
if (!debris.actiu) { if (!debris.actiu) {
continue; continue;
@@ -302,7 +302,7 @@ void DebrisManager::actualitzar(float delta_time) {
} }
} }
void DebrisManager::dibuixar() const { void DebrisManager::draw() const {
for (const auto& debris : debris_pool_) { for (const auto& debris : debris_pool_) {
if (!debris.actiu) { if (!debris.actiu) {
continue; continue;
+2 -2
View File
@@ -42,10 +42,10 @@ class DebrisManager {
const std::string& sound = Defaults::Sound::EXPLOSION); const std::string& sound = Defaults::Sound::EXPLOSION);
// Actualitzar tots els fragments actius // Actualitzar tots els fragments actius
void actualitzar(float delta_time); void update(float delta_time);
// Dibuixar tots els fragments actius // Dibuixar tots els fragments actius
void dibuixar() const; void draw() const;
// Reiniciar tots els fragments (neteja) // Reiniciar tots els fragments (neteja)
void reiniciar(); void reiniciar();
@@ -33,7 +33,7 @@ void GestorPuntuacioFlotant::crear(int punts, const Vec2& posicio) {
pf->actiu = true; pf->actiu = true;
} }
void GestorPuntuacioFlotant::actualitzar(float delta_time) { void GestorPuntuacioFlotant::update(float delta_time) {
for (auto& pf : pool_) { for (auto& pf : pool_) {
if (!pf.actiu) { if (!pf.actiu) {
continue; continue;
@@ -57,7 +57,7 @@ void GestorPuntuacioFlotant::actualitzar(float delta_time) {
} }
} }
void GestorPuntuacioFlotant::dibuixar() { void GestorPuntuacioFlotant::draw() {
for (const auto& pf : pool_) { for (const auto& pf : pool_) {
if (!pf.actiu) { if (!pf.actiu) {
continue; continue;
@@ -26,10 +26,10 @@ class GestorPuntuacioFlotant {
void crear(int punts, const Vec2& posicio); void crear(int punts, const Vec2& posicio);
// Actualitzar tots els números actius // Actualitzar tots els números actius
void actualitzar(float delta_time); void update(float delta_time);
// Dibuixar tots els números actius // Dibuixar tots els números actius
void dibuixar(); void draw();
// Reiniciar tots (neteja) // Reiniciar tots (neteja)
void reiniciar(); void reiniciar();
@@ -2,7 +2,7 @@
// © 1999 Visente i Sergi (versió Pascal) // © 1999 Visente i Sergi (versió Pascal)
// © 2025 Port a C++20 amb SDL3 // © 2025 Port a C++20 amb SDL3
#include "game/entities/bala.hpp" #include "game/entities/bullet.hpp"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@@ -11,15 +11,15 @@
#include "core/audio/audio.hpp" #include "core/audio/audio.hpp"
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/graphics/shape_loader.hpp" #include "core/graphics/shape_loader.hpp"
#include "core/rendering/shape_renderer.hpp" #include "core/rendering/shape_renderer.hpp"
#include "core/types.hpp" #include "core/types.hpp"
#include "game/constants.hpp" #include "game/constants.hpp"
Bala::Bala(SDL_Renderer* renderer) Bullet::Bullet(SDL_Renderer* renderer)
: Entitat(renderer), : Entity(renderer),
velocitat_(0.0F), velocity_(0.0F),
esta_(false), esta_(false),
owner_id_(0), owner_id_(0),
grace_timer_(0.0F) { grace_timer_(0.0F) {
@@ -27,23 +27,23 @@ Bala::Bala(SDL_Renderer* renderer)
brightness_ = Defaults::Brightness::BALA; brightness_ = Defaults::Brightness::BALA;
// [NUEVO] Carregar forma compartida des de fitxer // [NUEVO] Carregar forma compartida des de fitxer
forma_ = Graphics::ShapeLoader::load("bullet.shp"); shape_ = Graphics::ShapeLoader::load("bullet.shp");
if (!forma_ || !forma_->es_valida()) { if (!shape_ || !shape_->es_valida()) {
std::cerr << "[Bala] Error: no s'ha pogut carregar bullet.shp" << '\n'; std::cerr << "[Bullet] Error: no s'ha pogut carregar bullet.shp" << '\n';
} }
} }
void Bala::inicialitzar() { void Bullet::init() {
// Inicialment inactiva // Inicialment inactiva
esta_ = false; esta_ = false;
centre_ = {.x = 0.0F, .y = 0.0F}; center_ = {.x = 0.0F, .y = 0.0F};
angle_ = 0.0F; angle_ = 0.0F;
velocitat_ = 0.0F; velocity_ = 0.0F;
grace_timer_ = 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 // Activar bala i posicionar-la a la nau
// Basat en joc_asteroides.cpp línies 188-200 // 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; esta_ = true;
// Posició inicial = centre de la nau // Posició inicial = centre de la nau
centre_.x = posicio.x; center_.x = posicio.x;
centre_.y = posicio.y; center_.y = posicio.y;
// Angle = angle de la nau (dispara en la direcció que apunta) // Angle = angle de la nau (dispara en la direcció que apunta)
angle_ = angle; 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) // Velocitat alta (el joc Pascal original usava 7 px/frame)
// 7 px/frame × 20 FPS = 140 px/s // 7 px/frame × 20 FPS = 140 px/s
velocitat_ = 140.0F; velocity_ = 140.0F;
// Activar grace period (prevents instant self-collision) // Activar grace period (prevents instant self-collision)
grace_timer_ = Defaults::Game::BULLET_GRACE_PERIOD; 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); Audio::get()->playSound(Defaults::Sound::LASER, Audio::Group::GAME);
} }
void Bala::actualitzar(float delta_time) { void Bullet::update(float delta_time) {
if (esta_) { if (esta_) {
// Decrementar grace timer // Decrementar grace timer
if (grace_timer_ > 0.0F) { if (grace_timer_ > 0.0F) {
@@ -83,30 +83,30 @@ void Bala::actualitzar(float delta_time) {
} }
} }
void Bala::dibuixar() const { void Bullet::draw() const {
if (esta_ && forma_) { if (esta_ && shape_) {
// [NUEVO] Usar render_shape en lloc de rota_pol // [NUEVO] Usar render_shape en lloc de rota_pol
// Les bales roten segons l'angle de trajectòria // 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 // Moviment rectilini de la bala
// Basat en el codi Pascal original: procedure mou_bales // Basat en el codi Pascal original: procedure mou_bales
// Copiat EXACTAMENT de joc_asteroides.cpp línies 396-419 // Copiat EXACTAMENT de joc_asteroides.cpp línies 396-419
// Calcular nova posició (moviment polar time-based) // Calcular nova posició (moviment polar time-based)
// velocitat ja està en px/s (140 px/s), només cal multiplicar per delta_time // 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) // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt)
float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F)); float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F)); float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
// Acumulació directa amb precisió subpíxel // Acumulació directa amb precisió subpíxel
centre_.y += dy; center_.y += dy;
centre_.x += dx; center_.x += dx;
// Desactivar si surt de la zona de joc (no rebota com els ORNIs) // Desactivar si surt de la zona de joc (no rebota com els ORNIs)
// CORRECCIÓ: Usar límits segurs amb radi de la bala // CORRECCIÓ: Usar límits segurs amb radi de la bala
@@ -120,8 +120,8 @@ void Bala::mou(float delta_time) {
min_y, min_y,
max_y); max_y);
if (centre_.x < min_x || centre_.x > max_x || if (center_.x < min_x || center_.x > max_x ||
centre_.y < min_y || centre_.y > max_y) { center_.y < min_y || center_.y > max_y) {
esta_ = false; esta_ = false;
} }
} }
@@ -8,28 +8,28 @@
#include <cstdint> #include <cstdint>
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/types.hpp" #include "core/types.hpp"
class Bala : public Entities::Entitat { class Bullet : public Entities::Entity {
public: public:
Bala() Bullet()
: Entitat(nullptr) {} : Entity(nullptr) {}
Bala(SDL_Renderer* renderer); Bullet(SDL_Renderer* renderer);
void inicialitzar() override; void init() override;
void disparar(const Vec2& posicio, float angle, uint8_t owner_id); void disparar(const Vec2& posicio, float angle, uint8_t owner_id);
void actualitzar(float delta_time) override; void update(float delta_time) override;
void dibuixar() const override; void draw() const override;
// Override: Interfície d'Entitat // Override: Interfície d'Entity
[[nodiscard]] bool esta_actiu() const override { return esta_; } [[nodiscard]] bool isActive() const override { return esta_; }
// Override: Interfície de col·lisió // Override: Interfície de col·lisió
[[nodiscard]] float get_collision_radius() const override { [[nodiscard]] float getCollisionRadius() const override {
return Defaults::Entities::BULLET_RADIUS; return Defaults::Entities::BULLET_RADIUS;
} }
[[nodiscard]] bool es_collidable() const override { [[nodiscard]] bool isCollidable() const override {
return esta_ && grace_timer_ <= 0.0F; return esta_ && grace_timer_ <= 0.0F;
} }
@@ -40,8 +40,8 @@ class Bala : public Entities::Entitat {
void desactivar() { esta_ = false; } void desactivar() { esta_ = false; }
private: private:
// Membres específics de Bala (heretats: renderer_, forma_, centre_, angle_, brightness_) // Membres específics de Bullet (heretats: renderer_, shape_, center_, angle_, brightness_)
float velocitat_; float velocity_;
bool esta_; bool esta_;
uint8_t owner_id_; // 0=P1, 1=P2 uint8_t owner_id_; // 0=P1, 1=P2
float grace_timer_; // Grace period timer (0.0 = vulnerable) float grace_timer_; // Grace period timer (0.0 = vulnerable)
@@ -2,7 +2,7 @@
// © 1999 Visente i Sergi (versió Pascal) // © 1999 Visente i Sergi (versió Pascal)
// © 2025 Port a C++20 amb SDL3 // © 2025 Port a C++20 amb SDL3
#include "game/entities/enemic.hpp" #include "game/entities/enemy.hpp"
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
@@ -10,19 +10,19 @@
#include <iostream> #include <iostream>
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/graphics/shape_loader.hpp" #include "core/graphics/shape_loader.hpp"
#include "core/rendering/shape_renderer.hpp" #include "core/rendering/shape_renderer.hpp"
#include "core/types.hpp" #include "core/types.hpp"
#include "game/constants.hpp" #include "game/constants.hpp"
Enemic::Enemic(SDL_Renderer* renderer) Enemy::Enemy(SDL_Renderer* renderer)
: Entitat(renderer), : Entity(renderer),
velocitat_(0.0F), velocity_(0.0F),
drotacio_(0.0F), drotacio_(0.0F),
rotacio_(0.0F), rotacio_(0.0F),
esta_(false), esta_(false),
tipus_(TipusEnemic::PENTAGON), type_(EnemyType::PENTAGON),
tracking_timer_(0.0F), tracking_timer_(0.0F),
ship_position_(nullptr), ship_position_(nullptr),
tracking_strength_(0.5F), // Default tracking strength tracking_strength_(0.5F), // Default tracking strength
@@ -30,57 +30,57 @@ Enemic::Enemic(SDL_Renderer* renderer)
// [NUEVO] Brightness específic per enemics // [NUEVO] Brightness específic per enemics
brightness_ = Defaults::Brightness::ENEMIC; 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 // 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 // Guardar tipus
tipus_ = tipus; type_ = tipus;
// Carregar forma segons el tipus // Carregar forma segons el tipus
const char* shape_file; const char* shape_file;
float drotacio_min; float drotacio_min;
float drotacio_max; float drotacio_max;
switch (tipus_) { switch (type_) {
case TipusEnemic::PENTAGON: case EnemyType::PENTAGON:
shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE; 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_min = Defaults::Enemies::Pentagon::DROTACIO_MIN;
drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX; drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX;
break; break;
case TipusEnemic::QUADRAT: case EnemyType::QUADRAT:
shape_file = Defaults::Enemies::Quadrat::SHAPE_FILE; 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_min = Defaults::Enemies::Quadrat::DROTACIO_MIN;
drotacio_max = Defaults::Enemies::Quadrat::DROTACIO_MAX; drotacio_max = Defaults::Enemies::Quadrat::DROTACIO_MAX;
tracking_timer_ = 0.0F; tracking_timer_ = 0.0F;
break; break;
case TipusEnemic::MOLINILLO: case EnemyType::MOLINILLO:
shape_file = Defaults::Enemies::Molinillo::SHAPE_FILE; 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_min = Defaults::Enemies::Molinillo::DROTACIO_MIN;
drotacio_max = Defaults::Enemies::Molinillo::DROTACIO_MAX; drotacio_max = Defaults::Enemies::Molinillo::DROTACIO_MAX;
break; break;
default: default:
// Fallback segur: usar valors de PENTAGON // Fallback segur: usar valors de PENTAGON
std::cerr << "[Enemic] Error: tipus desconegut (" std::cerr << "[Enemy] Error: tipus desconegut ("
<< static_cast<int>(tipus_) << "), utilitzant PENTAGON\n"; << static_cast<int>(type_) << "), utilitzant PENTAGON\n";
shape_file = Defaults::Enemies::Pentagon::SHAPE_FILE; 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_min = Defaults::Enemies::Pentagon::DROTACIO_MIN;
drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX; drotacio_max = Defaults::Enemies::Pentagon::DROTACIO_MAX;
break; break;
} }
// Carregar forma // Carregar forma
forma_ = Graphics::ShapeLoader::load(shape_file); shape_ = Graphics::ShapeLoader::load(shape_file);
if (!forma_ || !forma_->es_valida()) { if (!shape_ || !shape_->es_valida()) {
std::cerr << "[Enemic] Error: no s'ha pogut carregar " << shape_file << '\n'; std::cerr << "[Enemy] Error: no s'ha pogut carregar " << shape_file << '\n';
} }
// [MODIFIED] Posició aleatòria amb comprovació de seguretat // [MODIFIED] Posició aleatòria amb comprovació de seguretat
@@ -103,8 +103,8 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
float candidate_y; float candidate_y;
if (intent_spawn_safe(*ship_pos, candidate_x, candidate_y)) { if (intent_spawn_safe(*ship_pos, candidate_x, candidate_y)) {
centre_.x = candidate_x; center_.x = candidate_x;
centre_.y = candidate_y; center_.y = candidate_y;
found_safe_position = true; found_safe_position = true;
break; break;
} }
@@ -114,18 +114,18 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
// Fallback: spawn anywhere (user's preference) // Fallback: spawn anywhere (user's preference)
int range_x = static_cast<int>(max_x - min_x); int range_x = static_cast<int>(max_x - min_x);
int range_y = static_cast<int>(max_y - min_y); int range_y = static_cast<int>(max_y - min_y);
centre_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x)); center_.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_.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'; << Defaults::Enemies::Spawn::MAX_SPAWN_ATTEMPTS << " intents" << '\n';
} }
} else { } else {
// [EXISTING] No ship position: spawn anywhere (backward compatibility) // [EXISTING] No ship position: spawn anywhere (backward compatibility)
int range_x = static_cast<int>(max_x - min_x); int range_x = static_cast<int>(max_x - min_x);
int range_y = static_cast<int>(max_y - min_y); int range_y = static_cast<int>(max_y - min_y);
centre_.x = static_cast<float>((std::rand() % range_x) + static_cast<int>(min_x)); center_.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_.y = static_cast<float>((std::rand() % range_y) + static_cast<int>(min_y));
} }
// Angle aleatori de moviment // Angle aleatori de moviment
@@ -137,7 +137,7 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
rotacio_ = 0.0F; rotacio_ = 0.0F;
// Inicialitzar estat d'animació // Inicialitzar estat d'animació
animacio_ = AnimacioEnemic(); // Reset to defaults animacio_ = EnemyAnimation(); // Reset to defaults
animacio_.drotacio_base = drotacio_; animacio_.drotacio_base = drotacio_;
animacio_.drotacio_objetivo = drotacio_; animacio_.drotacio_objetivo = drotacio_;
animacio_.drotacio_t = 1.0F; // Start without interpolating animacio_.drotacio_t = 1.0F; // Start without interpolating
@@ -150,7 +150,7 @@ void Enemic::inicialitzar(TipusEnemic tipus, const Vec2* ship_pos) {
esta_ = true; esta_ = true;
} }
void Enemic::actualitzar(float delta_time) { void Enemy::update(float delta_time) {
if (esta_) { if (esta_) {
// [NEW] Update invulnerability timer and brightness // [NEW] Update invulnerability timer and brightness
if (timer_invulnerabilitat_ > 0.0F) { if (timer_invulnerabilitat_ > 0.0F) {
@@ -179,26 +179,26 @@ void Enemic::actualitzar(float delta_time) {
} }
} }
void Enemic::dibuixar() const { void Enemy::draw() const {
if (esta_ && forma_) { if (esta_ && shape_) {
// Calculate animated scale (includes invulnerability LERP) // Calculate animated scale (includes invulnerability LERP)
float escala = calcular_escala_actual(); float escala = calcular_escala_actual();
// brightness_ is already updated in actualitzar() // brightness_ is already updated in update()
Rendering::render_shape(renderer_, forma_, centre_, rotacio_, escala, 1.0F, brightness_); 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 // Dispatcher: crida el comportament específic segons el tipus
switch (tipus_) { switch (type_) {
case TipusEnemic::PENTAGON: case EnemyType::PENTAGON:
comportament_pentagon(delta_time); comportament_pentagon(delta_time);
break; break;
case TipusEnemic::QUADRAT: case EnemyType::QUADRAT:
comportament_quadrat(delta_time); comportament_quadrat(delta_time);
break; break;
case TipusEnemic::MOLINILLO: case EnemyType::MOLINILLO:
comportament_molinillo(delta_time); comportament_molinillo(delta_time);
break; break;
default: 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) // Pentagon: zigzag esquivador (frequent direction changes)
// Similar a comportament original però amb probabilitat més alta // 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) // Calcular desplaçament (angle-PI/2 perquè angle=0 apunta amunt)
float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F)); float dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F)); float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
float new_y = centre_.y + dy; float new_y = center_.y + dy;
float new_x = centre_.x + dx; float new_x = center_.x + dx;
// Obtenir límits segurs // Obtenir límits segurs
float min_x; 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 // Zigzag: canvi d'angle més freqüent en tocar límits
if (new_y >= min_y && new_y <= max_y) { if (new_y >= min_y && new_y <= max_y) {
centre_.y = new_y; center_.y = new_y;
} else { } else {
// Probabilitat més alta de canvi d'angle // Probabilitat més alta de canvi d'angle
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) { 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) { if (new_x >= min_x && new_x <= max_x) {
centre_.x = new_x; center_.x = new_x;
} else { } else {
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) { if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Pentagon::CANVI_ANGLE_PROB) {
float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) * 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) // Quadrat: perseguidor (tracks player position)
// Update tracking timer // Update tracking timer
@@ -267,8 +267,8 @@ void Enemic::comportament_quadrat(float delta_time) {
if (ship_position_ != nullptr) { if (ship_position_ != nullptr) {
// Calculate angle to ship // Calculate angle to ship
float dx = ship_position_->x - centre_.x; float dx = ship_position_->x - center_.x;
float dy = ship_position_->y - centre_.y; float dy = ship_position_->y - center_.y;
float target_angle = std::atan2(dy, dx) + (Constants::PI / 2.0F); float target_angle = std::atan2(dy, dx) + (Constants::PI / 2.0F);
// Interpolate toward target angle // Interpolate toward target angle
@@ -288,12 +288,12 @@ void Enemic::comportament_quadrat(float delta_time) {
} }
// Move in current direction // 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 dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F)); float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
float new_y = centre_.y + dy; float new_y = center_.y + dy;
float new_x = centre_.x + dx; float new_x = center_.x + dx;
// Obtenir límits segurs // Obtenir límits segurs
float min_x; float min_x;
@@ -308,25 +308,25 @@ void Enemic::comportament_quadrat(float delta_time) {
// Bounce on walls (simple reflection) // Bounce on walls (simple reflection)
if (new_y >= min_y && new_y <= max_y) { if (new_y >= min_y && new_y <= max_y) {
centre_.y = new_y; center_.y = new_y;
} else { } else {
angle_ = -angle_; // Vertical reflection angle_ = -angle_; // Vertical reflection
} }
if (new_x >= min_x && new_x <= max_x) { if (new_x >= min_x && new_x <= max_x) {
centre_.x = new_x; center_.x = new_x;
} else { } else {
angle_ = Constants::PI - angle_; // Horizontal reflection 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) // Molinillo: agressiu (fast, straight lines, proximity spin-up)
// Check proximity to ship for spin-up effect // Check proximity to ship for spin-up effect
if (ship_position_ != nullptr) { if (ship_position_ != nullptr) {
float dx = ship_position_->x - centre_.x; float dx = ship_position_->x - center_.x;
float dy = ship_position_->y - centre_.y; float dy = ship_position_->y - center_.y;
float distance = std::sqrt((dx * dx) + (dy * dy)); float distance = std::sqrt((dx * dx) + (dy * dy));
if (distance < Defaults::Enemies::Molinillo::PROXIMITY_DISTANCE) { if (distance < Defaults::Enemies::Molinillo::PROXIMITY_DISTANCE) {
@@ -340,12 +340,12 @@ void Enemic::comportament_molinillo(float delta_time) {
} }
// Fast straight-line movement // 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 dy = velocitat_efectiva * std::sin(angle_ - (Constants::PI / 2.0F));
float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F)); float dx = velocitat_efectiva * std::cos(angle_ - (Constants::PI / 2.0F));
float new_y = centre_.y + dy; float new_y = center_.y + dy;
float new_x = centre_.x + dx; float new_x = center_.x + dx;
// Obtenir límits segurs // Obtenir límits segurs
float min_x; float min_x;
@@ -360,7 +360,7 @@ void Enemic::comportament_molinillo(float delta_time) {
// Rare angle changes on wall hits // Rare angle changes on wall hits
if (new_y >= min_y && new_y <= max_y) { if (new_y >= min_y && new_y <= max_y) {
centre_.y = new_y; center_.y = new_y;
} else { } else {
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) { if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) {
float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) * 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) { if (new_x >= min_x && new_x <= max_x) {
centre_.x = new_x; center_.x = new_x;
} else { } else {
if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) { if (static_cast<float>(std::rand()) / RAND_MAX < Defaults::Enemies::Molinillo::CANVI_ANGLE_PROB) {
float rand_angle = (static_cast<float>(std::rand()) / RAND_MAX) * 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_palpitacio(delta_time);
actualitzar_rotacio_accelerada(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) { if (animacio_.palpitacio_activa) {
// Advance phase (2π * frequency * dt) // Advance phase (2π * frequency * dt)
animacio_.palpitacio_fase += 2.0F * Constants::PI * animacio_.palpitacio_frequencia * delta_time; 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) { if (animacio_.drotacio_t < 1.0F) {
// Transitioning to new target // Transitioning to new target
animacio_.drotacio_t += delta_time / animacio_.drotacio_duracio; 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; float escala = 1.0F;
// [NEW] Invulnerability LERP prioritza sobre palpitació // [NEW] Invulnerability LERP prioritza sobre palpitació
@@ -497,33 +497,33 @@ float Enemic::calcular_escala_actual() const {
// [NEW] Stage system API implementations // [NEW] Stage system API implementations
float Enemic::get_base_velocity() const { float Enemy::get_base_velocity() const {
switch (tipus_) { switch (type_) {
case TipusEnemic::PENTAGON: case EnemyType::PENTAGON:
return Defaults::Enemies::Pentagon::VELOCITAT; return Defaults::Enemies::Pentagon::VELOCITAT;
case TipusEnemic::QUADRAT: case EnemyType::QUADRAT:
return Defaults::Enemies::Quadrat::VELOCITAT; return Defaults::Enemies::Quadrat::VELOCITAT;
case TipusEnemic::MOLINILLO: case EnemyType::MOLINILLO:
return Defaults::Enemies::Molinillo::VELOCITAT; return Defaults::Enemies::Molinillo::VELOCITAT;
default: default:
return Defaults::Enemies::Pentagon::VELOCITAT; // Fallback segur 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 the base rotation speed (drotacio_base if available, otherwise current drotacio_)
return animacio_.drotacio_base != 0.0F ? animacio_.drotacio_base : 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 // Only applies to QUADRAT type
if (tipus_ == TipusEnemic::QUADRAT) { if (type_ == EnemyType::QUADRAT) {
tracking_strength_ = strength; tracking_strength_ = strength;
} }
} }
// [NEW] Safe spawn helper - checks if position is away from ship // [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 // Generate random position within safe bounds
float min_x; float min_x;
float max_x; float max_x;
@@ -9,19 +9,19 @@
#include <cstdint> #include <cstdint>
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/types.hpp" #include "core/types.hpp"
#include "game/constants.hpp" #include "game/constants.hpp"
// Tipus d'enemic // Tipus d'enemic
enum class TipusEnemic : uint8_t { enum class EnemyType : uint8_t {
PENTAGON = 0, // Pentàgon esquivador (zigzag) PENTAGON = 0, // Pentàgon esquivador (zigzag)
QUADRAT = 1, // Quadrat perseguidor (tracks ship) QUADRAT = 1, // Quadrat perseguidor (tracks ship)
MOLINILLO = 2 // Molinillo agressiu (fast, spinning) MOLINILLO = 2 // Molinillo agressiu (fast, spinning)
}; };
// Estat d'animació (palpitació i rotació accelerada) // Estat d'animació (palpitació i rotació accelerada)
struct AnimacioEnemic { struct EnemyAnimation {
// Palpitation (breathing effect) // Palpitation (breathing effect)
bool palpitacio_activa = false; bool palpitacio_activa = false;
float palpitacio_fase = 0.0F; // Phase in cycle (0.0-2π) 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) float drotacio_duracio = 0.0F; // Duration of transition (seconds)
}; };
class Enemic : public Entities::Entitat { class Enemy : public Entities::Entity {
public: public:
Enemic() Enemy()
: Entitat(nullptr) {} : Entity(nullptr) {}
Enemic(SDL_Renderer* renderer); Enemy(SDL_Renderer* renderer);
void inicialitzar() override { inicialitzar(TipusEnemic::PENTAGON, nullptr); } void init() override { init(EnemyType::PENTAGON, nullptr); }
void inicialitzar(TipusEnemic tipus, const Vec2* ship_pos = nullptr); void init(EnemyType tipus, const Vec2* ship_pos = nullptr);
void actualitzar(float delta_time) override; void update(float delta_time) override;
void dibuixar() const override; void draw() const override;
// Override: Interfície d'Entitat // Override: Interfície d'Entity
[[nodiscard]] bool esta_actiu() const override { return esta_; } [[nodiscard]] bool isActive() const override { return esta_; }
// Override: Interfície de col·lisió // Override: Interfície de col·lisió
[[nodiscard]] float get_collision_radius() const override { [[nodiscard]] float getCollisionRadius() const override {
return Defaults::Entities::ENEMY_RADIUS; return Defaults::Entities::ENEMY_RADIUS;
} }
[[nodiscard]] bool es_collidable() const override { [[nodiscard]] bool isCollidable() const override {
return esta_ && timer_invulnerabilitat_ <= 0.0F; return esta_ && timer_invulnerabilitat_ <= 0.0F;
} }
// Getters (API pública sense canvis) // Getters (API pública sense canvis)
void destruir() { esta_ = false; } void destruir() { esta_ = false; }
[[nodiscard]] float get_drotacio() const { return drotacio_; } [[nodiscard]] float get_drotacio() const { return drotacio_; }
[[nodiscard]] Vec2 get_velocitat_vector() const { [[nodiscard]] Vec2 getVelocityVector() const {
return { return {
.x = velocitat_ * std::cos(angle_ - (Constants::PI / 2.0F)), .x = velocity_ * std::cos(angle_ - (Constants::PI / 2.0F)),
.y = velocitat_ * std::sin(angle_ - (Constants::PI / 2.0F))}; .y = velocity_ * std::sin(angle_ - (Constants::PI / 2.0F))};
} }
// Set ship position reference for tracking behavior // Set ship position reference for tracking behavior
@@ -73,10 +73,10 @@ class Enemic : public Entities::Entitat {
// [NEW] Getters for stage system (base stats) // [NEW] Getters for stage system (base stats)
[[nodiscard]] float get_base_velocity() const; [[nodiscard]] float get_base_velocity() const;
[[nodiscard]] float get_base_rotation() 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) // [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) { void set_rotation(float rot) {
drotacio_ = rot; drotacio_ = rot;
animacio_.drotacio_base = rot; animacio_.drotacio_base = rot;
@@ -84,21 +84,21 @@ class Enemic : public Entities::Entitat {
void set_tracking_strength(float strength); void set_tracking_strength(float strength);
// [NEW] Invulnerability queries // [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_; } [[nodiscard]] float get_temps_invulnerabilitat() const { return timer_invulnerabilitat_; }
private: private:
// Membres específics d'Enemic (heretats: renderer_, forma_, centre_, angle_, brightness_) // Membres específics d'Enemy (heretats: renderer_, shape_, center_, angle_, brightness_)
float velocitat_; float velocity_;
float drotacio_; // Delta rotació visual (rad/s) float drotacio_; // Delta rotació visual (rad/s)
float rotacio_; // Rotació visual acumulada float rotacio_; // Rotació visual acumulada
bool esta_; bool esta_;
// [NEW] Enemy type and configuration // [NEW] Enemy type and configuration
TipusEnemic tipus_; EnemyType type_;
// [NEW] Animation state // [NEW] Animation state
AnimacioEnemic animacio_; EnemyAnimation animacio_;
// [NEW] Behavior state (type-specific) // [NEW] Behavior state (type-specific)
float tracking_timer_; // For Quadrat: time since last angle update float tracking_timer_; // For Quadrat: time since last angle update
-62
View File
@@ -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) // © 1999 Visente i Sergi (versió Pascal)
// © 2025 Port a C++20 amb SDL3 // © 2025 Port a C++20 amb SDL3
#include "game/entities/nau.hpp" #include "game/entities/ship.hpp"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
@@ -12,7 +12,7 @@
#include <iostream> #include <iostream>
#include "core/defaults.hpp" #include "core/defaults.hpp"
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/graphics/shape_loader.hpp" #include "core/graphics/shape_loader.hpp"
#include "core/input/input.hpp" #include "core/input/input.hpp"
#include "core/input/input_types.hpp" #include "core/input/input_types.hpp"
@@ -20,23 +20,23 @@
#include "core/types.hpp" #include "core/types.hpp"
#include "game/constants.hpp" #include "game/constants.hpp"
Nau::Nau(SDL_Renderer* renderer, const char* shape_file) Ship::Ship(SDL_Renderer* renderer, const char* shape_file)
: Entitat(renderer), : Entity(renderer),
velocitat_(0.0F), velocity_(0.0F),
esta_tocada_(false), is_hit_(false),
invulnerable_timer_(0.0F) { invulnerable_timer_(0.0F) {
// [NUEVO] Brightness específic per naus // [NUEVO] Brightness específic per naus
brightness_ = Defaults::Brightness::NAU; brightness_ = Defaults::Brightness::NAU;
// [NUEVO] Carregar forma compartida des de fitxer // [NUEVO] Carregar forma compartida des de fitxer
forma_ = Graphics::ShapeLoader::load(shape_file); shape_ = Graphics::ShapeLoader::load(shape_file);
if (!forma_ || !forma_->es_valida()) { if (!shape_ || !shape_->es_valida()) {
std::cerr << "[Nau] Error: no s'ha pogut carregar " << shape_file << '\n'; 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) // 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
@@ -46,20 +46,20 @@ void Nau::inicialitzar(const Vec2* spawn_point, bool activar_invulnerabilitat) {
// Use custom spawn point if provided, otherwise use center // Use custom spawn point if provided, otherwise use center
if (spawn_point != nullptr) { if (spawn_point != nullptr) {
centre_.x = spawn_point->x; center_.x = spawn_point->x;
centre_.y = spawn_point->y; center_.y = spawn_point->y;
} else { } else {
// Default: center of play area // Default: center of play area
float centre_x; float centre_x;
float centre_y; float centre_y;
Constants::obtenir_centre_zona(centre_x, centre_y); Constants::obtenir_centre_zona(centre_x, centre_y);
centre_.x = static_cast<int>(centre_x); center_.x = static_cast<int>(centre_x);
centre_.y = static_cast<int>(centre_y); center_.y = static_cast<int>(centre_y);
} }
// Estat inicial // Estat inicial
angle_ = 0.0F; angle_ = 0.0F;
velocitat_ = 0.0F; velocity_ = 0.0F;
// Activar invulnerabilidad solo si es respawn // Activar invulnerabilidad solo si es respawn
if (activar_invulnerabilitat) { if (activar_invulnerabilitat) {
@@ -68,14 +68,14 @@ void Nau::inicialitzar(const Vec2* spawn_point, bool activar_invulnerabilitat) {
invulnerable_timer_ = 0.0F; 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) // Processar input continu (com teclapuls() del Pascal original)
// Basat en joc_asteroides.cpp línies 66-85 // Basat en joc_asteroides.cpp línies 66-85
// Només processa input si la nau està viva // Només processa input si la nau està viva
if (esta_tocada_) { if (is_hit_) {
return; 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 (input->checkActionPlayer1(InputAction::THRUST, Input::ALLOW_REPEAT)) {
if (velocitat_ < Defaults::Physics::MAX_VELOCITY) { if (velocity_ < Defaults::Physics::MAX_VELOCITY) {
velocitat_ += Defaults::Physics::ACCELERATION * delta_time; velocity_ += Defaults::Physics::ACCELERATION * delta_time;
velocitat_ = std::min(velocitat_, Defaults::Physics::MAX_VELOCITY); velocity_ = std::min(velocity_, Defaults::Physics::MAX_VELOCITY);
} }
} }
} else { } 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 (input->checkActionPlayer2(InputAction::THRUST, Input::ALLOW_REPEAT)) {
if (velocitat_ < Defaults::Physics::MAX_VELOCITY) { if (velocity_ < Defaults::Physics::MAX_VELOCITY) {
velocitat_ += Defaults::Physics::ACCELERATION * delta_time; velocity_ += Defaults::Physics::ACCELERATION * delta_time;
velocitat_ = std::min(velocitat_, Defaults::Physics::MAX_VELOCITY); velocity_ = std::min(velocity_, Defaults::Physics::MAX_VELOCITY);
} }
} }
} }
} }
void Nau::actualitzar(float delta_time) { void Ship::update(float delta_time) {
// Només actualitzar si la nau està viva // Només update si la nau està viva
if (esta_tocada_) { if (is_hit_) {
return; return;
} }
@@ -130,17 +130,17 @@ void Nau::actualitzar(float delta_time) {
} }
// Aplicar física (moviment + fricció) // Aplicar física (moviment + fricció)
aplicar_fisica(delta_time); applyPhysics(delta_time);
} }
void Nau::dibuixar() const { void Ship::draw() const {
// Només dibuixar si la nau està viva // Només draw si la nau està viva
if (esta_tocada_) { if (is_hit_) {
return; return;
} }
// Si invulnerable, parpadear (toggle on/off) // Si invulnerable, parpadear (toggle on/off)
if (es_invulnerable()) { if (isInvulnerable()) {
// Calcular ciclo de parpadeo // Calcular ciclo de parpadeo
float blink_cycle = Defaults::Ship::BLINK_VISIBLE_TIME + float blink_cycle = Defaults::Ship::BLINK_VISIBLE_TIME +
Defaults::Ship::BLINK_INVISIBLE_TIME; Defaults::Ship::BLINK_INVISIBLE_TIME;
@@ -152,7 +152,7 @@ void Nau::dibuixar() const {
} }
} }
if (!forma_) { if (!shape_) {
return; return;
} }
@@ -164,25 +164,25 @@ void Nau::dibuixar() const {
// [NUEVO] Convertir suma de velocitat_visual a escala multiplicativa // [NUEVO] Convertir suma de velocitat_visual a escala multiplicativa
// Radio base del ship = 12 px // Radio base del ship = 12 px
// velocitat_visual = 0-6 → r = 12-18 → escala = 1.0-1.5 // 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); 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 // Aplicar física de moviment
// Basat en joc_asteroides.cpp línies 87-113 // Basat en joc_asteroides.cpp línies 87-113
// Calcular nova posició basada en velocitat i angle // Calcular nova posició basada en velocitat i angle
// S'usa (angle - PI/2) perquè angle=0 apunta cap amunt, no cap a la dreta // 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 = float dy =
((velocitat_ * delta_time) * std::sin(angle_ - (Constants::PI / 2.0F))) + ((velocity_ * delta_time) * std::sin(angle_ - (Constants::PI / 2.0F))) +
centre_.y; center_.y;
float dx = float dx =
((velocitat_ * delta_time) * std::cos(angle_ - (Constants::PI / 2.0F))) + ((velocity_ * delta_time) * std::cos(angle_ - (Constants::PI / 2.0F))) +
centre_.x; center_.x;
// Boundary checking amb radi de la nau // Boundary checking amb radi de la nau
// CORRECCIÓ: Usar límits segurs i inequalitats inclusives // CORRECCIÓ: Usar límits segurs i inequalitats inclusives
@@ -198,16 +198,16 @@ void Nau::aplicar_fisica(float delta_time) {
// Inequalitats inclusives (>= i <=) // Inequalitats inclusives (>= i <=)
if (dy >= min_y && dy <= max_y) { if (dy >= min_y && dy <= max_y) {
centre_.y = dy; center_.y = dy;
} }
if (dx >= min_x && dx <= max_x) { if (dx >= min_x && dx <= max_x) {
centre_.x = dx; center_.x = dx;
} }
// Fricció - desacceleració gradual (time-based) // Fricció - desacceleració gradual (time-based)
if (velocitat_ > 0.1F) { if (velocity_ > 0.1F) {
velocitat_ -= Defaults::Physics::FRICTION * delta_time; velocity_ -= Defaults::Physics::FRICTION * delta_time;
velocitat_ = std::max(velocitat_, 0.0F); velocity_ = std::max(velocity_, 0.0F);
} }
} }
+62
View File
@@ -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);
};
+109 -109
View File
@@ -12,7 +12,7 @@
#include <vector> #include <vector>
#include "core/audio/audio.hpp" #include "core/audio/audio.hpp"
#include "core/entities/entitat.hpp" #include "core/entities/entity.hpp"
#include "core/input/input.hpp" #include "core/input/input.hpp"
#include "core/input/mouse.hpp" #include "core/input/mouse.hpp"
#include "core/math/easing.hpp" #include "core/math/easing.hpp"
@@ -49,17 +49,17 @@ EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context)
(void)opcio; // Suprimir warning de variable no usada (void)opcio; // Suprimir warning de variable no usada
// Inicialitzar naus amb renderer (P1=ship.shp, P2=ship2.shp) // Inicialitzar naus amb renderer (P1=ship.shp, P2=ship2.shp)
naus_[0] = Nau(sdl.obte_renderer(), "ship.shp"); // Jugador 1: nave estàndar naus_[0] = Ship(sdl.obte_renderer(), "ship.shp"); // Jugador 1: nave estàndar
naus_[1] = Nau(sdl.obte_renderer(), "ship2.shp"); // Jugador 2: interceptor amb ales naus_[1] = Ship(sdl.obte_renderer(), "ship2.shp"); // Jugador 2: interceptor amb ales
// Inicialitzar bales amb renderer // Inicialitzar bales amb renderer
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala = Bala(sdl.obte_renderer()); bala = Bullet(sdl.obte_renderer());
} }
// Inicialitzar enemics amb renderer // Inicialitzar enemics amb renderer
for (auto& enemy : orni_) { for (auto& enemy : orni_) {
enemy = Enemic(sdl.obte_renderer()); enemy = Enemy(sdl.obte_renderer());
} }
} }
@@ -67,7 +67,7 @@ void EscenaJoc::executar() {
std::cout << "Escena Joc: Inicialitzant...\n"; std::cout << "Escena Joc: Inicialitzant...\n";
// Inicialitzar estat del joc // Inicialitzar estat del joc
inicialitzar(); init();
SDL_Event event; SDL_Event event;
Uint64 last_time = SDL_GetTicks(); Uint64 last_time = SDL_GetTicks();
@@ -102,7 +102,7 @@ void EscenaJoc::executar() {
} }
// Actualitzar física del joc amb delta_time real // Actualitzar física del joc amb delta_time real
actualitzar(delta_time); update(delta_time);
// Actualitzar sistema d'audio // Actualitzar sistema d'audio
Audio::update(); Audio::update();
@@ -117,7 +117,7 @@ void EscenaJoc::executar() {
sdl_.updateRenderingContext(); sdl_.updateRenderingContext();
// Dibuixar joc // Dibuixar joc
dibuixar(); draw();
// Presentar renderer (swap buffers) // Presentar renderer (swap buffers)
sdl_.presenta(); sdl_.presenta();
@@ -126,7 +126,7 @@ void EscenaJoc::executar() {
std::cout << "Escena Joc: Finalitzant...\n"; std::cout << "Escena Joc: Finalitzant...\n";
} }
void EscenaJoc::inicialitzar() { void EscenaJoc::init() {
// Inicialitzar generador de números aleatoris // Inicialitzar generador de números aleatoris
// Basat en el codi Pascal original: line 376 // Basat en el codi Pascal original: line 376
std::srand(static_cast<unsigned>(std::time(nullptr))); std::srand(static_cast<unsigned>(std::time(nullptr)));
@@ -142,10 +142,10 @@ void EscenaJoc::inicialitzar() {
// [NEW] Initialize stage manager // [NEW] Initialize stage manager
stage_manager_ = std::make_unique<StageSystem::StageManager>(stage_config_.get()); stage_manager_ = std::make_unique<StageSystem::StageManager>(stage_config_.get());
stage_manager_->inicialitzar(); stage_manager_->init();
// [NEW] Set ship position reference for safe spawn (P1 for now, TODO: dual tracking) // [NEW] Set ship position reference for safe spawn (P1 for now, TODO: dual tracking)
stage_manager_->get_spawn_controller().set_ship_position(&naus_[0].get_centre()); stage_manager_->get_spawn_controller().set_ship_position(&naus_[0].getCenter());
// Inicialitzar timers de muerte per jugador // Inicialitzar timers de muerte per jugador
itocado_per_jugador_[0] = 0.0F; itocado_per_jugador_[0] = 0.0F;
@@ -175,13 +175,13 @@ void EscenaJoc::inicialitzar() {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu) { if (jugador_actiu) {
// Jugador actiu: inicialitzar normalment // Jugador actiu: init normalment
Vec2 spawn_pos = obtenir_punt_spawn(i); Vec2 spawn_pos = obtenir_punt_spawn(i);
naus_[i].inicialitzar(&spawn_pos, false); // No invulnerability at start naus_[i].init(&spawn_pos, false); // No invulnerability at start
std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inicialitzat\n"; std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inicialitzat\n";
} else { } else {
// Jugador inactiu: marcar com a mort permanent // Jugador inactiu: marcar com a mort permanent
naus_[i].marcar_tocada(); naus_[i].markHit();
itocado_per_jugador_[i] = 999.0F; // Valor sentinella (permanent inactiu) itocado_per_jugador_[i] = 999.0F; // Valor sentinella (permanent inactiu)
vides_per_jugador_[i] = 0; // Sense vides vides_per_jugador_[i] = 0; // Sense vides
std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inactiu\n"; std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inactiu\n";
@@ -190,14 +190,14 @@ void EscenaJoc::inicialitzar() {
// [MODIFIED] Initialize enemies as inactive (stage system will spawn them) // [MODIFIED] Initialize enemies as inactive (stage system will spawn them)
for (auto& enemy : orni_) { for (auto& enemy : orni_) {
enemy = Enemic(sdl_.obte_renderer()); enemy = Enemy(sdl_.obte_renderer());
enemy.set_ship_position(&naus_[0].get_centre()); // Set ship reference (P1 for now) enemy.set_ship_position(&naus_[0].getCenter()); // Set ship reference (P1 for now)
// DON'T call enemy.inicialitzar() here - stage system handles spawning // DON'T call enemy.init() here - stage system handles spawning
} }
// Inicialitzar bales (now 6 instead of 3) // Inicialitzar bales (now 6 instead of 3)
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.inicialitzar(); bala.init();
} }
// [ELIMINAT] Iniciar música de joc (ara es gestiona en stage_manager) // [ELIMINAT] Iniciar música de joc (ara es gestiona en stage_manager)
@@ -208,7 +208,7 @@ void EscenaJoc::inicialitzar() {
init_hud_rect_sound_played_ = false; init_hud_rect_sound_played_ = false;
} }
void EscenaJoc::actualitzar(float delta_time) { void EscenaJoc::update(float delta_time) {
// Processar disparos (state-based, no event-based) // Processar disparos (state-based, no event-based)
if (estat_game_over_ == EstatGameOver::NONE) { if (estat_game_over_ == EstatGameOver::NONE) {
auto* input = Input::get(); auto* input = Input::get();
@@ -271,13 +271,13 @@ void EscenaJoc::actualitzar(float delta_time) {
// Still update enemies, bullets, and effects during continue screen // Still update enemies, bullets, and effects during continue screen
for (auto& enemy : orni_) { for (auto& enemy : orni_) {
enemy.actualitzar(delta_time); enemy.update(delta_time);
} }
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.actualitzar(delta_time); bala.update(delta_time);
} }
debris_manager_.actualitzar(delta_time); debris_manager_.update(delta_time);
gestor_puntuacio_.actualitzar(delta_time); gestor_puntuacio_.update(delta_time);
return; return;
} }
@@ -297,15 +297,15 @@ void EscenaJoc::actualitzar(float delta_time) {
// Enemies and bullets continue moving during game over // Enemies and bullets continue moving during game over
for (auto& enemy : orni_) { for (auto& enemy : orni_) {
enemy.actualitzar(delta_time); enemy.update(delta_time);
} }
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.actualitzar(delta_time); bala.update(delta_time);
} }
debris_manager_.actualitzar(delta_time); debris_manager_.update(delta_time);
gestor_puntuacio_.actualitzar(delta_time); gestor_puntuacio_.update(delta_time);
return; return;
} }
@@ -327,7 +327,7 @@ void EscenaJoc::actualitzar(float delta_time) {
if (vides_per_jugador_[i] > 0) { if (vides_per_jugador_[i] > 0) {
// Respawn ship en spawn position con invulnerabilidad // Respawn ship en spawn position con invulnerabilidad
Vec2 spawn_pos = obtenir_punt_spawn(i); Vec2 spawn_pos = obtenir_punt_spawn(i);
naus_[i].inicialitzar(&spawn_pos, true); naus_[i].init(&spawn_pos, true);
itocado_per_jugador_[i] = 0.0F; itocado_per_jugador_[i] = 0.0F;
} else { } else {
// Player is permanently dead (out of lives) // Player is permanently dead (out of lives)
@@ -352,15 +352,15 @@ void EscenaJoc::actualitzar(float delta_time) {
if (algun_jugador_mort) { if (algun_jugador_mort) {
// Enemies and bullets continue moving during death sequence // Enemies and bullets continue moving during death sequence
for (auto& enemy : orni_) { for (auto& enemy : orni_) {
enemy.actualitzar(delta_time); enemy.update(delta_time);
} }
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.actualitzar(delta_time); bala.update(delta_time);
} }
debris_manager_.actualitzar(delta_time); debris_manager_.update(delta_time);
gestor_puntuacio_.actualitzar(delta_time); gestor_puntuacio_.update(delta_time);
// Don't return - allow alive players to continue playing // Don't return - allow alive players to continue playing
} }
@@ -372,9 +372,9 @@ void EscenaJoc::actualitzar(float delta_time) {
switch (estat) { switch (estat) {
case StageSystem::EstatStage::INIT_HUD: { case StageSystem::EstatStage::INIT_HUD: {
// Update stage manager timer (pot canviar l'estat!) // Update stage manager timer (pot canviar l'estat!)
stage_manager_->actualitzar(delta_time); stage_manager_->update(delta_time);
// [FIX] Si l'estat ha canviat durant actualitzar(), sortir immediatament // [FIX] Si l'estat ha canviat durant update(), sortir immediatament
// per evitar recalcular la posició de la nau amb el nou timer // per evitar recalcular la posició de la nau amb el nou timer
if (stage_manager_->get_estat() != StageSystem::EstatStage::INIT_HUD) { if (stage_manager_->get_estat() != StageSystem::EstatStage::INIT_HUD) {
break; break;
@@ -398,12 +398,12 @@ void EscenaJoc::actualitzar(float delta_time) {
// [MODIFICAT] Animar AMBAS naus con sus progress respectivos // [MODIFICAT] Animar AMBAS naus con sus progress respectivos
if (config_partida_.jugador1_actiu && ship1_progress < 1.0F) { if (config_partida_.jugador1_actiu && ship1_progress < 1.0F) {
Vec2 pos_p1 = calcular_posicio_nau_init_hud(ship1_progress, 0); Vec2 pos_p1 = calcular_posicio_nau_init_hud(ship1_progress, 0);
naus_[0].set_centre(pos_p1); naus_[0].setCenter(pos_p1);
} }
if (config_partida_.jugador2_actiu && ship2_progress < 1.0F) { if (config_partida_.jugador2_actiu && ship2_progress < 1.0F) {
Vec2 pos_p2 = calcular_posicio_nau_init_hud(ship2_progress, 1); Vec2 pos_p2 = calcular_posicio_nau_init_hud(ship2_progress, 1);
naus_[1].set_centre(pos_p2); naus_[1].setCenter(pos_p2);
} }
// Una vegada l'animació acaba, permetre control normal // Una vegada l'animació acaba, permetre control normal
@@ -416,36 +416,36 @@ void EscenaJoc::actualitzar(float delta_time) {
// [DEBUG] Log entrada a LEVEL_START // [DEBUG] Log entrada a LEVEL_START
static bool first_entry = true; static bool first_entry = true;
if (first_entry) { if (first_entry) {
std::cout << "[LEVEL_START] ENTERED with P1 pos.y=" << naus_[0].get_centre().y << '\n'; std::cout << "[LEVEL_START] ENTERED with P1 pos.y=" << naus_[0].getCenter().y << '\n';
first_entry = false; first_entry = false;
} }
// Update countdown timer // Update countdown timer
stage_manager_->actualitzar(delta_time); stage_manager_->update(delta_time);
// [NEW] Allow both ships movement and shooting during intro // [NEW] Allow both ships movement and shooting during intro
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players
naus_[i].processar_input(delta_time, i); naus_[i].processInput(delta_time, i);
naus_[i].actualitzar(delta_time); naus_[i].update(delta_time);
} }
} }
// [NEW] Update bullets // [NEW] Update bullets
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.actualitzar(delta_time); bala.update(delta_time);
} }
// [NEW] Update debris // [NEW] Update debris
debris_manager_.actualitzar(delta_time); debris_manager_.update(delta_time);
break; break;
} }
case StageSystem::EstatStage::PLAYING: { case StageSystem::EstatStage::PLAYING: {
// [NEW] Update stage manager (spawns enemies, pause if BOTH dead) // [NEW] Update stage manager (spawns enemies, pause if BOTH dead)
bool pausar_spawn = (itocado_per_jugador_[0] > 0.0F && itocado_per_jugador_[1] > 0.0F); bool pausar_spawn = (itocado_per_jugador_[0] > 0.0F && itocado_per_jugador_[1] > 0.0F);
stage_manager_->get_spawn_controller().actualitzar(delta_time, orni_, pausar_spawn); stage_manager_->get_spawn_controller().update(delta_time, orni_, pausar_spawn);
// [NEW] Check stage completion (only when at least one player alive) // [NEW] Check stage completion (only when at least one player alive)
bool algun_jugador_viu = (itocado_per_jugador_[0] == 0.0F || itocado_per_jugador_[1] == 0.0F); bool algun_jugador_viu = (itocado_per_jugador_[0] == 0.0F || itocado_per_jugador_[1] == 0.0F);
@@ -462,68 +462,68 @@ void EscenaJoc::actualitzar(float delta_time) {
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players
naus_[i].processar_input(delta_time, i); naus_[i].processInput(delta_time, i);
naus_[i].actualitzar(delta_time); naus_[i].update(delta_time);
} }
} }
for (auto& enemy : orni_) { for (auto& enemy : orni_) {
enemy.actualitzar(delta_time); enemy.update(delta_time);
} }
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.actualitzar(delta_time); bala.update(delta_time);
} }
detectar_col·lisions_bales_enemics(); detectar_col·lisions_bales_enemics();
detectar_col·lisio_naus_enemics(); detectar_col·lisio_naus_enemics();
detectar_col·lisions_bales_jugadors(); detectar_col·lisions_bales_jugadors();
debris_manager_.actualitzar(delta_time); debris_manager_.update(delta_time);
gestor_puntuacio_.actualitzar(delta_time); gestor_puntuacio_.update(delta_time);
break; break;
} }
case StageSystem::EstatStage::LEVEL_COMPLETED: case StageSystem::EstatStage::LEVEL_COMPLETED:
// Update countdown timer // Update countdown timer
stage_manager_->actualitzar(delta_time); stage_manager_->update(delta_time);
// [NEW] Allow both ships movement and shooting during outro // [NEW] Allow both ships movement and shooting during outro
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { // Only active, alive players
naus_[i].processar_input(delta_time, i); naus_[i].processInput(delta_time, i);
naus_[i].actualitzar(delta_time); naus_[i].update(delta_time);
} }
} }
// [NEW] Update bullets (allow last shots to continue) // [NEW] Update bullets (allow last shots to continue)
for (auto& bala : bales_) { for (auto& bala : bales_) {
bala.actualitzar(delta_time); bala.update(delta_time);
} }
// [NEW] Update debris (from last destroyed enemies) // [NEW] Update debris (from last destroyed enemies)
debris_manager_.actualitzar(delta_time); debris_manager_.update(delta_time);
gestor_puntuacio_.actualitzar(delta_time); gestor_puntuacio_.update(delta_time);
break; break;
} }
} }
void EscenaJoc::dibuixar() { void EscenaJoc::draw() {
// Handle CONTINUE screen // Handle CONTINUE screen
if (estat_game_over_ == EstatGameOver::CONTINUE) { if (estat_game_over_ == EstatGameOver::CONTINUE) {
// Draw game background elements first // Draw game background elements first
dibuixar_marges(); dibuixar_marges();
for (const auto& enemy : orni_) { for (const auto& enemy : orni_) {
enemy.dibuixar(); enemy.draw();
} }
for (const auto& bala : bales_) { for (const auto& bala : bales_) {
bala.dibuixar(); bala.draw();
} }
debris_manager_.dibuixar(); debris_manager_.draw();
gestor_puntuacio_.dibuixar(); gestor_puntuacio_.draw();
dibuixar_marcador(); dibuixar_marcador();
// Draw CONTINUE screen overlay // Draw CONTINUE screen overlay
@@ -537,15 +537,15 @@ void EscenaJoc::dibuixar() {
dibuixar_marges(); dibuixar_marges();
for (const auto& enemy : orni_) { for (const auto& enemy : orni_) {
enemy.dibuixar(); enemy.draw();
} }
for (const auto& bala : bales_) { for (const auto& bala : bales_) {
bala.dibuixar(); bala.draw();
} }
debris_manager_.dibuixar(); debris_manager_.draw();
gestor_puntuacio_.dibuixar(); gestor_puntuacio_.draw();
// Draw centered "GAME OVER" text // Draw centered "GAME OVER" text
const std::string game_over_text = "GAME OVER"; const std::string game_over_text = "GAME OVER";
@@ -610,12 +610,12 @@ void EscenaJoc::dibuixar() {
} }
// [MODIFICAT] Dibuixar naus amb progress independent // [MODIFICAT] Dibuixar naus amb progress independent
if (ship1_progress > 0.0F && config_partida_.jugador1_actiu && !naus_[0].esta_tocada()) { if (ship1_progress > 0.0F && config_partida_.jugador1_actiu && !naus_[0].isHit()) {
naus_[0].dibuixar(); naus_[0].draw();
} }
if (ship2_progress > 0.0F && config_partida_.jugador2_actiu && !naus_[1].esta_tocada()) { if (ship2_progress > 0.0F && config_partida_.jugador2_actiu && !naus_[1].isHit()) {
naus_[1].dibuixar(); naus_[1].draw();
} }
break; break;
@@ -627,18 +627,18 @@ void EscenaJoc::dibuixar() {
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) {
naus_[i].dibuixar(); naus_[i].draw();
} }
} }
// [NEW] Draw bullets // [NEW] Draw bullets
for (const auto& bala : bales_) { for (const auto& bala : bales_) {
bala.dibuixar(); bala.draw();
} }
// [NEW] Draw debris // [NEW] Draw debris
debris_manager_.dibuixar(); debris_manager_.draw();
gestor_puntuacio_.dibuixar(); gestor_puntuacio_.draw();
// [EXISTING] Draw intro message and score // [EXISTING] Draw intro message and score
dibuixar_missatge_stage(stage_manager_->get_missatge_level_start()); dibuixar_missatge_stage(stage_manager_->get_missatge_level_start());
@@ -652,20 +652,20 @@ void EscenaJoc::dibuixar() {
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) {
naus_[i].dibuixar(); naus_[i].draw();
} }
} }
for (const auto& enemy : orni_) { for (const auto& enemy : orni_) {
enemy.dibuixar(); enemy.draw();
} }
for (const auto& bala : bales_) { for (const auto& bala : bales_) {
bala.dibuixar(); bala.draw();
} }
debris_manager_.dibuixar(); debris_manager_.draw();
gestor_puntuacio_.dibuixar(); gestor_puntuacio_.draw();
dibuixar_marcador(); dibuixar_marcador();
break; break;
@@ -675,18 +675,18 @@ void EscenaJoc::dibuixar() {
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 2; i++) {
bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu; bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) { if (jugador_actiu && itocado_per_jugador_[i] == 0.0F) {
naus_[i].dibuixar(); naus_[i].draw();
} }
} }
// [NEW] Draw bullets (allow last shots to be visible) // [NEW] Draw bullets (allow last shots to be visible)
for (const auto& bala : bales_) { for (const auto& bala : bales_) {
bala.dibuixar(); bala.draw();
} }
// [NEW] Draw debris (from last destroyed enemies) // [NEW] Draw debris (from last destroyed enemies)
debris_manager_.dibuixar(); debris_manager_.draw();
gestor_puntuacio_.dibuixar(); gestor_puntuacio_.draw();
// [EXISTING] Draw completion message and score // [EXISTING] Draw completion message and score
dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_COMPLETED); dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_COMPLETED);
@@ -699,28 +699,28 @@ void EscenaJoc::tocado(uint8_t player_id) {
// Death sequence: 3 phases // Death sequence: 3 phases
// Phase 1: First call (itocado_per_jugador_[player_id] == 0) - trigger explosion // Phase 1: First call (itocado_per_jugador_[player_id] == 0) - trigger explosion
// Phase 2: Animation (0 < itocado_ < 3.0s) - debris animation // Phase 2: Animation (0 < itocado_ < 3.0s) - debris animation
// Phase 3: Respawn or game over (itocado_ >= 3.0s) - handled in actualitzar() // Phase 3: Respawn or game over (itocado_ >= 3.0s) - handled in update()
if (itocado_per_jugador_[player_id] == 0.0F) { if (itocado_per_jugador_[player_id] == 0.0F) {
// *** PHASE 1: TRIGGER DEATH *** // *** PHASE 1: TRIGGER DEATH ***
// Mark ship as dead (stops rendering and input) // Mark ship as dead (stops rendering and input)
naus_[player_id].marcar_tocada(); naus_[player_id].markHit();
// Create ship explosion // Create ship explosion
const Vec2& ship_pos = naus_[player_id].get_centre(); const Vec2& ship_pos = naus_[player_id].getCenter();
float ship_angle = naus_[player_id].get_angle(); float ship_angle = naus_[player_id].getAngle();
Vec2 vel_nau = naus_[player_id].get_velocitat_vector(); Vec2 vel_nau = naus_[player_id].getVelocityVector();
// Reduir a 80% la velocitat heretada per la nau (més realista) // Reduir a 80% la velocitat heretada per la nau (més realista)
Vec2 vel_nau_80 = {.x = vel_nau.x * 0.8F, .y = vel_nau.y * 0.8F}; Vec2 vel_nau_80 = {.x = vel_nau.x * 0.8F, .y = vel_nau.y * 0.8F};
debris_manager_.explotar( debris_manager_.explotar(
naus_[player_id].get_forma(), // Ship shape (3 lines) naus_[player_id].getShape(), // Ship shape (3 lines)
ship_pos, // Center position ship_pos, // Center position
ship_angle, // Ship orientation ship_angle, // Ship orientation
1.0F, // Normal scale 1.0F, // Normal scale
Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s Defaults::Physics::Debris::VELOCITAT_BASE, // 80 px/s
naus_[player_id].get_brightness(), // Heredar brightness naus_[player_id].getBrightness(), // Heredar brightness
vel_nau_80, // Heredar 80% velocitat vel_nau_80, // Heredar 80% velocitat
0.0F, // Nave: trayectorias rectas (sin drotacio) 0.0F, // Nave: trayectorias rectas (sin drotacio)
0.0F, // Sin herencia visual (rotación aleatoria) 0.0F, // Sin herencia visual (rotación aleatoria)
@@ -730,8 +730,8 @@ void EscenaJoc::tocado(uint8_t player_id) {
// Start death timer (non-zero to avoid re-triggering) // Start death timer (non-zero to avoid re-triggering)
itocado_per_jugador_[player_id] = 0.001F; itocado_per_jugador_[player_id] = 0.001F;
} }
// Phase 2 is automatic (debris updates in actualitzar()) // Phase 2 is automatic (debris updates in update())
// Phase 3 is handled in actualitzar() when itocado_per_jugador_ >= DEATH_DURATION // Phase 3 is handled in update() when itocado_per_jugador_ >= DEATH_DURATION
} }
void EscenaJoc::dibuixar_marges() const { void EscenaJoc::dibuixar_marges() const {
@@ -957,18 +957,18 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() {
if (Physics::check_collision(bala, enemic, AMPLIFIER)) { if (Physics::check_collision(bala, enemic, AMPLIFIER)) {
// *** COL·LISIÓ DETECTADA *** // *** COL·LISIÓ DETECTADA ***
const Vec2& pos_enemic = enemic.get_centre(); const Vec2& pos_enemic = enemic.getCenter();
// 1. Calculate score for enemy type // 1. Calculate score for enemy type
int punts = 0; int punts = 0;
switch (enemic.get_tipus()) { switch (enemic.getType()) {
case TipusEnemic::PENTAGON: case EnemyType::PENTAGON:
punts = Defaults::Enemies::Scoring::PENTAGON_SCORE; punts = Defaults::Enemies::Scoring::PENTAGON_SCORE;
break; break;
case TipusEnemic::QUADRAT: case EnemyType::QUADRAT:
punts = Defaults::Enemies::Scoring::QUADRAT_SCORE; punts = Defaults::Enemies::Scoring::QUADRAT_SCORE;
break; break;
case TipusEnemic::MOLINILLO: case EnemyType::MOLINILLO:
punts = Defaults::Enemies::Scoring::MOLINILLO_SCORE; punts = Defaults::Enemies::Scoring::MOLINILLO_SCORE;
break; break;
} }
@@ -984,14 +984,14 @@ void EscenaJoc::detectar_col·lisions_bales_enemics() {
enemic.destruir(); enemic.destruir();
// 2. Crear explosió de fragments // 2. Crear explosió de fragments
Vec2 vel_enemic = enemic.get_velocitat_vector(); Vec2 vel_enemic = enemic.getVelocityVector();
debris_manager_.explotar( debris_manager_.explotar(
enemic.get_forma(), // Forma vectorial del pentàgon enemic.getShape(), // Forma vectorial del pentàgon
pos_enemic, // Posició central pos_enemic, // Posició central
0.0F, // Angle (enemic té rotació interna) 0.0F, // Angle (enemic té rotació interna)
1.0F, // Escala normal 1.0F, // Escala normal
VELOCITAT_EXPLOSIO, // 50 px/s (explosió suau) VELOCITAT_EXPLOSIO, // 50 px/s (explosió suau)
enemic.get_brightness(), // Heredar brightness enemic.getBrightness(), // Heredar brightness
vel_enemic, // Heredar velocitat vel_enemic, // Heredar velocitat
enemic.get_drotacio(), // Heredar velocitat angular (trayectorias curvas) enemic.get_drotacio(), // Heredar velocitat angular (trayectorias curvas)
0.0F // Sin herencia visual (rotación aleatoria) 0.0F // Sin herencia visual (rotación aleatoria)
@@ -1017,17 +1017,17 @@ void EscenaJoc::detectar_col·lisio_naus_enemics() {
if (itocado_per_jugador_[i] > 0.0F) { if (itocado_per_jugador_[i] > 0.0F) {
continue; continue;
} }
if (!naus_[i].esta_viva()) { if (!naus_[i].isAlive()) {
continue; continue;
} }
if (naus_[i].es_invulnerable()) { if (naus_[i].isInvulnerable()) {
continue; continue;
} }
// Check collision with all active enemies // Check collision with all active enemies
for (const auto& enemic : orni_) { for (const auto& enemic : orni_) {
// Skip collision if enemy is invulnerable // Skip collision if enemy is invulnerable
if (enemic.es_invulnerable()) { if (enemic.isInvulnerable()) {
continue; continue;
} }
@@ -1068,10 +1068,10 @@ void EscenaJoc::detectar_col·lisions_bales_jugadors() {
if (itocado_per_jugador_[player_id] > 0.0F) { if (itocado_per_jugador_[player_id] > 0.0F) {
continue; continue;
} }
if (!naus_[player_id].esta_viva()) { if (!naus_[player_id].isAlive()) {
continue; continue;
} }
if (naus_[player_id].es_invulnerable()) { if (naus_[player_id].isInvulnerable()) {
continue; continue;
} }
@@ -1206,13 +1206,13 @@ void EscenaJoc::disparar_bala(uint8_t player_id) {
if (itocado_per_jugador_[player_id] > 0.0F) { if (itocado_per_jugador_[player_id] > 0.0F) {
return; return;
} }
if (!naus_[player_id].esta_viva()) { if (!naus_[player_id].isAlive()) {
return; return;
} }
// Calcular posición en la punta de la nave // Calcular posición en la punta de la nave
const Vec2& ship_centre = naus_[player_id].get_centre(); const Vec2& ship_centre = naus_[player_id].getCenter();
float ship_angle = naus_[player_id].get_angle(); float ship_angle = naus_[player_id].getAngle();
constexpr float LOCAL_TIP_X = 0.0F; constexpr float LOCAL_TIP_X = 0.0F;
constexpr float LOCAL_TIP_Y = -12.0F; constexpr float LOCAL_TIP_Y = -12.0F;
@@ -1296,7 +1296,7 @@ void EscenaJoc::processar_input_continue() {
// Spawn with invulnerability // Spawn with invulnerability
Vec2 spawn_pos = obtenir_punt_spawn(player_to_revive); Vec2 spawn_pos = obtenir_punt_spawn(player_to_revive);
naus_[player_to_revive].inicialitzar(&spawn_pos, true); naus_[player_to_revive].init(&spawn_pos, true);
// Check if other player wants to continue too // Check if other player wants to continue too
if (p1_start && p2_start) { if (p1_start && p2_start) {
@@ -1306,7 +1306,7 @@ void EscenaJoc::processar_input_continue() {
itocado_per_jugador_[other_player] = 0.0F; itocado_per_jugador_[other_player] = 0.0F;
config_partida_.jugador2_actiu = true; config_partida_.jugador2_actiu = true;
Vec2 spawn_pos2 = obtenir_punt_spawn(other_player); Vec2 spawn_pos2 = obtenir_punt_spawn(other_player);
naus_[other_player].inicialitzar(&spawn_pos2, true); naus_[other_player].init(&spawn_pos2, true);
} }
// Resume game // Resume game
@@ -1392,7 +1392,7 @@ void EscenaJoc::unir_jugador(uint8_t player_id) {
// Spawn with invulnerability // Spawn with invulnerability
Vec2 spawn_pos = obtenir_punt_spawn(player_id); Vec2 spawn_pos = obtenir_punt_spawn(player_id);
naus_[player_id].inicialitzar(&spawn_pos, true); naus_[player_id].init(&spawn_pos, true);
// No visual message, just spawn (per user requirement) // No visual message, just spawn (per user requirement)
+9 -9
View File
@@ -18,9 +18,9 @@
#include "game/constants.hpp" #include "game/constants.hpp"
#include "game/effects/debris_manager.hpp" #include "game/effects/debris_manager.hpp"
#include "game/effects/gestor_puntuacio_flotant.hpp" #include "game/effects/gestor_puntuacio_flotant.hpp"
#include "game/entities/bala.hpp" #include "game/entities/bullet.hpp"
#include "game/entities/enemic.hpp" #include "game/entities/enemy.hpp"
#include "game/entities/nau.hpp" #include "game/entities/ship.hpp"
#include "game/stage_system/stage_config.hpp" #include "game/stage_system/stage_config.hpp"
#include "game/stage_system/stage_manager.hpp" #include "game/stage_system/stage_manager.hpp"
@@ -38,9 +38,9 @@ class EscenaJoc {
~EscenaJoc() = default; ~EscenaJoc() = default;
void executar(); // Bucle principal de l'escena void executar(); // Bucle principal de l'escena
void inicialitzar(); void init();
void actualitzar(float delta_time); void update(float delta_time);
void dibuixar(); void draw();
private: private:
SDLManager& sdl_; SDLManager& sdl_;
@@ -52,9 +52,9 @@ class EscenaJoc {
Effects::GestorPuntuacioFlotant gestor_puntuacio_; Effects::GestorPuntuacioFlotant gestor_puntuacio_;
// Estat del joc // Estat del joc
std::array<Nau, 2> naus_; // [0]=P1, [1]=P2 std::array<Ship, 2> naus_; // [0]=P1, [1]=P2
std::array<Enemic, Constants::MAX_ORNIS> orni_; std::array<Enemy, Constants::MAX_ORNIS> orni_;
std::array<Bala, Constants::MAX_BALES * 2> bales_; // 6 balas: P1=[0,1,2], P2=[3,4,5] std::array<Bullet, Constants::MAX_BALES * 2> bales_; // 6 balas: P1=[0,1,2], P2=[3,4,5]
std::array<float, 2> itocado_per_jugador_; // Death timers per player (seconds) std::array<float, 2> itocado_per_jugador_; // Death timers per player (seconds)
// Lives and game over system // Lives and game over system
+10 -10
View File
@@ -108,7 +108,7 @@ void EscenaLogo::executar() {
} }
// Actualitzar lògica // Actualitzar lògica
actualitzar(delta_time); update(delta_time);
// Actualitzar colors oscil·lats (efecte verd global) // Actualitzar colors oscil·lats (efecte verd global)
sdl_.updateColors(delta_time); sdl_.updateColors(delta_time);
@@ -117,7 +117,7 @@ void EscenaLogo::executar() {
sdl_.updateRenderingContext(); sdl_.updateRenderingContext();
// Dibuixar // Dibuixar
dibuixar(); draw();
} }
std::cout << "Escena Logo: Finalitzant...\n"; std::cout << "Escena Logo: Finalitzant...\n";
@@ -164,7 +164,7 @@ void EscenaLogo::inicialitzar_lletres() {
// IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL // IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL
// per que les posicions finals coincideixin amb la mida real de les lletres // per que les posicions finals coincideixin amb la mida real de les lletres
float ancho = ancho_sin_escalar * ESCALA_FINAL; float ancho = ancho_sin_escalar * ESCALA_FINAL;
float offset_centre = (forma->get_centre().x - min_x) * ESCALA_FINAL; float offset_centre = (forma->getCenter().x - min_x) * ESCALA_FINAL;
lletres_.push_back({forma, lletres_.push_back({forma,
{.x = 0.0F, .y = 0.0F}, // Posició es calcularà després {.x = 0.0F, .y = 0.0F}, // Posició es calcularà després
@@ -264,7 +264,7 @@ void EscenaLogo::actualitzar_explosions(float delta_time) {
} }
} }
void EscenaLogo::actualitzar(float delta_time) { void EscenaLogo::update(float delta_time) {
temps_estat_actual_ += delta_time; temps_estat_actual_ += delta_time;
switch (estat_actual_) { switch (estat_actual_) {
@@ -326,10 +326,10 @@ void EscenaLogo::actualitzar(float delta_time) {
} }
// Actualitzar animacions de debris // Actualitzar animacions de debris
debris_manager_->actualitzar(delta_time); debris_manager_->update(delta_time);
} }
void EscenaLogo::dibuixar() { void EscenaLogo::draw() {
// Fons negre // Fons negre
sdl_.neteja(0, 0, 0); sdl_.neteja(0, 0, 0);
@@ -407,10 +407,10 @@ void EscenaLogo::dibuixar() {
} }
} }
// POST_EXPLOSION: No dibuixar lletres, només debris (a baix) // POST_EXPLOSION: No draw lletres, només debris (a baix)
// Sempre dibuixar debris (si n'hi ha d'actius) // Sempre draw debris (si n'hi ha d'actius)
debris_manager_->dibuixar(); debris_manager_->draw();
sdl_.presenta(); sdl_.presenta();
} }
@@ -420,5 +420,5 @@ auto EscenaLogo::checkSkipButtonPressed() -> bool {
} }
void EscenaLogo::processar_events(const SDL_Event& event) { void EscenaLogo::processar_events(const SDL_Event& event) {
// No procesar eventos genéricos aquí - la lógica se movió a actualitzar() // No procesar eventos genéricos aquí - la lógica se movió a update()
} }
+2 -2
View File
@@ -79,9 +79,9 @@ class EscenaLogo {
// Mètodes privats // Mètodes privats
void inicialitzar_lletres(); void inicialitzar_lletres();
void actualitzar(float delta_time); void update(float delta_time);
void actualitzar_explosions(float delta_time); void actualitzar_explosions(float delta_time);
void dibuixar(); void draw();
void processar_events(const SDL_Event& event); void processar_events(const SDL_Event& event);
auto checkSkipButtonPressed() -> bool; auto checkSkipButtonPressed() -> bool;
+13 -13
View File
@@ -79,7 +79,7 @@ EscenaTitol::EscenaTitol(SDLManager& sdl, ContextEscenes& context)
// Inicialitzar animador de naus 3D // Inicialitzar animador de naus 3D
ship_animator_ = std::make_unique<Title::ShipAnimator>(sdl_.obte_renderer()); ship_animator_ = std::make_unique<Title::ShipAnimator>(sdl_.obte_renderer());
ship_animator_->inicialitzar(); ship_animator_->init();
if (estat_actual_ == EstatTitol::MAIN) { if (estat_actual_ == EstatTitol::MAIN) {
// Jump to MAIN: empezar entrada inmediatamente // Jump to MAIN: empezar entrada inmediatamente
@@ -145,7 +145,7 @@ void EscenaTitol::inicialitzar_titol() {
// Escalar ancho, altura i offset amb LOGO_SCALE // Escalar ancho, altura i offset amb LOGO_SCALE
float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE; float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE; float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float offset_centre = (forma->get_centre().x - min_x) * Defaults::Title::Layout::LOGO_SCALE; float offset_centre = (forma->getCenter().x - min_x) * Defaults::Title::Layout::LOGO_SCALE;
lletres_orni_.push_back({forma, {.x = 0.0F, .y = 0.0F}, ancho, altura, offset_centre}); lletres_orni_.push_back({forma, {.x = 0.0F, .y = 0.0F}, ancho, altura, offset_centre});
@@ -219,7 +219,7 @@ void EscenaTitol::inicialitzar_titol() {
// Escalar ancho, altura i offset amb LOGO_SCALE // Escalar ancho, altura i offset amb LOGO_SCALE
float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE; float ancho = ancho_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE; float altura = altura_sin_escalar * Defaults::Title::Layout::LOGO_SCALE;
float offset_centre = (forma->get_centre().x - min_x) * Defaults::Title::Layout::LOGO_SCALE; float offset_centre = (forma->getCenter().x - min_x) * Defaults::Title::Layout::LOGO_SCALE;
lletres_attack_.push_back({forma, {.x = 0.0F, .y = 0.0F}, ancho, altura, offset_centre}); lletres_attack_.push_back({forma, {.x = 0.0F, .y = 0.0F}, ancho, altura, offset_centre});
@@ -295,7 +295,7 @@ void EscenaTitol::executar() {
} }
// Actualitzar lògica // Actualitzar lògica
actualitzar(delta_time); update(delta_time);
// Actualitzar sistema d'audio // Actualitzar sistema d'audio
Audio::update(); Audio::update();
@@ -310,7 +310,7 @@ void EscenaTitol::executar() {
sdl_.updateRenderingContext(); sdl_.updateRenderingContext();
// Dibuixar // Dibuixar
dibuixar(); draw();
// Presentar renderer (swap buffers) // Presentar renderer (swap buffers)
sdl_.presenta(); sdl_.presenta();
@@ -319,10 +319,10 @@ void EscenaTitol::executar() {
std::cout << "Escena Titol: Finalitzant...\n"; std::cout << "Escena Titol: Finalitzant...\n";
} }
void EscenaTitol::actualitzar(float delta_time) { void EscenaTitol::update(float delta_time) {
// Actualitzar starfield (sempre actiu) // Actualitzar starfield (sempre actiu)
if (starfield_) { if (starfield_) {
starfield_->actualitzar(delta_time); starfield_->update(delta_time);
} }
// Actualitzar naus (quan visibles) // Actualitzar naus (quan visibles)
@@ -331,7 +331,7 @@ void EscenaTitol::actualitzar(float delta_time) {
estat_actual_ == EstatTitol::STARFIELD || estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) { estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
ship_animator_->actualitzar(delta_time); ship_animator_->update(delta_time);
} }
switch (estat_actual_) { switch (estat_actual_) {
@@ -541,10 +541,10 @@ void EscenaTitol::actualitzar_animacio_logo(float delta_time) {
} }
} }
void EscenaTitol::dibuixar() { void EscenaTitol::draw() {
// Dibuixar starfield de fons (en tots els estats excepte BLACK_SCREEN) // Dibuixar starfield de fons (en tots els estats excepte BLACK_SCREEN)
if (starfield_ && estat_actual_ != EstatTitol::BLACK_SCREEN) { if (starfield_ && estat_actual_ != EstatTitol::BLACK_SCREEN) {
starfield_->dibuixar(); starfield_->draw();
} }
// Dibuixar naus (després starfield, abans logo) // Dibuixar naus (després starfield, abans logo)
@@ -553,7 +553,7 @@ void EscenaTitol::dibuixar() {
estat_actual_ == EstatTitol::STARFIELD || estat_actual_ == EstatTitol::STARFIELD ||
estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::MAIN ||
estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) { estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE)) {
ship_animator_->dibuixar(); ship_animator_->draw();
} }
// En els estats STARFIELD_FADE_IN i STARFIELD, només mostrar starfield (sense text) // En els estats STARFIELD_FADE_IN i STARFIELD, només mostrar starfield (sense text)
@@ -562,7 +562,7 @@ void EscenaTitol::dibuixar() {
} }
// Estat MAIN i PLAYER_JOIN_PHASE: Dibuixar títol i text (sobre el starfield) // Estat MAIN i PLAYER_JOIN_PHASE: Dibuixar títol i text (sobre el starfield)
// BLACK_SCREEN: no dibuixar res (fons negre ja està netejat) // BLACK_SCREEN: no draw res (fons negre ja està netejat)
if (estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) { if (estat_actual_ == EstatTitol::MAIN || estat_actual_ == EstatTitol::PLAYER_JOIN_PHASE) {
// === Calcular i renderitzar ombra (només si animació activa) === // === Calcular i renderitzar ombra (només si animació activa) ===
if (animacio_activa_) { if (animacio_activa_) {
@@ -726,5 +726,5 @@ auto EscenaTitol::checkStartGameButtonPressed() -> bool {
} }
void EscenaTitol::processar_events(const SDL_Event& event) { void EscenaTitol::processar_events(const SDL_Event& event) {
// No procesar eventos genéricos aquí - la lógica se movió a actualitzar() // No procesar eventos genéricos aquí - la lógica se movió a update()
} }
+2 -2
View File
@@ -102,9 +102,9 @@ class EscenaTitol {
static constexpr float DURACIO_LERP = 2.0F; // 2s per arribar a amplitud completa static constexpr float DURACIO_LERP = 2.0F; // 2s per arribar a amplitud completa
// Mètodes privats // Mètodes privats
void actualitzar(float delta_time); void update(float delta_time);
void actualitzar_animacio_logo(float delta_time); // Actualitza l'animació orbital del logo void actualitzar_animacio_logo(float delta_time); // Actualitza l'animació orbital del logo
void dibuixar(); void draw();
void processar_events(const SDL_Event& event); void processar_events(const SDL_Event& event);
auto checkSkipButtonPressed() -> bool; auto checkSkipButtonPressed() -> bool;
auto checkStartGameButtonPressed() -> bool; auto checkStartGameButtonPressed() -> bool;
+16 -16
View File
@@ -10,7 +10,7 @@
#include <utility> #include <utility>
#include "core/types.hpp" #include "core/types.hpp"
#include "game/entities/enemic.hpp" #include "game/entities/enemy.hpp"
#include "stage_config.hpp" #include "stage_config.hpp"
namespace StageSystem { namespace StageSystem {
@@ -44,7 +44,7 @@ void SpawnController::reset() {
index_spawn_actual_ = 0; index_spawn_actual_ = 0;
} }
void SpawnController::actualitzar(float delta_time, std::array<Enemic, 15>& orni_array, bool pausar) { void SpawnController::update(float delta_time, std::array<Enemy, 15>& orni_array, bool pausar) {
if ((config_ == nullptr) || spawn_queue_.empty()) { if ((config_ == nullptr) || spawn_queue_.empty()) {
return; return;
} }
@@ -66,7 +66,7 @@ void SpawnController::actualitzar(float delta_time, std::array<Enemic, 15>& orni
if (temps_transcorregut_ >= event.temps_spawn) { if (temps_transcorregut_ >= event.temps_spawn) {
// Find first inactive enemy // Find first inactive enemy
for (auto& enemic : orni_array) { for (auto& enemic : orni_array) {
if (!enemic.esta_actiu()) { if (!enemic.isActive()) {
spawn_enemic(enemic, event.tipus, ship_position_); spawn_enemic(enemic, event.tipus, ship_position_);
event.spawnejat = true; event.spawnejat = true;
index_spawn_actual_++; index_spawn_actual_++;
@@ -89,13 +89,13 @@ bool SpawnController::tots_enemics_spawnejats() const {
return index_spawn_actual_ >= spawn_queue_.size(); return index_spawn_actual_ >= spawn_queue_.size();
} }
bool SpawnController::tots_enemics_destruits(const std::array<Enemic, 15>& orni_array) const { bool SpawnController::tots_enemics_destruits(const std::array<Enemy, 15>& orni_array) const {
if (!tots_enemics_spawnejats()) { if (!tots_enemics_spawnejats()) {
return false; return false;
} }
for (const auto& enemic : orni_array) { for (const auto& enemic : orni_array) {
if (enemic.esta_actiu()) { if (enemic.isActive()) {
return false; return false;
} }
} }
@@ -103,10 +103,10 @@ bool SpawnController::tots_enemics_destruits(const std::array<Enemic, 15>& orni_
return true; return true;
} }
uint8_t SpawnController::get_enemics_vius(const std::array<Enemic, 15>& orni_array) const { uint8_t SpawnController::get_enemics_vius(const std::array<Enemy, 15>& orni_array) const {
uint8_t count = 0; uint8_t count = 0;
for (const auto& enemic : orni_array) { for (const auto& enemic : orni_array) {
if (enemic.esta_actiu()) { if (enemic.isActive()) {
count++; count++;
} }
} }
@@ -126,38 +126,38 @@ void SpawnController::generar_spawn_events() {
float spawn_time = config_->config_spawn.delay_inicial + float spawn_time = config_->config_spawn.delay_inicial +
(i * config_->config_spawn.interval_spawn); (i * config_->config_spawn.interval_spawn);
TipusEnemic tipus = seleccionar_tipus_aleatori(); EnemyType tipus = seleccionar_tipus_aleatori();
spawn_queue_.push_back({spawn_time, tipus, false}); spawn_queue_.push_back({spawn_time, tipus, false});
} }
} }
TipusEnemic SpawnController::seleccionar_tipus_aleatori() const { EnemyType SpawnController::seleccionar_tipus_aleatori() const {
if (config_ == nullptr) { if (config_ == nullptr) {
return TipusEnemic::PENTAGON; return EnemyType::PENTAGON;
} }
// Weighted random selection based on distribution // Weighted random selection based on distribution
int rand_val = std::rand() % 100; int rand_val = std::rand() % 100;
if (std::cmp_less(rand_val, config_->distribucio.pentagon)) { if (std::cmp_less(rand_val, config_->distribucio.pentagon)) {
return TipusEnemic::PENTAGON; return EnemyType::PENTAGON;
} }
if (rand_val < config_->distribucio.pentagon + config_->distribucio.quadrat) { if (rand_val < config_->distribucio.pentagon + config_->distribucio.quadrat) {
return TipusEnemic::QUADRAT; return EnemyType::QUADRAT;
} }
return TipusEnemic::MOLINILLO; return EnemyType::MOLINILLO;
} }
void SpawnController::spawn_enemic(Enemic& enemic, TipusEnemic tipus, const Vec2* ship_pos) { void SpawnController::spawn_enemic(Enemy& enemic, EnemyType tipus, const Vec2* ship_pos) {
// Initialize enemy (with safe spawn if ship_pos provided) // Initialize enemy (with safe spawn if ship_pos provided)
enemic.inicialitzar(tipus, ship_pos); enemic.init(tipus, ship_pos);
// Apply difficulty multipliers // Apply difficulty multipliers
aplicar_multiplicadors(enemic); aplicar_multiplicadors(enemic);
} }
void SpawnController::aplicar_multiplicadors(Enemic& enemic) const { void SpawnController::aplicar_multiplicadors(Enemy& enemic) const {
if (config_ == nullptr) { if (config_ == nullptr) {
return; return;
} }
@@ -8,7 +8,7 @@
#include <vector> #include <vector>
#include "core/types.hpp" #include "core/types.hpp"
#include "game/entities/enemic.hpp" #include "game/entities/enemy.hpp"
#include "stage_config.hpp" #include "stage_config.hpp"
namespace StageSystem { namespace StageSystem {
@@ -16,7 +16,7 @@ namespace StageSystem {
// Informació de spawn planificat // Informació de spawn planificat
struct SpawnEvent { struct SpawnEvent {
float temps_spawn; // Temps absolut (segons) per spawnejar float temps_spawn; // Temps absolut (segons) per spawnejar
TipusEnemic tipus; // Tipus d'enemic EnemyType tipus; // Tipus d'enemic
bool spawnejat; // Ja s'ha processat? bool spawnejat; // Ja s'ha processat?
}; };
@@ -30,12 +30,12 @@ class SpawnController {
void reset(); // Clear all pending spawns void reset(); // Clear all pending spawns
// Update // Update
void actualitzar(float delta_time, std::array<Enemic, 15>& orni_array, bool pausar = false); void update(float delta_time, std::array<Enemy, 15>& orni_array, bool pausar = false);
// Status queries // Status queries
[[nodiscard]] bool tots_enemics_spawnejats() const; [[nodiscard]] bool tots_enemics_spawnejats() const;
[[nodiscard]] bool tots_enemics_destruits(const std::array<Enemic, 15>& orni_array) const; [[nodiscard]] bool tots_enemics_destruits(const std::array<Enemy, 15>& orni_array) const;
[[nodiscard]] uint8_t get_enemics_vius(const std::array<Enemic, 15>& orni_array) const; [[nodiscard]] uint8_t get_enemics_vius(const std::array<Enemy, 15>& orni_array) const;
[[nodiscard]] uint8_t get_enemics_spawnejats() const; [[nodiscard]] uint8_t get_enemics_spawnejats() const;
// [NEW] Set ship position reference for safe spawn // [NEW] Set ship position reference for safe spawn
@@ -49,9 +49,9 @@ class SpawnController {
// Spawn generation // Spawn generation
void generar_spawn_events(); void generar_spawn_events();
[[nodiscard]] TipusEnemic seleccionar_tipus_aleatori() const; [[nodiscard]] EnemyType seleccionar_tipus_aleatori() const;
void spawn_enemic(Enemic& enemic, TipusEnemic tipus, const Vec2* ship_pos = nullptr); void spawn_enemic(Enemy& enemic, EnemyType tipus, const Vec2* ship_pos = nullptr);
void aplicar_multiplicadors(Enemic& enemic) const; void aplicar_multiplicadors(Enemy& enemic) const;
const Vec2* ship_position_; // [NEW] Non-owning pointer to ship position const Vec2* ship_position_; // [NEW] Non-owning pointer to ship position
}; };
+4 -4
View File
@@ -24,7 +24,7 @@ StageManager::StageManager(const ConfigSistemaStages* config)
} }
} }
void StageManager::inicialitzar() { void StageManager::init() {
stage_actual_ = 1; stage_actual_ = 1;
carregar_stage(stage_actual_); carregar_stage(stage_actual_);
canviar_estat(EstatStage::INIT_HUD); canviar_estat(EstatStage::INIT_HUD);
@@ -33,7 +33,7 @@ void StageManager::inicialitzar() {
<< '\n'; << '\n';
} }
void StageManager::actualitzar(float delta_time, bool pausar_spawn) { void StageManager::update(float delta_time, bool pausar_spawn) {
switch (estat_) { switch (estat_) {
case EstatStage::INIT_HUD: case EstatStage::INIT_HUD:
processar_init_hud(delta_time); processar_init_hud(delta_time);
@@ -129,10 +129,10 @@ void StageManager::processar_level_start(float delta_time) {
void StageManager::processar_playing(float delta_time, bool pausar_spawn) { void StageManager::processar_playing(float delta_time, bool pausar_spawn) {
// Update spawn controller (pauses when pausar_spawn = true) // Update spawn controller (pauses when pausar_spawn = true)
// Note: The actual enemy array update happens in EscenaJoc::actualitzar() // Note: The actual enemy array update happens in EscenaJoc::update()
// This is just for internal timekeeping // This is just for internal timekeeping
(void)delta_time; // Spawn controller is updated externally (void)delta_time; // Spawn controller is updated externally
(void)pausar_spawn; // Passed to spawn_controller_.actualitzar() by EscenaJoc (void)pausar_spawn; // Passed to spawn_controller_.update() by EscenaJoc
} }
void StageManager::processar_level_completed(float delta_time) { void StageManager::processar_level_completed(float delta_time) {
+2 -2
View File
@@ -24,8 +24,8 @@ class StageManager {
explicit StageManager(const ConfigSistemaStages* config); explicit StageManager(const ConfigSistemaStages* config);
// Lifecycle // Lifecycle
void inicialitzar(); // Reset to stage 1 void init(); // Reset to stage 1
void actualitzar(float delta_time, bool pausar_spawn = false); void update(float delta_time, bool pausar_spawn = false);
// Stage progression // Stage progression
void stage_completat(); // Call when all enemies destroyed void stage_completat(); // Call when all enemies destroyed
+3 -3
View File
@@ -17,7 +17,7 @@ ShipAnimator::ShipAnimator(SDL_Renderer* renderer)
: renderer_(renderer) { : renderer_(renderer) {
} }
void ShipAnimator::inicialitzar() { void ShipAnimator::init() {
// Carregar formes de naus amb perspectiva pre-calculada // Carregar formes de naus amb perspectiva pre-calculada
auto forma_p1 = Graphics::ShapeLoader::load("ship_perspective.shp"); // Perspectiva esquerra auto forma_p1 = Graphics::ShapeLoader::load("ship_perspective.shp"); // Perspectiva esquerra
auto forma_p2 = Graphics::ShapeLoader::load("ship2_perspective.shp"); // Perspectiva dreta auto forma_p2 = Graphics::ShapeLoader::load("ship2_perspective.shp"); // Perspectiva dreta
@@ -33,7 +33,7 @@ void ShipAnimator::inicialitzar() {
configurar_nau_p2(naus_[1]); configurar_nau_p2(naus_[1]);
} }
void ShipAnimator::actualitzar(float delta_time) { void ShipAnimator::update(float delta_time) {
// Dispatcher segons estat de cada nau // Dispatcher segons estat de cada nau
for (auto& nau : naus_) { for (auto& nau : naus_) {
if (!nau.visible) { if (!nau.visible) {
@@ -54,7 +54,7 @@ void ShipAnimator::actualitzar(float delta_time) {
} }
} }
void ShipAnimator::dibuixar() const { void ShipAnimator::draw() const {
for (const auto& nau : naus_) { for (const auto& nau : naus_) {
if (!nau.visible) { if (!nau.visible) {
continue; continue;
+3 -3
View File
@@ -64,9 +64,9 @@ class ShipAnimator {
explicit ShipAnimator(SDL_Renderer* renderer); explicit ShipAnimator(SDL_Renderer* renderer);
// Cicle de vida // Cicle de vida
void inicialitzar(); void init();
void actualitzar(float delta_time); void update(float delta_time);
void dibuixar() const; void draw() const;
// Control d'estat (cridat per EscenaTitol) // Control d'estat (cridat per EscenaTitol)
void start_entry_animation(); void start_entry_animation();