2
0

RandomMapTab.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. /*
  2. * RandomMapTab.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 "RandomMapTab.h"
  12. #include "CSelectionBase.h"
  13. #include "CLobbyScreen.h"
  14. #include "SelectionTab.h"
  15. #include "../CServerHandler.h"
  16. #include "../GameEngine.h"
  17. #include "../GameInstance.h"
  18. #include "../gui/MouseButton.h"
  19. #include "../gui/WindowHandler.h"
  20. #include "../gui/Shortcut.h"
  21. #include "../widgets/CComponent.h"
  22. #include "../widgets/ComboBox.h"
  23. #include "../widgets/Buttons.h"
  24. #include "../widgets/MiscWidgets.h"
  25. #include "../widgets/ObjectLists.h"
  26. #include "../widgets/Slider.h"
  27. #include "../widgets/TextControls.h"
  28. #include "../widgets/GraphicalPrimitiveCanvas.h"
  29. #include "../widgets/CTextInput.h"
  30. #include "../windows/GUIClasses.h"
  31. #include "../windows/InfoWindows.h"
  32. #include "../../lib/texts/CGeneralTextHandler.h"
  33. #include "../../lib/mapping/CMapInfo.h"
  34. #include "../../lib/mapping/CMapHeader.h"
  35. #include "../../lib/mapping/MapFormat.h"
  36. #include "../../lib/rmg/CMapGenOptions.h"
  37. #include "../../lib/rmg/CRmgTemplateStorage.h"
  38. #include "../../lib/filesystem/Filesystem.h"
  39. #include "../../lib/RoadHandler.h"
  40. #include "../../lib/CConfigHandler.h"
  41. #include "../../lib/serializer/JsonSerializer.h"
  42. #include "../../lib/serializer/JsonDeserializer.h"
  43. RandomMapTab::RandomMapTab():
  44. InterfaceObjectConfigurable(),
  45. templateIndex(0)
  46. {
  47. recActions = 0;
  48. mapGenOptions = std::make_shared<CMapGenOptions>();
  49. addCallback("toggleMapSize", [this](int btnId)
  50. {
  51. onToggleMapSize(btnId);
  52. });
  53. addCallback("toggleTwoLevels", [&](bool on)
  54. {
  55. mapGenOptions->setLevels(on ? 2 : 1);
  56. if(mapGenOptions->getMapTemplate())
  57. if(!mapGenOptions->getMapTemplate()->matchesSize(int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), mapGenOptions->getLevels()}))
  58. setTemplate(nullptr);
  59. updateMapInfoByHost();
  60. });
  61. addCallback("setPlayersCount", [&](int btnId)
  62. {
  63. mapGenOptions->setHumanOrCpuPlayerCount(btnId);
  64. setMapGenOptions(mapGenOptions);
  65. updateMapInfoByHost();
  66. });
  67. addCallback("setTeamsCount", [&](int btnId)
  68. {
  69. mapGenOptions->setTeamCount(btnId);
  70. updateMapInfoByHost();
  71. });
  72. addCallback("setCompOnlyPlayers", [&](int btnId)
  73. {
  74. mapGenOptions->setCompOnlyPlayerCount(btnId);
  75. setMapGenOptions(mapGenOptions);
  76. updateMapInfoByHost();
  77. });
  78. addCallback("setCompOnlyTeams", [&](int btnId)
  79. {
  80. mapGenOptions->setCompOnlyTeamCount(btnId);
  81. updateMapInfoByHost();
  82. });
  83. addCallback("setWaterContent", [&](int btnId)
  84. {
  85. mapGenOptions->setWaterContent(static_cast<EWaterContent::EWaterContent>(btnId));
  86. updateMapInfoByHost();
  87. });
  88. addCallback("setMonsterStrength", [&](int btnId)
  89. {
  90. if(btnId < 0)
  91. mapGenOptions->setMonsterStrength(EMonsterStrength::RANDOM);
  92. else
  93. mapGenOptions->setMonsterStrength(static_cast<EMonsterStrength::EMonsterStrength>(btnId)); //value 2 to 4
  94. updateMapInfoByHost();
  95. });
  96. //new callbacks available only from mod
  97. addCallback("teamAlignments", [&](int)
  98. {
  99. ENGINE->windows().createAndPushWindow<TeamAlignments>(*this);
  100. });
  101. for(const auto & road : LIBRARY->roadTypeHandler->objects)
  102. {
  103. std::string cbRoadType = "selectRoad_" + road->getJsonKey();
  104. addCallback(cbRoadType, [&, roadID = road->getId()](bool on)
  105. {
  106. mapGenOptions->setRoadEnabled(roadID, on);
  107. updateMapInfoByHost();
  108. });
  109. }
  110. const JsonNode config(JsonPath::builtin("config/widgets/randomMapTab.json"));
  111. build(config);
  112. if(auto w = widget<CButton>("buttonShowRandomMaps"))
  113. {
  114. w->addCallback([&]()
  115. {
  116. (static_cast<CLobbyScreen *>(parent))->toggleTab((static_cast<CLobbyScreen *>(parent))->tabSel);
  117. (static_cast<CLobbyScreen *>(parent))->tabSel->showRandom = true;
  118. (static_cast<CLobbyScreen *>(parent))->tabSel->filter(0, true);
  119. });
  120. }
  121. //set combo box callbacks
  122. if(auto w = widget<ComboBox>("templateList"))
  123. {
  124. auto getTemplates = [](){
  125. auto templates = LIBRARY->tplh->getTemplates();
  126. boost::range::sort(templates, [](const CRmgTemplate * a, const CRmgTemplate * b){
  127. return a->getName() < b->getName();
  128. });
  129. return templates;
  130. };
  131. w->onConstructItems = [getTemplates](std::vector<const void *> & curItems){
  132. curItems.push_back(nullptr); //default template
  133. for(auto & t : getTemplates())
  134. curItems.push_back(t);
  135. };
  136. w->onSetItem = [&](const void * item){
  137. this->setTemplate(reinterpret_cast<const CRmgTemplate *>(item));
  138. };
  139. w->getItemText = [this](int idx, const void * item){
  140. if(item)
  141. return reinterpret_cast<const CRmgTemplate *>(item)->getName();
  142. if(idx == 0)
  143. return readText(variables["randomTemplate"]);
  144. return std::string("");
  145. };
  146. w->addCallback([this, getTemplates]() // no real dropdown... - instead open dialog
  147. {
  148. std::vector<std::string> texts;
  149. std::vector<std::string> popupTexts;
  150. texts.push_back(readText(variables["randomTemplate"]));
  151. popupTexts.push_back("");
  152. auto selectedTemplate = mapGenOptions->getMapTemplate();
  153. const auto& templates = getTemplates();
  154. for(int i = 0; i < templates.size(); i++)
  155. {
  156. if(selectedTemplate)
  157. {
  158. if(templates[i]->getId() == selectedTemplate->getId())
  159. templateIndex = i + 1;
  160. }
  161. else
  162. templateIndex = 0;
  163. texts.push_back(templates[i]->getName());
  164. popupTexts.push_back("{" + templates[i]->getName() + "}" + (templates[i]->getDescription().empty() ? "" : "\n\n") + templates[i]->getDescription());
  165. }
  166. ENGINE->windows().popWindows(1);
  167. auto window = std::make_shared<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.templatesSelect.hover"), LIBRARY->generaltexth->translate("vcmi.lobby.templatesSelect.help"), [this](int index){
  168. widget<ComboBox>("templateList")->setItem(index);
  169. }, templateIndex, std::vector<std::shared_ptr<IImage>>(), true, true);
  170. window->onPopup = [popupTexts](int index){
  171. if(!popupTexts[index].empty())
  172. CRClickPopup::createAndPush(popupTexts[index]);
  173. };
  174. ENGINE->windows().pushWindow(window);
  175. });
  176. }
  177. loadOptions();
  178. }
  179. void RandomMapTab::onToggleMapSize(int btnId)
  180. {
  181. if(btnId == -1)
  182. return;
  183. auto mapSizeVal = getStandardMapSizes();
  184. auto setTemplateForSize = [this](){
  185. if(mapGenOptions->getMapTemplate())
  186. if(!mapGenOptions->getMapTemplate()->matchesSize(int3{mapGenOptions->getWidth(), mapGenOptions->getHeight(), mapGenOptions->getLevels()}))
  187. setTemplate(nullptr);
  188. updateMapInfoByHost();
  189. };
  190. if(btnId == mapSizeVal.size() - 1)
  191. {
  192. ENGINE->windows().createAndPushWindow<SetSizeWindow>(int3(mapGenOptions->getWidth(), mapGenOptions->getHeight(), mapGenOptions->getLevels()), mapGenOptions->getMapTemplate(), [this, setTemplateForSize](int3 ret){
  193. if(ret.z > 2)
  194. {
  195. std::shared_ptr<CInfoWindow> temp = CInfoWindow::create(LIBRARY->generaltexth->translate("vcmi.lobby.customRmgSize.experimental"), PlayerColor(0), {}); //TODO: multilevel support
  196. ENGINE->windows().pushWindow(temp);
  197. }
  198. mapGenOptions->setWidth(ret.x);
  199. mapGenOptions->setHeight(ret.y);
  200. mapGenOptions->setLevels(ret.z);
  201. setTemplateForSize();
  202. });
  203. return;
  204. }
  205. mapGenOptions->setWidth(mapSizeVal[btnId]);
  206. mapGenOptions->setHeight(mapSizeVal[btnId]);
  207. setTemplateForSize();
  208. }
  209. void RandomMapTab::updateMapInfoByHost()
  210. {
  211. if(GAME->server().isGuest())
  212. return;
  213. // Generate header info
  214. mapInfo = std::make_shared<CMapInfo>();
  215. mapInfo->isRandomMap = true;
  216. mapInfo->mapHeader = std::make_unique<CMapHeader>();
  217. mapInfo->mapHeader->version = EMapFormat::VCMI;
  218. mapInfo->mapHeader->name.appendLocalString(EMetaText::GENERAL_TXT, 740);
  219. mapInfo->mapHeader->description.appendLocalString(EMetaText::GENERAL_TXT, 741);
  220. if(mapGenOptions->getWaterContent() != EWaterContent::RANDOM)
  221. mapInfo->mapHeader->banWaterHeroes(mapGenOptions->getWaterContent() != EWaterContent::NONE);
  222. const auto * temp = mapGenOptions->getMapTemplate();
  223. if (temp)
  224. {
  225. auto randomTemplateDescription = temp->getDescription();
  226. if (!randomTemplateDescription.empty())
  227. {
  228. auto description = std::string("\n\n") + randomTemplateDescription;
  229. mapInfo->mapHeader->description.appendRawString(description);
  230. }
  231. for (const auto & hero : temp->getBannedHeroes())
  232. mapInfo->mapHeader->allowedHeroes.erase(hero);
  233. for (const auto & hero : temp->getEnabledHeroes())
  234. mapInfo->mapHeader->allowedHeroes.insert(hero);
  235. }
  236. mapInfo->mapHeader->difficulty = EMapDifficulty::NORMAL;
  237. mapInfo->mapHeader->height = mapGenOptions->getHeight();
  238. mapInfo->mapHeader->width = mapGenOptions->getWidth();
  239. mapInfo->mapHeader->mapLayers.clear();
  240. for(int i = 0; i < mapGenOptions->getLevels(); i++)
  241. {
  242. if(i == 0)
  243. mapInfo->mapHeader->mapLayers.push_back(MapLayerId::SURFACE);
  244. else if(i == 1)
  245. mapInfo->mapHeader->mapLayers.push_back(MapLayerId::UNDERGROUND);
  246. else
  247. mapInfo->mapHeader->mapLayers.push_back(MapLayerId::UNKNOWN);
  248. }
  249. // Generate player information
  250. int playersToGen = mapGenOptions->getMaxPlayersCount();
  251. mapInfo->mapHeader->howManyTeams = playersToGen;
  252. //TODO: Assign all human-controlled colors in first place
  253. for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
  254. {
  255. mapInfo->mapHeader->players[i].canComputerPlay = false;
  256. mapInfo->mapHeader->players[i].canHumanPlay = false;
  257. }
  258. std::vector<PlayerColor> availableColors;
  259. for (ui8 color = 0; color < PlayerColor::PLAYER_LIMIT_I; color++)
  260. {
  261. availableColors.push_back(PlayerColor(color));
  262. }
  263. //First restore known players
  264. for (auto& player : mapGenOptions->getPlayersSettings())
  265. {
  266. PlayerInfo playerInfo;
  267. playerInfo.isFactionRandom = (player.second.getStartingTown() == FactionID::RANDOM);
  268. playerInfo.canComputerPlay = (player.second.getPlayerType() != EPlayerType::HUMAN);
  269. playerInfo.canHumanPlay = (player.second.getPlayerType() != EPlayerType::COMP_ONLY);
  270. auto team = player.second.getTeam();
  271. playerInfo.team = team;
  272. playerInfo.hasMainTown = true;
  273. playerInfo.generateHeroAtMainTown = true;
  274. mapInfo->mapHeader->players[player.first.getNum()] = playerInfo;
  275. vstd::erase(availableColors, player.first);
  276. }
  277. mapInfoChanged(mapInfo, mapGenOptions);
  278. }
  279. void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
  280. {
  281. mapGenOptions = opts;
  282. //Prepare allowed options - add all, then erase the ones above the limit
  283. for(int i = 0; i <= PlayerColor::PLAYER_LIMIT_I; ++i)
  284. {
  285. playerCountAllowed.insert(i);
  286. compCountAllowed.insert(i);
  287. if (i >= 2)
  288. {
  289. playerTeamsAllowed.insert(i);
  290. }
  291. if (i >= 1)
  292. {
  293. compTeamsAllowed.insert(i);
  294. }
  295. }
  296. std::set<int> humanCountAllowed;
  297. auto * tmpl = mapGenOptions->getMapTemplate();
  298. if(tmpl)
  299. {
  300. playerCountAllowed = tmpl->getPlayers().getNumbers();
  301. humanCountAllowed = tmpl->getHumanPlayers().getNumbers(); // Unused now?
  302. }
  303. si8 playerLimit = opts->getPlayerLimit();
  304. si8 humanOrCpuPlayerCount = opts->getHumanOrCpuPlayerCount();
  305. si8 compOnlyPlayersCount = opts->getCompOnlyPlayerCount();
  306. if(humanOrCpuPlayerCount != CMapGenOptions::RANDOM_SIZE)
  307. {
  308. vstd::erase_if(compCountAllowed, [playerLimit, humanOrCpuPlayerCount](int el)
  309. {
  310. return (playerLimit - humanOrCpuPlayerCount) < el;
  311. });
  312. vstd::erase_if(playerTeamsAllowed, [humanOrCpuPlayerCount](int el)
  313. {
  314. return humanOrCpuPlayerCount <= el;
  315. });
  316. }
  317. else // Random
  318. {
  319. vstd::erase_if(compCountAllowed, [playerLimit](int el)
  320. {
  321. return (playerLimit - 1) < el; // Must leave at least 1 human player
  322. });
  323. vstd::erase_if(playerTeamsAllowed, [playerLimit](int el)
  324. {
  325. return playerLimit <= el;
  326. });
  327. }
  328. if(!playerTeamsAllowed.count(opts->getTeamCount()))
  329. {
  330. opts->setTeamCount(CMapGenOptions::RANDOM_SIZE);
  331. }
  332. if(compOnlyPlayersCount != CMapGenOptions::RANDOM_SIZE)
  333. {
  334. // This setting doesn't impact total number of players
  335. vstd::erase_if(compTeamsAllowed, [compOnlyPlayersCount](int el)
  336. {
  337. return compOnlyPlayersCount<= el;
  338. });
  339. if(!compTeamsAllowed.count(opts->getCompOnlyTeamCount()))
  340. {
  341. opts->setCompOnlyTeamCount(CMapGenOptions::RANDOM_SIZE);
  342. }
  343. }
  344. if(auto w = widget<CToggleGroup>("groupMapSize"))
  345. {
  346. const auto & mapSizes = getStandardMapSizes();
  347. for(auto toggle : w->buttons)
  348. {
  349. if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second))
  350. {
  351. int3 size( mapSizes[toggle.first], mapSizes[toggle.first], mapGenOptions->getLevels());
  352. bool sizeAllowed = !mapGenOptions->getMapTemplate() || mapGenOptions->getMapTemplate()->matchesSize(size);
  353. button->block(!sizeAllowed && !(toggle.first == mapSizes.size() - 1));
  354. }
  355. }
  356. auto position = vstd::find_pos(getStandardMapSizes(), opts->getWidth());
  357. w->setSelected(position == mapSizes.size() - 1 || opts->getWidth() != opts->getHeight() ? -1 : position);
  358. }
  359. if(auto w = widget<CToggleButton>("buttonTwoLevels"))
  360. {
  361. int possibleLevelCount = 2;
  362. if(mapGenOptions->getMapTemplate())
  363. {
  364. auto sizes = mapGenOptions->getMapTemplate()->getMapSizes();
  365. possibleLevelCount = sizes.second.z - sizes.first.z + 1;
  366. }
  367. w->setSelectedSilent(opts->getLevels() == 2);
  368. w->block(possibleLevelCount < 2);
  369. }
  370. if(auto w = widget<CToggleGroup>("groupMaxPlayers"))
  371. {
  372. w->setSelected(opts->getHumanOrCpuPlayerCount());
  373. deactivateButtonsFrom(*w, playerCountAllowed);
  374. }
  375. if(auto w = widget<CToggleGroup>("groupMaxTeams"))
  376. {
  377. w->setSelected(opts->getTeamCount());
  378. deactivateButtonsFrom(*w, playerTeamsAllowed);
  379. }
  380. if(auto w = widget<CToggleGroup>("groupCompOnlyPlayers"))
  381. {
  382. w->setSelected(opts->getCompOnlyPlayerCount());
  383. deactivateButtonsFrom(*w, compCountAllowed);
  384. }
  385. if(auto w = widget<CToggleGroup>("groupCompOnlyTeams"))
  386. {
  387. w->setSelected(opts->getCompOnlyTeamCount());
  388. deactivateButtonsFrom(*w, compTeamsAllowed);
  389. }
  390. if(auto w = widget<CToggleGroup>("groupWaterContent"))
  391. {
  392. w->setSelected(opts->getWaterContent());
  393. if(opts->getMapTemplate())
  394. {
  395. std::set<int> allowedWater(opts->getMapTemplate()->getWaterContentAllowed().begin(), opts->getMapTemplate()->getWaterContentAllowed().end());
  396. deactivateButtonsFrom(*w, allowedWater);
  397. }
  398. else
  399. deactivateButtonsFrom(*w, {-1});
  400. }
  401. if(auto w = widget<CToggleGroup>("groupMonsterStrength"))
  402. w->setSelected(opts->getMonsterStrength());
  403. if(auto w = widget<CButton>("templateButton"))
  404. {
  405. if(tmpl)
  406. w->setTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
  407. else
  408. w->setTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
  409. }
  410. for(const auto & r : LIBRARY->roadTypeHandler->objects)
  411. {
  412. // Workaround for vcmi-extras bug
  413. std::string jsonKey = r->getJsonKey();
  414. std::string identifier = jsonKey.substr(jsonKey.find(':')+1);
  415. if(auto w = widget<CToggleButton>(identifier))
  416. {
  417. w->setSelected(opts->isRoadEnabled(r->getId()));
  418. }
  419. }
  420. }
  421. void RandomMapTab::setTemplate(const CRmgTemplate * tmpl)
  422. {
  423. mapGenOptions->setMapTemplate(tmpl);
  424. setMapGenOptions(mapGenOptions);
  425. if(auto w = widget<CButton>("templateButton"))
  426. {
  427. if(tmpl)
  428. w->setTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
  429. else
  430. w->setTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
  431. }
  432. if(auto w = widget<ComboBox>("templateList"))
  433. {
  434. if(tmpl)
  435. w->setTextOverlay(tmpl->getName(), EFonts::FONT_SMALL, Colors::WHITE);
  436. else
  437. w->setTextOverlay(readText(variables["randomTemplate"]), EFonts::FONT_SMALL, Colors::WHITE);
  438. }
  439. updateMapInfoByHost();
  440. }
  441. void RandomMapTab::deactivateButtonsFrom(CToggleGroup & group, const std::set<int> & allowed)
  442. {
  443. logGlobal->debug("Blocking buttons");
  444. for(auto toggle : group.buttons)
  445. {
  446. if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second))
  447. {
  448. if(allowed.count(CMapGenOptions::RANDOM_SIZE)
  449. || allowed.count(toggle.first)
  450. || toggle.first == CMapGenOptions::RANDOM_SIZE)
  451. {
  452. button->block(false);
  453. }
  454. else
  455. {
  456. button->block(true);
  457. }
  458. }
  459. }
  460. }
  461. std::vector<int> RandomMapTab::getStandardMapSizes()
  462. {
  463. return {CMapHeader::MAP_SIZE_SMALL, CMapHeader::MAP_SIZE_MIDDLE, CMapHeader::MAP_SIZE_LARGE, CMapHeader::MAP_SIZE_XLARGE, CMapHeader::MAP_SIZE_HUGE, CMapHeader::MAP_SIZE_XHUGE, CMapHeader::MAP_SIZE_GIANT};
  464. }
  465. void TeamAlignmentsWidget::checkTeamCount()
  466. {
  467. //Do not allow to select one team only
  468. std::set<TeamID> teams;
  469. for (int plId = 0; plId < players.size(); ++plId)
  470. {
  471. teams.insert(TeamID(players[plId]->getSelected()));
  472. }
  473. if (teams.size() < 2)
  474. {
  475. //Do not let player close the window
  476. buttonOk->block(true);
  477. }
  478. else
  479. {
  480. buttonOk->block(false);
  481. }
  482. }
  483. TeamAlignments::TeamAlignments(RandomMapTab & randomMapTab)
  484. : CWindowObject(BORDERED)
  485. {
  486. OBJECT_CONSTRUCTION;
  487. widget = std::make_shared<TeamAlignmentsWidget>(randomMapTab);
  488. pos = widget->pos;
  489. updateShadow();
  490. center();
  491. }
  492. TeamAlignmentsWidget::TeamAlignmentsWidget(RandomMapTab & randomMapTab):
  493. InterfaceObjectConfigurable()
  494. {
  495. const JsonNode config(JsonPath::builtin("config/widgets/randomMapTeamsWidget.json"));
  496. variables = config["variables"];
  497. //int totalPlayers = randomMapTab.obtainMapGenOptions().getPlayerLimit();
  498. int totalPlayers = randomMapTab.obtainMapGenOptions().getMaxPlayersCount();
  499. assert(totalPlayers <= PlayerColor::PLAYER_LIMIT_I);
  500. auto playerSettings = randomMapTab.obtainMapGenOptions().getPlayersSettings();
  501. variables["totalPlayers"].Integer() = totalPlayers;
  502. pos.w = variables["windowSize"]["x"].Integer() + totalPlayers * variables["cellMargin"]["x"].Integer();
  503. auto widthExtend = std::max(pos.w, 220) - pos.w; // too small for buttons
  504. pos.w += widthExtend;
  505. pos.h = variables["windowSize"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer();
  506. variables["backgroundRect"]["x"].Integer() = 0;
  507. variables["backgroundRect"]["y"].Integer() = 0;
  508. variables["backgroundRect"]["w"].Integer() = pos.w;
  509. variables["backgroundRect"]["h"].Integer() = pos.h;
  510. variables["okButtonPosition"]["x"].Integer() = variables["buttonsOffset"]["ok"]["x"].Integer();
  511. variables["okButtonPosition"]["y"].Integer() = variables["buttonsOffset"]["ok"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer();
  512. variables["cancelButtonPosition"]["x"].Integer() = variables["buttonsOffset"]["cancel"]["x"].Integer();
  513. variables["cancelButtonPosition"]["y"].Integer() = variables["buttonsOffset"]["cancel"]["y"].Integer() + totalPlayers * variables["cellMargin"]["y"].Integer();
  514. addCallback("ok", [&](int)
  515. {
  516. for(int plId = 0; plId < players.size(); ++plId)
  517. {
  518. randomMapTab.obtainMapGenOptions().setPlayerTeam(PlayerColor(plId), TeamID(players[plId]->getSelected()));
  519. }
  520. randomMapTab.updateMapInfoByHost();
  521. for(auto & window : ENGINE->windows().findWindows<TeamAlignments>())
  522. ENGINE->windows().popWindow(window);
  523. });
  524. addCallback("cancel", [&](int)
  525. {
  526. for(auto & window : ENGINE->windows().findWindows<TeamAlignments>())
  527. ENGINE->windows().popWindow(window);
  528. });
  529. build(config);
  530. center(pos);
  531. OBJECT_CONSTRUCTION;
  532. // Window should have X * X columns, where X is max players allowed for current settings
  533. // For random player count, X is 8
  534. if (totalPlayers > playerSettings.size())
  535. {
  536. auto savedPlayers = randomMapTab.obtainMapGenOptions().getSavedPlayersMap();
  537. for (const auto & player : savedPlayers)
  538. {
  539. if (!vstd::contains(playerSettings, player.first))
  540. {
  541. playerSettings[player.first] = player.second;
  542. }
  543. }
  544. }
  545. std::vector<CMapGenOptions::CPlayerSettings> settingsVec;
  546. for (const auto & player : playerSettings)
  547. {
  548. settingsVec.push_back(player.second);
  549. }
  550. for(int plId = 0; plId < totalPlayers; ++plId)
  551. {
  552. players.push_back(std::make_shared<CToggleGroup>([&, totalPlayers, plId](int sel)
  553. {
  554. variables["player_id"].Integer() = plId;
  555. OBJECT_CONSTRUCTION_TARGETED(players[plId].get());
  556. for(int teamId = 0; teamId < totalPlayers; ++teamId)
  557. {
  558. auto button = std::dynamic_pointer_cast<CToggleButton>(players[plId]->buttons[teamId]);
  559. assert(button);
  560. if(sel == teamId)
  561. {
  562. button->setOverlay(buildWidget(variables["flagsAnimation"]));
  563. }
  564. else
  565. {
  566. button->setOverlay(nullptr);
  567. }
  568. button->addCallback([this](bool)
  569. {
  570. checkTeamCount();
  571. });
  572. }
  573. }));
  574. OBJECT_CONSTRUCTION_TARGETED(players.back().get());
  575. for(int teamId = 0; teamId < totalPlayers; ++teamId)
  576. {
  577. variables["point"]["x"].Integer() = variables["cellOffset"]["x"].Integer() + plId * variables["cellMargin"]["x"].Integer() + (widthExtend / 2);
  578. variables["point"]["y"].Integer() = variables["cellOffset"]["y"].Integer() + teamId * variables["cellMargin"]["y"].Integer();
  579. auto button = buildWidget(variables["button"]);
  580. players.back()->addToggle(teamId, std::dynamic_pointer_cast<CToggleBase>(button));
  581. }
  582. // plId is not necessarily player color, just an index
  583. auto team = settingsVec.at(plId).getTeam();
  584. if(team == TeamID::NO_TEAM)
  585. {
  586. logGlobal->warn("Player %d (id %d) has uninitialized team", settingsVec.at(plId).getColor(), plId);
  587. players.back()->setSelected(plId);
  588. }
  589. else
  590. players.back()->setSelected(team.getNum());
  591. }
  592. buttonOk = widget<CButton>("buttonOK");
  593. buttonCancel = widget<CButton>("buttonCancel");
  594. }
  595. void RandomMapTab::saveOptions(const CMapGenOptions & options)
  596. {
  597. JsonNode data;
  598. JsonSerializer ser(nullptr, data);
  599. ser.serializeStruct("lastSettings", const_cast<CMapGenOptions & >(options));
  600. // FIXME: Do not nest fields
  601. Settings rmgSettings = persistentStorage.write["rmg"];
  602. rmgSettings["rmg"] = data;
  603. }
  604. void RandomMapTab::loadOptions()
  605. {
  606. JsonNode rmgSettings = persistentStorage["rmg"]["rmg"];
  607. if (!rmgSettings.Struct().empty())
  608. {
  609. rmgSettings.setModScope(ModScope::scopeGame());
  610. mapGenOptions.reset(new CMapGenOptions());
  611. JsonDeserializer handler(nullptr, rmgSettings);
  612. handler.serializeStruct("lastSettings", *mapGenOptions);
  613. // Will check template and set other options as well
  614. setTemplate(mapGenOptions->getMapTemplate());
  615. if(auto w = widget<ComboBox>("templateList"))
  616. {
  617. w->setItem(mapGenOptions->getMapTemplate());
  618. }
  619. } else
  620. {
  621. // Default settings
  622. mapGenOptions->setRoadEnabled(RoadId(Road::DIRT_ROAD), true);
  623. mapGenOptions->setRoadEnabled(RoadId(Road::GRAVEL_ROAD), true);
  624. mapGenOptions->setRoadEnabled(RoadId(Road::COBBLESTONE_ROAD), true);
  625. }
  626. updateMapInfoByHost();
  627. // TODO: Save & load difficulty?
  628. }
  629. SetSizeWindow::SetSizeWindow(int3 initSize, const CRmgTemplate * mapTemplate, std::function<void(int3)> cb)
  630. : CWindowObject(BORDERED)
  631. {
  632. OBJECT_CONSTRUCTION;
  633. pos.w = 300;
  634. pos.h = 180;
  635. updateShadow();
  636. center();
  637. background = std::make_shared<FilledTexturePlayerColored>(Rect(0, 0, pos.w, pos.h));
  638. background->setPlayerColor(PlayerColor(1));
  639. buttonCancel = std::make_shared<CButton>(Point(160, 140), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this, cb](){ close();}, EShortcut::GLOBAL_CANCEL);
  640. buttonOk = std::make_shared<CButton>(Point(70, 140), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this, cb](){
  641. close();
  642. if(cb)
  643. cb(int3(std::max(1, std::stoi(numInputs[0]->getText())), std::max(1, std::stoi(numInputs[1]->getText())), std::max(1, std::stoi(numInputs[2]->getText()))));
  644. }, EShortcut::GLOBAL_ACCEPT);
  645. int3 minSize = mapTemplate ? mapTemplate->getMapSizes().first : int3{36,36,1};
  646. int3 maxSize = mapTemplate ? mapTemplate->getMapSizes().second : int3{999,999,9};
  647. MetaString minSizeString;
  648. MetaString maxSizeString;
  649. minSizeString.appendTextID("vcmi.randomMapTab.template.minimalSize");
  650. maxSizeString.appendTextID("vcmi.randomMapTab.template.maximalSize");
  651. minSizeString.replaceNumber(minSize.x);
  652. minSizeString.replaceNumber(minSize.y);
  653. minSizeString.replaceNumber(minSize.z);
  654. maxSizeString.replaceNumber(maxSize.x);
  655. maxSizeString.replaceNumber(maxSize.y);
  656. maxSizeString.replaceNumber(maxSize.z);
  657. if (mapTemplate)
  658. {
  659. MetaString templateTitle;
  660. templateTitle.appendRawString("%s: %s");
  661. templateTitle.replaceTextID("vcmi.randomMapTab.widgets.templateLabel");
  662. templateTitle.replaceRawString(mapTemplate->getName());
  663. titles.push_back(std::make_shared<CLabel>(10, 40, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, templateTitle.toString()));
  664. }
  665. sizeLabels.push_back(std::make_shared<CLabel>(10, 60, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, minSizeString.toString()));
  666. sizeLabels.push_back(std::make_shared<CLabel>(10, 80, FONT_SMALL, ETextAlignment::CENTERLEFT, Colors::WHITE, maxSizeString.toString()));
  667. const auto checkTemplateSize = [this, mapTemplate](const std::string &){
  668. int3 mapSize {
  669. std::stoi(numInputs[0]->getText()),
  670. std::stoi(numInputs[1]->getText()),
  671. std::stoi(numInputs[2]->getText())
  672. };
  673. bool isSizeLegal = mapTemplate ? mapTemplate->matchesSize(mapSize) : (mapSize.x * mapSize.y >= 36*36);
  674. buttonOk->block(!isSizeLegal);
  675. auto labelColors = isSizeLegal ? Colors::WHITE : Colors::RED;
  676. for (const auto & label : sizeLabels)
  677. label->setColor(labelColors);
  678. };
  679. titles.push_back(std::make_shared<CLabel>(150, 15, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->translate("vcmi.lobby.customRmgSize.title")));
  680. for(int i = 0; i < 3; i++)
  681. {
  682. Rect r(10 + i * 100, 110, 80, 20);
  683. rectangles.push_back(std::make_shared<TransparentFilledRectangle>(Rect(r.topLeft() - Point(0,18), r.dimensions()), ColorRGBA(0, 0, 0, 64), ColorRGBA(64, 64, 64, 64), 1));
  684. rectangles.push_back(std::make_shared<TransparentFilledRectangle>(r, ColorRGBA(0, 0, 0, 128), ColorRGBA(64, 64, 64, 64), 1));
  685. titles.push_back(std::make_shared<CLabel>(50 + i * 100, 100, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->translate("vcmi.lobby.customRmgSize." + std::to_string(i))));
  686. numInputs.push_back(std::make_shared<CTextInput>(r, EFonts::FONT_SMALL, ETextAlignment::CENTER, false));
  687. numInputs.back()->setFilterNumber(0, i < 2 ? 999 : 9);
  688. numInputs.back()->setCallback(checkTemplateSize);
  689. }
  690. numInputs[0]->setText(std::to_string(initSize.x));
  691. numInputs[1]->setText(std::to_string(initSize.y));
  692. numInputs[2]->setText(std::to_string(initSize.z));
  693. }