combat_mode_test.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #include "core/component.h"
  2. #include "core/entity.h"
  3. #include "core/world.h"
  4. #include "systems/combat_system/attack_processor.h"
  5. #include "systems/combat_system/combat_mode_processor.h"
  6. #include "systems/owner_registry.h"
  7. #include <gtest/gtest.h>
  8. using namespace Engine::Core;
  9. using namespace Game::Systems;
  10. class CombatModeTest : public ::testing::Test {
  11. protected:
  12. void SetUp() override {
  13. world = std::make_unique<World>();
  14. OwnerRegistry::instance().clear();
  15. }
  16. void TearDown() override { world.reset(); }
  17. std::unique_ptr<World> world;
  18. };
  19. TEST_F(CombatModeTest, NoAttackModeWhenMovingNearEnemy) {
  20. // Create an attacker unit
  21. auto *attacker = world->create_entity();
  22. auto *attacker_transform =
  23. attacker->add_component<TransformComponent>(0.0F, 0.0F, 0.0F);
  24. auto *attacker_unit =
  25. attacker->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  26. attacker_unit->owner_id = 1;
  27. auto *attacker_attack = attacker->add_component<AttackComponent>();
  28. attacker_attack->can_melee = true;
  29. attacker_attack->can_ranged = false;
  30. attacker_attack->preferred_mode = AttackComponent::CombatMode::Auto;
  31. // Create an enemy unit nearby
  32. auto *enemy = world->create_entity();
  33. auto *enemy_transform =
  34. enemy->add_component<TransformComponent>(2.0F, 0.0F, 2.0F);
  35. auto *enemy_unit = enemy->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  36. enemy_unit->owner_id = 2;
  37. // Update combat mode - should not trigger attack mode because unit is not
  38. // engaged
  39. Combat::update_combat_mode(attacker, world.get(), attacker_attack);
  40. // Combat mode should remain in default (Melee for melee-only units) and not
  41. // switch based on proximity
  42. EXPECT_EQ(attacker_attack->current_mode, AttackComponent::CombatMode::Melee);
  43. EXPECT_FALSE(attacker_attack->in_melee_lock);
  44. }
  45. TEST_F(CombatModeTest, AttackModeTriggersWhenEngaged) {
  46. // Create an attacker unit
  47. auto *attacker = world->create_entity();
  48. auto *attacker_transform =
  49. attacker->add_component<TransformComponent>(0.0F, 0.0F, 0.0F);
  50. auto *attacker_unit =
  51. attacker->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  52. attacker_unit->owner_id = 1;
  53. auto *attacker_attack = attacker->add_component<AttackComponent>();
  54. attacker_attack->can_melee = true;
  55. attacker_attack->can_ranged = true;
  56. attacker_attack->preferred_mode = AttackComponent::CombatMode::Auto;
  57. attacker_attack->melee_range = 3.0F;
  58. attacker_attack->range = 10.0F;
  59. // Create an enemy unit nearby
  60. auto *enemy = world->create_entity();
  61. auto *enemy_transform =
  62. enemy->add_component<TransformComponent>(2.0F, 0.0F, 2.0F);
  63. auto *enemy_unit = enemy->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  64. enemy_unit->owner_id = 2;
  65. // Engage in combat by adding attack target
  66. auto *attack_target = attacker->add_component<AttackTargetComponent>();
  67. attack_target->target_id = enemy->get_id();
  68. attack_target->should_chase = true;
  69. // Update combat mode - should now calculate proper mode since engaged
  70. Combat::update_combat_mode(attacker, world.get(), attacker_attack);
  71. // Since enemy is close (within melee range), should be in melee mode
  72. EXPECT_EQ(attacker_attack->current_mode, AttackComponent::CombatMode::Melee);
  73. }
  74. TEST_F(CombatModeTest, BuildingsExcludedFromCombatMode) {
  75. // Create an attacker unit
  76. auto *attacker = world->create_entity();
  77. auto *attacker_transform =
  78. attacker->add_component<TransformComponent>(0.0F, 0.0F, 0.0F);
  79. auto *attacker_unit =
  80. attacker->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  81. attacker_unit->owner_id = 1;
  82. auto *attacker_attack = attacker->add_component<AttackComponent>();
  83. attacker_attack->can_melee = true;
  84. attacker_attack->can_ranged = true;
  85. attacker_attack->preferred_mode = AttackComponent::CombatMode::Auto;
  86. attacker_attack->melee_range = 3.0F;
  87. attacker_attack->range = 10.0F;
  88. // Create an enemy building nearby
  89. auto *building = world->create_entity();
  90. auto *building_transform =
  91. building->add_component<TransformComponent>(2.0F, 0.0F, 2.0F);
  92. auto *building_unit =
  93. building->add_component<UnitComponent>(500, 500, 0.0F, 12.0F);
  94. building_unit->owner_id = 2;
  95. building->add_component<BuildingComponent>();
  96. // Engage with the building by adding attack target
  97. auto *attack_target = attacker->add_component<AttackTargetComponent>();
  98. attack_target->target_id = building->get_id();
  99. attack_target->should_chase = false;
  100. // Update combat mode - buildings should be excluded from calculation
  101. Combat::update_combat_mode(attacker, world.get(), attacker_attack);
  102. // Since building is excluded, should use ranged mode (default for
  103. // ranged-capable units)
  104. EXPECT_EQ(attacker_attack->current_mode, AttackComponent::CombatMode::Ranged);
  105. }
  106. TEST_F(CombatModeTest, RangedUnitUsesRangedModeWhenNotEngaged) {
  107. // Create a ranged attacker unit
  108. auto *attacker = world->create_entity();
  109. auto *attacker_transform =
  110. attacker->add_component<TransformComponent>(0.0F, 0.0F, 0.0F);
  111. auto *attacker_unit =
  112. attacker->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  113. attacker_unit->owner_id = 1;
  114. auto *attacker_attack = attacker->add_component<AttackComponent>();
  115. attacker_attack->can_melee = false;
  116. attacker_attack->can_ranged = true;
  117. attacker_attack->preferred_mode = AttackComponent::CombatMode::Auto;
  118. // Create an enemy unit nearby
  119. auto *enemy = world->create_entity();
  120. auto *enemy_transform =
  121. enemy->add_component<TransformComponent>(5.0F, 0.0F, 5.0F);
  122. auto *enemy_unit = enemy->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  123. enemy_unit->owner_id = 2;
  124. // Update combat mode - should keep ranged mode as default
  125. Combat::update_combat_mode(attacker, world.get(), attacker_attack);
  126. // Ranged unit should be in ranged mode
  127. EXPECT_EQ(attacker_attack->current_mode, AttackComponent::CombatMode::Ranged);
  128. EXPECT_FALSE(attacker_attack->in_melee_lock);
  129. }
  130. TEST_F(CombatModeTest, BuildingsDoNotMoveInMeleeLock) {
  131. // Create a regular unit
  132. auto *unit = world->create_entity();
  133. auto *unit_transform =
  134. unit->add_component<TransformComponent>(0.0F, 0.0F, 0.0F);
  135. auto *unit_comp = unit->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  136. unit_comp->owner_id = 1;
  137. auto *unit_attack = unit->add_component<AttackComponent>();
  138. unit_attack->can_melee = true;
  139. unit_attack->can_ranged = false;
  140. // Create a building (defense tower) at distance
  141. auto *building = world->create_entity();
  142. auto *building_transform =
  143. building->add_component<TransformComponent>(10.0F, 0.0F, 0.0F);
  144. auto *building_comp =
  145. building->add_component<UnitComponent>(500, 500, 0.0F, 12.0F);
  146. building_comp->owner_id = 2;
  147. auto *building_attack = building->add_component<AttackComponent>();
  148. building_attack->can_melee = false;
  149. building_attack->can_ranged = true;
  150. building->add_component<BuildingComponent>();
  151. // Store initial positions
  152. float const initial_unit_x = unit_transform->position.x;
  153. float const initial_building_x = building_transform->position.x;
  154. // Simulate melee lock initiation
  155. unit_attack->in_melee_lock = true;
  156. unit_attack->melee_lock_target_id = building->get_id();
  157. building_attack->in_melee_lock = true;
  158. building_attack->melee_lock_target_id = unit->get_id();
  159. // Process attacks which includes melee lock processing
  160. Game::Systems::Combat::process_attacks(world.get(), 0.016F);
  161. // Regular unit should have moved toward the building
  162. EXPECT_NE(unit_transform->position.x, initial_unit_x);
  163. // Building should NOT have moved
  164. EXPECT_EQ(building_transform->position.x, initial_building_x);
  165. }
  166. TEST_F(CombatModeTest, HomeDoesNotMoveInMeleeLock) {
  167. // Create a regular unit
  168. auto *unit = world->create_entity();
  169. auto *unit_transform =
  170. unit->add_component<TransformComponent>(0.0F, 0.0F, 0.0F);
  171. auto *unit_comp = unit->add_component<UnitComponent>(100, 100, 1.0F, 12.0F);
  172. unit_comp->owner_id = 1;
  173. auto *unit_attack = unit->add_component<AttackComponent>();
  174. unit_attack->can_melee = true;
  175. unit_attack->can_ranged = false;
  176. // Create a home building at distance
  177. auto *home = world->create_entity();
  178. auto *home_transform =
  179. home->add_component<TransformComponent>(10.0F, 0.0F, 0.0F);
  180. auto *home_comp = home->add_component<UnitComponent>(1000, 1000, 0.0F, 15.0F);
  181. home_comp->owner_id = 2;
  182. auto *home_attack = home->add_component<AttackComponent>();
  183. home_attack->can_melee = false;
  184. home_attack->can_ranged = false;
  185. home->add_component<BuildingComponent>();
  186. // Store initial positions
  187. float const initial_unit_x = unit_transform->position.x;
  188. float const initial_home_x = home_transform->position.x;
  189. // Simulate melee lock initiation
  190. unit_attack->in_melee_lock = true;
  191. unit_attack->melee_lock_target_id = home->get_id();
  192. home_attack->in_melee_lock = true;
  193. home_attack->melee_lock_target_id = unit->get_id();
  194. // Process attacks which includes melee lock processing
  195. Game::Systems::Combat::process_attacks(world.get(), 0.016F);
  196. // Regular unit should have moved toward the home
  197. EXPECT_NE(unit_transform->position.x, initial_unit_x);
  198. // Home building should NOT have moved
  199. EXPECT_EQ(home_transform->position.x, initial_home_x);
  200. }