ai_command_applier.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #include "ai_command_applier.h"
  2. #include "../../core/component.h"
  3. #include "../../core/world.h"
  4. #include "../../game_config.h"
  5. #include "../../units/troop_config.h"
  6. #include "../command_service.h"
  7. #include "ai_utils.h"
  8. #include "systems/ai_system/ai_types.h"
  9. #include "units/spawn_type.h"
  10. #include "units/troop_type.h"
  11. #include <QVector3D>
  12. #include <cstddef>
  13. #include <qvectornd.h>
  14. #include <vector>
  15. namespace Game::Systems::AI {
  16. namespace {
  17. constexpr const char *BUILDING_TYPE_HOME = "home";
  18. constexpr const char *BUILDING_TYPE_DEFENSE_TOWER = "defense_tower";
  19. constexpr const char *BUILDING_TYPE_BARRACKS = "barracks";
  20. constexpr float BUILD_TIME_HOME = 20.0F;
  21. constexpr float BUILD_TIME_DEFENSE_TOWER = 25.0F;
  22. constexpr float BUILD_TIME_BARRACKS = 30.0F;
  23. constexpr float BUILD_TIME_DEFAULT = 20.0F;
  24. } // namespace
  25. void AICommandApplier::apply(Engine::Core::World &world, int ai_owner_id,
  26. const std::vector<AICommand> &commands) {
  27. for (const auto &command : commands) {
  28. switch (command.type) {
  29. case AICommandType::MoveUnits: {
  30. if (command.units.empty()) {
  31. break;
  32. }
  33. std::vector<float> expanded_x;
  34. std::vector<float> expanded_y;
  35. std::vector<float> expanded_z;
  36. if (command.move_target_x.size() != command.units.size()) {
  37. replicate_last_target_if_needed(
  38. command.move_target_x, command.move_target_y, command.move_target_z,
  39. command.units.size(), expanded_x, expanded_y, expanded_z);
  40. } else {
  41. expanded_x = command.move_target_x;
  42. expanded_y = command.move_target_y;
  43. expanded_z = command.move_target_z;
  44. }
  45. if (expanded_x.empty()) {
  46. break;
  47. }
  48. std::vector<Engine::Core::EntityID> owned_units;
  49. std::vector<QVector3D> owned_targets;
  50. owned_units.reserve(command.units.size());
  51. owned_targets.reserve(command.units.size());
  52. for (std::size_t idx = 0; idx < command.units.size(); ++idx) {
  53. auto entity_id = command.units[idx];
  54. auto *entity = world.get_entity(entity_id);
  55. if (entity == nullptr) {
  56. continue;
  57. }
  58. auto *unit = entity->get_component<Engine::Core::UnitComponent>();
  59. if ((unit == nullptr) || unit->owner_id != ai_owner_id) {
  60. continue;
  61. }
  62. owned_units.push_back(entity_id);
  63. owned_targets.emplace_back(expanded_x[idx], expanded_y[idx],
  64. expanded_z[idx]);
  65. }
  66. if (owned_units.empty()) {
  67. break;
  68. }
  69. CommandService::MoveOptions opts;
  70. opts.allow_direct_fallback = true;
  71. opts.clear_attack_intent = false;
  72. opts.group_move = owned_units.size() > 1;
  73. CommandService::move_units(world, owned_units, owned_targets, opts);
  74. break;
  75. }
  76. case AICommandType::AttackTarget: {
  77. if (command.units.empty() || command.target_id == 0) {
  78. break;
  79. }
  80. std::vector<Engine::Core::EntityID> owned_units;
  81. owned_units.reserve(command.units.size());
  82. for (auto entity_id : command.units) {
  83. auto *entity = world.get_entity(entity_id);
  84. if (entity == nullptr) {
  85. continue;
  86. }
  87. auto *unit = entity->get_component<Engine::Core::UnitComponent>();
  88. if ((unit == nullptr) || unit->owner_id != ai_owner_id) {
  89. continue;
  90. }
  91. owned_units.push_back(entity_id);
  92. }
  93. if (owned_units.empty()) {
  94. break;
  95. }
  96. CommandService::attack_target(world, owned_units, command.target_id,
  97. command.should_chase);
  98. break;
  99. }
  100. case AICommandType::StartProduction: {
  101. auto *entity = world.get_entity(command.building_id);
  102. if (entity == nullptr) {
  103. break;
  104. }
  105. auto *production =
  106. entity->get_component<Engine::Core::ProductionComponent>();
  107. if (production == nullptr) {
  108. break;
  109. }
  110. if (production->in_progress) {
  111. break;
  112. }
  113. auto *unit = entity->get_component<Engine::Core::UnitComponent>();
  114. if ((unit != nullptr) && unit->owner_id != ai_owner_id) {
  115. break;
  116. }
  117. int const current_troops =
  118. Engine::Core::World::count_troops_for_player(ai_owner_id);
  119. int const max_troops =
  120. Game::GameConfig::instance().get_max_troops_per_player();
  121. Game::Units::TroopType const product_type = production->product_type;
  122. int const production_cost =
  123. Game::Units::TroopConfig::instance().getProductionCost(product_type);
  124. if (current_troops + production_cost > max_troops) {
  125. break;
  126. }
  127. production->product_type = command.product_type;
  128. production->time_remaining = production->build_time;
  129. production->in_progress = true;
  130. break;
  131. }
  132. case AICommandType::StartBuilderConstruction: {
  133. if (command.units.empty() || command.construction_type.empty()) {
  134. break;
  135. }
  136. for (auto entity_id : command.units) {
  137. auto *entity = world.get_entity(entity_id);
  138. if (entity == nullptr) {
  139. continue;
  140. }
  141. auto *unit = entity->get_component<Engine::Core::UnitComponent>();
  142. if ((unit == nullptr) || unit->owner_id != ai_owner_id) {
  143. continue;
  144. }
  145. if (unit->spawn_type != Game::Units::SpawnType::Builder) {
  146. continue;
  147. }
  148. auto *builder_prod =
  149. entity->get_component<Engine::Core::BuilderProductionComponent>();
  150. if (builder_prod == nullptr) {
  151. continue;
  152. }
  153. builder_prod->product_type = command.construction_type;
  154. builder_prod->has_construction_site = true;
  155. builder_prod->construction_site_x = command.construction_site_x;
  156. builder_prod->construction_site_z = command.construction_site_z;
  157. builder_prod->at_construction_site = false;
  158. builder_prod->in_progress = false;
  159. builder_prod->construction_complete = false;
  160. builder_prod->is_placement_preview = false;
  161. if (command.construction_type == BUILDING_TYPE_HOME) {
  162. builder_prod->build_time = BUILD_TIME_HOME;
  163. } else if (command.construction_type == BUILDING_TYPE_DEFENSE_TOWER) {
  164. builder_prod->build_time = BUILD_TIME_DEFENSE_TOWER;
  165. } else if (command.construction_type == BUILDING_TYPE_BARRACKS) {
  166. builder_prod->build_time = BUILD_TIME_BARRACKS;
  167. } else {
  168. builder_prod->build_time = BUILD_TIME_DEFAULT;
  169. }
  170. builder_prod->time_remaining = builder_prod->build_time;
  171. }
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. } // namespace Game::Systems::AI