From 18e05e36e672ec056052dcd3b385a9727d03a865 Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Fri, 22 May 2026 18:42:23 +0200 Subject: [PATCH] feat(bullet): debris en trencar-se amb so HIT mogut des d'enemy.herir() --- source/game/entities/bullet.cpp | 24 ++---------- source/game/entities/enemy.cpp | 4 +- source/game/scenes/game_scene.cpp | 11 ++++-- source/game/systems/collision_system.cpp | 50 +++++++++++++++++++++++- source/game/systems/collision_system.hpp | 7 ++++ 5 files changed, 69 insertions(+), 27 deletions(-) diff --git a/source/game/entities/bullet.cpp b/source/game/entities/bullet.cpp index 352ba08..dc77574 100644 --- a/source/game/entities/bullet.cpp +++ b/source/game/entities/bullet.cpp @@ -80,26 +80,10 @@ void Bullet::disparar(const Vec2& position, float angle, uint8_t owner_id) { } void Bullet::update(float /*delta_time*/) { - if (!is_active_) { - return; - } - - // El movimiento real lo hace PhysicsWorld::update() (integración). - // Aquí solo lógica de estado: detectar salida del PLAYAREA i desactivar. - // Sense marge de seguretat: la bala mor quan la seva aresta toca el border visual - // (centre a BULLET_RADIUS del límit). El MARGE_SEGURETAT de getSafePlayAreaBounds - // és per a spawn d'enemics, no per a desactivació de bales. - float min_x; - float max_x; - float min_y; - float max_y; - Constants::getPlayAreaBounds(min_x, max_x, min_y, max_y); - constexpr float R = Defaults::Entities::BULLET_RADIUS; - - if (body_.position.x < min_x + R || body_.position.x > max_x - R || - body_.position.y < min_y + R || body_.position.y > max_y - R) { - desactivar(); - } + // No-op: la desactivació per fora-de-zona viu a + // Systems::Collision::desactivateOutOfBoundsBullets() perquè així té accés + // al DebrisManager i pot generar el "trencament" visual de la bala alhora. + // El moviment l'integra PhysicsWorld; postUpdate sincronitza center_ i prev_position_. } void Bullet::postUpdate(float /*delta_time*/) { diff --git a/source/game/entities/enemy.cpp b/source/game/entities/enemy.cpp index 197406a..b739635 100644 --- a/source/game/entities/enemy.cpp +++ b/source/game/entities/enemy.cpp @@ -8,7 +8,6 @@ #include #include -#include "core/audio/audio.hpp" #include "core/defaults.hpp" #include "core/entities/entity.hpp" #include "core/graphics/shape_loader.hpp" @@ -277,7 +276,8 @@ void Enemy::destruir() { void Enemy::herir(uint8_t shooter_id) { wounded_timer_ = Defaults::Enemies::Wounded::DURATION; last_hit_by_ = shooter_id; - Audio::get()->playSound(Defaults::Sound::HIT, Audio::Group::GAME); + // El so HIT ara el reprodueix la bala quan es trenca en debris + // (Systems::Collision::breakBullet), no l'enemic en entrar a HURT. } void Enemy::applyImpulse(const Vec2& impulse) { diff --git a/source/game/scenes/game_scene.cpp b/source/game/scenes/game_scene.cpp index f36fdbd..b7b35ca 100644 --- a/source/game/scenes/game_scene.cpp +++ b/source/game/scenes/game_scene.cpp @@ -296,6 +296,7 @@ auto GameScene::stepContinueScreen(float delta_time) -> bool { for (auto& bullet : bullets_) { bullet.update(delta_time); } + Systems::Collision::desactivateOutOfBoundsBullets(bullets_, debris_manager_); debris_manager_.update(delta_time); firework_manager_.update(delta_time); floating_score_manager_.update(delta_time); @@ -321,6 +322,7 @@ auto GameScene::stepGameOver(float delta_time) -> bool { for (auto& bullet : bullets_) { bullet.update(delta_time); } + Systems::Collision::desactivateOutOfBoundsBullets(bullets_, debris_manager_); debris_manager_.update(delta_time); firework_manager_.update(delta_time); floating_score_manager_.update(delta_time); @@ -438,6 +440,7 @@ void GameScene::runStageLevelStart(float delta_time) { for (auto& bullet : bullets_) { bullet.update(delta_time); } + Systems::Collision::desactivateOutOfBoundsBullets(bullets_, debris_manager_); debris_manager_.update(delta_time); firework_manager_.update(delta_time); } @@ -466,13 +469,14 @@ void GameScene::runStagePlaying(float delta_time) { enemy.update(delta_time); } - // Col·lisions primer, després `bullet.update()`: si una bala el mateix frame xoca - // amb un enemic i alhora surt del PLAYAREA, ha de comptar com a impacte abans de - // ser desactivada per fora-de-zona. + // Col·lisions primer, després desactivació per fora-de-zona: així una bala que + // el mateix frame xoca amb un enemic i alhora surt del PLAYAREA es compta com a + // impacte abans no se la trenqui per sortir. runCollisionDetections(); for (auto& bullet : bullets_) { bullet.update(delta_time); } + Systems::Collision::desactivateOutOfBoundsBullets(bullets_, debris_manager_); debris_manager_.update(delta_time); firework_manager_.update(delta_time); floating_score_manager_.update(delta_time); @@ -490,6 +494,7 @@ void GameScene::runStageLevelCompleted(float delta_time) { for (auto& bullet : bullets_) { bullet.update(delta_time); } + Systems::Collision::desactivateOutOfBoundsBullets(bullets_, debris_manager_); debris_manager_.update(delta_time); firework_manager_.update(delta_time); floating_score_manager_.update(delta_time); diff --git a/source/game/systems/collision_system.cpp b/source/game/systems/collision_system.cpp index 8cdcf72..bb02518 100644 --- a/source/game/systems/collision_system.cpp +++ b/source/game/systems/collision_system.cpp @@ -7,6 +7,7 @@ #include "core/audio/audio.hpp" #include "core/physics/collision.hpp" #include "core/types.hpp" +#include "game/constants.hpp" namespace Systems::Collision { @@ -81,6 +82,29 @@ namespace Systems::Collision { // No heretem color: el burst usa el blanc per defecte per a un feel més lluminós. ctx.firework_manager.spawn(ENEMY_POS); } + + // Trenca una bala en debris (8 fragments de l'octàgon) + so HIT + desactiva. + // S'invoca des de qualsevol desactivació de bala (impacte amb enemic, amb jugador, + // o sortida del PLAYAREA) per a un feedback visual i sonor consistent. + void breakBullet(Effects::DebrisManager& debris_manager, Bullet& bullet) { + constexpr float DEBRIS_VELOCITY = 60.0F; + debris_manager.explode( + bullet.getShape(), + bullet.getCenter(), + bullet.getAngle(), + 1.0F, // scale + DEBRIS_VELOCITY, + bullet.getBrightness(), + Vec2{}, // sense herència de velocitat (fragments radials) + 0.0F, // sense velocity angular heretada + 0.0F, // sense rotació visual heretada + Defaults::Sound::HIT, + Defaults::Palette::BULLET, + Defaults::Physics::Debris::TEMPS_VIDA, + Defaults::Physics::Debris::ACCELERACIO, + 1); // sense duplicat de segments + bullet.desactivar(); + } } // anonymous namespace void detectBulletEnemy(Context& ctx) { @@ -114,7 +138,7 @@ namespace Systems::Collision { enemy.herir(SHOOTER); } - bullet.desactivar(); + breakBullet(ctx.debris_manager, bullet); break; // Una bala impacta a un enemy y muere } } @@ -242,7 +266,7 @@ namespace Systems::Collision { ctx.on_player_hit(player_id); ctx.lives_per_player[BULLET_OWNER]++; Audio::get()->playSound(Defaults::Sound::FRIENDLY_FIRE_HIT, Audio::Group::GAME); - bullet.desactivar(); + breakBullet(ctx.debris_manager, bullet); break; // Una bullet solo impacta una vez por frame } } @@ -256,4 +280,26 @@ namespace Systems::Collision { detectBulletPlayer(ctx); } + void desactivateOutOfBoundsBullets( + std::array(Defaults::Entities::MAX_BALES) * 2>& bullets, + Effects::DebrisManager& debris_manager) { + float min_x; + float max_x; + float min_y; + float max_y; + Constants::getPlayAreaBounds(min_x, max_x, min_y, max_y); + constexpr float R = Defaults::Entities::BULLET_RADIUS; + + for (auto& bullet : bullets) { + if (!bullet.isActive()) { + continue; + } + const Vec2& pos = bullet.getCenter(); + if (pos.x < min_x + R || pos.x > max_x - R || + pos.y < min_y + R || pos.y > max_y - R) { + breakBullet(debris_manager, bullet); + } + } + } + } // namespace Systems::Collision diff --git a/source/game/systems/collision_system.hpp b/source/game/systems/collision_system.hpp index 5836a52..49f8647 100644 --- a/source/game/systems/collision_system.hpp +++ b/source/game/systems/collision_system.hpp @@ -70,4 +70,11 @@ namespace Systems::Collision { // Las tres en orden lógico del frame. void detectAll(Context& ctx); + // Desactiva les bales que han sortit del PLAYAREA, generant debris visual + // (8 fragments de l'octàgon) i el so HIT. Cal cridar-la després de detectAll() + // perquè una bala que el mateix frame xoca i alhora surt es comptabilitzi com a impacte. + void desactivateOutOfBoundsBullets( + std::array(Defaults::Entities::MAX_BALES) * 2>& bullets, + Effects::DebrisManager& debris_manager); + } // namespace Systems::Collision