Files
orni_attack/source/game/effects/debris_manager.cpp
Sergio Valor 7f6af6dd00 style: aplicar checks modernize-* (215 fixes)
Cambios aplicados:
- [[nodiscard]] añadido a funciones que retornan valores
- .starts_with() en lugar de .find() == 0
- Inicializadores designados {.x=0, .y=0}
- auto en castings obvios
- = default para constructores triviales
- Funciones deleted movidas a public
- std::numbers::pi_v<float> (C++20)

Checks excluidos:
- use-trailing-return-type: Estilo controversial
- avoid-c-arrays: Arrays C aceptables en ciertos contextos
2025-12-18 20:16:46 +01:00

385 lines
14 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// debris_manager.cpp - Implementació del gestor de fragments
// © 2025 Port a C++20 amb SDL3
#include "debris_manager.hpp"
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include "core/audio/audio.hpp"
#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"
namespace Effects {
// Helper: transformar punt amb rotació, escala i trasllació
// (Copiat de shape_renderer.cpp:12-34)
static Punt transform_point(const Punt& point, const Punt& shape_centre, const Punt& posicio, float angle, float escala) {
// 1. Centrar el punt respecte al centre de la forma
float centered_x = point.x - shape_centre.x;
float centered_y = point.y - shape_centre.y;
// 2. Aplicar escala al punt centrat
float scaled_x = centered_x * escala;
float scaled_y = centered_y * escala;
// 3. Aplicar rotació
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);
// 4. Aplicar trasllació a posició mundial
return {.x = rotated_x + posicio.x, .y = rotated_y + posicio.y};
}
DebrisManager::DebrisManager(SDL_Renderer* renderer)
: renderer_(renderer) {
// Inicialitzar tots els debris com inactius
for (auto& debris : debris_pool_) {
debris.actiu = false;
}
}
void DebrisManager::explotar(const std::shared_ptr<Graphics::Shape>& shape,
const Punt& centre,
float angle,
float escala,
float velocitat_base,
float brightness,
const Punt& velocitat_objecte,
float velocitat_angular,
float factor_herencia_visual,
const std::string& sound) {
if (!shape || !shape->es_valida()) {
return;
}
// Reproducir sonido de explosión
Audio::get()->playSound(sound, Audio::Group::GAME);
// Obtenir centre de la forma per a transformacions
const Punt& shape_centre = shape->get_centre();
// Iterar sobre totes les primitives de la forma
for (const auto& primitive : shape->get_primitives()) {
// Processar cada segment de línia
std::vector<std::pair<Punt, Punt>> segments;
if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
// Polyline: extreure segments consecutius
for (size_t i = 0; i < primitive.points.size() - 1; i++) {
segments.emplace_back(primitive.points[i], primitive.points[i + 1]);
}
} else { // PrimitiveType::LINE
// Line: un únic segment
if (primitive.points.size() >= 2) {
segments.emplace_back(primitive.points[0], primitive.points[1]);
}
}
// Crear debris per a cada segment
for (const auto& [local_p1, local_p2] : segments) {
// 1. Transformar punts locals → coordenades mundials
Punt world_p1 =
transform_point(local_p1, shape_centre, centre, angle, escala);
Punt world_p2 =
transform_point(local_p2, shape_centre, centre, angle, escala);
// 2. Trobar slot lliure
Debris* debris = trobar_slot_lliure();
if (debris == nullptr) {
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
return; // Pool ple
}
// 3. Inicialitzar geometria
debris->p1 = world_p1;
debris->p2 = world_p2;
// 4. Calcular direcció d'explosió (radial, des del centre cap a fora)
Punt direccio = calcular_direccio_explosio(world_p1, world_p2, centre);
// 5. Velocitat inicial (base ± variació aleatòria + velocitat heretada)
float speed =
velocitat_base +
(((std::rand() / static_cast<float>(RAND_MAX)) * 2.0F - 1.0F) *
Defaults::Physics::Debris::VARIACIO_VELOCITAT);
// Heredar velocitat de l'objecte original (suma vectorial)
debris->velocitat.x = (direccio.x * speed) + velocitat_objecte.x;
debris->velocitat.y = (direccio.y * speed) + velocitat_objecte.y;
debris->acceleracio = Defaults::Physics::Debris::ACCELERACIO;
// 6. Herència de velocitat angular amb cap + conversió d'excés
// 6a. Rotació de TRAYECTORIA amb cap + conversió tangencial
if (std::abs(velocitat_angular) > 0.01F) {
// FASE 1: Aplicar herència i variació (igual que abans)
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: Aplicar cap i calcular excés
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) {
// Excés: convertir a velocitat tangencial
float excess = abs_ang - CAP;
// Radi de la forma (enemics = 20 px)
float radius = 20.0F;
// Velocitat tangencial = ω_excés × radi
float v_tangential = excess * radius;
// Direcció tangencial: perpendicular a la radial (90° CCW)
// Si direccio = (dx, dy), tangent = (-dy, dx)
float tangent_x = -direccio.y;
float tangent_y = direccio.x;
// Afegir velocitat tangencial (suma vectorial)
debris->velocitat.x += tangent_x * v_tangential;
debris->velocitat.y += tangent_y * v_tangential;
// Aplicar cap a velocitat angular (preservar signe)
debris->velocitat_rot = sign_ang * CAP;
} else {
// Per sota del cap: comportament normal
debris->velocitat_rot = velocitat_ang_heretada;
}
} else {
debris->velocitat_rot = 0.0F; // Nave: sin curvas
}
// 6b. Rotació VISUAL (proporcional según factor_herencia_visual)
if (factor_herencia_visual > 0.01F && std::abs(velocitat_angular) > 0.01F) {
// Heredar rotación visual con factor proporcional
debris->velocitat_rot_visual = debris->velocitat_rot * factor_herencia_visual;
// Variació aleatòria petita (±5%) per naturalitat
float variacio_visual =
((std::rand() / static_cast<float>(RAND_MAX)) * 0.1F) - 0.05F;
debris->velocitat_rot_visual *= (1.0F + variacio_visual);
} else {
// Rotació 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ó en sentit contrari
if (std::rand() % 2 == 0) {
debris->velocitat_rot_visual = -debris->velocitat_rot_visual;
}
}
debris->angle_rotacio = 0.0F;
// 7. Configurar vida i shrinking
debris->temps_vida = 0.0F;
debris->temps_max = Defaults::Physics::Debris::TEMPS_VIDA;
debris->factor_shrink = Defaults::Physics::Debris::SHRINK_RATE;
// 8. Heredar brightness
debris->brightness = brightness;
// 9. Activar
debris->actiu = true;
}
}
}
void DebrisManager::actualitzar(float delta_time) {
for (auto& debris : debris_pool_) {
if (!debris.actiu) {
continue;
}
// 1. Actualitzar temps de vida
debris.temps_vida += delta_time;
// Desactivar si ha superat temps màxim
if (debris.temps_vida >= debris.temps_max) {
debris.actiu = false;
continue;
}
// 2. Actualitzar velocitat (desacceleració)
// Aplicar fricció en la direcció del moviment
float speed = std::sqrt((debris.velocitat.x * debris.velocitat.x) +
(debris.velocitat.y * debris.velocitat.y));
if (speed > 1.0F) {
// Calcular direcció normalitzada
float dir_x = debris.velocitat.x / speed;
float dir_y = debris.velocitat.y / speed;
// Aplicar acceleració negativa (fricció)
float nova_speed = speed + (debris.acceleracio * delta_time);
nova_speed = std::max(nova_speed, 0.0F);
debris.velocitat.x = dir_x * nova_speed;
debris.velocitat.y = dir_y * nova_speed;
} else {
// Velocitat molt baixa, aturar
debris.velocitat.x = 0.0F;
debris.velocitat.y = 0.0F;
}
// 2b. Rotar vector de velocitat (trayectoria curva)
if (std::abs(debris.velocitat_rot) > 0.01F) {
// Calcular angle de rotació aquest frame
float dangle = debris.velocitat_rot * delta_time;
// Rotar vector de velocitat usant matriu de rotació 2D
float vel_x_old = debris.velocitat.x;
float vel_y_old = debris.velocitat.y;
float cos_a = std::cos(dangle);
float sin_a = std::sin(dangle);
debris.velocitat.x = (vel_x_old * cos_a) - (vel_y_old * sin_a);
debris.velocitat.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 centre del segment
Punt centre = {.x = (debris.p1.x + debris.p2.x) / 2.0F,
.y = (debris.p1.y + debris.p2.y) / 2.0F};
// 4. Actualitzar posició del centre
centre.x += debris.velocitat.x * delta_time;
centre.y += debris.velocitat.y * delta_time;
// 5. Actualitzar rotació VISUAL
debris.angle_rotacio += debris.velocitat_rot_visual * delta_time;
// 6. Aplicar shrinking (reducció de distància entre punts)
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 distància original entre punts
float dx = debris.p2.x - debris.p1.x;
float dy = debris.p2.y - debris.p1.y;
// 7. Reconstruir segment amb nova mida i rotació
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 = centre.x - (half_length * std::cos(new_angle));
debris.p1.y = centre.y - (half_length * std::sin(new_angle));
debris.p2.x = centre.x + (half_length * std::cos(new_angle));
debris.p2.y = centre.y + (half_length * std::sin(new_angle));
}
}
void DebrisManager::dibuixar() const {
for (const auto& debris : debris_pool_) {
if (!debris.actiu) {
continue;
}
// Dibuixar segment de línia amb brightness heretat
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),
true,
debris.brightness);
}
}
Debris* DebrisManager::trobar_slot_lliure() {
for (auto& debris : debris_pool_) {
if (!debris.actiu) {
return &debris;
}
}
return nullptr; // Pool ple
}
Punt DebrisManager::calcular_direccio_explosio(const Punt& p1,
const Punt& p2,
const Punt& centre_objecte) const {
// 1. Calcular centre 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 centre de l'objecte cap al centre del segment
// Això garanteix que la direcció sempre apunte cap a fora (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 centre (cas extrem molt 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. Afegir variació aleatòria petita (±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::reiniciar() {
for (auto& debris : debris_pool_) {
debris.actiu = false;
}
}
int DebrisManager::get_num_actius() const {
int count = 0;
for (const auto& debris : debris_pool_) {
if (debris.actiu) {
count++;
}
}
return count;
}
} // namespace Effects