BattleOnlyMode.cpp 20 KB

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