feat(debris): vida híbrida (mínima + umbral velocidad) + multiplier para enemigos

This commit is contained in:
2026-05-21 12:07:50 +02:00
parent 44aa4e76e2
commit efd18ff852
5 changed files with 388 additions and 357 deletions
+338 -325
View File
@@ -14,375 +14,388 @@
namespace Effects {
// Helper: transformar point con rotación, scale i traslación
// (Copiat de shape_renderer.cpp:12-34)
static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
// 1. Centrar el point respecte al centro de la shape
float centered_x = point.x - shape_centre.x;
float centered_y = point.y - shape_centre.y;
// Helper: transformar point con rotación, scale i traslación
// (Copiat de shape_renderer.cpp:12-34)
static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
// 1. Centrar el point respecte al centro de la shape
float centered_x = point.x - shape_centre.x;
float centered_y = point.y - shape_centre.y;
// 2. Aplicar scale al point centrat
float scaled_x = centered_x * scale;
float scaled_y = centered_y * scale;
// 2. Aplicar scale al point centrat
float scaled_x = centered_x * scale;
float scaled_y = centered_y * scale;
// 3. Aplicar rotación
float cos_a = std::cos(angle);
float sin_a = std::sin(angle);
// 3. Aplicar rotación
float cos_a = std::cos(angle);
float sin_a = std::sin(angle);
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a);
float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a);
float rotated_x = (scaled_x * cos_a) - (scaled_y * sin_a);
float rotated_y = (scaled_x * sin_a) + (scaled_y * cos_a);
// 4. Aplicar traslación a posición mundial
return {.x = rotated_x + position.x, .y = rotated_y + position.y};
}
DebrisManager::DebrisManager(Rendering::Renderer* renderer)
: renderer_(renderer) {
// Inicialitzar todos los debris como inactius
for (auto& debris : debris_pool_) {
debris.active = false;
}
}
void DebrisManager::explode(const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& centro,
float angle,
float scale,
float velocitat_base,
float brightness,
const Vec2& velocitat_objecte,
float velocitat_angular,
float factor_herencia_visual,
const std::string& sound,
SDL_Color color) {
if (!shape || !shape->isValid()) {
return;
// 4. Aplicar traslación a posición mundial
return {.x = rotated_x + position.x, .y = rotated_y + position.y};
}
// Reproducir sonido de explosión
Audio::get()->playSound(sound, Audio::Group::GAME);
DebrisManager::DebrisManager(Rendering::Renderer* renderer)
: renderer_(renderer) {
// Inicialitzar todos los debris como inactius
for (auto& debris : debris_pool_) {
debris.active = false;
}
}
const Vec2& shape_centre = shape->getCenter();
void DebrisManager::explode(const std::shared_ptr<Graphics::Shape>& shape,
const Vec2& centro,
float angle,
float scale,
float velocitat_base,
float brightness,
const Vec2& velocitat_objecte,
float velocitat_angular,
float factor_herencia_visual,
const std::string& sound,
SDL_Color color,
float lifetime,
float friction,
int segment_multiplier) {
if (!shape || !shape->isValid()) {
return;
}
for (const auto& primitive : shape->getPrimitives()) {
for (const auto& [local_p1, local_p2] : extractSegments(primitive)) {
// Transformar points locals → coordenades mundials
Vec2 world_p1 = transformPoint(local_p1, shape_centre, centro, angle, scale);
Vec2 world_p2 = transformPoint(local_p2, shape_centre, centro, angle, scale);
// Reproducir sonido de explosión
Audio::get()->playSound(sound, Audio::Group::GAME);
// Si el pool es ple, no té sentit continuar amb la resta de segments
if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness,
velocitat_objecte, velocitat_angular,
factor_herencia_visual, color)) {
return;
const Vec2& shape_centre = shape->getCenter();
// Multiplier: cada segment s'emet N vegades amb direccions aleatòries
// distintes (la variació ±15° de computeExplosionDirection ho garanteix).
const int COPIES = std::max(1, segment_multiplier);
for (int copy = 0; copy < COPIES; copy++) {
for (const auto& primitive : shape->getPrimitives()) {
for (const auto& [local_p1, local_p2] : extractSegments(primitive)) {
// Transformar points locals → coordenades mundials
Vec2 world_p1 = transformPoint(local_p1, shape_centre, centro, angle, scale);
Vec2 world_p2 = transformPoint(local_p2, shape_centre, centro, angle, scale);
// Si el pool es ple, no té sentit continuar amb la resta de segments
if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness, velocitat_objecte, velocitat_angular, factor_herencia_visual, color, lifetime, friction)) {
return;
}
}
}
}
}
}
auto DebrisManager::extractSegments(const Graphics::ShapePrimitive& primitive)
-> std::vector<std::pair<Vec2, Vec2>> {
std::vector<std::pair<Vec2, Vec2>> segments;
auto DebrisManager::extractSegments(const Graphics::ShapePrimitive& primitive)
-> std::vector<std::pair<Vec2, Vec2>> {
std::vector<std::pair<Vec2, Vec2>> segments;
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
// Polyline: extreure segments consecutius
for (size_t i = 0; i + 1 < primitive.points.size(); i++) {
segments.emplace_back(primitive.points[i], primitive.points[i + 1]);
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
// Polyline: extreure segments consecutius
for (size_t i = 0; i + 1 < primitive.points.size(); i++) {
segments.emplace_back(primitive.points[i], primitive.points[i + 1]);
}
return segments;
}
// PrimitiveType::LINE: un únic segment (si té els 2 punts)
if (primitive.points.size() >= 2) {
segments.emplace_back(primitive.points[0], primitive.points[1]);
}
return segments;
}
// PrimitiveType::LINE: un únic segment (si té els 2 punts)
if (primitive.points.size() >= 2) {
segments.emplace_back(primitive.points[0], primitive.points[1]);
}
return segments;
}
auto DebrisManager::spawnDebris(const Vec2& world_p1, const Vec2& world_p2,
const Vec2& centro, float velocitat_base, float brightness,
const Vec2& velocitat_objecte, float velocitat_angular,
float factor_herencia_visual, SDL_Color color) -> bool {
Debris* debris = findFreeSlot();
if (debris == nullptr) {
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
return false;
}
// Geometria
debris->p1 = world_p1;
debris->p2 = world_p2;
// Direcció radial (desde el centro hacia el segment)
Vec2 direccio = computeExplosionDirection(world_p1, world_p2, centro);
// Velocidad inicial (base ± variació aleatòria + velocity heretada de l'objecte)
float speed =
velocitat_base +
(((std::rand() / static_cast<float>(RAND_MAX)) * 2.0F - 1.0F) *
Defaults::Physics::Debris::VARIACIO_VELOCITAT);
debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x;
debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y;
debris->acceleration = Defaults::Physics::Debris::ACCELERACIO;
// Rotación de trayectoria (con conversió a tangencial si excedeix cap)
applyAngularVelocity(*debris, direccio, velocitat_angular);
// Rotación visual (proporcional o aleatòria)
applyVisualRotation(*debris, velocitat_angular, factor_herencia_visual);
debris->angle_rotacio = 0.0F;
// Vida i shrinking
debris->temps_vida = 0.0F;
debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA;
debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE;
// Visuals heretades
debris->brightness = brightness;
debris->color = color;
debris->active = true;
return true;
}
void DebrisManager::applyAngularVelocity(Debris& debris, const Vec2& direccio,
float velocitat_angular) {
if (std::abs(velocitat_angular) <= 0.01F) {
debris.velocitat_rot = 0.0F; // Nave: sin curvas
return;
}
// FASE 1: Aplicar herència i variació
float factor_herencia =
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN +
((std::rand() / static_cast<float>(RAND_MAX)) *
(Defaults::Physics::Debris::FACTOR_HERENCIA_MAX -
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN));
float velocitat_ang_heretada = velocitat_angular * factor_herencia;
float variacio = ((std::rand() / static_cast<float>(RAND_MAX)) * 0.2F) - 0.1F;
velocitat_ang_heretada *= (1.0F + variacio);
// FASE 2: Cap a la velocity màxima; l'excés es converteix en tangencial
constexpr float CAP = Defaults::Physics::Debris::VELOCITAT_ROT_MAX;
float abs_ang = std::abs(velocitat_ang_heretada);
float sign_ang = (velocitat_ang_heretada >= 0.0F) ? 1.0F : -1.0F;
if (abs_ang <= CAP) {
debris.velocitat_rot = velocitat_ang_heretada;
return;
}
// Excés: converteix l'excés de velocitat angular en velocitat tangencial lineal
float excess = abs_ang - CAP;
constexpr float RADIUS = 20.0F; // Radi típic de la shape (enemigos = 20 px)
float v_tangential = excess * RADIUS;
// Direcció tangencial: perpendicular a la radial (90° CCW): tangent = (-dy, dx)
debris.velocity.x += -direccio.y * v_tangential;
debris.velocity.y += direccio.x * v_tangential;
// Velocitat angular limitada al cap (preservant el signe)
debris.velocitat_rot = sign_ang * CAP;
}
void DebrisManager::applyVisualRotation(Debris& debris, float velocitat_angular,
float factor_herencia_visual) {
if (factor_herencia_visual > 0.01F && std::abs(velocitat_angular) > 0.01F) {
// Heredar rotación visual con factor proporcional + ±5% de variació
debris.velocitat_rot_visual = debris.velocitat_rot * factor_herencia_visual;
float variacio_visual =
((std::rand() / static_cast<float>(RAND_MAX)) * 0.1F) - 0.05F;
debris.velocitat_rot_visual *= (1.0F + variacio_visual);
return;
}
// Rotación visual aleatòria (factor = 0.0 o sin velocidad angular)
debris.velocitat_rot_visual =
Defaults::Physics::Debris::ROTACIO_MIN +
((std::rand() / static_cast<float>(RAND_MAX)) *
(Defaults::Physics::Debris::ROTACIO_MAX -
Defaults::Physics::Debris::ROTACIO_MIN));
// 50% probabilitat de rotación en sentit contrari
if (std::rand() % 2 == 0) {
debris.velocitat_rot_visual = -debris.velocitat_rot_visual;
}
}
void DebrisManager::update(float delta_time) {
for (auto& debris : debris_pool_) {
if (!debris.active) {
continue;
auto DebrisManager::spawnDebris(const Vec2& world_p1, const Vec2& world_p2, const Vec2& centro, float velocitat_base, float brightness, const Vec2& velocitat_objecte, float velocitat_angular, float factor_herencia_visual, SDL_Color color, float lifetime, float friction) -> bool {
Debris* debris = findFreeSlot();
if (debris == nullptr) {
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
return false;
}
// 1. Actualitzar time de vida
debris.temps_vida += delta_time;
// Geometria
debris->p1 = world_p1;
debris->p2 = world_p2;
// Desactivar si ha superat time màxim
if (debris.temps_vida >= debris.temps_max) {
debris.active = false;
continue;
// Direcció radial (desde el centro hacia el segment)
Vec2 direccio = computeExplosionDirection(world_p1, world_p2, centro);
// Velocidad inicial (base ± variació aleatòria + velocity heretada de l'objecte)
float speed =
velocitat_base +
(((std::rand() / static_cast<float>(RAND_MAX)) * 2.0F - 1.0F) *
Defaults::Physics::Debris::VARIACIO_VELOCITAT);
debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x;
debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y;
debris->acceleration = friction;
// Rotación de trayectoria (con conversió a tangencial si excedeix cap)
applyAngularVelocity(*debris, direccio, velocitat_angular);
// Rotación visual (proporcional o aleatòria)
applyVisualRotation(*debris, velocitat_angular, factor_herencia_visual);
debris->angle_rotacio = 0.0F;
// Vida i shrinking — min_lifetime és el temps mínim garantit; després
// el fragment mor quan |velocity| < MIN_SPEED_TO_DIE.
debris->temps_vida = 0.0F;
debris->min_lifetime = lifetime;
debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE;
// Visuals heretades
debris->brightness = brightness;
debris->color = color;
debris->active = true;
return true;
}
void DebrisManager::applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular) {
if (std::abs(velocitat_angular) <= 0.01F) {
debris.velocitat_rot = 0.0F; // Nave: sin curvas
return;
}
// 2. Actualitzar velocity (desacceleració)
// Aplicar fricció en la direcció del movement
float speed = std::sqrt((debris.velocity.x * debris.velocity.x) +
(debris.velocity.y * debris.velocity.y));
// FASE 1: Aplicar herència i variació
float factor_herencia =
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN +
((std::rand() / static_cast<float>(RAND_MAX)) *
(Defaults::Physics::Debris::FACTOR_HERENCIA_MAX -
Defaults::Physics::Debris::FACTOR_HERENCIA_MIN));
float velocitat_ang_heretada = velocitat_angular * factor_herencia;
float variacio = ((std::rand() / static_cast<float>(RAND_MAX)) * 0.2F) - 0.1F;
velocitat_ang_heretada *= (1.0F + variacio);
if (speed > 1.0F) {
// Calcular direcció normalitzada
float dir_x = debris.velocity.x / speed;
float dir_y = debris.velocity.y / speed;
// FASE 2: Cap a la velocity màxima; l'excés es converteix en tangencial
constexpr float CAP = Defaults::Physics::Debris::VELOCITAT_ROT_MAX;
float abs_ang = std::abs(velocitat_ang_heretada);
float sign_ang = (velocitat_ang_heretada >= 0.0F) ? 1.0F : -1.0F;
// Aplicar aceleración negativa (fricció)
float nova_speed = speed + (debris.acceleration * delta_time);
nova_speed = std::max(nova_speed, 0.0F);
debris.velocity.x = dir_x * nova_speed;
debris.velocity.y = dir_y * nova_speed;
} else {
// Velocidad mucho baixa, aturar
debris.velocity.x = 0.0F;
debris.velocity.y = 0.0F;
if (abs_ang <= CAP) {
debris.velocitat_rot = velocitat_ang_heretada;
return;
}
// 2b. Rotar vector de velocity (trayectoria curva)
if (std::abs(debris.velocitat_rot) > 0.01F) {
// Calcular angle de rotación este frame
float dangle = debris.velocitat_rot * delta_time;
// Excés: converteix l'excés de velocitat angular en velocitat tangencial lineal
float excess = abs_ang - CAP;
constexpr float RADIUS = 20.0F; // Radi típic de la shape (enemigos = 20 px)
float v_tangential = excess * RADIUS;
// Rotar vector de velocity usant matriu de rotación 2D
float vel_x_old = debris.velocity.x;
float vel_y_old = debris.velocity.y;
// Direcció tangencial: perpendicular a la radial (90° CCW): tangent = (-dy, dx)
debris.velocity.x += -direccio.y * v_tangential;
debris.velocity.y += direccio.x * v_tangential;
float cos_a = std::cos(dangle);
float sin_a = std::sin(dangle);
// Velocitat angular limitada al cap (preservant el signe)
debris.velocitat_rot = sign_ang * CAP;
}
debris.velocity.x = (vel_x_old * cos_a) - (vel_y_old * sin_a);
debris.velocity.y = (vel_x_old * sin_a) + (vel_y_old * cos_a);
void DebrisManager::applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual) {
if (factor_herencia_visual > 0.01F && std::abs(velocitat_angular) > 0.01F) {
// Heredar rotación visual con factor proporcional + ±5% de variació
debris.velocitat_rot_visual = debris.velocitat_rot * factor_herencia_visual;
float variacio_visual =
((std::rand() / static_cast<float>(RAND_MAX)) * 0.1F) - 0.05F;
debris.velocitat_rot_visual *= (1.0F + variacio_visual);
return;
}
// 2c. Aplicar fricció angular (desacceleració gradual)
if (std::abs(debris.velocitat_rot) > 0.01F) {
float sign = (debris.velocitat_rot > 0) ? 1.0F : -1.0F;
float reduccion =
Defaults::Physics::Debris::FRICCIO_ANGULAR * delta_time;
debris.velocitat_rot -= sign * reduccion;
// Rotación visual aleatòria (factor = 0.0 o sin velocidad angular)
debris.velocitat_rot_visual =
Defaults::Physics::Debris::ROTACIO_MIN +
((std::rand() / static_cast<float>(RAND_MAX)) *
(Defaults::Physics::Debris::ROTACIO_MAX -
Defaults::Physics::Debris::ROTACIO_MIN));
// Evitar canvi de signe (no pot passar de CW a CCW)
if ((debris.velocitat_rot > 0) != (sign > 0)) {
debris.velocitat_rot = 0.0F;
// 50% probabilitat de rotación en sentit contrari
if (std::rand() % 2 == 0) {
debris.velocitat_rot_visual = -debris.velocitat_rot_visual;
}
}
void DebrisManager::update(float delta_time) {
for (auto& debris : debris_pool_) {
if (!debris.active) {
continue;
}
// 1. Actualitzar time de vida
debris.temps_vida += delta_time;
// Política de mort: viu sí o sí durant min_lifetime; després mor
// quan la velocity cau per sota d'un llindar. Així els fragments
// ràpids no desapareixen en moviment.
if (debris.temps_vida >= debris.min_lifetime) {
const float SPEED_SQ = (debris.velocity.x * debris.velocity.x) +
(debris.velocity.y * debris.velocity.y);
if (SPEED_SQ < Defaults::Physics::Debris::MIN_SPEED_TO_DIE_SQ) {
debris.active = false;
continue;
}
}
// 2. Actualitzar velocity (desacceleració)
// Aplicar fricció en la direcció del movement
float speed = std::sqrt((debris.velocity.x * debris.velocity.x) +
(debris.velocity.y * debris.velocity.y));
if (speed > 1.0F) {
// Calcular direcció normalitzada
float dir_x = debris.velocity.x / speed;
float dir_y = debris.velocity.y / speed;
// Aplicar aceleración negativa (fricció)
float nova_speed = speed + (debris.acceleration * delta_time);
nova_speed = std::max(nova_speed, 0.0F);
debris.velocity.x = dir_x * nova_speed;
debris.velocity.y = dir_y * nova_speed;
} else {
// Velocidad mucho baixa, aturar
debris.velocity.x = 0.0F;
debris.velocity.y = 0.0F;
}
// 2b. Rotar vector de velocity (trayectoria curva)
if (std::abs(debris.velocitat_rot) > 0.01F) {
// Calcular angle de rotación este frame
float dangle = debris.velocitat_rot * delta_time;
// Rotar vector de velocity usant matriu de rotación 2D
float vel_x_old = debris.velocity.x;
float vel_y_old = debris.velocity.y;
float cos_a = std::cos(dangle);
float sin_a = std::sin(dangle);
debris.velocity.x = (vel_x_old * cos_a) - (vel_y_old * sin_a);
debris.velocity.y = (vel_x_old * sin_a) + (vel_y_old * cos_a);
}
// 2c. Aplicar fricció angular (desacceleració gradual)
if (std::abs(debris.velocitat_rot) > 0.01F) {
float sign = (debris.velocitat_rot > 0) ? 1.0F : -1.0F;
float reduccion =
Defaults::Physics::Debris::FRICCIO_ANGULAR * delta_time;
debris.velocitat_rot -= sign * reduccion;
// Evitar canvi de signe (no pot passar de CW a CCW)
if ((debris.velocitat_rot > 0) != (sign > 0)) {
debris.velocitat_rot = 0.0F;
}
}
// 3. Calcular centro del segment
Vec2 centro = {.x = (debris.p1.x + debris.p2.x) / 2.0F,
.y = (debris.p1.y + debris.p2.y) / 2.0F};
// 4. Actualitzar posición del centro
centro.x += debris.velocity.x * delta_time;
centro.y += debris.velocity.y * delta_time;
// 5. Actualitzar rotación VISUAL
debris.angle_rotacio += debris.velocitat_rot_visual * delta_time;
// 6. Aplicar shrinking (reducció de distancia entre points).
// El shrink es normalitza al min_lifetime (capat a 1.0) perquè els
// fragments que viuen més no es continuïn fent més petits per sempre.
const float SHRINK_T = std::min(debris.temps_vida / debris.min_lifetime, 1.0F);
float shrink_factor = 1.0F - (debris.factor_shrink * SHRINK_T);
shrink_factor = std::max(0.0F, shrink_factor); // No negatiu
// Calcular distancia original entre points
float dx = debris.p2.x - debris.p1.x;
float dy = debris.p2.y - debris.p1.y;
// 7. Reconstruir segment con nueva mida i rotación
float half_length = std::sqrt((dx * dx) + (dy * dy)) * shrink_factor / 2.0F;
float original_angle = std::atan2(dy, dx);
float new_angle = original_angle + debris.angle_rotacio;
debris.p1.x = centro.x - (half_length * std::cos(new_angle));
debris.p1.y = centro.y - (half_length * std::sin(new_angle));
debris.p2.x = centro.x + (half_length * std::cos(new_angle));
debris.p2.y = centro.y + (half_length * std::sin(new_angle));
}
}
void DebrisManager::draw() const {
for (const auto& debris : debris_pool_) {
if (!debris.active) {
continue;
}
// Dibujar segmento con brightness y color heredados del padre.
Rendering::linea(renderer_,
static_cast<int>(debris.p1.x),
static_cast<int>(debris.p1.y),
static_cast<int>(debris.p2.x),
static_cast<int>(debris.p2.y),
debris.brightness,
0.0F,
debris.color);
}
}
auto DebrisManager::findFreeSlot() -> Debris* {
for (auto& debris : debris_pool_) {
if (!debris.active) {
return &debris;
}
}
// 3. Calcular centro del segment
Vec2 centro = {.x = (debris.p1.x + debris.p2.x) / 2.0F,
.y = (debris.p1.y + debris.p2.y) / 2.0F};
// 4. Actualitzar posición del centro
centro.x += debris.velocity.x * delta_time;
centro.y += debris.velocity.y * delta_time;
// 5. Actualitzar rotación VISUAL
debris.angle_rotacio += debris.velocitat_rot_visual * delta_time;
// 6. Aplicar shrinking (reducció de distancia entre points)
float shrink_factor =
1.0F - (debris.factor_shrink * debris.temps_vida / debris.temps_max);
shrink_factor = std::max(0.0F, shrink_factor); // No negatiu
// Calcular distancia original entre points
float dx = debris.p2.x - debris.p1.x;
float dy = debris.p2.y - debris.p1.y;
// 7. Reconstruir segment con nueva mida i rotación
float half_length = std::sqrt((dx * dx) + (dy * dy)) * shrink_factor / 2.0F;
float original_angle = std::atan2(dy, dx);
float new_angle = original_angle + debris.angle_rotacio;
debris.p1.x = centro.x - (half_length * std::cos(new_angle));
debris.p1.y = centro.y - (half_length * std::sin(new_angle));
debris.p2.x = centro.x + (half_length * std::cos(new_angle));
debris.p2.y = centro.y + (half_length * std::sin(new_angle));
return nullptr; // Pool ple
}
}
void DebrisManager::draw() const {
for (const auto& debris : debris_pool_) {
if (!debris.active) {
continue;
auto DebrisManager::computeExplosionDirection(const Vec2& p1,
const Vec2& p2,
const Vec2& centre_objecte) -> Vec2 {
// 1. Calcular centro del segment
float centro_seg_x = (p1.x + p2.x) / 2.0F;
float centro_seg_y = (p1.y + p2.y) / 2.0F;
// 2. Calcular vector des del centro de l'objecte hacia el centro del segment
// Això garanteix que la direcció siempre apunte hacia fuera (direcció radial)
float dx = centro_seg_x - centre_objecte.x;
float dy = centro_seg_y - centre_objecte.y;
// 3. Normalitzar (obtenir vector unitari)
float length = std::sqrt((dx * dx) + (dy * dy));
if (length < 0.001F) {
// Segment al centro (cas extrem mucho improbable), retornar direcció aleatòria
float angle_rand =
(std::rand() / static_cast<float>(RAND_MAX)) * 2.0F * Defaults::Math::PI;
return {.x = std::cos(angle_rand), .y = std::sin(angle_rand)};
}
// Dibujar segmento con brightness y color heredados del padre.
Rendering::linea(renderer_,
static_cast<int>(debris.p1.x),
static_cast<int>(debris.p1.y),
static_cast<int>(debris.p2.x),
static_cast<int>(debris.p2.y),
debris.brightness, 0.0F, debris.color);
}
}
dx /= length;
dy /= length;
auto DebrisManager::findFreeSlot() -> Debris* {
for (auto& debris : debris_pool_) {
if (!debris.active) {
return &debris;
// 4. Añadir variació aleatòria pequeña (±15°) per varietat visual
float angle_variacio =
((std::rand() % 30) - 15) * Defaults::Math::PI / 180.0F;
float cos_v = std::cos(angle_variacio);
float sin_v = std::sin(angle_variacio);
float final_x = (dx * cos_v) - (dy * sin_v);
float final_y = (dx * sin_v) + (dy * cos_v);
return {.x = final_x, .y = final_y};
}
void DebrisManager::reset() {
for (auto& debris : debris_pool_) {
debris.active = false;
}
}
return nullptr; // Pool ple
}
auto DebrisManager::computeExplosionDirection(const Vec2& p1,
const Vec2& p2,
const Vec2& centre_objecte) -> Vec2 {
// 1. Calcular centro del segment
float centro_seg_x = (p1.x + p2.x) / 2.0F;
float centro_seg_y = (p1.y + p2.y) / 2.0F;
// 2. Calcular vector des del centro de l'objecte hacia el centro del segment
// Això garanteix que la direcció siempre apunte hacia fuera (direcció radial)
float dx = centro_seg_x - centre_objecte.x;
float dy = centro_seg_y - centre_objecte.y;
// 3. Normalitzar (obtenir vector unitari)
float length = std::sqrt((dx * dx) + (dy * dy));
if (length < 0.001F) {
// Segment al centro (cas extrem mucho improbable), retornar direcció aleatòria
float angle_rand =
(std::rand() / static_cast<float>(RAND_MAX)) * 2.0F * Defaults::Math::PI;
return {.x = std::cos(angle_rand), .y = std::sin(angle_rand)};
}
dx /= length;
dy /= length;
// 4. Añadir variació aleatòria pequeña (±15°) per varietat visual
float angle_variacio =
((std::rand() % 30) - 15) * Defaults::Math::PI / 180.0F;
float cos_v = std::cos(angle_variacio);
float sin_v = std::sin(angle_variacio);
float final_x = (dx * cos_v) - (dy * sin_v);
float final_y = (dx * sin_v) + (dy * cos_v);
return {.x = final_x, .y = final_y};
}
void DebrisManager::reset() {
for (auto& debris : debris_pool_) {
debris.active = false;
}
}
auto DebrisManager::getActiveCount() const -> int {
int count = 0;
for (const auto& debris : debris_pool_) {
if (debris.active) {
count++;
auto DebrisManager::getActiveCount() const -> int {
int count = 0;
for (const auto& debris : debris_pool_) {
if (debris.active) {
count++;
}
}
return count;
}
return count;
}
} // namespace Effects