defense_tower_system.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. #include "defense_tower_system.h"
  2. #include "../core/component.h"
  3. #include "../core/world.h"
  4. #include "../units/spawn_type.h"
  5. #include "../visuals/team_colors.h"
  6. #include "arrow_system.h"
  7. #include "combat_system/combat_utils.h"
  8. #include "combat_system/damage_processor.h"
  9. #include "owner_registry.h"
  10. #include <cmath>
  11. #include <random>
  12. namespace Game::Systems {
  13. namespace {
  14. thread_local std::mt19937 gen(std::random_device{}());
  15. auto find_nearest_enemy_in_range(Engine::Core::Entity *tower,
  16. Engine::Core::World *world,
  17. float range) -> Engine::Core::Entity * {
  18. auto *tower_unit = tower->get_component<Engine::Core::UnitComponent>();
  19. auto *tower_transform =
  20. tower->get_component<Engine::Core::TransformComponent>();
  21. if (tower_unit == nullptr || tower_transform == nullptr) {
  22. return nullptr;
  23. }
  24. float best_dist_sq = range * range;
  25. Engine::Core::Entity *best_target = nullptr;
  26. auto &owner_registry = OwnerRegistry::instance();
  27. auto entities = world->get_entities_with<Engine::Core::UnitComponent>();
  28. for (auto *entity : entities) {
  29. if (entity == tower) {
  30. continue;
  31. }
  32. if (entity->has_component<Engine::Core::PendingRemovalComponent>()) {
  33. continue;
  34. }
  35. auto *target_unit = entity->get_component<Engine::Core::UnitComponent>();
  36. if (target_unit == nullptr || target_unit->health <= 0) {
  37. continue;
  38. }
  39. if (target_unit->owner_id == tower_unit->owner_id) {
  40. continue;
  41. }
  42. if (owner_registry.are_allies(tower_unit->owner_id,
  43. target_unit->owner_id)) {
  44. continue;
  45. }
  46. if (entity->has_component<Engine::Core::BuildingComponent>()) {
  47. continue;
  48. }
  49. auto *target_transform =
  50. entity->get_component<Engine::Core::TransformComponent>();
  51. if (target_transform == nullptr) {
  52. continue;
  53. }
  54. float const dx = target_transform->position.x - tower_transform->position.x;
  55. float const dz = target_transform->position.z - tower_transform->position.z;
  56. float const dist_sq = dx * dx + dz * dz;
  57. if (dist_sq < best_dist_sq) {
  58. best_dist_sq = dist_sq;
  59. best_target = entity;
  60. }
  61. }
  62. return best_target;
  63. }
  64. void spawn_tower_arrows(Engine::Core::Entity *tower,
  65. Engine::Core::Entity *target, ArrowSystem *arrow_sys) {
  66. if (arrow_sys == nullptr) {
  67. return;
  68. }
  69. auto *tower_t = tower->get_component<Engine::Core::TransformComponent>();
  70. auto *tgt_t = target->get_component<Engine::Core::TransformComponent>();
  71. auto *tower_u = tower->get_component<Engine::Core::UnitComponent>();
  72. if (tower_t == nullptr || tgt_t == nullptr) {
  73. return;
  74. }
  75. QVector3D const tower_pos(tower_t->position.x, tower_t->position.y + 2.0F,
  76. tower_t->position.z);
  77. QVector3D const target_pos(tgt_t->position.x, tgt_t->position.y,
  78. tgt_t->position.z);
  79. QVector3D const dir = (target_pos - tower_pos).normalized();
  80. QVector3D const color =
  81. (tower_u != nullptr)
  82. ? Game::Visuals::team_colorForOwner(tower_u->owner_id)
  83. : QVector3D(0.8F, 0.9F, 1.0F);
  84. std::uniform_real_distribution<float> spread_dist(-0.3F, 0.3F);
  85. QVector3D const perpendicular(-dir.z(), 0.0F, dir.x());
  86. float const lateral_offset = spread_dist(gen);
  87. QVector3D const start =
  88. tower_pos + dir * 0.5F + perpendicular * lateral_offset;
  89. QVector3D const end =
  90. target_pos + QVector3D(0.0F, 0.8F, 0.0F) + perpendicular * lateral_offset;
  91. constexpr float kArrowSpeed = 12.0F;
  92. arrow_sys->spawn_arrow(start, end, color, kArrowSpeed);
  93. }
  94. } // namespace
  95. void DefenseTowerSystem::update(Engine::Core::World *world, float delta_time) {
  96. auto *arrow_sys = world->get_system<ArrowSystem>();
  97. auto entities = world->get_entities_with<Engine::Core::UnitComponent>();
  98. for (auto *tower : entities) {
  99. if (tower->has_component<Engine::Core::PendingRemovalComponent>()) {
  100. continue;
  101. }
  102. auto *tower_unit = tower->get_component<Engine::Core::UnitComponent>();
  103. if (tower_unit == nullptr || tower_unit->health <= 0) {
  104. continue;
  105. }
  106. if (tower_unit->spawn_type != Game::Units::SpawnType::DefenseTower) {
  107. continue;
  108. }
  109. if (!tower->has_component<Engine::Core::BuildingComponent>()) {
  110. continue;
  111. }
  112. auto *attack_comp = tower->get_component<Engine::Core::AttackComponent>();
  113. if (attack_comp == nullptr) {
  114. continue;
  115. }
  116. attack_comp->time_since_last += delta_time;
  117. if (attack_comp->time_since_last < attack_comp->cooldown) {
  118. continue;
  119. }
  120. auto *target =
  121. find_nearest_enemy_in_range(tower, world, attack_comp->range);
  122. if (target == nullptr) {
  123. continue;
  124. }
  125. spawn_tower_arrows(tower, target, arrow_sys);
  126. Combat::deal_damage(world, target, attack_comp->damage, tower->get_id());
  127. attack_comp->time_since_last = 0.0F;
  128. }
  129. }
  130. } // namespace Game::Systems