CMapGenerator.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. #include "StdInc.h"
  2. #include "CMapGenerator.h"
  3. #include "../Mapping/CMap.h"
  4. #include "../VCMI_Lib.h"
  5. #include "../CGeneralTextHandler.h"
  6. #include "../Mapping/CMapEditManager.h"
  7. #include "../CObjectHandler.h"
  8. #include "../CDefObjInfoHandler.h"
  9. #include "../GameConstants.h"
  10. #include "../CTownHandler.h"
  11. #include "../StringConstants.h"
  12. CMapGenerator::CMapGenerator(const CMapGenOptions & mapGenOptions, const std::map<TPlayerColor, CPlayerSettings> & players, int randomSeed) :
  13. mapGenOptions(mapGenOptions), randomSeed(randomSeed), players(players)
  14. {
  15. gen.seed(randomSeed);
  16. validateOptions();
  17. }
  18. CMapGenerator::~CMapGenerator()
  19. {
  20. }
  21. std::unique_ptr<CMap> CMapGenerator::generate()
  22. {
  23. finalizeMapGenOptions();
  24. //TODO select a template based on the map gen options or adapt it if necessary
  25. map = std::unique_ptr<CMap>(new CMap());
  26. addHeaderInfo();
  27. terViewPatternConfig = std::unique_ptr<CTerrainViewPatternConfig>(new CTerrainViewPatternConfig());
  28. mapMgr = std::unique_ptr<CMapEditManager>(new CMapEditManager(terViewPatternConfig.get(), map.get(), randomSeed));
  29. genTerrain();
  30. genTowns();
  31. return std::move(map);
  32. }
  33. void CMapGenerator::validateOptions() const
  34. {
  35. int playersCnt = 0;
  36. int compOnlyPlayersCnt = 0;
  37. BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
  38. {
  39. if(pair.second.getPlayerType() == CPlayerSettings::COMP_ONLY)
  40. {
  41. ++compOnlyPlayersCnt;
  42. }
  43. else
  44. {
  45. ++playersCnt;
  46. }
  47. }
  48. if(mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
  49. {
  50. if(playersCnt != GameConstants::PLAYER_LIMIT)
  51. {
  52. throw std::runtime_error(std::string("If the count of players is random size, ")
  53. + "the count of the items in the players map should equal GameConstants::PLAYER_LIMIT.");
  54. }
  55. if(playersCnt == mapGenOptions.getPlayersCnt())
  56. {
  57. throw std::runtime_error(std::string("If the count of players is random size, ")
  58. + "all items in the players map should be either of player type AI or HUMAN.");
  59. }
  60. }
  61. else
  62. {
  63. if(mapGenOptions.getCompOnlyPlayersCnt() != CMapGenOptions::RANDOM_SIZE)
  64. {
  65. if(playersCnt != mapGenOptions.getPlayersCnt() || compOnlyPlayersCnt != mapGenOptions.getCompOnlyPlayersCnt())
  66. {
  67. throw std::runtime_error(std::string("The count of players and computer only players in the players map ")
  68. + "doesn't conform with the specified map gen options.");
  69. }
  70. }
  71. else
  72. {
  73. if(playersCnt != mapGenOptions.getPlayersCnt() || (playersCnt == mapGenOptions.getPlayersCnt()
  74. && compOnlyPlayersCnt != GameConstants::PLAYER_LIMIT - playersCnt))
  75. {
  76. throw std::runtime_error(std::string("If the count of players is fixed and the count of comp only players random, ")
  77. + "the items in the players map should equal GameConstants::PLAYER_LIMIT.");
  78. }
  79. }
  80. }
  81. if(countHumanPlayers() < 1)
  82. {
  83. throw std::runtime_error("1 human player is required at least");
  84. }
  85. BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
  86. {
  87. if(pair.first != pair.second.getColor())
  88. {
  89. throw std::runtime_error("The color of an item in player settings and the key of it has to be the same.");
  90. }
  91. }
  92. }
  93. void CMapGenerator::finalizeMapGenOptions()
  94. {
  95. if(mapGenOptions.getPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
  96. {
  97. mapGenOptions.setPlayersCnt(gen.getInteger(countHumanPlayers(), GameConstants::PLAYER_LIMIT));
  98. // Remove AI players only from the end of the players map if necessary
  99. for(auto itrev = players.end(); itrev != players.begin();)
  100. {
  101. auto it = itrev;
  102. --it;
  103. if(players.size() == mapGenOptions.getPlayersCnt())
  104. {
  105. break;
  106. }
  107. const CPlayerSettings & pSettings = it->second;
  108. if(pSettings.getPlayerType() == CPlayerSettings::AI)
  109. {
  110. players.erase(it);
  111. }
  112. else
  113. {
  114. --itrev;
  115. }
  116. }
  117. }
  118. if(mapGenOptions.getTeamsCnt() == CMapGenOptions::RANDOM_SIZE)
  119. {
  120. mapGenOptions.setTeamsCnt(gen.getInteger(0, mapGenOptions.getPlayersCnt() - 1));
  121. }
  122. if(mapGenOptions.getCompOnlyPlayersCnt() == CMapGenOptions::RANDOM_SIZE)
  123. {
  124. mapGenOptions.setCompOnlyPlayersCnt(gen.getInteger(0, 8 - mapGenOptions.getPlayersCnt()));
  125. int totalPlayersCnt = mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt();
  126. // Remove comp only players only from the end of the players map if necessary
  127. for(auto itrev = players.end(); itrev != players.begin();)
  128. {
  129. auto it = itrev;
  130. --it;
  131. if(players.size() <= totalPlayersCnt)
  132. {
  133. break;
  134. }
  135. const CPlayerSettings & pSettings = it->second;
  136. if(pSettings.getPlayerType() == CPlayerSettings::COMP_ONLY)
  137. {
  138. players.erase(it);
  139. }
  140. else
  141. {
  142. --itrev;
  143. }
  144. }
  145. // Add some comp only players if necessary
  146. int compOnlyPlayersToAdd = totalPlayersCnt - players.size();
  147. for(int i = 0; i < compOnlyPlayersToAdd; ++i)
  148. {
  149. CPlayerSettings pSettings;
  150. pSettings.setPlayerType(CPlayerSettings::COMP_ONLY);
  151. pSettings.setColor(getNextPlayerColor());
  152. players[pSettings.getColor()] = pSettings;
  153. }
  154. }
  155. if(mapGenOptions.getCompOnlyTeamsCnt() == CMapGenOptions::RANDOM_SIZE)
  156. {
  157. mapGenOptions.setCompOnlyTeamsCnt(gen.getInteger(0, std::max(mapGenOptions.getCompOnlyPlayersCnt() - 1, 0)));
  158. }
  159. // There should be at least 2 players (1-player-maps aren't allowed)
  160. if(mapGenOptions.getPlayersCnt() + mapGenOptions.getCompOnlyPlayersCnt() < 2)
  161. {
  162. CPlayerSettings pSettings;
  163. pSettings.setPlayerType(CPlayerSettings::AI);
  164. pSettings.setColor(getNextPlayerColor());
  165. players[pSettings.getColor()] = pSettings;
  166. mapGenOptions.setPlayersCnt(2);
  167. }
  168. // 1 team isn't allowed
  169. if(mapGenOptions.getTeamsCnt() == 1 && mapGenOptions.getCompOnlyPlayersCnt() == 0)
  170. {
  171. mapGenOptions.setTeamsCnt(0);
  172. }
  173. if(mapGenOptions.getWaterContent() == EWaterContent::RANDOM)
  174. {
  175. mapGenOptions.setWaterContent(static_cast<EWaterContent::EWaterContent>(gen.getInteger(0, 2)));
  176. }
  177. if(mapGenOptions.getMonsterStrength() == EMonsterStrength::RANDOM)
  178. {
  179. mapGenOptions.setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(gen.getInteger(0, 2)));
  180. }
  181. }
  182. std::string CMapGenerator::getMapDescription() const
  183. {
  184. const std::string waterContentStr[3] = { "none", "normal", "islands" };
  185. const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" };
  186. std::stringstream ss;
  187. ss << "Map created by the Random Map Generator.\nTemplate was <MOCK>, ";
  188. ss << "Random seed was " << randomSeed << ", size " << map->width << "x";
  189. ss << map->height << ", levels " << (map->twoLevel ? "2" : "1") << ", ";
  190. ss << "humans " << static_cast<int>(mapGenOptions.getPlayersCnt()) << ", computers ";
  191. ss << static_cast<int>(mapGenOptions.getCompOnlyPlayersCnt()) << ", water " << waterContentStr[mapGenOptions.getWaterContent()];
  192. ss << ", monster " << monsterStrengthStr[mapGenOptions.getMonsterStrength()] << ", second expansion map";
  193. BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
  194. {
  195. const CPlayerSettings & pSettings = pair.second;
  196. if(pSettings.getPlayerType() == CPlayerSettings::HUMAN)
  197. {
  198. ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor()] << " is human";
  199. }
  200. if(pSettings.getStartingTown() != CPlayerSettings::RANDOM_TOWN)
  201. {
  202. ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor()]
  203. << " town choice is " << ETownType::names[pSettings.getStartingTown()];
  204. }
  205. }
  206. return ss.str();
  207. }
  208. void CMapGenerator::addPlayerInfo()
  209. {
  210. // Calculate which team numbers exist
  211. std::array<std::list<int>, 2> teamNumbers; // 0= cpu/human, 1= cpu only
  212. int teamOffset = 0;
  213. for(int i = 0; i < 2; ++i)
  214. {
  215. int playersCnt = i == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getCompOnlyPlayersCnt();
  216. int teamsCnt = i == 0 ? mapGenOptions.getTeamsCnt() : mapGenOptions.getCompOnlyTeamsCnt();
  217. if(playersCnt == 0)
  218. {
  219. continue;
  220. }
  221. int playersPerTeam = playersCnt /
  222. (teamsCnt == 0 ? playersCnt : teamsCnt);
  223. int teamsCntNorm = teamsCnt;
  224. if(teamsCntNorm == 0)
  225. {
  226. teamsCntNorm = playersCnt;
  227. }
  228. for(int j = 0; j < teamsCntNorm; ++j)
  229. {
  230. for(int k = 0; k < playersPerTeam; ++k)
  231. {
  232. teamNumbers[i].push_back(j + teamOffset);
  233. }
  234. }
  235. for(int j = 0; j < playersCnt - teamsCntNorm * playersPerTeam; ++j)
  236. {
  237. teamNumbers[i].push_back(j + teamOffset);
  238. }
  239. teamOffset += teamsCntNorm;
  240. }
  241. // Team numbers are assigned randomly to every player
  242. BOOST_FOREACH(const tPlayersMap::value_type & pair, players)
  243. {
  244. const CPlayerSettings & pSettings = pair.second;
  245. PlayerInfo player;
  246. player.canComputerPlay = true;
  247. int j = pSettings.getPlayerType() == CPlayerSettings::COMP_ONLY ? 1 : 0;
  248. if(j == 0)
  249. {
  250. player.canHumanPlay = true;
  251. }
  252. auto itTeam = std::next(teamNumbers[j].begin(), gen.getInteger(0, teamNumbers[j].size() - 1));
  253. player.team = *itTeam;
  254. teamNumbers[j].erase(itTeam);
  255. map->players[pSettings.getColor()] = player;
  256. }
  257. map->howManyTeams = (mapGenOptions.getTeamsCnt() == 0 ? mapGenOptions.getPlayersCnt() : mapGenOptions.getTeamsCnt())
  258. + (mapGenOptions.getCompOnlyTeamsCnt() == 0 ? mapGenOptions.getCompOnlyPlayersCnt() : mapGenOptions.getCompOnlyTeamsCnt());
  259. }
  260. int CMapGenerator::countHumanPlayers() const
  261. {
  262. return static_cast<int>(std::count_if(players.begin(), players.end(), [](const std::pair<TPlayerColor, CPlayerSettings> & pair)
  263. {
  264. return pair.second.getPlayerType() == CPlayerSettings::HUMAN;
  265. }));
  266. }
  267. void CMapGenerator::genTerrain()
  268. {
  269. map->initTerrain(); //FIXME nicer solution
  270. mapMgr->clearTerrain();
  271. mapMgr->drawTerrain(ETerrainType::GRASS, 10, 10, 20, 30, false);
  272. }
  273. void CMapGenerator::genTowns()
  274. {
  275. //FIXME mock gen
  276. const int3 townPos[2] = { int3(17, 13, 0), int3(25,13, 0) };
  277. const TFaction townTypes[2] = { ETownType::CASTLE, ETownType::DUNGEON };
  278. for(auto it = players.begin(); it != players.end(); ++it)
  279. {
  280. TPlayerColor owner = it->first;
  281. int pos = std::distance(players.begin(), it);
  282. int side = pos % 2;
  283. CGTownInstance * town = new CGTownInstance();
  284. town->ID = Obj::TOWN;
  285. town->subID = townTypes[side];
  286. town->tempOwner = owner;
  287. town->defInfo = VLC->dobjinfo->gobjs[town->ID][town->subID];
  288. town->builtBuildings.insert(EBuilding::FORT);
  289. town->builtBuildings.insert(-50);
  290. mapMgr->insertObject(town, townPos[side].x, townPos[side].y + (pos / 2) * 5, false);
  291. map->players[owner].allowedFactions.clear();
  292. map->players[owner].allowedFactions.insert(townTypes[side]);
  293. }
  294. }
  295. void CMapGenerator::addHeaderInfo()
  296. {
  297. map->version = EMapFormat::SOD;
  298. map->width = mapGenOptions.getWidth();
  299. map->height = mapGenOptions.getHeight();
  300. map->twoLevel = mapGenOptions.getHasTwoLevels();
  301. map->name = VLC->generaltexth->allTexts[740];
  302. map->description = getMapDescription();
  303. map->difficulty = 1;
  304. addPlayerInfo();
  305. }
  306. TPlayerColor CMapGenerator::getNextPlayerColor() const
  307. {
  308. for(TPlayerColor i = 0; i < GameConstants::PLAYER_LIMIT; ++i)
  309. {
  310. if(players.find(i) == players.end())
  311. {
  312. return i;
  313. }
  314. }
  315. throw std::runtime_error("Shouldn't happen. No free player color exists.");
  316. }
  317. CMapGenerator::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(AI)
  318. {
  319. }
  320. int CMapGenerator::CPlayerSettings::getColor() const
  321. {
  322. return color;
  323. }
  324. void CMapGenerator::CPlayerSettings::setColor(int value)
  325. {
  326. if(value >= 0 && value < GameConstants::PLAYER_LIMIT)
  327. {
  328. color = value;
  329. }
  330. else
  331. {
  332. throw std::runtime_error("The color of the player is not in a valid range.");
  333. }
  334. }
  335. int CMapGenerator::CPlayerSettings::getStartingTown() const
  336. {
  337. return startingTown;
  338. }
  339. void CMapGenerator::CPlayerSettings::setStartingTown(int value)
  340. {
  341. if(value >= -1 && value < static_cast<int>(VLC->townh->towns.size()))
  342. {
  343. startingTown = value;
  344. }
  345. else
  346. {
  347. throw std::runtime_error("The starting town of the player is not in a valid range.");
  348. }
  349. }
  350. CMapGenerator::CPlayerSettings::EPlayerType CMapGenerator::CPlayerSettings::getPlayerType() const
  351. {
  352. return playerType;
  353. }
  354. void CMapGenerator::CPlayerSettings::setPlayerType(EPlayerType value)
  355. {
  356. playerType = value;
  357. }