- 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>
- Defaults::Palette::WOUNDED ({255,215,0}) dorat per a parpadeig
- Defaults::Enemies::Wounded::{DURATION, BLINK_HZ}
- Enemy: wounded_timer_, wound_expired_this_frame_
- API: herir(), isWounded(), getWoundedTimer(),
woundExpiredThisFrame(), consumeWoundExpired(), applyImpulse()
- update() decrementa timer i marca expiració al creuar 0
- destruir() reseteja l'estat wounded
Sense efectes visuals ni canvis de comportament: cap callsite invoca
encara herir() ni applyImpulse(). Build verda i smoke test xvfb OK.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reemplaça core/defaults.hpp pels subheaders concrets a director.cpp i
config_yaml.cpp (silencia unused-includes de clangd). Marca el umbrella
amb IWYU pragma: begin_exports/end_exports per evitar falsos positius
als consumidors transitius.
Separa el tamany lògic (1280×720) del render target offscreen. Llista
tancada de 5 presets 16:9 (720p/900p/1080p/1440p/2160p) llegida de
rendering.render_{width,height} amb fallback a 1280×720 si invàlida.
Inclou API resizeRenderTarget() preparada per al menú de servei futur.
Pas 7 final del hallazgo #28. La capa de game/options havia esdevingut
exclusivament una capa de persistència YAML que llegia i escrivia
Config::EngineConfig. El nom "Options" no reflectia bé aquest rol.
Canvis:
- Renomenats fitxers: game/options.{hpp,cpp} → game/config_yaml.{hpp,cpp}
(preservant la història de git via mv).
- Renomenat namespace: Options → ConfigYaml.
- Esborrades del .hpp les referències-alias inline (window, rendering,
player1, player2, keyboard_controls, gamepad_controls, console) que
ja no tenien call-sites externs (només existien per a la transició).
El .hpp ara només exposa engine_config + version + path + funcions.
- A config_yaml.cpp s'introdueixen aliases internes (anonymous namespace)
per mantenir llegible el codi de la implementació, sense exposar-les.
- Actualitzat main.cpp per a usar ConfigYaml::*.
- Actualitzats els comentaris stale a sdl_manager.hpp, director.hpp,
engine_config.hpp i audio.hpp.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pas 1/N del hallazgo #28. Sense canvi de comportament:
Nou: source/core/config/engine_config.hpp
- Defineix Config::EngineConfig (POD) amb les sub-structs WindowConfig,
RenderingConfig, KeyboardBindings, GamepadBindings, PlayerBindings i
el flag console.
- Sense singletons ni virtuals; la inversió real es fa en commits
posteriors injectant Config::EngineConfig& per constructor.
Modificat: source/game/options.hpp
- Elimina les struct definitions locals (Window, Rendering, ...).
- Afegeix Options::engine_config (única font de veritat).
- Conserva Options::window, Options::rendering, player1, player2,
keyboard_controls, gamepad_controls i console com a referències
inline a camps d'engine_config. Cost runtime zero, callsites
existents no requereixen cap canvi.
Hallazgo #28 de CODE_REVIEW.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>