combat_utils.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. #include "combat_utils.h"
  2. #include "../../core/component.h"
  3. #include "../../core/world.h"
  4. #include "../../units/spawn_type.h"
  5. #include "../owner_registry.h"
  6. #include "../spatial_grid.h"
  7. namespace Game::Systems::Combat {
  8. namespace {
  9. thread_local SpatialGrid t_unit_grid(15.0F);
  10. }
  11. auto is_unit_in_hold_mode(Engine::Core::Entity *entity) -> bool {
  12. if (entity == nullptr) {
  13. return false;
  14. }
  15. auto *hold_mode = entity->get_component<Engine::Core::HoldModeComponent>();
  16. return (hold_mode != nullptr) && hold_mode->active;
  17. }
  18. auto is_unit_in_guard_mode(Engine::Core::Entity *entity) -> bool {
  19. if (entity == nullptr) {
  20. return false;
  21. }
  22. auto *guard_mode = entity->get_component<Engine::Core::GuardModeComponent>();
  23. return (guard_mode != nullptr) && guard_mode->active;
  24. }
  25. auto is_building(Engine::Core::Entity *entity) -> bool {
  26. if (entity == nullptr) {
  27. return false;
  28. }
  29. return entity->has_component<Engine::Core::BuildingComponent>();
  30. }
  31. auto is_in_range(Engine::Core::Entity *attacker, Engine::Core::Entity *target,
  32. float range) -> bool {
  33. auto *attacker_transform =
  34. attacker->get_component<Engine::Core::TransformComponent>();
  35. auto *target_transform =
  36. target->get_component<Engine::Core::TransformComponent>();
  37. if ((attacker_transform == nullptr) || (target_transform == nullptr)) {
  38. return false;
  39. }
  40. float const dx =
  41. target_transform->position.x - attacker_transform->position.x;
  42. float const dz =
  43. target_transform->position.z - attacker_transform->position.z;
  44. float const dy =
  45. target_transform->position.y - attacker_transform->position.y;
  46. float const distance_squared = dx * dx + dz * dz;
  47. float const scale_x = target_transform->scale.x;
  48. float const scale_z = target_transform->scale.z;
  49. float const target_radius = std::max(scale_x, scale_z) * 0.5F;
  50. float const effective_range = range + target_radius;
  51. if (distance_squared > effective_range * effective_range) {
  52. return false;
  53. }
  54. auto *attacker_atk = attacker->get_component<Engine::Core::AttackComponent>();
  55. if ((attacker_atk != nullptr) &&
  56. attacker_atk->current_mode ==
  57. Engine::Core::AttackComponent::CombatMode::Melee) {
  58. float const height_diff = std::abs(dy);
  59. if (height_diff > attacker_atk->max_height_difference) {
  60. return false;
  61. }
  62. }
  63. return true;
  64. }
  65. auto is_unit_idle(Engine::Core::Entity *unit) -> bool {
  66. auto *hold_mode = unit->get_component<Engine::Core::HoldModeComponent>();
  67. if ((hold_mode != nullptr) && hold_mode->active) {
  68. return false;
  69. }
  70. auto *guard_mode = unit->get_component<Engine::Core::GuardModeComponent>();
  71. if ((guard_mode != nullptr) && guard_mode->active &&
  72. guard_mode->returning_to_guard_position) {
  73. return false;
  74. }
  75. auto *attack_target =
  76. unit->get_component<Engine::Core::AttackTargetComponent>();
  77. if ((attack_target != nullptr) && attack_target->target_id != 0) {
  78. return false;
  79. }
  80. auto *movement = unit->get_component<Engine::Core::MovementComponent>();
  81. if ((movement != nullptr) && movement->has_target) {
  82. return false;
  83. }
  84. auto *attack_comp = unit->get_component<Engine::Core::AttackComponent>();
  85. if ((attack_comp != nullptr) && attack_comp->in_melee_lock) {
  86. return false;
  87. }
  88. auto *patrol = unit->get_component<Engine::Core::PatrolComponent>();
  89. return (patrol == nullptr) || !patrol->patrolling;
  90. }
  91. auto find_nearest_enemy_from_list(
  92. Engine::Core::Entity *unit,
  93. const std::vector<Engine::Core::Entity *> &all_units, Engine::Core::World *,
  94. float max_range) -> Engine::Core::Entity * {
  95. auto *unit_comp = unit->get_component<Engine::Core::UnitComponent>();
  96. auto *unit_transform =
  97. unit->get_component<Engine::Core::TransformComponent>();
  98. if ((unit_comp == nullptr) || (unit_transform == nullptr)) {
  99. return nullptr;
  100. }
  101. auto &owner_registry = Game::Systems::OwnerRegistry::instance();
  102. Engine::Core::Entity *nearest_enemy = nullptr;
  103. float nearest_dist_sq = max_range * max_range;
  104. for (auto *target : all_units) {
  105. if (target == unit) {
  106. continue;
  107. }
  108. if (target->has_component<Engine::Core::PendingRemovalComponent>()) {
  109. continue;
  110. }
  111. auto *target_unit = target->get_component<Engine::Core::UnitComponent>();
  112. if ((target_unit == nullptr) || target_unit->health <= 0) {
  113. continue;
  114. }
  115. if (target_unit->owner_id == unit_comp->owner_id) {
  116. continue;
  117. }
  118. if (owner_registry.are_allies(unit_comp->owner_id, target_unit->owner_id)) {
  119. continue;
  120. }
  121. if (target->has_component<Engine::Core::BuildingComponent>()) {
  122. continue;
  123. }
  124. auto *target_transform =
  125. target->get_component<Engine::Core::TransformComponent>();
  126. if (target_transform == nullptr) {
  127. continue;
  128. }
  129. float const dx = target_transform->position.x - unit_transform->position.x;
  130. float const dz = target_transform->position.z - unit_transform->position.z;
  131. float const dist_sq = dx * dx + dz * dz;
  132. if (dist_sq < nearest_dist_sq) {
  133. nearest_dist_sq = dist_sq;
  134. nearest_enemy = target;
  135. }
  136. }
  137. return nearest_enemy;
  138. }
  139. auto find_nearest_enemy(Engine::Core::Entity *unit, Engine::Core::World *world,
  140. float max_range) -> Engine::Core::Entity * {
  141. auto *unit_comp = unit->get_component<Engine::Core::UnitComponent>();
  142. auto *unit_transform =
  143. unit->get_component<Engine::Core::TransformComponent>();
  144. if ((unit_comp == nullptr) || (unit_transform == nullptr)) {
  145. return nullptr;
  146. }
  147. t_unit_grid.clear();
  148. auto units = world->get_entities_with<Engine::Core::UnitComponent>();
  149. for (auto *entity : units) {
  150. auto *transform = entity->get_component<Engine::Core::TransformComponent>();
  151. if (transform != nullptr) {
  152. t_unit_grid.insert(entity->get_id(), transform->position.x,
  153. transform->position.z);
  154. }
  155. }
  156. auto nearby_ids = t_unit_grid.get_entities_in_range(
  157. unit_transform->position.x, unit_transform->position.z, max_range);
  158. auto &owner_registry = Game::Systems::OwnerRegistry::instance();
  159. Engine::Core::Entity *nearest_enemy = nullptr;
  160. float nearest_dist_sq = max_range * max_range;
  161. for (Engine::Core::EntityID target_id : nearby_ids) {
  162. if (target_id == unit->get_id()) {
  163. continue;
  164. }
  165. auto *target = world->get_entity(target_id);
  166. if (target == nullptr) {
  167. continue;
  168. }
  169. if (target->has_component<Engine::Core::PendingRemovalComponent>()) {
  170. continue;
  171. }
  172. auto *target_unit = target->get_component<Engine::Core::UnitComponent>();
  173. if ((target_unit == nullptr) || target_unit->health <= 0) {
  174. continue;
  175. }
  176. if (target_unit->owner_id == unit_comp->owner_id) {
  177. continue;
  178. }
  179. if (owner_registry.are_allies(unit_comp->owner_id, target_unit->owner_id)) {
  180. continue;
  181. }
  182. if (target->has_component<Engine::Core::BuildingComponent>()) {
  183. continue;
  184. }
  185. auto *target_transform =
  186. target->get_component<Engine::Core::TransformComponent>();
  187. if (target_transform == nullptr) {
  188. continue;
  189. }
  190. float const dx = target_transform->position.x - unit_transform->position.x;
  191. float const dz = target_transform->position.z - unit_transform->position.z;
  192. float const dist_sq = dx * dx + dz * dz;
  193. if (dist_sq < nearest_dist_sq) {
  194. nearest_dist_sq = dist_sq;
  195. nearest_enemy = target;
  196. }
  197. }
  198. return nearest_enemy;
  199. }
  200. auto should_auto_engage_melee(Engine::Core::Entity *unit) -> bool {
  201. if (unit == nullptr) {
  202. return false;
  203. }
  204. auto *unit_comp = unit->get_component<Engine::Core::UnitComponent>();
  205. if (unit_comp == nullptr) {
  206. return false;
  207. }
  208. switch (unit_comp->spawn_type) {
  209. case Game::Units::SpawnType::Archer:
  210. case Game::Units::SpawnType::HorseArcher:
  211. case Game::Units::SpawnType::Healer:
  212. case Game::Units::SpawnType::Catapult:
  213. case Game::Units::SpawnType::Ballista:
  214. return false;
  215. case Game::Units::SpawnType::Knight:
  216. case Game::Units::SpawnType::Spearman:
  217. case Game::Units::SpawnType::MountedKnight:
  218. case Game::Units::SpawnType::HorseSpearman:
  219. return true;
  220. case Game::Units::SpawnType::Barracks:
  221. return false;
  222. }
  223. return false;
  224. }
  225. } // namespace Game::Systems::Combat