feat(collision): primer impacte fereix, segon mata; mort diferida via timer (Fase 3)

- Defaults::Physics::Debris::ENEMY_VELOCITY_INHERITANCE (placeholder 1.0).
- Enemy::herir(shooter_id) emmagatzema last_hit_by_ per a atribució posterior.
- collision_system: helper anònim explodeNow(ctx, enemy, shooter_id) que
  llegeix velocity/dades ABANS de destruir() (corregeix bug latent: el codi
  anterior llegia getVelocityVector() després de destruir, que zera velocity
  → l'explosió mai heretava inèrcia).
- detectBulletEnemy: primer impacte aplica impulse + herir(); segon impacte
  sobre enemy ferit dispara explodeNow immediata.
- processWoundedDeaths: explota enemics amb wound timer expirat aquest frame.
- detectAll: processWoundedDeaths abans de detectBulletEnemy (les expiracions
  maten primer; les bales del mateix frame ja no toquen el cos destruït).

Puntos s'atribueixen a la mort real, no a l'impacte inicial.

Build neta i smoke test xvfb OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
JailDesignerandClaude Opus 4.7 committed 2026-05-21 10:26:13 +02:00
1 parent dc2824a095
commit 5cb547db0a
5 files changed
+120 -70

No files matched your search

+3 -1
View File
@@ -255,10 +255,12 @@ void Enemy::destruir() {
body_.radius = 0.0F; // No colisiona mientras está inactivo
wounded_timer_ = 0.0F;
wound_expired_this_frame_ = false;
last_hit_by_ = 0xFF;
}
void Enemy::herir() {
void Enemy::herir(uint8_t shooter_id) {
wounded_timer_ = Defaults::Enemies::Wounded::DURATION;
last_hit_by_ = shooter_id;
}
void Enemy::applyImpulse(const Vec2& impulse) {
+4 -1
View File
@@ -86,11 +86,13 @@ class Enemy : public Entities::Entity {
[[nodiscard]] auto getInvulnerabilityTime() const -> float { return timer_invulnerabilitat_; }
// Estado "herido": entre primer impacto de bala y explosión diferida.
void herir();
// shooter_id: id del jugador que herí; 0xFF = sin atribución (cadena, etc.).
void herir(uint8_t shooter_id = 0xFF);
[[nodiscard]] auto isWounded() const -> bool { return wounded_timer_ > 0.0F; }
[[nodiscard]] auto getWoundedTimer() const -> float { return wounded_timer_; }
[[nodiscard]] auto woundExpiredThisFrame() const -> bool { return wound_expired_this_frame_; }
void consumeWoundExpired() { wound_expired_this_frame_ = false; }
[[nodiscard]] auto getLastHitBy() const -> uint8_t { return last_hit_by_; }
// Aplica un impulso (cambio inmediato de velocidad mass-aware) al cuerpo físico.
void applyImpulse(const Vec2& impulse);
@@ -118,6 +120,7 @@ class Enemy : public Entities::Entity {
// Estado "herido": timer cuenta atrás; al cruzar 0 se marca expiración.
float wounded_timer_{0.0F};
bool wound_expired_this_frame_{false};
uint8_t last_hit_by_{0xFF}; // 0xFF = sin atribución
// Métodos privados
void updateAnimation(float delta_time);