// debris_manager.cpp - Implementació del gestor de fragments // © 2025 Port a C++20 amb SDL3 #include "debris_manager.hpp" #include #include #include #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 {rotated_x + posicio.x, 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& shape, const Punt& centre, float angle, float escala, float velocitat_base, float brightness, const Punt& velocitat_objecte) { if (!shape || !shape->es_valida()) { return; } // Reproducir sonido de explosión Audio::get()->playSound(Defaults::Sound::EXPLOSION, 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> segments; if (primitive.type == Graphics::PrimitiveType::POLYLINE) { // Polyline: extreure segments consecutius for (size_t i = 0; i < primitive.points.size() - 1; i++) { segments.push_back({primitive.points[i], primitive.points[i + 1]}); } } else { // PrimitiveType::LINE // Line: un únic segment if (primitive.points.size() >= 2) { segments.push_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) { 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(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. Rotació lenta aleatòria debris->velocitat_rot = Defaults::Physics::Debris::ROTACIO_MIN + (std::rand() / static_cast(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 = -debris->velocitat_rot; } 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; if (nova_speed < 0.0f) 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; } // 3. Calcular centre del segment Punt centre = {(debris.p1.x + debris.p2.x) / 2.0f, (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ó debris.angle_rotacio += debris.velocitat_rot * 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(debris.p1.x), static_cast(debris.p1.y), static_cast(debris.p2.x), static_cast(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(RAND_MAX)) * 2.0f * Defaults::Math::PI; return {std::cos(angle_rand), 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 {final_x, 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