victory_service.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #include "victory_service.h"
  2. #include "core/event_manager.h"
  3. #include "game/core/component.h"
  4. #include "game/core/world.h"
  5. #include "game/map/map_definition.h"
  6. #include "game/systems/global_stats_registry.h"
  7. #include "game/systems/nation_registry.h"
  8. #include "game/systems/owner_registry.h"
  9. #include "units/spawn_type.h"
  10. #include <QDebug>
  11. #include <algorithm>
  12. #include <qglobal.h>
  13. namespace Game::Systems {
  14. namespace {
  15. constexpr float k_startup_delay_seconds = 0.35F;
  16. }
  17. VictoryService::VictoryService()
  18. : m_unit_died_subscription(
  19. [this](const Engine::Core::UnitDiedEvent &e) { on_unit_died(e); }),
  20. m_barrack_captured_subscription(
  21. [this](const Engine::Core::BarrackCapturedEvent &e) {
  22. on_barrack_captured(e);
  23. }),
  24. m_stats_registry(Game::Systems::GlobalStatsRegistry::instance()),
  25. m_owner_registry(Game::Systems::OwnerRegistry::instance()) {}
  26. VictoryService::~VictoryService() = default;
  27. void VictoryService::reset() {
  28. m_victoryState = "";
  29. m_elapsed_time = 0.0F;
  30. m_startup_delay = 0.0F;
  31. m_worldPtr = nullptr;
  32. m_victoryCallback = nullptr;
  33. m_keyStructures.clear();
  34. m_defeatConditions.clear();
  35. m_requiredKeyStructures = 0;
  36. }
  37. void VictoryService::configure(const Game::Map::VictoryConfig &config,
  38. int local_owner_id) {
  39. reset();
  40. m_local_owner_id = local_owner_id;
  41. if (config.victoryType == "elimination") {
  42. m_victoryType = VictoryType::Elimination;
  43. m_keyStructures = config.keyStructures;
  44. } else if (config.victoryType == "control_structures") {
  45. m_victoryType = VictoryType::ControlStructures;
  46. m_keyStructures = config.keyStructures;
  47. m_requiredKeyStructures = config.requiredKeyStructures;
  48. } else if (config.victoryType == "capture_structures") {
  49. m_victoryType = VictoryType::CaptureStructures;
  50. m_keyStructures = config.keyStructures;
  51. m_requiredKeyStructures = config.requiredKeyStructures;
  52. } else if (config.victoryType == "survive_time") {
  53. m_victoryType = VictoryType::SurviveTime;
  54. m_survive_time_duration = config.surviveTimeDuration;
  55. } else {
  56. m_victoryType = VictoryType::Elimination;
  57. m_keyStructures = {"barracks"};
  58. }
  59. m_defeatConditions.clear();
  60. for (const auto &condition : config.defeatConditions) {
  61. if (condition == "no_units") {
  62. m_defeatConditions.push_back(DefeatCondition::NoUnits);
  63. } else if (condition == "no_key_structures") {
  64. m_defeatConditions.push_back(DefeatCondition::NoKeyStructures);
  65. }
  66. }
  67. if (m_defeatConditions.empty()) {
  68. m_defeatConditions.push_back(DefeatCondition::NoKeyStructures);
  69. }
  70. m_startup_delay = k_startup_delay_seconds;
  71. }
  72. void VictoryService::update(Engine::Core::World &world, float delta_time) {
  73. if (!m_victoryState.isEmpty()) {
  74. return;
  75. }
  76. m_worldPtr = &world;
  77. if (m_startup_delay > 0.0F) {
  78. m_startup_delay = std::max(0.0F, m_startup_delay - delta_time);
  79. return;
  80. }
  81. if (m_victoryType == VictoryType::SurviveTime) {
  82. m_elapsed_time += delta_time;
  83. }
  84. check_victory_conditions(world);
  85. if (!m_victoryState.isEmpty()) {
  86. return;
  87. }
  88. check_defeat_conditions(world);
  89. }
  90. void VictoryService::on_unit_died(const Engine::Core::UnitDiedEvent &event) {}
  91. void VictoryService::on_barrack_captured(
  92. const Engine::Core::BarrackCapturedEvent &) {
  93. if ((m_worldPtr == nullptr) || !m_victoryState.isEmpty()) {
  94. return;
  95. }
  96. check_victory_conditions(*m_worldPtr);
  97. if (!m_victoryState.isEmpty()) {
  98. return;
  99. }
  100. check_defeat_conditions(*m_worldPtr);
  101. }
  102. void VictoryService::check_victory_conditions(Engine::Core::World &world) {
  103. bool victory = false;
  104. switch (m_victoryType) {
  105. case VictoryType::Elimination:
  106. victory = check_elimination(world);
  107. break;
  108. case VictoryType::SurviveTime:
  109. victory = check_survive_time();
  110. break;
  111. case VictoryType::ControlStructures:
  112. victory = check_control_structures(world, false);
  113. break;
  114. case VictoryType::CaptureStructures:
  115. victory = check_control_structures(world, true);
  116. break;
  117. case VictoryType::Custom:
  118. break;
  119. }
  120. if (victory) {
  121. m_victoryState = "victory";
  122. qInfo() << "VICTORY! Conditions met.";
  123. const auto &all_owners = m_owner_registry.get_all_owners();
  124. for (const auto &owner : all_owners) {
  125. if (owner.type == Game::Systems::OwnerType::Player ||
  126. owner.type == Game::Systems::OwnerType::AI) {
  127. m_stats_registry.mark_game_end(owner.owner_id);
  128. }
  129. }
  130. const auto *stats = m_stats_registry.get_stats(m_local_owner_id);
  131. if (stats != nullptr) {
  132. qInfo() << "Final Stats - Troops Recruited:" << stats->troops_recruited
  133. << "Enemies Killed:" << stats->enemies_killed
  134. << "Losses:" << stats->losses
  135. << "Barracks Owned:" << stats->barracks_owned
  136. << "Play Time:" << stats->play_time_sec << "seconds";
  137. }
  138. if (m_victoryCallback) {
  139. m_victoryCallback(m_victoryState);
  140. }
  141. }
  142. }
  143. void VictoryService::check_defeat_conditions(Engine::Core::World &world) {
  144. for (const auto &condition : m_defeatConditions) {
  145. bool defeat = false;
  146. switch (condition) {
  147. case DefeatCondition::NoUnits:
  148. defeat = check_no_units(world);
  149. break;
  150. case DefeatCondition::NoKeyStructures:
  151. defeat = check_no_key_structures(world);
  152. break;
  153. case DefeatCondition::TimeExpired:
  154. break;
  155. }
  156. if (defeat) {
  157. m_victoryState = "defeat";
  158. qInfo() << "DEFEAT! Condition met.";
  159. const auto &all_owners = m_owner_registry.get_all_owners();
  160. for (const auto &owner : all_owners) {
  161. if (owner.type == Game::Systems::OwnerType::Player ||
  162. owner.type == Game::Systems::OwnerType::AI) {
  163. m_stats_registry.mark_game_end(owner.owner_id);
  164. }
  165. }
  166. const auto *stats = m_stats_registry.get_stats(m_local_owner_id);
  167. if (stats != nullptr) {
  168. qInfo() << "Final Stats - Troops Recruited:" << stats->troops_recruited
  169. << "Enemies Killed:" << stats->enemies_killed
  170. << "Losses:" << stats->losses
  171. << "Barracks Owned:" << stats->barracks_owned
  172. << "Play Time:" << stats->play_time_sec << "seconds";
  173. }
  174. if (m_victoryCallback) {
  175. m_victoryCallback(m_victoryState);
  176. }
  177. return;
  178. }
  179. }
  180. }
  181. auto VictoryService::check_elimination(Engine::Core::World &world) -> bool {
  182. bool enemy_key_structures_alive = false;
  183. int const local_team = m_owner_registry.get_owner_team(m_local_owner_id);
  184. auto entities = world.get_entities_with<Engine::Core::UnitComponent>();
  185. for (auto *e : entities) {
  186. auto *unit = e->get_component<Engine::Core::UnitComponent>();
  187. if ((unit == nullptr) || unit->health <= 0) {
  188. continue;
  189. }
  190. if (unit->owner_id == m_local_owner_id) {
  191. continue;
  192. }
  193. if (m_owner_registry.are_allies(m_local_owner_id, unit->owner_id)) {
  194. continue;
  195. }
  196. QString const unit_type_str = QString::fromStdString(
  197. Game::Units::spawn_typeToString(unit->spawn_type));
  198. if (std::find(m_keyStructures.begin(), m_keyStructures.end(),
  199. unit_type_str) != m_keyStructures.end()) {
  200. enemy_key_structures_alive = true;
  201. break;
  202. }
  203. }
  204. return !enemy_key_structures_alive;
  205. }
  206. auto VictoryService::check_survive_time() const -> bool {
  207. return m_elapsed_time >= m_survive_time_duration;
  208. }
  209. auto VictoryService::check_control_structures(
  210. Engine::Core::World &world, bool require_captured) const -> bool {
  211. if (m_keyStructures.empty()) {
  212. return false;
  213. }
  214. int required = m_requiredKeyStructures;
  215. if (required <= 0) {
  216. required = 1;
  217. }
  218. auto &nation_registry = Game::Systems::NationRegistry::instance();
  219. const auto *local_nation =
  220. nation_registry.get_nation_for_player(m_local_owner_id);
  221. const bool has_local_nation = (local_nation != nullptr);
  222. const auto local_nation_id =
  223. has_local_nation ? local_nation->id : nation_registry.default_nation_id();
  224. int captured_count = 0;
  225. auto entities = world.get_entities_with<Engine::Core::UnitComponent>();
  226. for (auto *e : entities) {
  227. auto *unit = e->get_component<Engine::Core::UnitComponent>();
  228. if ((unit == nullptr) || unit->health <= 0) {
  229. continue;
  230. }
  231. if (unit->owner_id != m_local_owner_id) {
  232. continue;
  233. }
  234. const QString unit_type = QString::fromStdString(
  235. Game::Units::spawn_typeToString(unit->spawn_type));
  236. if (std::find(m_keyStructures.begin(), m_keyStructures.end(), unit_type) ==
  237. m_keyStructures.end()) {
  238. continue;
  239. }
  240. if (require_captured && has_local_nation) {
  241. auto *building = e->get_component<Engine::Core::BuildingComponent>();
  242. if (building == nullptr ||
  243. building->original_nation_id == local_nation_id) {
  244. continue;
  245. }
  246. }
  247. captured_count++;
  248. if (captured_count >= required) {
  249. return true;
  250. }
  251. }
  252. return false;
  253. }
  254. auto VictoryService::check_no_units(Engine::Core::World &world) const -> bool {
  255. auto entities = world.get_entities_with<Engine::Core::UnitComponent>();
  256. for (auto *e : entities) {
  257. auto *unit = e->get_component<Engine::Core::UnitComponent>();
  258. if ((unit == nullptr) || unit->health <= 0) {
  259. continue;
  260. }
  261. if (unit->owner_id == m_local_owner_id) {
  262. return false;
  263. }
  264. }
  265. return true;
  266. }
  267. auto VictoryService::check_no_key_structures(Engine::Core::World &world)
  268. -> bool {
  269. auto entities = world.get_entities_with<Engine::Core::UnitComponent>();
  270. for (auto *e : entities) {
  271. auto *unit = e->get_component<Engine::Core::UnitComponent>();
  272. if ((unit == nullptr) || unit->health <= 0) {
  273. continue;
  274. }
  275. if (unit->owner_id == m_local_owner_id) {
  276. QString const unit_type_str = QString::fromStdString(
  277. Game::Units::spawn_typeToString(unit->spawn_type));
  278. if (std::find(m_keyStructures.begin(), m_keyStructures.end(),
  279. unit_type_str) != m_keyStructures.end()) {
  280. return false;
  281. }
  282. }
  283. }
  284. return true;
  285. }
  286. } // namespace Game::Systems