refactor(debris): la bala impacta al cos O als trossos, mai a tots dos
This commit is contained in:
@@ -57,7 +57,8 @@ namespace Effects {
|
||||
SDL_Color color,
|
||||
float lifetime,
|
||||
float friction,
|
||||
int segment_multiplier) {
|
||||
int segment_multiplier,
|
||||
const Vec2& bullet_impulse_velocity) {
|
||||
if (!shape || !shape->isValid()) {
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +85,7 @@ namespace Effects {
|
||||
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)) {
|
||||
if (!spawnDebris(world_p1, world_p2, centro, velocitat_base, brightness, velocitat_objecte, velocitat_angular, factor_herencia_visual, color, lifetime, friction, bullet_impulse_velocity)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -110,7 +111,7 @@ namespace Effects {
|
||||
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, float lifetime, float friction) -> bool {
|
||||
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, const Vec2& bullet_impulse_velocity) -> bool {
|
||||
Debris* debris = findFreeSlot();
|
||||
if (debris == nullptr) {
|
||||
std::cerr << "[DebrisManager] Warning: no debris slots disponibles\n";
|
||||
@@ -131,13 +132,16 @@ namespace Effects {
|
||||
// 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)
|
||||
// Velocidad inicial (base ± variació aleatòria + velocity heretada de l'objecte +
|
||||
// velocitat de la bala escalada per BULLET_IMPULSE_FACTOR).
|
||||
float speed =
|
||||
velocitat_base +
|
||||
(((std::rand() / static_cast<float>(RAND_MAX)) * 2.0F - 1.0F) *
|
||||
Defaults::Physics::Debris::VARIACIO_SPEED);
|
||||
debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x;
|
||||
debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y;
|
||||
debris->velocity.x = (direccio.x * speed) + velocitat_objecte.x +
|
||||
(bullet_impulse_velocity.x * Defaults::Physics::Debris::BULLET_IMPULSE_FACTOR);
|
||||
debris->velocity.y = (direccio.y * speed) + velocitat_objecte.y +
|
||||
(bullet_impulse_velocity.y * Defaults::Physics::Debris::BULLET_IMPULSE_FACTOR);
|
||||
debris->acceleration = friction;
|
||||
|
||||
// Rotación de trayectoria (con conversió a tangencial si excedeix cap)
|
||||
|
||||
@@ -47,6 +47,11 @@ namespace Effects {
|
||||
// - lifetime: temps de vida del debris (s, per defecte TEMPS_VIDA = 2s)
|
||||
// - friction: desacceleració del debris (px/s², per defecte ACCELERACIO = -60)
|
||||
// - segment_multiplier: nombre de còpies per segment (per defecte 1 = sense duplicar)
|
||||
// - bullet_impulse_velocity: velocitat de la bala que ha causat l'impacte (px/s,
|
||||
// per defecte 0). S'aplica a cada fragment escalada per
|
||||
// Defaults::Physics::Debris::BULLET_IMPULSE_FACTOR, independent de
|
||||
// velocitat_objecte. Permet que els trossos "salten amb la força de la bala"
|
||||
// encara que el cos sigui pesat i amb prou feines es mogui.
|
||||
void explode(const std::shared_ptr<Graphics::Shape>& shape,
|
||||
const Vec2& centro,
|
||||
float angle,
|
||||
@@ -60,7 +65,8 @@ namespace Effects {
|
||||
SDL_Color color = {0, 0, 0, 0}, // alpha==0 → fragmentos usan oscilador global
|
||||
float lifetime = Defaults::Physics::Debris::TEMPS_VIDA,
|
||||
float friction = Defaults::Physics::Debris::ACCELERACIO,
|
||||
int segment_multiplier = 1);
|
||||
int segment_multiplier = 1,
|
||||
const Vec2& bullet_impulse_velocity = {.x = 0.0F, .y = 0.0F});
|
||||
|
||||
// Actualitzar todos los fragments active
|
||||
void update(float delta_time);
|
||||
@@ -97,7 +103,7 @@ namespace Effects {
|
||||
-> std::vector<std::pair<Vec2, Vec2>>;
|
||||
// Inicialitza un debris en un slot lliure i el deixa actiu. Retorna
|
||||
// false si el pool está ple (la cridadora ha d'aturar el bucle).
|
||||
auto 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;
|
||||
auto 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, const Vec2& bullet_impulse_velocity) -> bool;
|
||||
static void applyAngularVelocity(Debris& debris, const Vec2& direccio, float velocitat_angular);
|
||||
static void applyVisualRotation(Debris& debris, float velocitat_angular, float factor_herencia_visual);
|
||||
};
|
||||
|
||||
@@ -580,7 +580,7 @@ auto GameScene::buildCollisionContext() -> Systems::Collision::Context {
|
||||
.firework_manager = firework_manager_,
|
||||
.floating_score_manager = floating_score_manager_,
|
||||
.match_config = match_config_,
|
||||
.on_player_hit = [this](uint8_t pid) { tocado(pid); },
|
||||
.on_player_hit = [this](uint8_t pid, const Vec2& bv) { tocado(pid, bv); },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -764,7 +764,7 @@ void GameScene::drawLevelCompletedState() {
|
||||
drawScoreboard();
|
||||
}
|
||||
|
||||
void GameScene::tocado(uint8_t player_id) {
|
||||
void GameScene::tocado(uint8_t player_id, const Vec2& bullet_velocity) {
|
||||
// Death sequence: 3 phases
|
||||
// Phase 1: First call (hit_timer_per_player_[player_id] == 0) - trigger explosion
|
||||
// Phase 2: Animation (0 < itocado_ < 3.0s) - debris animation
|
||||
@@ -788,6 +788,9 @@ void GameScene::tocado(uint8_t player_id) {
|
||||
|
||||
// Mateixa dispersió i efecte que els debris d'enemic (lifetime,
|
||||
// friction, segment_multiplier alineats); només canvien sound i color.
|
||||
// bullet_velocity arriba a explode() com a impuls extra independent
|
||||
// de la inèrcia del cos del ship — els trossos volen amb la força
|
||||
// de la bala encara que el ship estiga quiet.
|
||||
debris_manager_.explode(
|
||||
ships_[player_id].getShape(),
|
||||
SHIP_POS,
|
||||
@@ -802,7 +805,8 @@ void GameScene::tocado(uint8_t player_id) {
|
||||
ships_[player_id].getConfig().colors.normal,
|
||||
Defaults::Physics::Debris::ENEMY_LIFETIME,
|
||||
Defaults::Physics::Debris::ENEMY_FRICTION,
|
||||
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER);
|
||||
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER,
|
||||
bullet_velocity);
|
||||
|
||||
// Start death timer (non-zero to avoid re-triggering)
|
||||
hit_timer_per_player_[player_id] = Defaults::Game::HIT_TIMER_TRIGGER_DEATH;
|
||||
|
||||
@@ -101,7 +101,10 @@ class GameScene final : public Scene {
|
||||
bool init_hud_rect_sound_played_{false}; // Flag para evitar repetir sonido del rectángulo
|
||||
|
||||
// Funciones privades
|
||||
void tocado(uint8_t player_id);
|
||||
// bullet_velocity: velocitat de la bala que ha causat la mort (Vec2{} si no
|
||||
// ve d'una bala). Es passa al debris perquè els fragments volin en direcció
|
||||
// de la bala (independent de la inèrcia del cos del ship).
|
||||
void tocado(uint8_t player_id, const Vec2& bullet_velocity = {.x = 0.0F, .y = 0.0F});
|
||||
void drawScoreboard(); // Dibuixar marcador de puntuación
|
||||
void fireBullet(uint8_t player_id); // Shoot bullet from player
|
||||
[[nodiscard]] auto getSpawnPoint(uint8_t player_id) const -> Vec2; // Get spawn position for player
|
||||
|
||||
@@ -150,7 +150,9 @@ namespace Systems::Collision {
|
||||
const float DEATH_FACTOR = ctx.ships[i].getConfig().physics.death_impact_factor;
|
||||
const Vec2 IMPULSE = SHIP_VEL * (ctx.ships[i].getBody().mass * DEATH_FACTOR);
|
||||
touched_enemy->applyImpulse(IMPULSE);
|
||||
ctx.on_player_hit(i);
|
||||
// Sense bala: cap impuls de bala per als debris (mort per
|
||||
// col·lisió cos-cos). Els debris hereten la inèrcia del ship.
|
||||
ctx.on_player_hit(i, Vec2{});
|
||||
} else {
|
||||
// Primer impacte → estat HURT (rebot físic ja resolt per PhysicsWorld;
|
||||
// l'enemic no rep dany per decisió de disseny).
|
||||
@@ -197,13 +199,12 @@ namespace Systems::Collision {
|
||||
}
|
||||
|
||||
// *** TEAMMATE HIT (friendly fire) ***
|
||||
// Víctima perd 1 vida, atacant en guanya 1. Apliquem l'impuls
|
||||
// de la bala a la nau ABANS de on_player_hit perquè tocado()
|
||||
// captura la velocitat per als debris (si no, queden quiets).
|
||||
const Vec2 BULLET_IMPULSE = bullet.getBody().velocity *
|
||||
(bullet.getBody().mass * bullet.getConfig().physics.impact_momentum_factor);
|
||||
ctx.ships[player_id].getBody().applyImpulse(BULLET_IMPULSE);
|
||||
ctx.on_player_hit(player_id);
|
||||
// Víctima perd 1 vida, atacant en guanya 1. Friendly fire sempre
|
||||
// mata: el bullet va als debris (via tocado) i NO al cos del ship
|
||||
// — el cos està a punt de desactivar-se, qualsevol impuls seria
|
||||
// double-count amb la velocitat que ja reben els trossos.
|
||||
const Vec2 BULLET_VEL = bullet.getBody().velocity;
|
||||
ctx.on_player_hit(player_id, BULLET_VEL);
|
||||
ctx.lives_per_player[BULLET_OWNER]++;
|
||||
Audio::get()->playSound(Defaults::Sound::FRIENDLY_FIRE_HIT, Audio::Group::GAME);
|
||||
breakBullet(ctx.debris_manager, bullet);
|
||||
@@ -237,15 +238,20 @@ namespace Systems::Collision {
|
||||
}
|
||||
|
||||
// *** BALA D'ENEMIC → SHIP ***
|
||||
// Apliquem l'impuls de la bala abans del hurt/death perquè la
|
||||
// velocitat de la nau quedi capturada per als debris.
|
||||
const Vec2 IMPULSE = bullet.getBody().velocity *
|
||||
(bullet.getBody().mass * bullet.getConfig().physics.impact_momentum_factor);
|
||||
ctx.ships[player_id].getBody().applyImpulse(IMPULSE);
|
||||
// Regla "cos XOR trossos": l'impuls de la bala s'aplica al cos
|
||||
// només si el ship sobreviu (fereix). Si el ship mor, el bullet
|
||||
// va directament als trossos (via tocado) i el cos no rep impuls
|
||||
// — els trossos ja porten la força de la bala, qualsevol impuls
|
||||
// afegit al cos seria double-count.
|
||||
const Vec2 BULLET_VEL = bullet.getBody().velocity;
|
||||
if (ctx.ships[player_id].isHurt()) {
|
||||
// Segon impacte durant HURT → mort.
|
||||
ctx.on_player_hit(player_id);
|
||||
ctx.on_player_hit(player_id, BULLET_VEL);
|
||||
} else {
|
||||
// Fereix: el cos sobreviu, rep l'impuls. No hi ha debris encara.
|
||||
const Vec2 IMPULSE = BULLET_VEL *
|
||||
(bullet.getBody().mass * bullet.getConfig().physics.impact_momentum_factor);
|
||||
ctx.ships[player_id].getBody().applyImpulse(IMPULSE);
|
||||
ctx.ships[player_id].hurt();
|
||||
}
|
||||
breakBullet(ctx.debris_manager, bullet);
|
||||
|
||||
@@ -40,8 +40,11 @@ namespace Systems::Collision {
|
||||
Effects::FireworkManager& firework_manager;
|
||||
Effects::FloatingScoreManager& floating_score_manager;
|
||||
const GameConfig::MatchConfig& match_config;
|
||||
// Trigger de muerte del jugador (GameScene::tocado).
|
||||
std::function<void(uint8_t /*player_id*/)> on_player_hit;
|
||||
// Trigger de muerte del jugador (GameScene::tocado). bullet_velocity es
|
||||
// la velocitat de la bala que ha causat la mort (Vec2{} si la mort no
|
||||
// ve d'una bala — col·lisió ship-enemy, etc.). Es passa al debris perquè
|
||||
// els trossos volin en direcció de la bala.
|
||||
std::function<void(uint8_t /*player_id*/, const Vec2& /*bullet_velocity*/)> on_player_hit;
|
||||
};
|
||||
|
||||
// Detecta colisiones bullet → enemy. Si hit:
|
||||
|
||||
@@ -24,10 +24,11 @@ namespace Systems::EnemyEvents {
|
||||
ctx.floating_score_manager.crear(POINTS, enemy.getCenter());
|
||||
}
|
||||
|
||||
void doCreateDebris(Systems::Collision::Context& ctx, const Enemy& enemy) {
|
||||
void doCreateDebris(Systems::Collision::Context& ctx, const Enemy& enemy, const Bullet* bullet) {
|
||||
constexpr float SPEED_EXPLOSIO = 80.0F;
|
||||
const Vec2 INHERITED_VEL = enemy.getVelocityVector() *
|
||||
Defaults::Physics::Debris::ENEMY_VELOCITY_INHERITANCE;
|
||||
const Vec2 BULLET_VEL = (bullet != nullptr) ? bullet->getBody().velocity : Vec2{};
|
||||
ctx.debris_manager.explode(
|
||||
enemy.getShape(),
|
||||
enemy.getCenter(),
|
||||
@@ -42,7 +43,8 @@ namespace Systems::EnemyEvents {
|
||||
enemy.getConfig().colors.normal,
|
||||
Defaults::Physics::Debris::ENEMY_LIFETIME,
|
||||
Defaults::Physics::Debris::ENEMY_FRICTION,
|
||||
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER);
|
||||
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER,
|
||||
BULLET_VEL);
|
||||
}
|
||||
|
||||
void doCreateFireworks(Systems::Collision::Context& ctx, const Enemy& enemy) {
|
||||
@@ -67,6 +69,24 @@ namespace Systems::EnemyEvents {
|
||||
|
||||
void dispatchEvent(Systems::Collision::Context& ctx, Enemy& enemy, EnemyEventType event, uint8_t shooter_id, const Bullet* bullet) {
|
||||
const auto& actions = enemy.getConfig().events.getActions(event);
|
||||
|
||||
// Pre-scan: aquest event matarà l'enemic? Si sí, l'impuls de la bala
|
||||
// va directament als debris (via doCreateDebris) i NO s'aplica al cos
|
||||
// — així evitem el "double-count" on els trossos hereten la velocitat
|
||||
// del cos (boostat per la bala) I a més el seu propi impuls de bala.
|
||||
// Regla: el bullet impacta al cos O als trossos, mai a tots dos.
|
||||
bool will_die = false;
|
||||
for (const auto& action : actions) {
|
||||
if (action.type == EnemyActionType::DESTROY) {
|
||||
will_die = true;
|
||||
break;
|
||||
}
|
||||
if (action.type == EnemyActionType::SET_HURT && enemy.isWounded()) {
|
||||
will_die = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& action : actions) {
|
||||
switch (action.type) {
|
||||
case EnemyActionType::SET_HURT:
|
||||
@@ -86,13 +106,15 @@ namespace Systems::EnemyEvents {
|
||||
doAddScore(ctx, enemy, shooter_id);
|
||||
break;
|
||||
case EnemyActionType::CREATE_DEBRIS:
|
||||
doCreateDebris(ctx, enemy);
|
||||
doCreateDebris(ctx, enemy, bullet);
|
||||
break;
|
||||
case EnemyActionType::CREATE_FIREWORKS:
|
||||
doCreateFireworks(ctx, enemy);
|
||||
break;
|
||||
case EnemyActionType::APPLY_IMPULSE:
|
||||
doApplyImpulse(enemy, bullet);
|
||||
if (!will_die) {
|
||||
doApplyImpulse(enemy, bullet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user