TurnOrderProcessor.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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 "../queries/QueriesProcessor.h"
  13. #include "../queries/MapQueries.h"
  14. #include "../CGameHandler.h"
  15. #include "../CVCMIServer.h"
  16. #include "../../lib/CPlayerState.h"
  17. #include "../../lib/NetPacks.h"
  18. #include "../../lib/pathfinder/CPathfinder.h"
  19. #include "../../lib/pathfinder/PathfinderOptions.h"
  20. TurnOrderProcessor::TurnOrderProcessor(CGameHandler * owner):
  21. gameHandler(owner)
  22. {
  23. }
  24. int TurnOrderProcessor::simturnsTurnsMaxLimit() const
  25. {
  26. return gameHandler->getStartInfo()->simturnsInfo.optionalTurns;
  27. }
  28. int TurnOrderProcessor::simturnsTurnsMinLimit() const
  29. {
  30. return gameHandler->getStartInfo()->simturnsInfo.requiredTurns;
  31. }
  32. void TurnOrderProcessor::updateContactStatus()
  33. {
  34. blockedContacts.clear();
  35. assert(actedPlayers.empty());
  36. assert(actingPlayers.empty());
  37. for (auto left : awaitingPlayers)
  38. {
  39. for(auto right : awaitingPlayers)
  40. {
  41. if (left == right)
  42. continue;
  43. if (computeCanActSimultaneously(left, right))
  44. blockedContacts.push_back({left, right});
  45. }
  46. }
  47. }
  48. bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) const
  49. {
  50. // TODO: refactor, cleanup and optimize
  51. boost::multi_array<bool, 3> leftReachability;
  52. boost::multi_array<bool, 3> rightReachability;
  53. int3 mapSize = gameHandler->getMapSize();
  54. leftReachability.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
  55. rightReachability.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
  56. const auto * leftInfo = gameHandler->getPlayerState(left, false);
  57. const auto * rightInfo = gameHandler->getPlayerState(right, false);
  58. for(const auto & hero : leftInfo->heroes)
  59. {
  60. CPathsInfo out(mapSize, hero);
  61. auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);
  62. CPathfinder pathfinder(gameHandler->gameState(), config);
  63. pathfinder.calculatePaths();
  64. for (int z = 0; z < mapSize.z; ++z)
  65. for (int y = 0; y < mapSize.y; ++y)
  66. for (int x = 0; x < mapSize.x; ++x)
  67. if (out.getNode({x,y,z})->reachable())
  68. leftReachability[z][x][y] = true;
  69. }
  70. for(const auto & hero : rightInfo->heroes)
  71. {
  72. CPathsInfo out(mapSize, hero);
  73. auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);
  74. CPathfinder pathfinder(gameHandler->gameState(), config);
  75. pathfinder.calculatePaths();
  76. for (int z = 0; z < mapSize.z; ++z)
  77. for (int y = 0; y < mapSize.y; ++y)
  78. for (int x = 0; x < mapSize.x; ++x)
  79. if (out.getNode({x,y,z})->reachable())
  80. rightReachability[z][x][y] = true;
  81. }
  82. for (int z = 0; z < mapSize.z; ++z)
  83. for (int y = 0; y < mapSize.y; ++y)
  84. for (int x = 0; x < mapSize.x; ++x)
  85. if (leftReachability[z][x][y] && rightReachability[z][x][y])
  86. return true;
  87. return false;
  88. }
  89. bool TurnOrderProcessor::isContactAllowed(PlayerColor active, PlayerColor waiting) const
  90. {
  91. assert(active != waiting);
  92. return !vstd::contains(blockedContacts, PlayerPair{active, waiting});
  93. }
  94. bool TurnOrderProcessor::computeCanActSimultaneously(PlayerColor active, PlayerColor waiting) const
  95. {
  96. const auto * activeInfo = gameHandler->getPlayerState(active, false);
  97. const auto * waitingInfo = gameHandler->getPlayerState(waiting, false);
  98. assert(active != waiting);
  99. assert(activeInfo);
  100. assert(waitingInfo);
  101. if (gameHandler->hasBothPlayersAtSameConnection(active, waiting))
  102. {
  103. if (!gameHandler->getStartInfo()->simturnsInfo.allowHumanWithAI)
  104. return false;
  105. // only one AI and one human can play simultaneoulsy from single connection
  106. if (activeInfo->human == waitingInfo->human)
  107. return false;
  108. }
  109. if (gameHandler->getDate(Date::DAY) < simturnsTurnsMinLimit())
  110. return true;
  111. if (gameHandler->getDate(Date::DAY) > simturnsTurnsMaxLimit())
  112. return false;
  113. if (playersInContact(active, waiting))
  114. return false;
  115. return true;
  116. }
  117. bool TurnOrderProcessor::mustActBefore(PlayerColor left, PlayerColor right) const
  118. {
  119. const auto * leftInfo = gameHandler->getPlayerState(left, false);
  120. const auto * rightInfo = gameHandler->getPlayerState(right, false);
  121. assert(left != right);
  122. assert(leftInfo && rightInfo);
  123. if (!leftInfo)
  124. return false;
  125. if (!rightInfo)
  126. return true;
  127. if (leftInfo->isHuman() && !rightInfo->isHuman())
  128. return true;
  129. if (!leftInfo->isHuman() && rightInfo->isHuman())
  130. return false;
  131. return left < right;
  132. }
  133. bool TurnOrderProcessor::canStartTurn(PlayerColor which) const
  134. {
  135. for (auto player : awaitingPlayers)
  136. {
  137. if (player != which && mustActBefore(player, which))
  138. return false;
  139. }
  140. for (auto player : actingPlayers)
  141. {
  142. if (player != which && isContactAllowed(player, which))
  143. return false;
  144. }
  145. return true;
  146. }
  147. void TurnOrderProcessor::doStartNewDay()
  148. {
  149. assert(awaitingPlayers.empty());
  150. assert(actingPlayers.empty());
  151. bool activePlayer = false;
  152. for (auto player : actedPlayers)
  153. {
  154. if (gameHandler->getPlayerState(player)->status == EPlayerStatus::INGAME)
  155. activePlayer = true;
  156. }
  157. if(!activePlayer)
  158. gameHandler->gameLobby()->setState(EServerState::GAMEPLAY_ENDED);
  159. std::swap(actedPlayers, awaitingPlayers);
  160. gameHandler->onNewTurn();
  161. updateContactStatus();
  162. tryStartTurnsForPlayers();
  163. }
  164. void TurnOrderProcessor::doStartPlayerTurn(PlayerColor which)
  165. {
  166. assert(gameHandler->getPlayerState(which));
  167. assert(gameHandler->getPlayerState(which)->status == EPlayerStatus::INGAME);
  168. //Note: on game load, "actingPlayer" might already contain list of players
  169. actingPlayers.insert(which);
  170. awaitingPlayers.erase(which);
  171. gameHandler->onPlayerTurnStarted(which);
  172. auto turnQuery = std::make_shared<TimerPauseQuery>(gameHandler, which);
  173. gameHandler->queries->addQuery(turnQuery);
  174. PlayerStartsTurn pst;
  175. pst.player = which;
  176. pst.queryID = turnQuery->queryID;
  177. gameHandler->sendAndApply(&pst);
  178. assert(!actingPlayers.empty());
  179. }
  180. void TurnOrderProcessor::doEndPlayerTurn(PlayerColor which)
  181. {
  182. assert(isPlayerMakingTurn(which));
  183. assert(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME);
  184. actingPlayers.erase(which);
  185. actedPlayers.insert(which);
  186. PlayerEndsTurn pet;
  187. pet.player = which;
  188. gameHandler->sendAndApply(&pet);
  189. if (!awaitingPlayers.empty())
  190. tryStartTurnsForPlayers();
  191. if (actingPlayers.empty())
  192. doStartNewDay();
  193. assert(!actingPlayers.empty());
  194. }
  195. void TurnOrderProcessor::addPlayer(PlayerColor which)
  196. {
  197. awaitingPlayers.insert(which);
  198. }
  199. void TurnOrderProcessor::onPlayerEndsGame(PlayerColor which)
  200. {
  201. awaitingPlayers.erase(which);
  202. actingPlayers.erase(which);
  203. actedPlayers.erase(which);
  204. if (!awaitingPlayers.empty())
  205. tryStartTurnsForPlayers();
  206. if (actingPlayers.empty())
  207. doStartNewDay();
  208. assert(!actingPlayers.empty());
  209. }
  210. bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which)
  211. {
  212. if (!isPlayerMakingTurn(which))
  213. {
  214. gameHandler->complain("Can not end turn for player that is not acting!");
  215. return false;
  216. }
  217. if(gameHandler->getPlayerStatus(which) != EPlayerStatus::INGAME)
  218. {
  219. gameHandler->complain("Can not end turn for player that is not in game!");
  220. return false;
  221. }
  222. if(gameHandler->queries->topQuery(which) != nullptr)
  223. {
  224. gameHandler->complain("Cannot end turn before resolving queries!");
  225. return false;
  226. }
  227. gameHandler->onPlayerTurnEnded(which);
  228. // it is possible that player have lost - e.g. spent 7 days without town
  229. // in this case - don't call doEndPlayerTurn - turn transfer was already handled by onPlayerEndsGame
  230. if(gameHandler->getPlayerStatus(which) == EPlayerStatus::INGAME)
  231. doEndPlayerTurn(which);
  232. return true;
  233. }
  234. void TurnOrderProcessor::onGameStarted()
  235. {
  236. if (actingPlayers.empty())
  237. updateContactStatus();
  238. // this may be game load - send notification to players that they can act
  239. auto actingPlayersCopy = actingPlayers;
  240. for (auto player : actingPlayersCopy)
  241. doStartPlayerTurn(player);
  242. tryStartTurnsForPlayers();
  243. }
  244. void TurnOrderProcessor::tryStartTurnsForPlayers()
  245. {
  246. auto awaitingPlayersCopy = awaitingPlayers;
  247. for (auto player : awaitingPlayersCopy)
  248. {
  249. if (canStartTurn(player))
  250. doStartPlayerTurn(player);
  251. }
  252. }
  253. bool TurnOrderProcessor::isPlayerAwaitsTurn(PlayerColor which) const
  254. {
  255. return vstd::contains(awaitingPlayers, which);
  256. }
  257. bool TurnOrderProcessor::isPlayerMakingTurn(PlayerColor which) const
  258. {
  259. return vstd::contains(actingPlayers, which);
  260. }
  261. bool TurnOrderProcessor::isPlayerAwaitsNewDay(PlayerColor which) const
  262. {
  263. return vstd::contains(actedPlayers, which);
  264. }