2
0

TurnOrderProcessor.cpp 8.3 KB

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