TurnOrderProcessor.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. * TurnOrderProcessor.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "TurnOrderProcessor.h"
  12. #include "PlayerMessageProcessor.h"
  13. #include "../queries/QueriesProcessor.h"
  14. #include "../queries/MapQueries.h"
  15. #include "../CGameHandler.h"
  16. #include "../CVCMIServer.h"
  17. #include "../../lib/CPlayerState.h"
  18. #include "../../lib/mapping/CMap.h"
  19. #include "../../lib/mapObjects/CGObjectInstance.h"
  20. #include "../../lib/mapObjects/CGHeroInstance.h"
  21. #include "../../lib/gameState/CGameState.h"
  22. #include "../../lib/pathfinder/CPathfinder.h"
  23. #include "../../lib/pathfinder/PathfinderOptions.h"
  24. TurnOrderProcessor::TurnOrderProcessor(CGameHandler * owner):
  25. gameHandler(owner)
  26. {
  27. }
  28. int TurnOrderProcessor::simturnsTurnsMaxLimit() const
  29. {
  30. if (simturnsMaxDurationDays)
  31. return *simturnsMaxDurationDays;
  32. return gameHandler->gameInfo().getStartInfo()->simturnsInfo.optionalTurns;
  33. }
  34. int TurnOrderProcessor::simturnsTurnsMinLimit() const
  35. {
  36. if (simturnsMinDurationDays)
  37. return *simturnsMinDurationDays;
  38. return gameHandler->gameInfo().getStartInfo()->simturnsInfo.requiredTurns;
  39. }
  40. std::vector<TurnOrderProcessor::PlayerPair> TurnOrderProcessor::computeContactStatus() const
  41. {
  42. std::vector<PlayerPair> result;
  43. assert(actedPlayers.empty());
  44. assert(actingPlayers.empty());
  45. for (auto left : awaitingPlayers)
  46. {
  47. for(auto right : awaitingPlayers)
  48. {
  49. if (left == right)
  50. continue;
  51. if (computeCanActSimultaneously(left, right))
  52. result.push_back({left, right});
  53. }
  54. }
  55. return result;
  56. }
  57. void TurnOrderProcessor::updateAndNotifyContactStatus()
  58. {
  59. auto newBlockedContacts = computeContactStatus();
  60. if (newBlockedContacts.empty())
  61. {
  62. // Simturns between all players have ended - send single global notification
  63. if (!blockedContacts.empty())
  64. gameHandler->playerMessages->broadcastSystemMessage(MetaString::createFromTextID("vcmi.broadcast.simturn.end"));
  65. }
  66. else
  67. {
  68. // Simturns between some players have ended - notify each pair
  69. for (auto const & contact : blockedContacts)
  70. {
  71. if (vstd::contains(newBlockedContacts, contact))
  72. continue;
  73. MetaString message;
  74. message.appendTextID("vcmi.broadcast.simturn.endBetween");
  75. message.replaceName(contact.a);
  76. message.replaceName(contact.b);
  77. gameHandler->playerMessages->broadcastSystemMessage(message);
  78. }
  79. }
  80. blockedContacts = newBlockedContacts;
  81. }
  82. bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) const
  83. {
  84. // TODO: refactor, cleanup and optimize
  85. boost::multi_array<bool, 3> leftReachability;
  86. boost::multi_array<bool, 3> rightReachability;
  87. int3 mapSize = gameHandler->gameInfo().getMapSize();
  88. leftReachability.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
  89. rightReachability.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
  90. const auto * leftInfo = gameHandler->gameInfo().getPlayerState(left, false);
  91. const auto * rightInfo = gameHandler->gameInfo().getPlayerState(right, false);
  92. for (auto obj : gameHandler->gameState().getMap().getObjects())
  93. {
  94. if (obj->asOwnable())
  95. {
  96. int3 pos = obj->visitablePos();
  97. if (obj->getOwner() == left)
  98. leftReachability[pos.z][pos.x][pos.y] = true;
  99. if (obj->getOwner() == right)
  100. rightReachability[pos.z][pos.x][pos.y] = true;
  101. }
  102. }
  103. for(const auto & hero : leftInfo->getHeroes())
  104. {
  105. CPathsInfo out(mapSize, hero);
  106. auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);
  107. config->options.ignoreGuards = true;
  108. config->options.turnLimit = 1;
  109. CPathfinder pathfinder(gameHandler->gameState(), config);
  110. pathfinder.calculatePaths();
  111. for (int z = 0; z < mapSize.z; ++z)
  112. for (int y = 0; y < mapSize.y; ++y)
  113. for (int x = 0; x < mapSize.x; ++x)
  114. if (out.getNode({x,y,z})->reachable())
  115. leftReachability[z][x][y] = true;
  116. }
  117. for(const auto & hero : rightInfo->getHeroes())
  118. {
  119. CPathsInfo out(mapSize, hero);
  120. auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);
  121. config->options.ignoreGuards = true;
  122. config->options.turnLimit = 1;
  123. CPathfinder pathfinder(gameHandler->gameState(), config);
  124. pathfinder.calculatePaths();
  125. for (int z = 0; z < mapSize.z; ++z)
  126. for (int y = 0; y < mapSize.y; ++y)
  127. for (int x = 0; x < mapSize.x; ++x)
  128. if (out.getNode({x,y,z})->reachable())
  129. rightReachability[z][x][y] = true;
  130. }
  131. for (int z = 0; z < mapSize.z; ++z)
  132. for (int y = 0; y < mapSize.y; ++y)
  133. for (int x = 0; x < mapSize.x; ++x)
  134. if (leftReachability[z][x][y] && rightReachability[z][x][y])
  135. return true;
  136. return false;
  137. }
  138. bool TurnOrderProcessor::isContactAllowed(PlayerColor active, PlayerColor waiting) const
  139. {
  140. assert(active != waiting);
  141. return !vstd::contains(blockedContacts, PlayerPair{active, waiting});
  142. }
  143. bool TurnOrderProcessor::computeCanActSimultaneously(PlayerColor active, PlayerColor waiting) const
  144. {
  145. const auto * activeInfo = gameHandler->gameInfo().getPlayerState(active, false);
  146. const auto * waitingInfo = gameHandler->gameInfo().getPlayerState(waiting, false);
  147. assert(active != waiting);
  148. assert(activeInfo);
  149. assert(waitingInfo);
  150. if (activeInfo->human != waitingInfo->human)
  151. {
  152. // only one AI and one human can play simultaneously from single connection
  153. if (!gameHandler->gameInfo().getStartInfo()->simturnsInfo.allowHumanWithAI)
  154. return false;
  155. }
  156. else
  157. {
  158. // two AI or two humans in hotseat can't play at the same time
  159. if (gameHandler->hasBothPlayersAtSameConnection(active, waiting))
  160. return false;
  161. }
  162. if (gameHandler->gameInfo().getDate(Date::DAY) < simturnsTurnsMinLimit())
  163. return true;
  164. if (gameHandler->gameInfo().getDate(Date::DAY) > simturnsTurnsMaxLimit())
  165. return false;
  166. if (gameHandler->gameInfo().getStartInfo()->simturnsInfo.ignoreAlliedContacts && activeInfo->team == waitingInfo->team)
  167. return true;
  168. if (playersInContact(active, waiting))
  169. return false;
  170. return true;
  171. }
  172. bool TurnOrderProcessor::mustActBefore(PlayerColor left, PlayerColor right) const
  173. {
  174. const auto * leftInfo = gameHandler->gameInfo().getPlayerState(left, false);
  175. const auto * rightInfo = gameHandler->gameInfo().getPlayerState(right, false);
  176. assert(left != right);
  177. assert(leftInfo && rightInfo);
  178. if (!leftInfo)
  179. return false;
  180. if (!rightInfo)
  181. return true;
  182. if (leftInfo->isHuman() && !rightInfo->isHuman())
  183. return true;
  184. if (!leftInfo->isHuman() && rightInfo->isHuman())
  185. return false;
  186. return false;
  187. }
  188. bool TurnOrderProcessor::canStartTurn(PlayerColor which) const
  189. {
  190. for (auto player : awaitingPlayers)
  191. {
  192. if (player != which && mustActBefore(player, which))
  193. return false;
  194. }
  195. for (auto player : actingPlayers)
  196. {
  197. if (player != which && isContactAllowed(player, which))
  198. return false;
  199. }
  200. return true;
  201. }
  202. void TurnOrderProcessor::doStartNewDay()
  203. {
  204. assert(awaitingPlayers.empty());
  205. assert(actingPlayers.empty());
  206. gameHandler->onNewTurn();
  207. bool activePlayer = false;
  208. for (auto player : actedPlayers)
  209. {
  210. if (gameHandler->gameInfo().getPlayerState(player)->status == EPlayerStatus::INGAME)
  211. activePlayer = true;
  212. }
  213. if(!activePlayer)
  214. {
  215. gameHandler->gameServer().setState(EServerState::SHUTDOWN);
  216. return;
  217. }
  218. std::swap(actedPlayers, awaitingPlayers);
  219. updateAndNotifyContactStatus();
  220. tryStartTurnsForPlayers();
  221. }
  222. void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
  223. {
  224. assert(gameHandler->gameInfo().getPlayerState(which));
  225. assert(gameHandler->gameInfo().getPlayerState(which)->status == EPlayerStatus::INGAME);
  226. // Only if player is actually starting his turn (and not loading from save)
  227. if (!actingPlayers.count(which))
  228. gameHandler->onPlayerTurnStarted(which);
  229. actingPlayers.insert(which);
  230. awaitingPlayers.erase(which);
  231. auto turnQuery = std::make_shared<TimerPauseQuery>(gameHandler, which);
  232. gameHandler->queries->addQuery(turnQuery);
  233. PlayerStartsTurn pst;
  234. pst.player = which;
  235. pst.queryID = turnQuery->queryID;
  236. gameHandler->sendAndApply(pst);
  237. assert(!actingPlayers.empty());
  238. }
  239. void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which)
  240. {
  241. assert(isPlayerMakingTurn(which));
  242. assert(gameHandler->gameInfo().getPlayerStatus(which) == EPlayerStatus::INGAME);
  243. actingPlayers.erase(which);
  244. actedPlayers.insert(which);
  245. PlayerEndsTurn pet;
  246. pet.player = which;
  247. gameHandler->sendAndApply(pet);
  248. if (!awaitingPlayers.empty())
  249. tryStartTurnsForPlayers();
  250. if (actingPlayers.empty())
  251. doStartNewDay();
  252. assert(!actingPlayers.empty());
  253. }
  254. void TurnOrderProcessor::addPlayer(PlayerColor which)
  255. {
  256. awaitingPlayers.insert(which);
  257. }
  258. void TurnOrderProcessor::onPlayerEndsGame(PlayerColor which)
  259. {
  260. awaitingPlayers.erase(which);
  261. actingPlayers.erase(which);
  262. actedPlayers.erase(which);
  263. if (!awaitingPlayers.empty())
  264. tryStartTurnsForPlayers();
  265. if (actingPlayers.empty())
  266. doStartNewDay();
  267. }
  268. bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
  269. {
  270. if (!isPlayerMakingTurn(which))
  271. {
  272. gameHandler->complain("Can not end turn for player that is not acting!");
  273. return false;
  274. }
  275. if(gameHandler->gameInfo().getPlayerStatus(which) != EPlayerStatus::INGAME)
  276. {
  277. gameHandler->complain("Can not end turn for player that is not in game!");
  278. return false;
  279. }
  280. if(gameHandler->queries->topQuery(which) != nullptr)
  281. {
  282. gameHandler->complain("Cannot end turn before resolving queries!");
  283. return false;
  284. }
  285. gameHandler->onPlayerTurnEnded(which);
  286. // it is possible that player have lost - e.g. spent 7 days without town
  287. // in this case - don't call doEndPlayerTurn - turn transfer was already handled by onPlayerEndsGame
  288. if(gameHandler->gameInfo().getPlayerStatus(which) == EPlayerStatus::INGAME)
  289. doEndPlayerTurn(which);
  290. return true;
  291. }
  292. void TurnOrderProcessor::onGameStarted()
  293. {
  294. if(gameHandler->gameInfo().getMapHeader()->battleOnly)
  295. {
  296. auto heroes = gameHandler->gameState().getMap().getObjects<CGHeroInstance>();
  297. auto hero1 = heroes.at(0);
  298. auto hero2 = heroes.at(1);
  299. gameHandler->startBattle(hero1, hero2);
  300. return;
  301. }
  302. if (actingPlayers.empty())
  303. blockedContacts = computeContactStatus();
  304. // this may be game load - send notification to players that they can act
  305. auto actingPlayersCopy = actingPlayers;
  306. for (auto player : actingPlayersCopy)
  307. doStartPlayerTurn(player);
  308. tryStartTurnsForPlayers();
  309. }
  310. void TurnOrderProcessor::tryStartTurnsForPlayers()
  311. {
  312. auto awaitingPlayersCopy = awaitingPlayers;
  313. for (auto player : awaitingPlayersCopy)
  314. {
  315. if (canStartTurn(player))
  316. doStartPlayerTurn(player);
  317. }
  318. }
  319. bool TurnOrderProcessor::isPlayerAwaitsTurn(PlayerColor which) const
  320. {
  321. return vstd::contains(awaitingPlayers, which);
  322. }
  323. bool TurnOrderProcessor::isPlayerMakingTurn(PlayerColor which) const
  324. {
  325. return vstd::contains(actingPlayers, which);
  326. }
  327. bool TurnOrderProcessor::isPlayerAwaitsNewDay(PlayerColor which) const
  328. {
  329. return vstd::contains(actedPlayers, which);
  330. }
  331. void TurnOrderProcessor::setMinSimturnsDuration(int days)
  332. {
  333. simturnsMinDurationDays = gameHandler->gameInfo().getDate(Date::DAY) + days;
  334. }
  335. void TurnOrderProcessor::setMaxSimturnsDuration(int days)
  336. {
  337. simturnsMaxDurationDays = gameHandler->gameInfo().getDate(Date::DAY) + days;
  338. }