resolveBodyPair afegeix early-out per a parells on a.radius<=0 o b.radius<=0.
Honra el comentari de bullet.cpp:30 ("radius=0 → sin colisión física,
cinemática pura") que abans no s'aplicava: amb bala radius=0 + enemic
radius=ENEMY_RADIUS, SUM_R era enemic radius i el body-body disparava
si la bala (a 700 px/s) penetrava el cos l'enemic entre frames.
Símptomes corregits:
- Pentagon: la bala "rebotava espectacularment" en lloc d'impactar.
- Quadrat: rebut un impulse double del cantó de la física que es
sumava (o cancel·lava, segons l'angle) al manual, fent l'efecte
inconsistent.
Ara la gameplay collision (Physics::checkCollision amb entity radius,
que ja és més generós) és l'única que tracta el parell bala-enemic.
A més: IMPACT_MOMENTUM_FACTOR 2.0 → 3.0 per compensar la pèrdua del
rebot físic i donar més empenta:
- Pentagon (m=5) Δv = 210 px/s
- Quadrat (m=8) Δv = 131 px/s
- Molinillo (m=4) Δv = 262 px/s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- IMPACT_MOMENTUM_FACTOR: 1.0 → 2.0 (doble del moment de la bala).
Pentagon Δv = 140 px/s (≈4× la seva velocity base), prou clar.
- Enemy::update: salta el switch de behavior (Pentagon zigzag,
Quadrat tracking, Molinillo proximity-spin) mentre wounded_timer_>0.
El enemic herit és un "cos mort" inert: només respon a la inèrcia
del impulse rebut i a les col·lisions físiques resoltes per
PhysicsWorld. Abans, el Quadrat renormalitzava la velocity cada 1s
cap al ship, esborrant la inèrcia.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sustitueix IMPACT_IMPULSE (magnitud arbitrària radial) per
IMPACT_MOMENTUM_FACTOR (factor de transferència del moment de la bala).
El impulse ara és bullet.body.velocity * (bullet.body.mass * factor),
és a dir el moment lineal real de la bala, dirigit cap a on viatjava.
Amb factor=1.0 i la bala (m=0.5, v=700 px/s):
- Pentagon (m=5) → Δv = 70 px/s (doble de la seva velocity base)
- Quadrat (m=8) → Δv = 44 px/s
- Molinillo (m=4) → Δv = 88 px/s
Visiblement notable durant el segon de "ferit" abans de l'explosió.
El factor és tunable per pujar/baixar segons gusts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Systems::Collision::detectWoundedChain itera parells d'enemics: si
exactament un està herit i toquen (Physics::checkCollision), el sa entra
en estat herit propagant last_hit_by_ → la cascada de morts segueix
acreditant el shooter original. El rebot físic ja el gestiona
PhysicsWorld; aquí només propaguem l'estat.
Hook a detectAll just després de detectBulletEnemy: les balles tenen
prioritat sobre la cadena del mateix frame.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enemy::draw() ara, si wounded_timer_ > 0, alterna entre el color del
tipus i Defaults::Palette::WOUNDED (dorat) a Wounded::BLINK_HZ usant
fmod sobre el periode del cicle — patró reutilitzat del Ship::draw()
d'invulnerabilitat però aplicat a color en lloc de visibilitat.
A 10 Hz amb DURATION=1s dóna ~10 parpadeigs visibles abans d'explotar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 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>
- Defaults::Physics::Bullet::IMPACT_IMPULSE (50 px·s placeholder)
- detectBulletEnemy: calcula normal bullet→enemy, normalitza
(fallback a direcció de bala o (0,-1) si estan solapats) i crida
enemy.applyImpulse(normal * IMPACT_IMPULSE) abans de destruir.
El destruir() immediat encara zera la velocity, així que l'efecte
visual no es nota: serà visible quan la Fase 3 difereixi la mort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>