161 lines
7.4 KiB
C++
161 lines
7.4 KiB
C++
// enemy_event_dispatcher.cpp - Implementació del dispatcher d'events d'enemic
|
|
// © 2026 JailDesigner
|
|
|
|
#include "game/systems/enemy_event_dispatcher.hpp"
|
|
|
|
#include <cstdint>
|
|
|
|
#include "core/defaults.hpp"
|
|
#include "core/types.hpp"
|
|
#include "game/entities/bullet.hpp"
|
|
#include "game/entities/bullet_config.hpp"
|
|
#include "game/entities/enemy_config.hpp"
|
|
|
|
namespace Systems::EnemyEvents {
|
|
|
|
namespace {
|
|
constexpr uint8_t NO_SHOOTER = 0xFF;
|
|
|
|
void doAddScore(Systems::Collision::Context& ctx, const Enemy& enemy, uint8_t shooter) {
|
|
const int POINTS = enemy.getConfig().score;
|
|
if (shooter != NO_SHOOTER) {
|
|
ctx.score_per_player[shooter] += POINTS;
|
|
}
|
|
ctx.floating_score_manager.crear(POINTS, enemy.getCenter());
|
|
}
|
|
|
|
// Helper compartit per CREATE_DEBRIS i CREATE_DEBRIS_PARTIAL: única
|
|
// crida a explode(), paràmetres alineats; només canvien piece_scale
|
|
// (1.0 explosió, 0.3 xip) i el color (cos vs hit-feedback).
|
|
void spawnDebrisForEnemy(Systems::Collision::Context& ctx, const Enemy& enemy, const Bullet* bullet, float piece_scale, SDL_Color color) {
|
|
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(),
|
|
0.0F,
|
|
1.0F,
|
|
SPEED_EXPLOSIO,
|
|
enemy.getBrightness(),
|
|
INHERITED_VEL,
|
|
0.0F,
|
|
0.0F,
|
|
Defaults::Sound::EXPLOSION,
|
|
color,
|
|
Defaults::Physics::Debris::ENEMY_LIFETIME,
|
|
Defaults::Physics::Debris::ENEMY_FRICTION,
|
|
Defaults::Physics::Debris::ENEMY_SEGMENT_MULTIPLIER,
|
|
BULLET_VEL,
|
|
piece_scale);
|
|
}
|
|
|
|
// Helper compartit per CREATE_FIREWORKS i CREATE_FIREWORKS_SMALL:
|
|
// mateixa crida a spawn(); els callers decideixen line_color, glow_color,
|
|
// n_points i initial_speed segons el "tamany" del burst (mort vs hit).
|
|
void spawnFireworksForEnemy(Systems::Collision::Context& ctx, const Enemy& enemy, int n_points, float initial_speed, SDL_Color line_color, SDL_Color glow_color) {
|
|
ctx.firework_manager.spawn(enemy.getCenter(),
|
|
line_color,
|
|
initial_speed,
|
|
n_points,
|
|
Defaults::FX::Firework::INITIAL_BRIGHTNESS,
|
|
/*glow=*/true,
|
|
glow_color);
|
|
}
|
|
|
|
void doApplyImpulse(Enemy& enemy, const Bullet* bullet) {
|
|
if (bullet == nullptr) {
|
|
return;
|
|
}
|
|
const Vec2 IMPULSE = bullet->getBody().velocity *
|
|
(bullet->getBody().mass * bullet->getConfig().physics.impact_momentum_factor);
|
|
enemy.applyImpulse(IMPULSE);
|
|
}
|
|
} // namespace
|
|
|
|
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) {
|
|
// Si una acció prèvia d'aquest chain ha destruït l'enemic
|
|
// (típicament DECREASE_HEALTH→ON_NO_HEALTH→SET_HURT-wounded→DESTROY),
|
|
// saltem la resta — no té sentit aplicar APPLY_IMPULSE o FLASH a un
|
|
// cos ja inactiu.
|
|
if (!enemy.isActive()) {
|
|
break;
|
|
}
|
|
switch (action.type) {
|
|
case EnemyActionType::SET_HURT:
|
|
if (enemy.isWounded()) {
|
|
// Segon hit sobre wounded → mort immediata (regla 2-hits).
|
|
dispatchEvent(ctx, enemy, EnemyEventType::ON_DESTROY, shooter_id, bullet);
|
|
enemy.destroy();
|
|
} else {
|
|
enemy.hurt(shooter_id);
|
|
}
|
|
break;
|
|
case EnemyActionType::DESTROY:
|
|
dispatchEvent(ctx, enemy, EnemyEventType::ON_DESTROY, shooter_id, bullet);
|
|
enemy.destroy();
|
|
break;
|
|
case EnemyActionType::ADD_SCORE:
|
|
doAddScore(ctx, enemy, shooter_id);
|
|
break;
|
|
case EnemyActionType::CREATE_DEBRIS:
|
|
// Explosió de mort: trossos en color cos (correcte físicament).
|
|
spawnDebrisForEnemy(ctx, enemy, bullet, 1.0F, enemy.getConfig().colors.normal);
|
|
break;
|
|
case EnemyActionType::CREATE_DEBRIS_PARTIAL:
|
|
// Xip d'impacte: trossos en color wounded (daurat) per
|
|
// diferenciar-los visualment del cos i marcar "damage".
|
|
spawnDebrisForEnemy(ctx, enemy, bullet, Defaults::Enemies::Debris::PARTIAL_PIECE_SCALE, enemy.getConfig().colors.wounded);
|
|
break;
|
|
case EnemyActionType::CREATE_FIREWORKS:
|
|
// Burst de mort: línia blanca + glow wounded (daurat) per
|
|
// marcar la mort com a esdeveniment "calent" i lluminós.
|
|
spawnFireworksForEnemy(ctx, enemy, Defaults::FX::Firework::N_POINTS, Defaults::FX::Firework::SPEED, Defaults::FX::Firework::DEFAULT_COLOR, enemy.getConfig().colors.wounded);
|
|
break;
|
|
case EnemyActionType::CREATE_FIREWORKS_SMALL:
|
|
// Espurna d'impacte: línia + glow tots dos en wounded
|
|
// (daurat) per contrastar amb el cos i unificar la "tema
|
|
// de damage" amb el debris parcial.
|
|
spawnFireworksForEnemy(ctx, enemy, Defaults::Enemies::Fireworks::SMALL_N_POINTS, Defaults::Enemies::Fireworks::SMALL_SPEED, enemy.getConfig().colors.wounded, enemy.getConfig().colors.wounded);
|
|
break;
|
|
case EnemyActionType::APPLY_IMPULSE:
|
|
if (!will_die) {
|
|
doApplyImpulse(enemy, bullet);
|
|
}
|
|
break;
|
|
case EnemyActionType::DECREASE_HEALTH:
|
|
enemy.decrementHealth(shooter_id);
|
|
if (enemy.getHealth() <= 0) {
|
|
dispatchEvent(ctx, enemy, EnemyEventType::ON_NO_HEALTH, shooter_id, bullet);
|
|
}
|
|
break;
|
|
case EnemyActionType::FLASH:
|
|
enemy.triggerFlash();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Systems::EnemyEvents
|