CGameStateCampaign.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. /*
  2. * CGameStateCampaign.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 "CGameStateCampaign.h"
  12. #include "CGameState.h"
  13. #include "QuestInfo.h"
  14. #include "../mapping/CMapEditManager.h"
  15. #include "../mapping/CCampaignHandler.h"
  16. #include "../mapObjects/CGHeroInstance.h"
  17. #include "../registerTypes/RegisterTypes.h"
  18. #include "../mapObjectConstructors/AObjectTypeHandler.h"
  19. #include "../mapObjectConstructors/CObjectClassesHandler.h"
  20. #include "../StartInfo.h"
  21. #include "../CBuildingHandler.h"
  22. #include "../CHeroHandler.h"
  23. #include "../mapping/CMap.h"
  24. #include "../ArtifactUtils.h"
  25. #include "../CPlayerState.h"
  26. #include "../serializer/CMemorySerializer.h"
  27. VCMI_LIB_NAMESPACE_BEGIN
  28. void CrossoverHeroesList::addHeroToBothLists(CGHeroInstance * hero)
  29. {
  30. heroesFromPreviousScenario.push_back(hero);
  31. heroesFromAnyPreviousScenarios.push_back(hero);
  32. }
  33. void CrossoverHeroesList::removeHeroFromBothLists(CGHeroInstance * hero)
  34. {
  35. heroesFromPreviousScenario -= hero;
  36. heroesFromAnyPreviousScenarios -= hero;
  37. }
  38. CampaignHeroReplacement::CampaignHeroReplacement(CGHeroInstance * hero, const ObjectInstanceID & heroPlaceholderId):
  39. hero(hero),
  40. heroPlaceholderId(heroPlaceholderId)
  41. {
  42. }
  43. CGameStateCampaign::CGameStateCampaign(CGameState * owner):
  44. gameState(owner)
  45. {
  46. assert(gameState->scenarioOps->mode == StartInfo::CAMPAIGN);
  47. assert(gameState->scenarioOps->campState != nullptr);
  48. }
  49. CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios() const
  50. {
  51. CrossoverHeroesList crossoverHeroes;
  52. auto campaignState = gameState->scenarioOps->campState;
  53. auto bonus = campaignState->getBonusForCurrentMap();
  54. if(bonus && bonus->type == CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO)
  55. {
  56. auto scenarioID = static_cast<CampaignScenarioID>(bonus->info2);
  57. std::vector<CGHeroInstance *> heroes;
  58. for(auto & node : campaignState->scenarios.at(scenarioID).crossoverHeroes)
  59. {
  60. auto * h = CampaignState::crossoverDeserialize(node);
  61. heroes.push_back(h);
  62. }
  63. crossoverHeroes.heroesFromAnyPreviousScenarios = heroes;
  64. crossoverHeroes.heroesFromPreviousScenario = heroes;
  65. return crossoverHeroes;
  66. }
  67. if(campaignState->mapsConquered.empty())
  68. return crossoverHeroes;
  69. for(auto mapNr : campaignState->mapsConquered)
  70. {
  71. // create a list of deleted heroes
  72. auto & scenario = campaignState->scenarios[mapNr];
  73. auto lostCrossoverHeroes = scenario.getLostCrossoverHeroes();
  74. // remove heroes which didn't reached the end of the scenario, but were available at the start
  75. for(auto * hero : lostCrossoverHeroes)
  76. {
  77. // auto hero = CCampaignState::crossoverDeserialize(node);
  78. vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
  79. {
  80. return hero->subID == h->subID;
  81. });
  82. }
  83. // now add heroes which completed the scenario
  84. for(auto node : scenario.crossoverHeroes)
  85. {
  86. auto * hero = CampaignState::crossoverDeserialize(node);
  87. // add new heroes and replace old heroes with newer ones
  88. auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [hero](CGHeroInstance * h)
  89. {
  90. return hero->subID == h->subID;
  91. });
  92. if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
  93. {
  94. // replace old hero with newer one
  95. crossoverHeroes.heroesFromAnyPreviousScenarios[it - crossoverHeroes.heroesFromAnyPreviousScenarios.begin()] = hero;
  96. }
  97. else
  98. {
  99. // add new hero
  100. crossoverHeroes.heroesFromAnyPreviousScenarios.push_back(hero);
  101. }
  102. if(mapNr == campaignState->mapsConquered.back())
  103. {
  104. crossoverHeroes.heroesFromPreviousScenario.push_back(hero);
  105. }
  106. }
  107. }
  108. return crossoverHeroes;
  109. }
  110. void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CampaignTravel & travelOptions)
  111. {
  112. // create heroes list for convenience iterating
  113. std::vector<CGHeroInstance *> crossoverHeroes;
  114. crossoverHeroes.reserve(campaignHeroReplacements.size());
  115. for(auto & campaignHeroReplacement : campaignHeroReplacements)
  116. {
  117. crossoverHeroes.push_back(campaignHeroReplacement.hero);
  118. }
  119. // TODO this logic (what should be kept) should be part of CScenarioTravel and be exposed via some clean set of methods
  120. if(!travelOptions.whatHeroKeeps.experience)
  121. {
  122. //trimming experience
  123. for(CGHeroInstance * cgh : crossoverHeroes)
  124. {
  125. cgh->initExp(gameState->getRandomGenerator());
  126. }
  127. }
  128. if(!travelOptions.whatHeroKeeps.primarySkills)
  129. {
  130. //trimming prim skills
  131. for(CGHeroInstance * cgh : crossoverHeroes)
  132. {
  133. for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
  134. {
  135. auto sel = Selector::type()(BonusType::PRIMARY_SKILL)
  136. .And(Selector::subtype()(g))
  137. .And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
  138. cgh->getBonusLocalFirst(sel)->val = cgh->type->heroClass->primarySkillInitial[g];
  139. }
  140. }
  141. }
  142. if(!travelOptions.whatHeroKeeps.secondarySkills)
  143. {
  144. //trimming sec skills
  145. for(CGHeroInstance * cgh : crossoverHeroes)
  146. {
  147. cgh->secSkills = cgh->type->secSkillsInit;
  148. cgh->recreateSecondarySkillsBonuses();
  149. }
  150. }
  151. if(!travelOptions.whatHeroKeeps.spells)
  152. {
  153. for(CGHeroInstance * cgh : crossoverHeroes)
  154. {
  155. cgh->removeSpellbook();
  156. }
  157. }
  158. if(!travelOptions.whatHeroKeeps.artifacts)
  159. {
  160. //trimming artifacts
  161. for(CGHeroInstance * hero : crossoverHeroes)
  162. {
  163. size_t totalArts = GameConstants::BACKPACK_START + hero->artifactsInBackpack.size();
  164. for (size_t i = 0; i < totalArts; i++ )
  165. {
  166. auto artifactPosition = ArtifactPosition((si32)i);
  167. if(artifactPosition == ArtifactPosition::SPELLBOOK) continue; // do not handle spellbook this way
  168. const ArtSlotInfo *info = hero->getSlot(artifactPosition);
  169. if(!info)
  170. continue;
  171. // TODO: why would there be nullptr artifacts?
  172. const CArtifactInstance *art = info->artifact;
  173. if(!art)
  174. continue;
  175. bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
  176. ArtifactLocation al(hero, artifactPosition);
  177. if(!takeable && !al.getSlot()->locked) //don't try removing locked artifacts -> it crashes #1719
  178. al.removeArtifact();
  179. }
  180. }
  181. }
  182. //trimming creatures
  183. for(CGHeroInstance * cgh : crossoverHeroes)
  184. {
  185. auto shouldSlotBeErased = [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
  186. {
  187. CreatureID::ECreatureID crid = j.second->getCreatureID().toEnum();
  188. return !travelOptions.monstersKeptByHero.count(crid);
  189. };
  190. auto stacksCopy = cgh->stacks; //copy of the map, so we can iterate iover it and remove stacks
  191. for(auto &slotPair : stacksCopy)
  192. if(shouldSlotBeErased(slotPair))
  193. cgh->eraseStack(slotPair.first);
  194. }
  195. // Removing short-term bonuses
  196. for(CGHeroInstance * cgh : crossoverHeroes)
  197. {
  198. cgh->removeBonusesRecursive(CSelector(Bonus::OneDay)
  199. .Or(CSelector(Bonus::OneWeek))
  200. .Or(CSelector(Bonus::NTurns))
  201. .Or(CSelector(Bonus::NDays))
  202. .Or(CSelector(Bonus::OneBattle)));
  203. }
  204. }
  205. void CGameStateCampaign::placeCampaignHeroes()
  206. {
  207. // WARNING: CURRENT CODE IS LIKELY INCORRECT AND LEADS TO MULTIPLE ISSUES WITH HERO TRANSFER
  208. // Approximate behavior according to testing H3 game logic.
  209. // 1) definitions:
  210. // - 'reserved heroes' are heroes that have fixed placeholder in unfinished maps. See CMapHeader::reservedCampaignHeroes
  211. // - 'campaign pool' are serialized heroes and is unique to a campaign
  212. // - 'scenario pool' are serialized heroes and is unique to a scenario
  213. //
  214. // 2) scenario end logic:
  215. // - at end of scenario, all 'reserved heroes' of a player go to 'campaign pool'
  216. // - at end of scenario, rest of player's heroes go to 'scenario pool'
  217. //
  218. // 3) scenario start logic
  219. // - at scenario start, all heroes that are placed on map but already exist in 'campaign pool' and are still 'reserved heroes' are replaced with other, randomly selected heroes (and probably marked as unavailable in map)
  220. // - at scenario start, all fixed placeholders are replaced with heroes from 'campaign pool', if exist
  221. // - at scenario start, all power placeholders owned by player are replaced by heroes from 'scenario pool' of last complete map, if exist
  222. // - exception: if starting bonus is 'select player' then power placeholders are taken from 'scenario pool' of linked map
  223. // place bonus hero
  224. auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
  225. bool campaignGiveHero = campaignBonus && campaignBonus->type == CampaignBonusType::HERO;
  226. if(campaignGiveHero)
  227. {
  228. auto playerColor = PlayerColor(campaignBonus->info1);
  229. auto it = gameState->scenarioOps->playerInfos.find(playerColor);
  230. if(it != gameState->scenarioOps->playerInfos.end())
  231. {
  232. auto heroTypeId = campaignBonus->info2;
  233. if(heroTypeId == 0xffff) // random bonus hero
  234. {
  235. heroTypeId = gameState->pickUnusedHeroTypeRandomly(playerColor);
  236. }
  237. gameState->placeStartingHero(playerColor, HeroTypeID(heroTypeId), gameState->map->players[playerColor.getNum()].posOfMainTown);
  238. }
  239. }
  240. // replace heroes placeholders
  241. auto crossoverHeroes = getCrossoverHeroesFromPreviousScenarios();
  242. if(!crossoverHeroes.heroesFromAnyPreviousScenarios.empty())
  243. {
  244. logGlobal->debug("\tGenerate list of hero placeholders");
  245. auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
  246. logGlobal->debug("\tPrepare crossover heroes");
  247. trimCrossoverHeroesParameters(campaignHeroReplacements, gameState->scenarioOps->campState->getCurrentScenario().travelOptions);
  248. // remove same heroes on the map which will be added through crossover heroes
  249. // INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes
  250. // with the same hero type id
  251. std::vector<CGHeroInstance *> removedHeroes;
  252. for(auto & campaignHeroReplacement : campaignHeroReplacements)
  253. {
  254. auto * hero = gameState->getUsedHero(HeroTypeID(campaignHeroReplacement.hero->subID));
  255. if(hero)
  256. {
  257. removedHeroes.push_back(hero);
  258. gameState->map->heroesOnMap -= hero;
  259. gameState->map->objects[hero->id.getNum()] = nullptr;
  260. gameState->map->removeBlockVisTiles(hero, true);
  261. }
  262. }
  263. logGlobal->debug("\tReplace placeholders with heroes");
  264. replaceHeroesPlaceholders(campaignHeroReplacements);
  265. // now add removed heroes again with unused type ID
  266. for(auto * hero : removedHeroes)
  267. {
  268. si32 heroTypeId = 0;
  269. if(hero->ID == Obj::HERO)
  270. {
  271. heroTypeId = gameState->pickUnusedHeroTypeRandomly(hero->tempOwner);
  272. }
  273. else if(hero->ID == Obj::PRISON)
  274. {
  275. auto unusedHeroTypeIds = gameState->getUnusedAllowedHeroes();
  276. if(!unusedHeroTypeIds.empty())
  277. {
  278. heroTypeId = (*RandomGeneratorUtil::nextItem(unusedHeroTypeIds, gameState->getRandomGenerator())).getNum();
  279. }
  280. else
  281. {
  282. logGlobal->error("No free hero type ID found to replace prison.");
  283. assert(0);
  284. }
  285. }
  286. else
  287. {
  288. assert(0); // should not happen
  289. }
  290. hero->subID = heroTypeId;
  291. hero->portrait = hero->subID;
  292. gameState->map->getEditManager()->insertObject(hero);
  293. }
  294. }
  295. }
  296. void CGameStateCampaign::giveCampaignBonusToHero(CGHeroInstance * hero)
  297. {
  298. const std::optional<CampaignBonus> & curBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
  299. if(!curBonus)
  300. return;
  301. assert(curBonus->isBonusForHero());
  302. //apply bonus
  303. switch(curBonus->type)
  304. {
  305. case CampaignBonusType::SPELL:
  306. {
  307. hero->addSpellToSpellbook(SpellID(curBonus->info2));
  308. break;
  309. }
  310. case CampaignBonusType::MONSTER:
  311. {
  312. for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
  313. {
  314. if(hero->slotEmpty(SlotID(i)))
  315. {
  316. hero->addToSlot(SlotID(i), CreatureID(curBonus->info2), curBonus->info3);
  317. break;
  318. }
  319. }
  320. break;
  321. }
  322. case CampaignBonusType::ARTIFACT:
  323. {
  324. if(!gameState->giveHeroArtifact(hero, static_cast<ArtifactID>(curBonus->info2)))
  325. logGlobal->error("Cannot give starting artifact - no free slots!");
  326. break;
  327. }
  328. case CampaignBonusType::SPELL_SCROLL:
  329. {
  330. CArtifactInstance * scroll = ArtifactUtils::createScroll(SpellID(curBonus->info2));
  331. const auto slot = ArtifactUtils::getArtAnyPosition(hero, scroll->getTypeId());
  332. if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
  333. scroll->putAt(ArtifactLocation(hero, slot));
  334. else
  335. logGlobal->error("Cannot give starting scroll - no free slots!");
  336. break;
  337. }
  338. case CampaignBonusType::PRIMARY_SKILL:
  339. {
  340. const ui8 * ptr = reinterpret_cast<const ui8 *>(&curBonus->info2);
  341. for(int g = 0; g < GameConstants::PRIMARY_SKILLS; ++g)
  342. {
  343. int val = ptr[g];
  344. if(val == 0)
  345. {
  346. continue;
  347. }
  348. auto bb = std::make_shared<Bonus>(
  349. BonusDuration::PERMANENT, BonusType::PRIMARY_SKILL, BonusSource::CAMPAIGN_BONUS, val, static_cast<int>(*gameState->scenarioOps->campState->currentMap), g
  350. );
  351. hero->addNewBonus(bb);
  352. }
  353. break;
  354. }
  355. case CampaignBonusType::SECONDARY_SKILL:
  356. {
  357. hero->setSecSkillLevel(SecondarySkill(curBonus->info2), curBonus->info3, true);
  358. break;
  359. }
  360. }
  361. }
  362. void CGameStateCampaign::replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements)
  363. {
  364. for(const auto & campaignHeroReplacement : campaignHeroReplacements)
  365. {
  366. auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(gameState->getObjInstance(campaignHeroReplacement.heroPlaceholderId));
  367. CGHeroInstance *heroToPlace = campaignHeroReplacement.hero;
  368. heroToPlace->id = campaignHeroReplacement.heroPlaceholderId;
  369. heroToPlace->tempOwner = heroPlaceholder->tempOwner;
  370. heroToPlace->pos = heroPlaceholder->pos;
  371. heroToPlace->type = VLC->heroh->objects[heroToPlace->subID];
  372. heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO,
  373. heroToPlace->type->heroClass->getIndex())->getTemplates().front();
  374. for(auto &&i : heroToPlace->stacks)
  375. i.second->type = VLC->creh->objects[i.second->getCreatureID()];
  376. auto fixArtifact = [&](CArtifactInstance * art)
  377. {
  378. art->artType = VLC->arth->objects[art->artType->getId()];
  379. gameState->map->artInstances.emplace_back(art);
  380. art->id = ArtifactInstanceID((si32)gameState->map->artInstances.size() - 1);
  381. };
  382. for(auto &&i : heroToPlace->artifactsWorn)
  383. fixArtifact(i.second.artifact);
  384. for(auto &&i : heroToPlace->artifactsInBackpack)
  385. fixArtifact(i.artifact);
  386. gameState->map->removeBlockVisTiles(heroPlaceholder, true);
  387. gameState->map->objects[heroPlaceholder->id.getNum()] = nullptr;
  388. gameState->map->instanceNames.erase(heroPlaceholder->instanceName);
  389. gameState->map->heroesOnMap.emplace_back(heroToPlace);
  390. gameState->map->objects[heroToPlace->id.getNum()] = heroToPlace;
  391. gameState->map->addBlockVisTiles(heroToPlace);
  392. gameState->map->instanceNames[heroToPlace->instanceName] = heroToPlace;
  393. delete heroPlaceholder;
  394. gameState->scenarioOps->campState->getCurrentScenario().placedCrossoverHeroes.push_back(CampaignState::crossoverSerialize(heroToPlace));
  395. }
  396. }
  397. std::vector<CampaignHeroReplacement> CGameStateCampaign::generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes)
  398. {
  399. std::vector<CampaignHeroReplacement> campaignHeroReplacements;
  400. //selecting heroes by type
  401. for(auto obj : gameState->map->objects)
  402. {
  403. if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
  404. {
  405. auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
  406. if(heroPlaceholder->subID != 0xFF) //select by type
  407. {
  408. auto it = range::find_if(crossoverHeroes.heroesFromAnyPreviousScenarios, [heroPlaceholder](CGHeroInstance * hero)
  409. {
  410. return hero->subID == heroPlaceholder->subID;
  411. });
  412. if(it != crossoverHeroes.heroesFromAnyPreviousScenarios.end())
  413. {
  414. auto * hero = *it;
  415. crossoverHeroes.removeHeroFromBothLists(hero);
  416. campaignHeroReplacements.emplace_back(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id);
  417. }
  418. }
  419. }
  420. }
  421. //selecting heroes by power
  422. range::sort(crossoverHeroes.heroesFromPreviousScenario, [](const CGHeroInstance * a, const CGHeroInstance * b)
  423. {
  424. return a->getHeroStrength() > b->getHeroStrength();
  425. }); //sort, descending strength
  426. // sort hero placeholders descending power
  427. std::vector<CGHeroPlaceholder *> heroPlaceholders;
  428. for(auto obj : gameState->map->objects)
  429. {
  430. if(obj && obj->ID == Obj::HERO_PLACEHOLDER)
  431. {
  432. auto * heroPlaceholder = dynamic_cast<CGHeroPlaceholder *>(obj.get());
  433. if(heroPlaceholder->subID == 0xFF) //select by power
  434. {
  435. heroPlaceholders.push_back(heroPlaceholder);
  436. }
  437. }
  438. }
  439. range::sort(heroPlaceholders, [](const CGHeroPlaceholder * a, const CGHeroPlaceholder * b)
  440. {
  441. return a->power > b->power;
  442. });
  443. for(int i = 0; i < heroPlaceholders.size(); ++i)
  444. {
  445. auto * heroPlaceholder = heroPlaceholders[i];
  446. if(crossoverHeroes.heroesFromPreviousScenario.size() > i)
  447. {
  448. auto * hero = crossoverHeroes.heroesFromPreviousScenario[i];
  449. campaignHeroReplacements.emplace_back(CMemorySerializer::deepCopy(*hero).release(), heroPlaceholder->id);
  450. }
  451. }
  452. return campaignHeroReplacements;
  453. }
  454. void CGameStateCampaign::initHeroes()
  455. {
  456. auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
  457. if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != 0xFFFE) //exclude generated heroes
  458. {
  459. //find human player
  460. PlayerColor humanPlayer=PlayerColor::NEUTRAL;
  461. for (auto & elem : gameState->players)
  462. {
  463. if(elem.second.human)
  464. {
  465. humanPlayer = elem.first;
  466. break;
  467. }
  468. }
  469. assert(humanPlayer != PlayerColor::NEUTRAL);
  470. std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = gameState->players[humanPlayer].heroes;
  471. if (chosenBonus->info1 == 0xFFFD) //most powerful
  472. {
  473. int maxB = -1;
  474. for (int b=0; b<heroes.size(); ++b)
  475. {
  476. if (maxB == -1 || heroes[b]->getTotalStrength() > heroes[maxB]->getTotalStrength())
  477. {
  478. maxB = b;
  479. }
  480. }
  481. if(maxB < 0)
  482. logGlobal->warn("Cannot give bonus to hero cause there are no heroes!");
  483. else
  484. giveCampaignBonusToHero(heroes[maxB]);
  485. }
  486. else //specific hero
  487. {
  488. for (auto & heroe : heroes)
  489. {
  490. if (heroe->subID == chosenBonus->info1)
  491. {
  492. giveCampaignBonusToHero(heroe);
  493. break;
  494. }
  495. }
  496. }
  497. }
  498. }
  499. void CGameStateCampaign::initStartingResources()
  500. {
  501. auto getHumanPlayerInfo = [&]() -> std::vector<const PlayerSettings *>
  502. {
  503. std::vector<const PlayerSettings *> ret;
  504. for(const auto & playerInfo : gameState->scenarioOps->playerInfos)
  505. {
  506. if(playerInfo.second.isControlledByHuman())
  507. ret.push_back(&playerInfo.second);
  508. }
  509. return ret;
  510. };
  511. auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
  512. if(chosenBonus && chosenBonus->type == CampaignBonusType::RESOURCE)
  513. {
  514. std::vector<const PlayerSettings *> people = getHumanPlayerInfo(); //players we will give resource bonus
  515. for(const PlayerSettings *ps : people)
  516. {
  517. std::vector<int> res; //resources we will give
  518. switch (chosenBonus->info1)
  519. {
  520. case 0: case 1: case 2: case 3: case 4: case 5: case 6:
  521. res.push_back(chosenBonus->info1);
  522. break;
  523. case 0xFD: //wood+ore
  524. res.push_back(GameResID(EGameResID::WOOD));
  525. res.push_back(GameResID(EGameResID::ORE));
  526. break;
  527. case 0xFE: //rare
  528. res.push_back(GameResID(EGameResID::MERCURY));
  529. res.push_back(GameResID(EGameResID::SULFUR));
  530. res.push_back(GameResID(EGameResID::CRYSTAL));
  531. res.push_back(GameResID(EGameResID::GEMS));
  532. break;
  533. default:
  534. assert(0);
  535. break;
  536. }
  537. //increasing resource quantity
  538. for (auto & re : res)
  539. {
  540. gameState->players[ps->color].resources[re] += chosenBonus->info2;
  541. }
  542. }
  543. }
  544. }
  545. void CGameStateCampaign::initTowns()
  546. {
  547. auto chosenBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
  548. if (chosenBonus && chosenBonus->type == CampaignBonusType::BUILDING)
  549. {
  550. for (int g=0; g<gameState->map->towns.size(); ++g)
  551. {
  552. PlayerState * owner = gameState->getPlayerState(gameState->map->towns[g]->getOwner());
  553. if (owner)
  554. {
  555. PlayerInfo & pi = gameState->map->players[owner->color.getNum()];
  556. if (owner->human && //human-owned
  557. gameState->map->towns[g]->pos == pi.posOfMainTown)
  558. {
  559. BuildingID buildingId;
  560. if(gameState->scenarioOps->campState->header.version == CampaignVersion::VCMI)
  561. buildingId = BuildingID(chosenBonus->info1);
  562. else
  563. buildingId = CBuildingHandler::campToERMU(chosenBonus->info1, gameState->map->towns[g]->subID, gameState->map->towns[g]->builtBuildings);
  564. gameState->map->towns[g]->builtBuildings.insert(buildingId);
  565. break;
  566. }
  567. }
  568. }
  569. }
  570. }
  571. bool CGameStateCampaign::playerHasStartingHero(PlayerColor playerColor) const
  572. {
  573. auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
  574. if (!campaignBonus)
  575. return false;
  576. if(campaignBonus->type == CampaignBonusType::HERO && playerColor == PlayerColor(campaignBonus->info1))
  577. return true;
  578. return false;
  579. }
  580. std::unique_ptr<CMap> CGameStateCampaign::getCurrentMap() const
  581. {
  582. return gameState->scenarioOps->campState->getMap(CampaignScenarioID::NONE);
  583. }
  584. VCMI_LIB_NAMESPACE_END