2
0

CMapGenOptions.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. /*
  2. * CMapGenOptions.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 "CMapGenOptions.h"
  12. #include "../mapping/CMapHeader.h"
  13. #include "CRmgTemplateStorage.h"
  14. #include "CRmgTemplate.h"
  15. #include "CRandomGenerator.h"
  16. #include "../VCMI_Lib.h"
  17. #include "../CTownHandler.h"
  18. VCMI_LIB_NAMESPACE_BEGIN
  19. CMapGenOptions::CMapGenOptions()
  20. : width(CMapHeader::MAP_SIZE_MIDDLE), height(CMapHeader::MAP_SIZE_MIDDLE), hasTwoLevels(true),
  21. humanOrCpuPlayerCount(RANDOM_SIZE), teamCount(RANDOM_SIZE), compOnlyPlayerCount(RANDOM_SIZE), compOnlyTeamCount(RANDOM_SIZE),
  22. waterContent(EWaterContent::RANDOM), monsterStrength(EMonsterStrength::RANDOM), mapTemplate(nullptr),
  23. customizedPlayers(false)
  24. {
  25. initPlayersMap();
  26. setRoadEnabled(RoadId(Road::DIRT_ROAD), true);
  27. setRoadEnabled(RoadId(Road::GRAVEL_ROAD), true);
  28. setRoadEnabled(RoadId(Road::COBBLESTONE_ROAD), true);
  29. }
  30. si32 CMapGenOptions::getWidth() const
  31. {
  32. return width;
  33. }
  34. void CMapGenOptions::setWidth(si32 value)
  35. {
  36. assert(value >= 1);
  37. width = value;
  38. }
  39. si32 CMapGenOptions::getHeight() const
  40. {
  41. return height;
  42. }
  43. void CMapGenOptions::setHeight(si32 value)
  44. {
  45. assert(value >= 1);
  46. height = value;
  47. }
  48. bool CMapGenOptions::getHasTwoLevels() const
  49. {
  50. return hasTwoLevels;
  51. }
  52. void CMapGenOptions::setHasTwoLevels(bool value)
  53. {
  54. hasTwoLevels = value;
  55. }
  56. si8 CMapGenOptions::getHumanOrCpuPlayerCount() const
  57. {
  58. return humanOrCpuPlayerCount;
  59. }
  60. void CMapGenOptions::setHumanOrCpuPlayerCount(si8 value)
  61. {
  62. // Set total player count (human + AI)?
  63. assert((value >= 1 && value <= PlayerColor::PLAYER_LIMIT_I) || value == RANDOM_SIZE);
  64. humanOrCpuPlayerCount = value;
  65. // Use template player limit, if any
  66. auto playerLimit = getPlayerLimit();
  67. auto possibleCompPlayersCount = playerLimit - std::max<si8>(0, humanOrCpuPlayerCount);
  68. if (compOnlyPlayerCount > possibleCompPlayersCount)
  69. {
  70. setCompOnlyPlayerCount(possibleCompPlayersCount);
  71. }
  72. resetPlayersMap();
  73. }
  74. si8 CMapGenOptions::getTotalPlayersCount() const
  75. {
  76. auto totalPlayers = 0;
  77. si8 humans = getHumanOrCpuPlayerCount();
  78. si8 cpus = getCompOnlyPlayerCount();
  79. if (humans == RANDOM_SIZE || cpus == RANDOM_SIZE)
  80. {
  81. totalPlayers = PlayerColor::PLAYER_LIMIT_I;
  82. }
  83. else
  84. {
  85. totalPlayers = humans + cpus;
  86. }
  87. assert (totalPlayers <= PlayerColor::PLAYER_LIMIT_I);
  88. assert (totalPlayers >= 2);
  89. return totalPlayers;
  90. }
  91. si8 CMapGenOptions::getTeamCount() const
  92. {
  93. return teamCount;
  94. }
  95. void CMapGenOptions::setTeamCount(si8 value)
  96. {
  97. assert(getHumanOrCpuPlayerCount() == RANDOM_SIZE || (value >= 0 && value < getHumanOrCpuPlayerCount()) || value == RANDOM_SIZE);
  98. teamCount = value;
  99. }
  100. si8 CMapGenOptions::getCompOnlyPlayerCount() const
  101. {
  102. return compOnlyPlayerCount;
  103. }
  104. si8 CMapGenOptions::getPlayerLimit() const
  105. {
  106. si8 playerLimit = PlayerColor::PLAYER_LIMIT_I;
  107. if (auto temp = getMapTemplate())
  108. {
  109. playerLimit = *boost::max_element(temp->getPlayers().getNumbers());
  110. }
  111. return playerLimit;
  112. }
  113. void CMapGenOptions::setCompOnlyPlayerCount(si8 value)
  114. {
  115. auto playerLimit = getPlayerLimit();
  116. assert(value == RANDOM_SIZE || (getHumanOrCpuPlayerCount() == RANDOM_SIZE || (value >= 0 && value <= playerLimit - getHumanOrCpuPlayerCount())));
  117. compOnlyPlayerCount = value;
  118. resetPlayersMap();
  119. }
  120. si8 CMapGenOptions::getCompOnlyTeamCount() const
  121. {
  122. return compOnlyTeamCount;
  123. }
  124. void CMapGenOptions::setCompOnlyTeamCount(si8 value)
  125. {
  126. assert(value == RANDOM_SIZE || compOnlyPlayerCount == RANDOM_SIZE || (value >= 0 && value <= std::max(compOnlyPlayerCount - 1, 0)));
  127. compOnlyTeamCount = value;
  128. }
  129. EWaterContent::EWaterContent CMapGenOptions::getWaterContent() const
  130. {
  131. return waterContent;
  132. }
  133. void CMapGenOptions::setWaterContent(EWaterContent::EWaterContent value)
  134. {
  135. waterContent = value;
  136. }
  137. EMonsterStrength::EMonsterStrength CMapGenOptions::getMonsterStrength() const
  138. {
  139. return monsterStrength;
  140. }
  141. void CMapGenOptions::setMonsterStrength(EMonsterStrength::EMonsterStrength value)
  142. {
  143. monsterStrength = value;
  144. }
  145. void CMapGenOptions::initPlayersMap()
  146. {
  147. players.clear();
  148. int realPlayersCnt = getHumanOrCpuPlayerCount();
  149. // TODO: Initialize settings for all color even if not present?
  150. for(int color = 0; color < getPlayerLimit(); ++color)
  151. {
  152. CPlayerSettings player;
  153. auto pc = PlayerColor(color);
  154. player.setColor(pc);
  155. /*
  156. if (vstd::contains(savedPlayerSettings, pc))
  157. {
  158. player.setTeam(savedPlayerSettings[pc].getTeam());
  159. player.setStartingTown(savedPlayerSettings[pc].getStartingTown());
  160. //TODO: Restore starting hero and bonus?
  161. }
  162. // Assign new owner of this player
  163. */
  164. auto playerType = EPlayerType::AI;
  165. // Color doesn't have to be continuous. Player colors can later be changed manually
  166. if (getHumanOrCpuPlayerCount() != RANDOM_SIZE && color < realPlayersCnt)
  167. {
  168. playerType = EPlayerType::HUMAN;
  169. }
  170. else if((getHumanOrCpuPlayerCount() != RANDOM_SIZE && color >= realPlayersCnt)
  171. || (compOnlyPlayerCount != RANDOM_SIZE && color >= (PlayerColor::PLAYER_LIMIT_I - compOnlyPlayerCount)))
  172. {
  173. playerType = EPlayerType::COMP_ONLY;
  174. }
  175. player.setPlayerType(playerType);
  176. players[pc] = player;
  177. }
  178. savePlayersMap();
  179. }
  180. void CMapGenOptions::resetPlayersMap()
  181. {
  182. // Called when number of player changes
  183. // TODO: Should it?
  184. //But do not update info about already made selections
  185. savePlayersMap();
  186. /*
  187. //Remove players who have undefined properties
  188. vstd::erase_if(players, [](const std::pair<PlayerColor, CPlayerSettings> & p)
  189. {
  190. return p.second.getPlayerType() != EPlayerType::AI && p.second.getStartingTown() == CPlayerSettings::RANDOM_TOWN;
  191. });
  192. */
  193. // FIXME: This should be total players count
  194. int realPlayersCnt = getHumanOrCpuPlayerCount();
  195. if (realPlayersCnt != RANDOM_SIZE)
  196. {
  197. //Trim the number of AI players, then CPU-only players, finally human players
  198. auto eraseLastPlayer = [this](EPlayerType playerType) -> bool
  199. {
  200. //FIXME: Infinite loop for 0 players
  201. for (auto it = players.rbegin(); it != players.rend(); ++it)
  202. {
  203. if (it->second.getPlayerType() == playerType)
  204. {
  205. players.erase(it->first);
  206. return true;
  207. }
  208. }
  209. return false; //Can't earse any player of this type
  210. };
  211. while (players.size() > realPlayersCnt)
  212. {
  213. while (eraseLastPlayer(EPlayerType::AI));
  214. while (eraseLastPlayer(EPlayerType::COMP_ONLY));
  215. while (eraseLastPlayer(EPlayerType::HUMAN));
  216. }
  217. }
  218. else
  219. {
  220. //If count is random, generate info for all the players
  221. realPlayersCnt = PlayerColor::PLAYER_LIMIT_I;
  222. }
  223. int realCompOnlyPlayersCnt = getCompOnlyPlayerCount();
  224. //int totalPlayersLimit = getPlayerLimit();
  225. //First colors from the list are assigned to human players, then to CPU players
  226. std::vector<PlayerColor> availableColors;
  227. for (ui8 color = 0; color < PlayerColor::PLAYER_LIMIT_I; color++)
  228. {
  229. availableColors.push_back(PlayerColor(color));
  230. }
  231. auto removeUsedColors = [this, &availableColors](EPlayerType playerType)
  232. {
  233. for (auto& player : players)
  234. {
  235. if (player.second.getPlayerType() == playerType)
  236. {
  237. vstd::erase(availableColors, player.second.getColor()); //FIXME: Where is this color initialized at lobby launch?
  238. }
  239. }
  240. };
  241. removeUsedColors(EPlayerType::HUMAN);
  242. removeUsedColors(EPlayerType::COMP_ONLY);
  243. //removeUsedColors(EPlayerType::AI);
  244. //Assign unused colors to remaining AI players
  245. while (players.size() < realPlayersCnt && !availableColors.empty())
  246. {
  247. auto color = availableColors.front();
  248. setPlayerTypeForStandardPlayer(color, EPlayerType::AI);
  249. players[color].setColor(color);
  250. availableColors.erase(availableColors.begin());
  251. if (vstd::contains(savedPlayerSettings, color))
  252. {
  253. setPlayerTeam(color, savedPlayerSettings.at(color).getTeam());
  254. // TODO: setter
  255. players[color].setStartingTown(savedPlayerSettings.at(color).getStartingTown());
  256. }
  257. else
  258. {
  259. logGlobal->warn("Adding settings for player %s", color.encode(color));
  260. // Usually, all players should be initialized in initPlayersMap()
  261. CPlayerSettings settings;
  262. players[color] = settings;
  263. }
  264. }
  265. // TODO: Assign players to teams at the beginning (if all players belong to the same team)
  266. std::set<TeamID> occupiedTeams;
  267. for(auto & player : players)
  268. {
  269. auto team = player.second.getTeam();
  270. if (team != TeamID::NO_TEAM)
  271. {
  272. occupiedTeams.insert(team);
  273. }
  274. }
  275. // TODO: Handle situation when we remove a player and remaining players belong to only one team
  276. for(auto & player : players)
  277. {
  278. if (player.second.getTeam() == TeamID::NO_TEAM)
  279. {
  280. //Find first unused team
  281. for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
  282. {
  283. TeamID team(i);
  284. if(!occupiedTeams.count(team))
  285. {
  286. player.second.setTeam(team);
  287. occupiedTeams.insert(team);
  288. break;
  289. }
  290. }
  291. }
  292. }
  293. /*
  294. for(int color = 0; color < totalPlayersLimit; ++color)
  295. {
  296. CPlayerSettings player;
  297. auto pc = PlayerColor(color);
  298. player.setColor(pc);
  299. auto playerType = EPlayerType::AI;
  300. if (getPlayerCount() != RANDOM_SIZE && color < realPlayersCnt)
  301. {
  302. playerType = EPlayerType::HUMAN;
  303. }
  304. else if((getPlayerCount() != RANDOM_SIZE && color >= realPlayersCnt)
  305. || (compOnlyPlayerCount != RANDOM_SIZE && color >= (PlayerColor::PLAYER_LIMIT_I-compOnlyPlayerCount)))
  306. {
  307. //FIXME: Allow humans to choose any color, even from the end of the list
  308. playerType = EPlayerType::COMP_ONLY;
  309. }
  310. player.setPlayerType(playerType);
  311. player.setTeam(rememberTeam[pc]);
  312. players[pc] = player;
  313. if (vstd::contains(rememberTownTypes, pc))
  314. players[pc].setStartingTown(rememberTownTypes[pc]);
  315. }
  316. */
  317. }
  318. void CMapGenOptions::savePlayersMap()
  319. {
  320. //Only save already configured players
  321. for (const auto& player : players)
  322. {
  323. savedPlayerSettings[player.first] = player.second;
  324. }
  325. }
  326. const std::map<PlayerColor, CMapGenOptions::CPlayerSettings> & CMapGenOptions::getSavedPlayersMap() const
  327. {
  328. return savedPlayerSettings;
  329. }
  330. const std::map<PlayerColor, CMapGenOptions::CPlayerSettings> & CMapGenOptions::getPlayersSettings() const
  331. {
  332. return players;
  333. }
  334. void CMapGenOptions::setStartingTownForPlayer(const PlayerColor & color, si32 town)
  335. {
  336. auto it = players.find(color);
  337. assert(it != players.end());
  338. it->second.setStartingTown(town);
  339. }
  340. void CMapGenOptions::setPlayerTypeForStandardPlayer(const PlayerColor & color, EPlayerType playerType)
  341. {
  342. // FIXME: Why actually not set it to COMP_ONLY? Ie. when swapping human to another color?
  343. assert(playerType != EPlayerType::COMP_ONLY);
  344. auto it = players.find(color);
  345. assert(it != players.end());
  346. it->second.setPlayerType(playerType);
  347. customizedPlayers = true;
  348. }
  349. const CRmgTemplate * CMapGenOptions::getMapTemplate() const
  350. {
  351. return mapTemplate;
  352. }
  353. void CMapGenOptions::setMapTemplate(const CRmgTemplate * value)
  354. {
  355. mapTemplate = value;
  356. //validate & adapt options according to template
  357. if(mapTemplate)
  358. {
  359. if(!mapTemplate->matchesSize(int3(getWidth(), getHeight(), 1 + getHasTwoLevels())))
  360. {
  361. auto sizes = mapTemplate->getMapSizes();
  362. setWidth(sizes.first.x);
  363. setHeight(sizes.first.y);
  364. setHasTwoLevels(sizes.first.z - 1);
  365. }
  366. if(!mapTemplate->getPlayers().isInRange(getHumanOrCpuPlayerCount()))
  367. setHumanOrCpuPlayerCount(RANDOM_SIZE);
  368. if(!mapTemplate->getCpuPlayers().isInRange(getCompOnlyPlayerCount()))
  369. setCompOnlyPlayerCount(RANDOM_SIZE);
  370. if(!mapTemplate->getWaterContentAllowed().count(getWaterContent()))
  371. setWaterContent(EWaterContent::RANDOM);
  372. }
  373. }
  374. void CMapGenOptions::setMapTemplate(const std::string & name)
  375. {
  376. if(!name.empty())
  377. setMapTemplate(VLC->tplh->getTemplate(name));
  378. }
  379. void CMapGenOptions::setRoadEnabled(const RoadId & roadType, bool enable)
  380. {
  381. if (enable)
  382. {
  383. enabledRoads.insert(roadType);
  384. }
  385. else
  386. {
  387. enabledRoads.erase(roadType);
  388. }
  389. }
  390. bool CMapGenOptions::isRoadEnabled(const RoadId & roadType) const
  391. {
  392. return enabledRoads.count(roadType);
  393. }
  394. bool CMapGenOptions::isRoadEnabled() const
  395. {
  396. return !enabledRoads.empty();
  397. }
  398. void CMapGenOptions::setPlayerTeam(const PlayerColor & color, const TeamID & team)
  399. {
  400. auto it = players.find(color);
  401. assert(it != players.end());
  402. // TODO: Make pivate friend method to avoid unintended changes?
  403. it->second.setTeam(team);
  404. customizedPlayers = true;
  405. }
  406. void CMapGenOptions::finalize(CRandomGenerator & rand)
  407. {
  408. logGlobal->info("RMG map: %dx%d, %s underground", getWidth(), getHeight(), getHasTwoLevels() ? "WITH" : "NO");
  409. logGlobal->info("RMG settings: players %d, teams %d, computer players %d, computer teams %d, water %d, monsters %d",
  410. static_cast<int>(getHumanOrCpuPlayerCount()), static_cast<int>(getTeamCount()), static_cast<int>(getCompOnlyPlayerCount()),
  411. static_cast<int>(getCompOnlyTeamCount()), static_cast<int>(getWaterContent()), static_cast<int>(getMonsterStrength()));
  412. if(!mapTemplate)
  413. {
  414. mapTemplate = getPossibleTemplate(rand);
  415. }
  416. assert(mapTemplate);
  417. logGlobal->info("RMG template name: %s", mapTemplate->getName());
  418. if (getHumanOrCpuPlayerCount() == RANDOM_SIZE)
  419. {
  420. auto possiblePlayers = mapTemplate->getPlayers().getNumbers();
  421. //ignore all non-randomized players, make sure these players will not be missing after roll
  422. possiblePlayers.erase(possiblePlayers.begin(), possiblePlayers.lower_bound(countHumanPlayers() + countCompOnlyPlayers()));
  423. assert(!possiblePlayers.empty());
  424. setHumanOrCpuPlayerCount (*RandomGeneratorUtil::nextItem(possiblePlayers, rand));
  425. updatePlayers();
  426. }
  427. if(teamCount == RANDOM_SIZE)
  428. {
  429. teamCount = rand.nextInt(getHumanOrCpuPlayerCount() - 1);
  430. if (teamCount == 1)
  431. teamCount = 0;
  432. }
  433. if(compOnlyPlayerCount == RANDOM_SIZE)
  434. {
  435. auto possiblePlayers = mapTemplate->getCpuPlayers().getNumbers();
  436. compOnlyPlayerCount = *RandomGeneratorUtil::nextItem(possiblePlayers, rand);
  437. updateCompOnlyPlayers();
  438. }
  439. if(compOnlyTeamCount == RANDOM_SIZE)
  440. {
  441. compOnlyTeamCount = rand.nextInt(std::max(compOnlyPlayerCount - 1, 0));
  442. }
  443. if(waterContent == EWaterContent::RANDOM)
  444. {
  445. auto allowedContent = mapTemplate->getWaterContentAllowed();
  446. if(!allowedContent.empty())
  447. {
  448. waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand);
  449. }
  450. else
  451. {
  452. waterContent = EWaterContent::NONE;
  453. }
  454. }
  455. if(monsterStrength == EMonsterStrength::RANDOM)
  456. {
  457. monsterStrength = static_cast<EMonsterStrength::EMonsterStrength>(rand.nextInt(EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));
  458. }
  459. assert (vstd::iswithin(waterContent, EWaterContent::NONE, EWaterContent::ISLANDS));
  460. assert (vstd::iswithin(monsterStrength, EMonsterStrength::GLOBAL_WEAK, EMonsterStrength::GLOBAL_STRONG));
  461. logGlobal->trace("Player config:");
  462. int cpuOnlyPlayers = 0;
  463. for(const auto & player : players)
  464. {
  465. std::string playerType;
  466. switch (player.second.getPlayerType())
  467. {
  468. case EPlayerType::AI:
  469. playerType = "AI";
  470. break;
  471. case EPlayerType::COMP_ONLY:
  472. playerType = "computer only";
  473. cpuOnlyPlayers++;
  474. break;
  475. case EPlayerType::HUMAN:
  476. playerType = "human only";
  477. break;
  478. default:
  479. assert(false);
  480. }
  481. // FIXME: Every player is player 0 with type of AI
  482. // FIXME: player.first != player.second.getColor()
  483. // TODO: Set player color everywhere players is set, or only once here
  484. logGlobal->trace("Player %d: %s", player.second.getColor(), playerType);
  485. }
  486. // FXIME: Do not set this again after options were set
  487. setCompOnlyPlayerCount(cpuOnlyPlayers); //human players are set automatically (?)
  488. logGlobal->info("Final player config: %d total, %d cpu-only", players.size(), static_cast<int>(getCompOnlyPlayerCount()));
  489. }
  490. void CMapGenOptions::updatePlayers()
  491. {
  492. // Remove non-human players only from the end of the players map if necessary
  493. for(auto itrev = players.end(); itrev != players.begin();)
  494. {
  495. auto it = itrev;
  496. --it;
  497. if (players.size() == getHumanOrCpuPlayerCount()) break;
  498. if(it->second.getPlayerType() != EPlayerType::HUMAN)
  499. {
  500. players.erase(it);
  501. }
  502. else
  503. {
  504. --itrev;
  505. }
  506. }
  507. }
  508. void CMapGenOptions::updateCompOnlyPlayers()
  509. {
  510. // Remove comp only players only from the end of the players map if necessary
  511. for(auto itrev = players.end(); itrev != players.begin();)
  512. {
  513. auto it = itrev;
  514. --it;
  515. if (players.size() <= getHumanOrCpuPlayerCount()) break;
  516. if(it->second.getPlayerType() == EPlayerType::COMP_ONLY)
  517. {
  518. players.erase(it);
  519. }
  520. else
  521. {
  522. --itrev;
  523. }
  524. }
  525. // Add some comp only players if necessary
  526. int compOnlyPlayersToAdd = static_cast<int>(getHumanOrCpuPlayerCount() - players.size());
  527. if (compOnlyPlayersToAdd < 0)
  528. {
  529. logGlobal->error("Incorrect number of players to add. Requested players %d, current players %d", humanOrCpuPlayerCount, players.size());
  530. assert (compOnlyPlayersToAdd < 0);
  531. }
  532. for(int i = 0; i < compOnlyPlayersToAdd; ++i)
  533. {
  534. CPlayerSettings pSettings;
  535. pSettings.setPlayerType(EPlayerType::COMP_ONLY);
  536. pSettings.setColor(getNextPlayerColor());
  537. players[pSettings.getColor()] = pSettings;
  538. }
  539. }
  540. int CMapGenOptions::countHumanPlayers() const
  541. {
  542. return static_cast<int>(boost::count_if(players, [](const std::pair<PlayerColor, CPlayerSettings> & pair)
  543. {
  544. return pair.second.getPlayerType() == EPlayerType::HUMAN;
  545. }));
  546. }
  547. int CMapGenOptions::countCompOnlyPlayers() const
  548. {
  549. return static_cast<int>(boost::count_if(players, [](const std::pair<PlayerColor, CPlayerSettings> & pair)
  550. {
  551. return pair.second.getPlayerType() == EPlayerType::COMP_ONLY;
  552. }));
  553. }
  554. PlayerColor CMapGenOptions::getNextPlayerColor() const
  555. {
  556. for(PlayerColor i = PlayerColor(0); i < PlayerColor::PLAYER_LIMIT; i.advance(1))
  557. {
  558. if(!players.count(i))
  559. {
  560. return i;
  561. }
  562. }
  563. logGlobal->error("Failed to get next player color");
  564. assert(false);
  565. return PlayerColor(0);
  566. }
  567. bool CMapGenOptions::checkOptions() const
  568. {
  569. if(mapTemplate)
  570. {
  571. return true;
  572. }
  573. else
  574. {
  575. CRandomGenerator gen;
  576. return getPossibleTemplate(gen) != nullptr;
  577. }
  578. }
  579. bool CMapGenOptions::arePlayersCustomized() const
  580. {
  581. return customizedPlayers;
  582. }
  583. std::vector<const CRmgTemplate *> CMapGenOptions::getPossibleTemplates() const
  584. {
  585. int3 tplSize(width, height, (hasTwoLevels ? 2 : 1));
  586. auto humanPlayers = countHumanPlayers();
  587. auto templates = VLC->tplh->getTemplates();
  588. vstd::erase_if(templates, [this, &tplSize, humanPlayers](const CRmgTemplate * tmpl)
  589. {
  590. if(!tmpl->matchesSize(tplSize))
  591. return true;
  592. if(!tmpl->isWaterContentAllowed(getWaterContent()))
  593. return true;
  594. if(getHumanOrCpuPlayerCount() != CMapGenOptions::RANDOM_SIZE)
  595. {
  596. if (!tmpl->getPlayers().isInRange(getHumanOrCpuPlayerCount()))
  597. return true;
  598. }
  599. else
  600. {
  601. // Human players shouldn't be banned when playing with random player count
  602. if(humanPlayers > *boost::min_element(tmpl->getPlayers().getNumbers()))
  603. return true;
  604. }
  605. if(compOnlyPlayerCount != CMapGenOptions::RANDOM_SIZE)
  606. {
  607. if (!tmpl->getCpuPlayers().isInRange(compOnlyPlayerCount))
  608. return true;
  609. }
  610. return false;
  611. });
  612. return templates;
  613. }
  614. const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand) const
  615. {
  616. auto templates = getPossibleTemplates();
  617. if(templates.empty())
  618. return nullptr;
  619. return *RandomGeneratorUtil::nextItem(templates, rand);
  620. }
  621. CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(FactionID::RANDOM), playerType(EPlayerType::AI), team(TeamID::NO_TEAM)
  622. {
  623. }
  624. PlayerColor CMapGenOptions::CPlayerSettings::getColor() const
  625. {
  626. return color;
  627. }
  628. void CMapGenOptions::CPlayerSettings::setColor(const PlayerColor & value)
  629. {
  630. assert(value >= PlayerColor(0) && value < PlayerColor::PLAYER_LIMIT);
  631. color = value;
  632. }
  633. si32 CMapGenOptions::CPlayerSettings::getStartingTown() const
  634. {
  635. return startingTown;
  636. }
  637. void CMapGenOptions::CPlayerSettings::setStartingTown(si32 value)
  638. {
  639. assert(value >= -1);
  640. if(value >= 0)
  641. {
  642. assert(value < static_cast<int>(VLC->townh->size()));
  643. assert((*VLC->townh)[value]->town != nullptr);
  644. }
  645. startingTown = value;
  646. }
  647. EPlayerType CMapGenOptions::CPlayerSettings::getPlayerType() const
  648. {
  649. return playerType;
  650. }
  651. void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType value)
  652. {
  653. playerType = value;
  654. }
  655. TeamID CMapGenOptions::CPlayerSettings::getTeam() const
  656. {
  657. return team;
  658. }
  659. void CMapGenOptions::CPlayerSettings::setTeam(const TeamID & value)
  660. {
  661. team = value;
  662. }
  663. VCMI_LIB_NAMESPACE_END