projectile_system.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #include "projectile_system.h"
  2. #include "../core/component.h"
  3. #include "../core/event_manager.h"
  4. #include "../core/world.h"
  5. #include "arrow_projectile.h"
  6. #include "stone_projectile.h"
  7. #include <algorithm>
  8. #include <cmath>
  9. #include <qvectornd.h>
  10. namespace Game::Systems {
  11. ProjectileSystem::ProjectileSystem()
  12. : m_arrow_config(GameConfig::instance().arrow()) {}
  13. void ProjectileSystem::spawn_arrow(const QVector3D &start, const QVector3D &end,
  14. const QVector3D &color, float speed,
  15. bool is_ballista_bolt) {
  16. QVector3D const delta = end - start;
  17. float const dist = delta.length();
  18. float arc_height;
  19. if (is_ballista_bolt) {
  20. arc_height = std::clamp(m_arrow_config.arc_height_multiplier * dist * 0.4F,
  21. m_arrow_config.arc_height_min * 0.5F,
  22. m_arrow_config.arc_height_max * 0.6F);
  23. } else {
  24. arc_height = std::clamp(m_arrow_config.arc_height_multiplier * dist,
  25. m_arrow_config.arc_height_min,
  26. m_arrow_config.arc_height_max);
  27. }
  28. float inv_dist = (dist > 0.001F) ? (1.0F / dist) : 1.0F;
  29. m_projectiles.push_back(std::make_unique<ArrowProjectile>(
  30. start, end, color, speed, arc_height, inv_dist, is_ballista_bolt));
  31. }
  32. void ProjectileSystem::spawn_stone(const QVector3D &start, const QVector3D &end,
  33. const QVector3D &color, float speed,
  34. float scale, bool should_apply_damage,
  35. int damage,
  36. Engine::Core::EntityID attacker_id,
  37. Engine::Core::EntityID target_id) {
  38. QVector3D const delta = end - start;
  39. float const dist = delta.length();
  40. constexpr float k_stone_arc_multiplier = 0.35F;
  41. constexpr float k_stone_arc_min = 1.0F;
  42. constexpr float k_stone_arc_max = 4.0F;
  43. float arc_height = std::clamp(k_stone_arc_multiplier * dist, k_stone_arc_min,
  44. k_stone_arc_max);
  45. float inv_dist = (dist > 0.001F) ? (1.0F / dist) : 1.0F;
  46. m_projectiles.push_back(std::make_unique<StoneProjectile>(
  47. start, end, color, speed, arc_height, inv_dist, scale,
  48. should_apply_damage, damage, attacker_id, target_id));
  49. }
  50. void ProjectileSystem::update(Engine::Core::World *world, float delta_time) {
  51. for (auto &projectile : m_projectiles) {
  52. projectile->update(delta_time);
  53. if (projectile->should_apply_damage() && world != nullptr) {
  54. apply_impact_damage(world, projectile.get());
  55. }
  56. if (!projectile->is_active()) {
  57. continue;
  58. }
  59. }
  60. m_projectiles.erase(
  61. std::remove_if(m_projectiles.begin(), m_projectiles.end(),
  62. [](const ProjectilePtr &p) { return !p->is_active(); }),
  63. m_projectiles.end());
  64. }
  65. void ProjectileSystem::apply_impact_damage(Engine::Core::World *world,
  66. Projectile *projectile) {
  67. if (projectile == nullptr) {
  68. return;
  69. }
  70. constexpr float k_min_progress_for_impact = 0.98F;
  71. if (projectile->get_progress() < k_min_progress_for_impact) {
  72. return;
  73. }
  74. if (projectile->get_target_id() == 0) {
  75. return;
  76. }
  77. auto *target = world->get_entity(projectile->get_target_id());
  78. if (target == nullptr) {
  79. return;
  80. }
  81. if (target->has_component<Engine::Core::PendingRemovalComponent>()) {
  82. return;
  83. }
  84. auto *target_unit = target->get_component<Engine::Core::UnitComponent>();
  85. if (target_unit == nullptr || target_unit->health <= 0) {
  86. return;
  87. }
  88. auto *hold_mode = target->get_component<Engine::Core::HoldModeComponent>();
  89. auto *transform = target->get_component<Engine::Core::TransformComponent>();
  90. bool const hold_active = (hold_mode != nullptr) && hold_mode->active;
  91. auto *target_transform =
  92. target->get_component<Engine::Core::TransformComponent>();
  93. if (target_transform != nullptr) {
  94. QVector3D const current_pos(target_transform->position.x,
  95. target_transform->position.y,
  96. target_transform->position.z);
  97. float const distance =
  98. (current_pos - projectile->get_target_locked_position()).length();
  99. constexpr float k_escape_radius = 1.5F;
  100. if (distance > k_escape_radius) {
  101. return;
  102. }
  103. }
  104. target_unit->health -= projectile->get_damage();
  105. projectile->deactivate();
  106. if (target_unit->health <= 0) {
  107. target_unit->health = 0;
  108. int killer_owner_id = 0;
  109. if (projectile->get_attacker_id() != 0 && world != nullptr) {
  110. auto *attacker = world->get_entity(projectile->get_attacker_id());
  111. if (attacker != nullptr) {
  112. auto *attacker_unit =
  113. attacker->get_component<Engine::Core::UnitComponent>();
  114. if (attacker_unit != nullptr) {
  115. killer_owner_id = attacker_unit->owner_id;
  116. }
  117. }
  118. }
  119. Engine::Core::EventManager::instance().publish(Engine::Core::UnitDiedEvent(
  120. projectile->get_target_id(), target_unit->owner_id,
  121. target_unit->spawn_type, projectile->get_attacker_id(),
  122. killer_owner_id));
  123. }
  124. }
  125. } // namespace Game::Systems