damage_processor.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #include "damage_processor.h"
  2. #include "../../core/component.h"
  3. #include "../../core/event_manager.h"
  4. #include "../../core/world.h"
  5. #include "../../units/spawn_type.h"
  6. #include "../building_collision_registry.h"
  7. #include <cmath>
  8. #include <optional>
  9. namespace Game::Systems::Combat {
  10. void deal_damage(Engine::Core::World *world, Engine::Core::Entity *target,
  11. int damage, Engine::Core::EntityID attacker_id) {
  12. auto *unit = target->get_component<Engine::Core::UnitComponent>();
  13. if (unit == nullptr) {
  14. return;
  15. }
  16. int const prev_health = unit->health;
  17. int const new_health = std::max(0, prev_health - damage);
  18. bool const is_killing_blow = (prev_health > 0 && prev_health <= damage);
  19. unit->health = new_health;
  20. int attacker_owner_id = 0;
  21. std::optional<Game::Units::SpawnType> attacker_type_opt;
  22. if (attacker_id != 0 && (world != nullptr)) {
  23. auto *attacker = world->get_entity(attacker_id);
  24. if (attacker != nullptr) {
  25. auto *attacker_unit =
  26. attacker->get_component<Engine::Core::UnitComponent>();
  27. if (attacker_unit != nullptr) {
  28. attacker_owner_id = attacker_unit->owner_id;
  29. attacker_type_opt = attacker_unit->spawn_type;
  30. }
  31. }
  32. }
  33. Game::Units::SpawnType const attacker_type =
  34. attacker_type_opt.value_or(Game::Units::SpawnType::Knight);
  35. Engine::Core::EventManager::instance().publish(Engine::Core::CombatHitEvent(
  36. attacker_id, target->get_id(), damage, attacker_type, is_killing_blow));
  37. if (unit->health > 0) {
  38. apply_hit_feedback(target, attacker_id, world);
  39. }
  40. if (target->has_component<Engine::Core::BuildingComponent>() &&
  41. unit->health > 0) {
  42. Engine::Core::EventManager::instance().publish(
  43. Engine::Core::BuildingAttackedEvent(target->get_id(), unit->owner_id,
  44. unit->spawn_type, attacker_id,
  45. attacker_owner_id, damage));
  46. }
  47. if (unit->health <= 0) {
  48. int const killer_owner_id = attacker_owner_id;
  49. Engine::Core::EventManager::instance().publish(Engine::Core::UnitDiedEvent(
  50. target->get_id(), unit->owner_id, unit->spawn_type, attacker_id,
  51. killer_owner_id));
  52. auto *target_atk = target->get_component<Engine::Core::AttackComponent>();
  53. if ((target_atk != nullptr) && target_atk->in_melee_lock &&
  54. target_atk->melee_lock_target_id != 0) {
  55. if (world != nullptr) {
  56. auto *lock_partner =
  57. world->get_entity(target_atk->melee_lock_target_id);
  58. if ((lock_partner != nullptr) &&
  59. !lock_partner
  60. ->has_component<Engine::Core::PendingRemovalComponent>()) {
  61. auto *partner_atk =
  62. lock_partner->get_component<Engine::Core::AttackComponent>();
  63. if ((partner_atk != nullptr) &&
  64. partner_atk->melee_lock_target_id == target->get_id()) {
  65. partner_atk->in_melee_lock = false;
  66. partner_atk->melee_lock_target_id = 0;
  67. }
  68. }
  69. }
  70. }
  71. if (target->has_component<Engine::Core::BuildingComponent>()) {
  72. BuildingCollisionRegistry::instance().unregister_building(
  73. target->get_id());
  74. }
  75. if (auto *r = target->get_component<Engine::Core::RenderableComponent>()) {
  76. r->visible = false;
  77. }
  78. if (auto *movement =
  79. target->get_component<Engine::Core::MovementComponent>()) {
  80. movement->has_target = false;
  81. movement->vx = 0.0F;
  82. movement->vz = 0.0F;
  83. movement->clear_path();
  84. movement->path_pending = false;
  85. }
  86. target->add_component<Engine::Core::PendingRemovalComponent>();
  87. }
  88. }
  89. void apply_hit_feedback(Engine::Core::Entity *target,
  90. Engine::Core::EntityID attacker_id,
  91. Engine::Core::World *world) {
  92. if (target == nullptr) {
  93. return;
  94. }
  95. auto *feedback = target->get_component<Engine::Core::HitFeedbackComponent>();
  96. if (feedback == nullptr) {
  97. feedback = target->add_component<Engine::Core::HitFeedbackComponent>();
  98. }
  99. if (feedback == nullptr) {
  100. return;
  101. }
  102. feedback->is_reacting = true;
  103. feedback->reaction_time = 0.0F;
  104. feedback->reaction_intensity = 1.0F;
  105. auto *target_transform =
  106. target->get_component<Engine::Core::TransformComponent>();
  107. if (target_transform != nullptr && attacker_id != 0 && world != nullptr) {
  108. auto *attacker = world->get_entity(attacker_id);
  109. if (attacker != nullptr) {
  110. auto *attacker_transform =
  111. attacker->get_component<Engine::Core::TransformComponent>();
  112. if (attacker_transform != nullptr) {
  113. float const dx =
  114. target_transform->position.x - attacker_transform->position.x;
  115. float const dz =
  116. target_transform->position.z - attacker_transform->position.z;
  117. float const dist = std::sqrt(dx * dx + dz * dz);
  118. if (dist > 0.001F) {
  119. float const knockback =
  120. Engine::Core::HitFeedbackComponent::kMaxKnockback;
  121. feedback->knockback_x = (dx / dist) * knockback;
  122. feedback->knockback_z = (dz / dist) * knockback;
  123. }
  124. }
  125. }
  126. }
  127. auto *combat_state =
  128. target->get_component<Engine::Core::CombatStateComponent>();
  129. if (combat_state != nullptr) {
  130. combat_state->is_hit_paused = true;
  131. combat_state->hit_pause_remaining =
  132. Engine::Core::CombatStateComponent::kHitPauseDuration;
  133. }
  134. }
  135. } // namespace Game::Systems::Combat