BattleOnlyMode.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. /*
  2. * BattleOnlyMode.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 "BattleOnlyMode.h"
  12. #include "../CServerHandler.h"
  13. #include "../GameEngine.h"
  14. #include "../GameInstance.h"
  15. #include "../render/IRenderHandler.h"
  16. #include "../render/CAnimation.h"
  17. #include "../render/Canvas.h"
  18. #include "../render/CanvasImage.h"
  19. #include "../gui/Shortcut.h"
  20. #include "../gui/WindowHandler.h"
  21. #include "../widgets/Buttons.h"
  22. #include "../widgets/GraphicalPrimitiveCanvas.h"
  23. #include "../widgets/TextControls.h"
  24. #include "../widgets/CTextInput.h"
  25. #include "../widgets/Images.h"
  26. #include "../windows/GUIClasses.h"
  27. #include "../windows/CHeroOverview.h"
  28. #include "../windows/CCreatureWindow.h"
  29. #include "../../lib/GameLibrary.h"
  30. #include "../../lib/gameState/CGameState.h"
  31. #include "../../lib/networkPacks/PacksForLobby.h"
  32. #include "../../lib/StartInfo.h"
  33. #include "../../lib/VCMIDirs.h"
  34. #include "../../lib/CRandomGenerator.h"
  35. #include "../../lib/callback/EditorCallback.h"
  36. #include "../../lib/entities/hero/CHero.h"
  37. #include "../../lib/entities/hero/CHeroClass.h"
  38. #include "../../lib/entities/hero/CHeroHandler.h"
  39. #include "../../lib/entities/faction/CTown.h"
  40. #include "../../lib/entities/faction/CTownHandler.h"
  41. #include "../../lib/mapObjects/CGHeroInstance.h"
  42. #include "../../lib/mapObjects/CGTownInstance.h"
  43. #include "../../lib/mapObjectConstructors/AObjectTypeHandler.h"
  44. #include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
  45. #include "../../lib/mapping/CMap.h"
  46. #include "../../lib/mapping/CMapInfo.h"
  47. #include "../../lib/mapping/CMapEditManager.h"
  48. #include "../../lib/mapping/CMapService.h"
  49. #include "../../lib/mapping/MapFormat.h"
  50. #include "../../lib/texts/CGeneralTextHandler.h"
  51. #include "../../lib/texts/MetaString.h"
  52. #include "../../lib/texts/TextOperations.h"
  53. #include "../../lib/filesystem/Filesystem.h"
  54. void BattleOnlyMode::openBattleWindow()
  55. {
  56. GAME->server().sendGuiAction(LobbyGuiAction::BATTLE_MODE);
  57. ENGINE->windows().createAndPushWindow<BattleOnlyModeWindow>();
  58. }
  59. BattleOnlyModeStartInfo::BattleOnlyModeStartInfo()
  60. : selectedTerrain(TerrainId::DIRT)
  61. , selectedTown(FactionID::NONE)
  62. {
  63. for(auto & element : selectedArmy)
  64. element = std::make_shared<CCreatureSet>();
  65. for(auto & element : primSkillLevel)
  66. for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
  67. element[i] = 0;
  68. }
  69. BattleOnlyModeWindow::BattleOnlyModeWindow()
  70. : CWindowObject(BORDERED)
  71. , startInfo(std::make_shared<BattleOnlyModeStartInfo>())
  72. {
  73. OBJECT_CONSTRUCTION;
  74. pos.w = 519;
  75. pos.h = 238;
  76. updateShadow();
  77. center();
  78. init();
  79. backgroundTexture = std::make_shared<FilledTexturePlayerColored>(Rect(0, 0, pos.w, pos.h));
  80. backgroundTexture->setPlayerColor(PlayerColor(1));
  81. buttonOk = std::make_shared<CButton>(Point(191, 203), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ startBattle(); }, EShortcut::GLOBAL_ACCEPT);
  82. buttonAbort = std::make_shared<CButton>(Point(265, 203), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){
  83. GAME->server().sendGuiAction(LobbyGuiAction::NO_TAB);
  84. close();
  85. }, EShortcut::GLOBAL_CANCEL);
  86. title = std::make_shared<CLabel>(260, 20, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode"));
  87. battlefieldSelector = std::make_shared<CButton>(Point(29, 174), AnimationPath::builtin("GSPButtonClear"), CButton::tooltip(), [this](){
  88. std::vector<std::string> texts;
  89. std::vector<std::shared_ptr<IImage>> images;
  90. auto & terrains = LIBRARY->terrainTypeHandler->objects;
  91. for (const auto & terrain : terrains)
  92. {
  93. texts.push_back(terrain->getNameTranslated());
  94. const auto & patterns = LIBRARY->terviewh->getTerrainViewPatterns(terrain->getId());
  95. TerrainViewPattern pattern;
  96. for(auto & p : patterns)
  97. if(p[0].id == "n1")
  98. pattern = p[0];
  99. auto image = ENGINE->renderHandler().loadImage(terrain->tilesFilename, pattern.mapping[0].first, 0, EImageBlitMode::OPAQUE);
  100. image->scaleTo(Point(23, 23), EScalingAlgorithm::NEAREST);
  101. images.push_back(image);
  102. }
  103. auto factions = LIBRARY->townh->getDefaultAllowed();
  104. for (const auto & faction : factions)
  105. {
  106. texts.push_back(faction.toFaction()->getNameTranslated());
  107. auto image = ENGINE->renderHandler().loadImage(AnimationPath::builtin("ITPA"), faction.toFaction()->town->clientInfo.icons[true][false] + 2, 0, EImageBlitMode::OPAQUE);
  108. image->scaleTo(Point(35, 23), EScalingAlgorithm::NEAREST);
  109. images.push_back(image);
  110. }
  111. ENGINE->windows().createAndPushWindow<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeBattlefield"), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeBattlefieldSelect"), [this, terrains, factions](int index){
  112. if(terrains.size() > index)
  113. {
  114. startInfo->selectedTerrain = terrains[index]->getId();
  115. startInfo->selectedTown = FactionID::NONE;
  116. }
  117. else
  118. {
  119. startInfo->selectedTerrain = TerrainId::NONE;
  120. auto it = std::next(factions.begin(), index - terrains.size());
  121. if (it != factions.end())
  122. startInfo->selectedTown = *it;
  123. }
  124. setTerrainButtonText();
  125. setOkButtonEnabled();
  126. }, (startInfo->selectedTerrain != TerrainId::NONE ? static_cast<int>(startInfo->selectedTerrain) : static_cast<int>(startInfo->selectedTown + terrains.size())), images, true, true);
  127. });
  128. battlefieldSelector->block(GAME->server().isGuest());
  129. buttonReset = std::make_shared<CButton>(Point(289, 174), AnimationPath::builtin("GSPButtonClear"), CButton::tooltip(), [this](){
  130. if(GAME->server().isHost())
  131. {
  132. startInfo->selectedTerrain = TerrainId::DIRT;
  133. startInfo->selectedTown = FactionID::NONE;
  134. }
  135. setTerrainButtonText();
  136. setOkButtonEnabled();
  137. if(GAME->server().isHost())
  138. {
  139. startInfo->selectedHero[0].reset();
  140. startInfo->selectedArmy[0]->clearSlots();
  141. for(size_t i=0; i<GameConstants::ARMY_SIZE; i++)
  142. heroSelector1->selectedArmyInput.at(i)->setText("1");
  143. heroSelector1->setHeroIcon();
  144. heroSelector1->setCreatureIcons();
  145. }
  146. startInfo->selectedHero[1].reset();
  147. startInfo->selectedArmy[1]->clearSlots();
  148. for(size_t i=0; i<GameConstants::ARMY_SIZE; i++)
  149. heroSelector2->selectedArmyInput.at(i)->setText("1");
  150. heroSelector2->setHeroIcon();
  151. heroSelector2->setCreatureIcons();
  152. redraw();
  153. });
  154. buttonReset->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeReset"), EFonts::FONT_SMALL, Colors::WHITE);
  155. setTerrainButtonText();
  156. setOkButtonEnabled();
  157. heroSelector1 = std::make_shared<BattleOnlyModeHeroSelector>(0, *this, Point(0, 40));
  158. heroSelector2 = std::make_shared<BattleOnlyModeHeroSelector>(1, *this, Point(260, 40));
  159. heroSelector1->setInputEnabled(GAME->server().isHost());
  160. }
  161. void BattleOnlyModeWindow::init()
  162. {
  163. map = std::make_unique<CMap>(nullptr);
  164. map->version = EMapFormat::VCMI;
  165. map->creationDateTime = std::time(nullptr);
  166. map->width = 10;
  167. map->height = 10;
  168. map->mapLevels = 1;
  169. map->battleOnly = true;
  170. map->name = MetaString::createFromTextID("vcmi.lobby.battleOnlyMode");
  171. cb = std::make_unique<EditorCallback>(map.get());
  172. }
  173. void BattleOnlyModeWindow::setTerrainButtonText()
  174. {
  175. battlefieldSelector->setTextOverlay(LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeBattlefield") + ": " + (startInfo->selectedTerrain != TerrainId::NONE ? startInfo->selectedTerrain.toEntity(LIBRARY)->getNameTranslated() : startInfo->selectedTown.toEntity(LIBRARY)->getNameTranslated()), EFonts::FONT_SMALL, Colors::WHITE);
  176. }
  177. void BattleOnlyModeWindow::setOkButtonEnabled()
  178. {
  179. bool canStart = (startInfo->selectedTerrain != TerrainId::NONE || startInfo->selectedTown != FactionID::NONE);
  180. canStart &= (startInfo->selectedHero[0] && ((startInfo->selectedHero[1]) || (startInfo->selectedTown != FactionID::NONE && startInfo->selectedArmy[1]->stacksCount())));
  181. buttonOk->block(!canStart || GAME->server().isGuest());
  182. buttonAbort->block(GAME->server().isGuest());
  183. }
  184. std::shared_ptr<IImage> drawBlackBox(Point size, std::string text)
  185. {
  186. auto image = ENGINE->renderHandler().createImage(size, CanvasScalingPolicy::AUTO);
  187. Canvas canvas = image->getCanvas();
  188. canvas.drawColor(Rect(0, 0, size.x, size.y), Colors::BLACK);
  189. canvas.drawText(Point(size.x / 2, size.y / 2), FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, text);
  190. return image;
  191. }
  192. BattleOnlyModeHeroSelector::BattleOnlyModeHeroSelector(int id, BattleOnlyModeWindow& p, Point position)
  193. : parent(p)
  194. , id(id)
  195. {
  196. OBJECT_CONSTRUCTION;
  197. pos.x += position.x;
  198. pos.y += position.y;
  199. backgroundImage = std::make_shared<CPicture>(ImagePath::builtin("heroSlotsBlue"), Point(3, 4));
  200. for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
  201. {
  202. auto image = std::make_shared<CAnimImage>(AnimationPath::builtin("PSKIL32"), i, 0, 78 + i * 36, 26);
  203. primSkills.push_back(image);
  204. primSkillsBorder.push_back(std::make_shared<GraphicalPrimitiveCanvas>(Rect(78 + i * 36, 26, 32, 32)));
  205. primSkillsBorder.back()->addRectangle(Point(0, 0), Point(32, 32), ColorRGBA(44, 108, 255));
  206. primSkillsInput.push_back(std::make_shared<CTextInput>(Rect(78 + i * 36, 58, 32, 16), EFonts::FONT_SMALL, ETextAlignment::CENTER, false));
  207. primSkillsInput.back()->setFilterNumber(0, 100);
  208. primSkillsInput.back()->setText("0");
  209. primSkillsInput.back()->setCallback([this, i, id](const std::string & text){
  210. parent.startInfo->primSkillLevel[id][i] = std::stoi(primSkillsInput[i]->getText());
  211. });
  212. }
  213. creatureImage.resize(GameConstants::ARMY_SIZE);
  214. for(size_t i=0; i<GameConstants::ARMY_SIZE; i++)
  215. {
  216. selectedArmyInput.push_back(std::make_shared<CTextInput>(Rect(5 + i * 36, 113, 32, 16), EFonts::FONT_SMALL, ETextAlignment::CENTER, false));
  217. selectedArmyInput.back()->setFilterNumber(1, 10000000, 3);
  218. selectedArmyInput.back()->setText("1");
  219. selectedArmyInput.back()->setCallback([this, i, id](const std::string & text){
  220. if(!parent.startInfo->selectedArmy[id]->slotEmpty(SlotID(i)))
  221. parent.startInfo->selectedArmy[id]->setCreature(SlotID(i), parent.startInfo->selectedArmy[id]->getCreature(SlotID(i))->getId(), TextOperations::parseMetric<int>(text));
  222. });
  223. }
  224. setHeroIcon();
  225. setCreatureIcons();
  226. }
  227. void BattleOnlyModeHeroSelector::setHeroIcon()
  228. {
  229. OBJECT_CONSTRUCTION;
  230. if(!parent.startInfo->selectedHero[id])
  231. {
  232. heroImage = std::make_shared<CPicture>(drawBlackBox(Point(58, 64), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeSelect")), Point(6, 7));
  233. heroLabel = std::make_shared<CLabel>(160, 16, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->translate("core.genrltxt.507"));
  234. for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
  235. primSkillsInput[i]->setText(std::to_string(parent.startInfo->primSkillLevel[id][i]));
  236. }
  237. else
  238. {
  239. heroImage = std::make_shared<CPicture>(ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("PortraitsLarge"), EImageBlitMode::COLORKEY)->getImage(parent.startInfo->selectedHero[id]->getHeroType()->imageIndex), Point(6, 7));
  240. heroLabel = std::make_shared<CLabel>(160, 16, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, parent.startInfo->selectedHero[id]->getNameTranslated());
  241. for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
  242. primSkillsInput[i]->setText("0");
  243. }
  244. heroImage->addLClickCallback([this](){
  245. auto allowedSet = LIBRARY->heroh->getDefaultAllowed();
  246. std::vector<HeroTypeID> heroes(allowedSet.begin(), allowedSet.end());
  247. std::sort(heroes.begin(), heroes.end(), [](auto a, auto b) {
  248. auto heroA = a.toHeroType();
  249. auto heroB = b.toHeroType();
  250. if (heroA->heroClass->getId() == heroB->heroClass->getId())
  251. return heroA->getNameTranslated() < heroB->getNameTranslated();
  252. if (heroA->heroClass->faction == heroB->heroClass->faction)
  253. return heroA->heroClass->getId() < heroB->heroClass->getId();
  254. return heroA->heroClass->faction < heroB->heroClass->faction;
  255. });
  256. int selectedIndex = !parent.startInfo->selectedHero[id] ? 0 : (1 + std::distance(heroes.begin(), std::find_if(heroes.begin(), heroes.end(), [this](auto heroID) {
  257. return heroID == parent.startInfo->selectedHero[id]->getHeroType()->getId();
  258. })));
  259. std::vector<std::string> texts;
  260. std::vector<std::shared_ptr<IImage>> images;
  261. // Add "no hero" option
  262. texts.push_back(LIBRARY->generaltexth->translate("core.genrltxt.507"));
  263. images.push_back(nullptr);
  264. for (const auto & h : heroes)
  265. {
  266. texts.push_back(h.toHeroType()->getNameTranslated());
  267. auto image = ENGINE->renderHandler().loadImage(AnimationPath::builtin("PortraitsSmall"), h.toHeroType()->imageIndex, 0, EImageBlitMode::OPAQUE);
  268. image->scaleTo(Point(35, 23), EScalingAlgorithm::NEAREST);
  269. images.push_back(image);
  270. }
  271. auto window = std::make_shared<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("object.core.hero.name"), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeHeroSelect"), [this, heroes](int index){
  272. if(index == 0)
  273. {
  274. parent.startInfo->selectedHero[id].reset();
  275. setHeroIcon();
  276. return;
  277. }
  278. index--;
  279. auto hero = heroes[index];
  280. auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::HERO, hero.toHeroType()->heroClass->getId());
  281. auto templates = factory->getTemplates();
  282. auto obj = std::dynamic_pointer_cast<CGHeroInstance>(factory->create(parent.cb.get(), templates.front()));
  283. obj->setHeroType(hero);
  284. parent.startInfo->selectedHero[id] = obj;
  285. for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
  286. parent.startInfo->primSkillLevel[id][i] = 0;
  287. setHeroIcon();
  288. parent.setOkButtonEnabled();
  289. }, selectedIndex, images, true, true);
  290. window->onPopup = [heroes](int index) {
  291. if(index == 0)
  292. return;
  293. index--;
  294. ENGINE->windows().createAndPushWindow<CHeroOverview>(heroes.at(index));
  295. };
  296. ENGINE->windows().pushWindow(window);
  297. });
  298. heroImage->addRClickCallback([this](){
  299. if(!parent.startInfo->selectedHero[id])
  300. return;
  301. ENGINE->windows().createAndPushWindow<CHeroOverview>(parent.startInfo->selectedHero[id]->getHeroType()->getId());
  302. });
  303. }
  304. void BattleOnlyModeHeroSelector::setCreatureIcons()
  305. {
  306. OBJECT_CONSTRUCTION;
  307. for(int i = 0; i < creatureImage.size(); i++)
  308. {
  309. if(parent.startInfo->selectedArmy[id]->slotEmpty(SlotID(i)))
  310. creatureImage[i] = std::make_shared<CPicture>(drawBlackBox(Point(32, 32), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeSelect")), Point(6 + i * 36, 78));
  311. else
  312. {
  313. auto creatureID = parent.startInfo->selectedArmy[id]->Slots().at(SlotID(i))->getCreatureID();
  314. creatureImage[i] = std::make_shared<CPicture>(ENGINE->renderHandler().loadAnimation(AnimationPath::builtin("CPRSMALL"), EImageBlitMode::COLORKEY)->getImage(LIBRARY->creh->objects.at(creatureID)->getIconIndex()), Point(6 + i * 36, 78));
  315. }
  316. creatureImage[i]->addLClickCallback([this, i](){
  317. auto allowedSet = LIBRARY->creh->getDefaultAllowed();
  318. std::vector<CreatureID> creatures(allowedSet.begin(), allowedSet.end());
  319. std::sort(creatures.begin(), creatures.end(), [](auto a, auto b) {
  320. auto creatureA = a.toCreature();
  321. auto creatureB = b.toCreature();
  322. if (creatureA->getLevel() == creatureB->getLevel())
  323. return creatureA->getNameSingularTranslated() < creatureB->getNameSingularTranslated();
  324. if (creatureA->getFactionID() == creatureB->getFactionID())
  325. return creatureA->getLevel() < creatureB->getLevel();
  326. return creatureA->getFactionID() < creatureB->getFactionID();
  327. });
  328. int selectedIndex = parent.startInfo->selectedArmy[id]->slotEmpty(SlotID(i)) ? 0 : (1 + std::distance(creatures.begin(), std::find_if(creatures.begin(), creatures.end(), [this, i](auto creatureID) {
  329. return creatureID == parent.startInfo->selectedArmy[id]->Slots().at(SlotID(i))->getId();
  330. })));
  331. std::vector<std::string> texts;
  332. std::vector<std::shared_ptr<IImage>> images;
  333. // Add "no creature" option
  334. texts.push_back(LIBRARY->generaltexth->translate("core.genrltxt.507"));
  335. images.push_back(nullptr);
  336. for (const auto & c : creatures)
  337. {
  338. texts.push_back(c.toCreature()->getNameSingularTranslated());
  339. auto image = ENGINE->renderHandler().loadImage(AnimationPath::builtin("CPRSMALL"), c.toCreature()->getIconIndex(), 0, EImageBlitMode::OPAQUE);
  340. image->scaleTo(Point(23, 23), EScalingAlgorithm::NEAREST);
  341. images.push_back(image);
  342. }
  343. auto window = std::make_shared<CObjectListWindow>(texts, nullptr, LIBRARY->generaltexth->translate("core.genrltxt.42"), LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyModeCreatureSelect"), [this, creatures, i](int index){
  344. if(index == 0)
  345. {
  346. if(!parent.startInfo->selectedArmy[id]->slotEmpty(SlotID(i)))
  347. parent.startInfo->selectedArmy[id]->eraseStack(SlotID(i));
  348. setCreatureIcons();
  349. return;
  350. }
  351. index--;
  352. auto creature = creatures.at(index).toCreature();
  353. parent.startInfo->selectedArmy[id]->setCreature(SlotID(i), creature->getId(), 100);
  354. selectedArmyInput[SlotID(i)]->setText("100");
  355. setCreatureIcons();
  356. }, selectedIndex, images, true, true);
  357. window->onPopup = [creatures](int index) {
  358. if(index == 0)
  359. return;
  360. index--;
  361. ENGINE->windows().createAndPushWindow<CStackWindow>(creatures.at(index).toCreature(), true);
  362. };
  363. ENGINE->windows().pushWindow(window);
  364. });
  365. creatureImage[i]->addRClickCallback([this, i](){
  366. if(parent.startInfo->selectedArmy[id]->slotEmpty(SlotID(i)))
  367. return;
  368. ENGINE->windows().createAndPushWindow<CStackWindow>(LIBRARY->creh->objects.at(parent.startInfo->selectedArmy[id]->Slots().at(SlotID(i))->getCreatureID()).get(), true);
  369. });
  370. }
  371. }
  372. void BattleOnlyModeWindow::startBattle()
  373. {
  374. auto rng = &CRandomGenerator::getDefault();
  375. map->initTerrain();
  376. map->getEditManager()->clearTerrain(rng);
  377. map->getEditManager()->getTerrainSelection().selectAll();
  378. map->getEditManager()->drawTerrain(startInfo->selectedTerrain == TerrainId::NONE ? TerrainId::DIRT : startInfo->selectedTerrain, 0, rng);
  379. map->players[0].canComputerPlay = true;
  380. map->players[0].canHumanPlay = true;
  381. map->players[1] = map->players[0];
  382. auto knownHeroes = LIBRARY->objtypeh->knownSubObjects(Obj::HERO);
  383. auto addHero = [&](int sel, PlayerColor color, const int3 & position)
  384. {
  385. startInfo->selectedHero[sel]->setOwner(color);
  386. startInfo->selectedHero[sel]->pos = position;
  387. for(size_t i=0; i<GameConstants::PRIMARY_SKILLS; i++)
  388. startInfo->selectedHero[sel]->pushPrimSkill(PrimarySkill(i), startInfo->primSkillLevel[sel][i]);
  389. startInfo->selectedHero[sel]->clearSlots();
  390. for(int slot = 0; slot < GameConstants::ARMY_SIZE; slot++)
  391. if(!startInfo->selectedArmy[sel]->slotEmpty(SlotID(slot)))
  392. startInfo->selectedHero[sel]->putStack(SlotID(slot), startInfo->selectedArmy[sel]->detachStack(SlotID(slot)));
  393. map->getEditManager()->insertObject(startInfo->selectedHero[sel]);
  394. };
  395. addHero(0, PlayerColor(0), int3(5, 6, 0));
  396. if(startInfo->selectedTown == FactionID::NONE)
  397. addHero(1, PlayerColor(1), int3(5, 5, 0));
  398. else
  399. {
  400. auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::TOWN, startInfo->selectedTown);
  401. auto templates = factory->getTemplates();
  402. auto obj = factory->create(cb.get(), templates.front());
  403. auto townObj = std::dynamic_pointer_cast<CGTownInstance>(obj);
  404. obj->setOwner(PlayerColor(1));
  405. obj->pos = int3(5, 5, 0);
  406. for (const auto & building : townObj->getTown()->getAllBuildings())
  407. townObj->addBuilding(building);
  408. if(!startInfo->selectedHero[1])
  409. {
  410. for(int slot = 0; slot < GameConstants::ARMY_SIZE; slot++)
  411. if(!startInfo->selectedArmy[1]->slotEmpty(SlotID(slot)))
  412. townObj->getArmy()->putStack(SlotID(slot), startInfo->selectedArmy[1]->detachStack(SlotID(slot)));
  413. }
  414. else
  415. addHero(1, PlayerColor(1), int3(5, 5, 0));
  416. map->getEditManager()->insertObject(townObj);
  417. }
  418. auto path = VCMIDirs::get().userDataPath() / "Maps";
  419. boost::filesystem::create_directories(path);
  420. const std::string fileName = "BattleOnlyMode.vmap";
  421. const auto fullPath = path / fileName;
  422. CMapService mapService;
  423. mapService.saveMap(map, fullPath);
  424. CResourceHandler::get()->updateFilteredFiles([&](const std::string & mount) { return true; });
  425. auto mapInfo = std::make_shared<CMapInfo>();
  426. mapInfo->mapInit("Maps/BattleOnlyMode");
  427. GAME->server().setMapInfo(mapInfo);
  428. ExtraOptionsInfo extraOptions;
  429. extraOptions.unlimitedReplay = true;
  430. GAME->server().setExtraOptionsInfo(extraOptions);
  431. GAME->server().sendStartGame();
  432. }