CGameStateCampaign.cpp 21 KB

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