AIUtility.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /*
  2. * AIUtility.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 "AIUtility.h"
  12. #include "AIGateway.h"
  13. #include "Goals/Goals.h"
  14. #include "../../lib/UnlockGuard.h"
  15. #include "../../lib/CConfigHandler.h"
  16. #include "../../lib/entities/artifact/CArtifact.h"
  17. #include "../../lib/mapObjects/MapObjects.h"
  18. #include "../../lib/mapObjects/CQuest.h"
  19. #include "../../lib/mapping/TerrainTile.h"
  20. #include "../../lib/gameState/QuestInfo.h"
  21. #include "../../lib/IGameSettings.h"
  22. #include "../../lib/bonuses/Limiters.h"
  23. #include "../../lib/bonuses/Propagators.h"
  24. #include <vcmi/CreatureService.h>
  25. namespace NK2AI
  26. {
  27. const CGObjectInstance * ObjectIdRef::operator->() const
  28. {
  29. return ccTl->getObj(id, false);
  30. }
  31. ObjectIdRef::operator const CGObjectInstance *() const
  32. {
  33. return ccTl->getObj(id, false);
  34. }
  35. ObjectIdRef::operator bool() const
  36. {
  37. return ccTl->getObj(id, false);
  38. }
  39. ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
  40. : id(_id)
  41. {
  42. }
  43. ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
  44. : id(obj->id)
  45. {
  46. }
  47. bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const
  48. {
  49. return id < rhs.id;
  50. }
  51. HeroPtr::HeroPtr(const CGHeroInstance * input, std::shared_ptr<CPlayerSpecificInfoCallback> cpsic)
  52. : hero(input), cpsic(cpsic)
  53. {
  54. }
  55. bool HeroPtr::operator<(const HeroPtr & rhs) const
  56. {
  57. return idOrNone() < rhs.idOrNone();
  58. }
  59. ObjectInstanceID HeroPtr::idOrNone() const
  60. {
  61. if (hero)
  62. return hero->id;
  63. return ObjectInstanceID::NONE;
  64. }
  65. std::string HeroPtr::nameOrDefault() const
  66. {
  67. if (hero)
  68. return hero->getNameTextID();
  69. return "<NO HERO>";
  70. }
  71. const CGHeroInstance * HeroPtr::get() const
  72. {
  73. assert(isValid());
  74. return hero;
  75. }
  76. bool HeroPtr::validate() const
  77. {
  78. // TODO: check if these all assertions every time we get info about hero affect efficiency
  79. // behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
  80. if(hero)
  81. {
  82. const auto *obj = cpsic->getObj(hero->id);
  83. //const bool owned = obj && obj->tempOwner == ai->playerID;
  84. if(!obj)
  85. return false;
  86. assert(hero == obj);
  87. return true;
  88. //assert(owned);
  89. }
  90. return false;
  91. }
  92. const CGHeroInstance * HeroPtr::operator->() const
  93. {
  94. assert(isValid());
  95. return hero;
  96. }
  97. bool HeroPtr::isValid() const
  98. {
  99. return validate();
  100. }
  101. const CGHeroInstance * HeroPtr::operator*() const
  102. {
  103. assert(isValid());
  104. return hero;
  105. }
  106. bool HeroPtr::operator==(const HeroPtr & rhs) const
  107. {
  108. return hero == rhs.get();
  109. }
  110. bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet * heroArmy, uint64_t dangerStrength, float safeAttackRatio)
  111. {
  112. const ui64 heroStrength = h->getHeroStrength() * heroArmy->getArmyStrength();
  113. if(dangerStrength)
  114. {
  115. return heroStrength > dangerStrength * safeAttackRatio;
  116. }
  117. return true; //there's no danger
  118. }
  119. bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength, float safeAttackRatio)
  120. {
  121. return isSafeToVisit(h, h, dangerStrength, safeAttackRatio);
  122. }
  123. bool isObjectRemovable(const CGObjectInstance * obj)
  124. {
  125. //FIXME: move logic to object property!
  126. switch (obj->ID)
  127. {
  128. case Obj::MONSTER:
  129. case Obj::RESOURCE:
  130. case Obj::CAMPFIRE:
  131. case Obj::TREASURE_CHEST:
  132. case Obj::ARTIFACT:
  133. case Obj::BORDERGUARD:
  134. case Obj::FLOTSAM:
  135. case Obj::PANDORAS_BOX:
  136. case Obj::OCEAN_BOTTLE:
  137. case Obj::SEA_CHEST:
  138. case Obj::SHIPWRECK_SURVIVOR:
  139. case Obj::SPELL_SCROLL:
  140. return true;
  141. default:
  142. return false;
  143. }
  144. }
  145. bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
  146. {
  147. // TODO: Such information should be provided by pathfinder
  148. // Tile must be free or with unoccupied boat
  149. if(!t->blocked())
  150. {
  151. return true;
  152. }
  153. else if(!fromWater) // do not try to board when in water sector
  154. {
  155. if(t->visitableObjects.size() == 1 && ccTl->getObjInstance(t->topVisitableObj())->ID == Obj::BOAT)
  156. return true;
  157. }
  158. return false;
  159. }
  160. bool isObjectPassable(const Nullkiller * aiNk, const CGObjectInstance * obj)
  161. {
  162. return isObjectPassable(obj, aiNk->playerID, aiNk->cc->getPlayerRelations(obj->tempOwner, aiNk->playerID));
  163. }
  164. // Pathfinder internal helper
  165. bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations)
  166. {
  167. if((obj->ID == Obj::GARRISON || obj->ID == Obj::GARRISON2)
  168. && objectRelations != PlayerRelations::ENEMIES)
  169. return true;
  170. if(obj->ID == Obj::BORDER_GATE)
  171. {
  172. auto quest = dynamic_cast<const CGKeys *>(obj);
  173. if(quest->wasMyColorVisited(playerColor))
  174. return true;
  175. }
  176. return false;
  177. }
  178. bool isBlockVisitObj(const int3 & pos)
  179. {
  180. if(auto obj = ccTl->getTopObj(pos))
  181. {
  182. if(obj->isBlockedVisitable()) //we can't stand on that object
  183. return true;
  184. }
  185. return false;
  186. }
  187. creInfo infoFromDC(const dwellingContent & dc)
  188. {
  189. creInfo ci;
  190. ci.count = dc.first;
  191. ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
  192. if (ci.creID != CreatureID::NONE)
  193. {
  194. ci.level = ci.creID.toCreature()->getLevel(); //this is creature tier, while tryRealize expects dwelling level. Ignore.
  195. }
  196. else
  197. {
  198. ci.level = 0;
  199. }
  200. return ci;
  201. }
  202. bool compareHeroStrength(const CGHeroInstance * h1, const CGHeroInstance * h2)
  203. {
  204. return h1->getTotalStrength() < h2->getTotalStrength();
  205. }
  206. bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
  207. {
  208. return a1->getArmyStrength() < a2->getArmyStrength();
  209. }
  210. double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_ptr<Bonus> & bonus)
  211. {
  212. if (bonus->propagator && bonus->limiter && bonus->propagator->getPropagatorType() == BonusNodeType::BATTLE_WIDE)
  213. {
  214. // assume that this is battle wide / other side propagator+limiter
  215. // consider it as fully relevant since we don't know about future combat when equipping artifacts
  216. return 1.0;
  217. }
  218. const auto & getArmyRatioAffectedByLimiter = [&]()
  219. {
  220. if (!bonus->limiter)
  221. return 1.0;
  222. uint64_t totalStrength = 0;
  223. uint64_t affectedStrength = 0;
  224. const BonusList stillUndecided;
  225. for (const auto & slot : hero->Slots())
  226. {
  227. const auto allBonuses = slot.second->getAllBonuses(Selector::all);
  228. BonusLimitationContext context = {*bonus, *slot.second, *allBonuses, stillUndecided};
  229. uint64_t unitStrength = slot.second->getPower();
  230. if (bonus->limiter->limit(context) == ILimiter::EDecision::ACCEPT)
  231. affectedStrength += unitStrength;
  232. totalStrength += unitStrength;
  233. }
  234. if (totalStrength == 0)
  235. return 0.0;
  236. return static_cast<double>(affectedStrength) / totalStrength;
  237. };
  238. const auto & getArmyPercentageWithBonus = [&](BonusType type)
  239. {
  240. uint64_t totalStrength = 0;
  241. uint64_t affectedStrength = 0;
  242. for (const auto & slot : hero->Slots())
  243. {
  244. uint64_t unitStrength = slot.second->getPower();
  245. if (slot.second->hasBonusOfType(type))
  246. affectedStrength += unitStrength;
  247. totalStrength += unitStrength;
  248. }
  249. if (totalStrength == 0)
  250. return 0.0;
  251. return static_cast<double>(affectedStrength) / totalStrength;
  252. };
  253. const auto & getSpellSchoolKnownSpellsFactor = [&](SpellSchool school)
  254. {
  255. uint64_t totalWeight = 0;
  256. uint64_t knownWeight = 0;
  257. for (auto spellID : LIBRARY->spellh->getDefaultAllowed())
  258. {
  259. auto spell = spellID.toEntity(LIBRARY);
  260. if (!spell->hasSchool(school))
  261. continue;
  262. uint64_t spellLevel = spell->getLevel();
  263. uint64_t spellWeight = spellLevel * spellLevel;
  264. if (!hero->spellbookContainsSpell(spellID))
  265. knownWeight += spellWeight;
  266. totalWeight += spellWeight;
  267. }
  268. if (totalWeight == 0)
  269. return 0.0;
  270. return static_cast<double>(knownWeight) / totalWeight;
  271. };
  272. const auto & getSpellLevelKnownSpellsFactor = [&](int level)
  273. {
  274. uint64_t totalWeight = 0;
  275. uint64_t knownWeight = 0;
  276. for (auto spellID : LIBRARY->spellh->getDefaultAllowed())
  277. {
  278. auto spell = spellID.toEntity(LIBRARY);
  279. if (spell->getLevel() != level)
  280. continue;
  281. if (!hero->spellbookContainsSpell(spellID))
  282. knownWeight += 1;
  283. totalWeight += 1;
  284. }
  285. if (totalWeight == 0)
  286. return 0.0;
  287. return static_cast<double>(knownWeight) / totalWeight;
  288. };
  289. constexpr double notRelevant = 0.0; // artifact is not useful in current conditions
  290. constexpr double relevant = 1.0;
  291. constexpr double veryRelevant = 2.0; // for very situational artifacts, e.g. skill-specific, or army composition-specific
  292. switch (bonus->type)
  293. {
  294. case BonusType::MOVEMENT:
  295. if (hero->getBoat() && bonus->subtype == BonusCustomSubtype::heroMovementSea)
  296. return veryRelevant;
  297. if (!hero->getBoat() && bonus->subtype == BonusCustomSubtype::heroMovementLand)
  298. return relevant;
  299. return notRelevant;
  300. case BonusType::STACKS_SPEED:
  301. case BonusType::STACK_HEALTH:
  302. return getArmyRatioAffectedByLimiter();
  303. case BonusType::MORALE:
  304. return getArmyRatioAffectedByLimiter() * (1 - getArmyPercentageWithBonus(BonusType::UNDEAD)); // TODO: other unaffected, e.g. Golems
  305. case BonusType::LUCK:
  306. return getArmyRatioAffectedByLimiter(); // Do we have luck?
  307. case BonusType::PRIMARY_SKILL:
  308. if (bonus->subtype == PrimarySkill::ATTACK || bonus->subtype == PrimarySkill::DEFENSE)
  309. return getArmyRatioAffectedByLimiter(); // e.g. Vial of Dragonblood - consider only affected unit
  310. else
  311. return relevant; // spellpower / knowledge - always relevant
  312. case BonusType::WATER_WALKING:
  313. case BonusType::FLYING_MOVEMENT:
  314. return hero->getBoat() ? notRelevant : relevant; // boat can't fly
  315. case BonusType::WHIRLPOOL_PROTECTION:
  316. return hero->getBoat() ? relevant : notRelevant;
  317. case BonusType::UNDEAD_RAISE_PERCENTAGE:
  318. return hero->hasBonusOfType(BonusType::IMPROVED_NECROMANCY) ? veryRelevant : notRelevant;
  319. case BonusType::SPELL_DAMAGE:
  320. case BonusType::SPELL_DURATION:
  321. return hero->hasSpellbook() ? relevant : notRelevant;
  322. case BonusType::PERCENTAGE_DAMAGE_BOOST:
  323. if (bonus->subtype == BonusCustomSubtype::damageTypeRanged)
  324. return veryRelevant * getArmyPercentageWithBonus(BonusType::SHOOTER);
  325. if (bonus->subtype == BonusCustomSubtype::damageTypeMelee)
  326. return veryRelevant * (1 - getArmyPercentageWithBonus(BonusType::SHOOTER));
  327. return 0;
  328. case BonusType::MANA_PERCENTAGE_REGENERATION:
  329. case BonusType::MANA_REGENERATION:
  330. return hero->hasSpellbook() ? relevant : notRelevant;
  331. case BonusType::LEARN_BATTLE_SPELL_CHANCE:
  332. return hero->hasBonusOfType(BonusType::LEARN_BATTLE_SPELL_LEVEL_LIMIT) ? relevant : notRelevant;
  333. case BonusType::NO_DISTANCE_PENALTY:
  334. case BonusType::NO_WALL_PENALTY:
  335. return getArmyPercentageWithBonus(BonusType::SHOOTER) * veryRelevant;
  336. case BonusType::SPELLS_OF_SCHOOL:
  337. if (!hero->hasSpellbook())
  338. return notRelevant;
  339. return 1 - getSpellSchoolKnownSpellsFactor(bonus->subtype.as<SpellSchool>());
  340. case BonusType::SPELLS_OF_LEVEL:
  341. if (!hero->hasSpellbook())
  342. return notRelevant;
  343. return 1 - getSpellLevelKnownSpellsFactor(bonus->subtype.getNum());
  344. // Potential TODO's
  345. // case BonusType::MAGIC_RESISTANCE:
  346. // case BonusType::FREE_SHIP_BOARDING:
  347. // case BonusType::GENERATE_RESOURCE:
  348. // case BonusType::CREATURE_GROWTH:
  349. //
  350. // case BonusType::SPELLS_OF_LEVEL:
  351. // case BonusType::SIGHT_RADIUS:
  352. }
  353. return 1.0;
  354. }
  355. int32_t getArtifactBonusScoreImpl(const std::shared_ptr<Bonus> & bonus)
  356. {
  357. switch (bonus->type)
  358. {
  359. case BonusType::MOVEMENT:
  360. if (bonus->subtype == BonusCustomSubtype::heroMovementLand)
  361. return bonus->val * 20;
  362. if (bonus->subtype == BonusCustomSubtype::heroMovementSea)
  363. return bonus->val * 10;
  364. return 0;
  365. case BonusType::STACKS_SPEED:
  366. return bonus->val * 8000;
  367. case BonusType::MORALE:
  368. return bonus->val * 1500;
  369. case BonusType::LUCK:
  370. return bonus->val * 1000;
  371. case BonusType::PRIMARY_SKILL:
  372. return bonus->val * 1000;
  373. case BonusType::SURRENDER_DISCOUNT:
  374. return 0; // irrelevant in gameplay
  375. case BonusType::WATER_WALKING:
  376. return 5000;
  377. case BonusType::FREE_SHIP_BOARDING:
  378. return 10000;
  379. case BonusType::WHIRLPOOL_PROTECTION:
  380. return 5000;
  381. case BonusType::FLYING_MOVEMENT:
  382. return 20000;
  383. case BonusType::UNDEAD_RAISE_PERCENTAGE:
  384. return bonus->val * 400;
  385. case BonusType::GENERATE_RESOURCE:
  386. return bonus->val * LIBRARY->objh->resVals.at(bonus->subtype.as<GameResID>().getNum()) * 10;
  387. case BonusType::SPELL_DURATION:
  388. return bonus->val * 200;
  389. case BonusType::MAGIC_RESISTANCE:
  390. return bonus->val * 400;
  391. case BonusType::PERCENTAGE_DAMAGE_BOOST:
  392. if (bonus->subtype == BonusCustomSubtype::damageTypeRanged)
  393. return bonus->val * 200;
  394. if (bonus->subtype == BonusCustomSubtype::damageTypeMelee)
  395. return bonus->val * 500;
  396. return 0;
  397. case BonusType::CREATURE_GROWTH:
  398. return (1+bonus->subtype.getNum()) * bonus->val * 400;
  399. case BonusType::MANA_PERCENTAGE_REGENERATION:
  400. return bonus->val * 150;
  401. case BonusType::MANA_REGENERATION:
  402. return bonus->val * 500;
  403. case BonusType::SPELLS_OF_SCHOOL:
  404. return 20000;
  405. case BonusType::SPELLS_OF_LEVEL:
  406. return bonus->subtype.getNum() * 6000;
  407. case BonusType::SPELL_DAMAGE:
  408. return bonus->val * 120;
  409. case BonusType::SIGHT_RADIUS:
  410. return bonus->val * 1000;
  411. case BonusType::LEARN_BATTLE_SPELL_CHANCE:
  412. return 0; // irrelevant in gameplay
  413. case BonusType::STACK_HEALTH:
  414. return bonus->val * 5000;
  415. case BonusType::NO_DISTANCE_PENALTY:
  416. return 10000;
  417. case BonusType::NO_WALL_PENALTY:
  418. return 5000;
  419. }
  420. return 0;
  421. // Additional bonuses to consider from H3 artifacts:
  422. // MIND_IMMUNITY
  423. // BLOCK_MAGIC_ABOVE
  424. // SPELL_IMMUNITY
  425. // NEGATE_ALL_NATURAL_IMMUNITIES
  426. // SPELL_RESISTANCE_AURA
  427. // SPELL
  428. // BATTLE_NO_FLEEING
  429. // BLOCK_ALL_MAGIC
  430. // NONEVIL_ALIGNMENT_MIX
  431. // OPENING_BATTLE_SPELL
  432. // IMPROVED_NECROMANCY
  433. // HP_REGENERATION
  434. // CREATURE_GROWTH_PERCENT
  435. // LEVEL_SPELL_IMMUNITY
  436. // FREE_SHOOTING
  437. // FULL_MANA_REGENERATION
  438. }
  439. int32_t getArtifactBonusScore(const std::shared_ptr<Bonus> & bonus)
  440. {
  441. if (bonus->propagator && bonus->propagator->getPropagatorType() == BonusNodeType::BATTLE_WIDE)
  442. {
  443. if (bonus->limiter)
  444. {
  445. // assume that this is battle wide / other side propagator+limiter -> invert value
  446. return -getArtifactBonusScoreImpl(bonus);
  447. }
  448. else
  449. {
  450. return 0; // TODO? How to consider battle-wide bonuses that affect everyone?
  451. }
  452. }
  453. else
  454. {
  455. return getArtifactBonusScoreImpl(bonus);
  456. }
  457. }
  458. int64_t getPotentialArtifactScore(const CArtifact * type)
  459. {
  460. int64_t totalScore = 0;
  461. for (const auto & bonus : type->getExportedBonusList())
  462. totalScore += getArtifactBonusScore(bonus);
  463. if (type->hasParts())
  464. {
  465. for (const auto & part : type->getConstituents())
  466. {
  467. for (const auto & bonus : part->getExportedBonusList())
  468. totalScore += getArtifactBonusScore(bonus);
  469. }
  470. }
  471. int64_t finalScore = std::max<int64_t>(type->getPrice() / 5, totalScore );
  472. return finalScore;
  473. }
  474. int64_t getArtifactScoreForHero(const CGHeroInstance * hero, const CArtifactInstance * artifact)
  475. {
  476. if (artifact->isScroll())
  477. {
  478. auto spellID = artifact->getScrollSpellID();
  479. auto spell = spellID.toEntity(LIBRARY);
  480. if (hero->getSpellsInSpellbook().count(spellID))
  481. return 0;
  482. else
  483. return spell->getLevel() * 100;
  484. }
  485. const CArtifact * type = artifact->getType();
  486. int64_t totalScore = 0;
  487. if (type->getId() == ArtifactID::SPELLBOOK)
  488. return 0;
  489. for (const auto & bonus : type->getExportedBonusList())
  490. totalScore += getArtifactBonusRelevance(hero, bonus) * getArtifactBonusScore(bonus);
  491. if (type->hasParts())
  492. {
  493. for (const auto & part : type->getConstituents())
  494. {
  495. for (const auto & bonus : part->getExportedBonusList())
  496. totalScore += getArtifactBonusRelevance(hero, bonus) * getArtifactBonusScore(bonus);
  497. }
  498. }
  499. return totalScore;
  500. }
  501. bool isWeeklyRevisitable(const PlayerColor & playerID, const CGObjectInstance * obj)
  502. {
  503. if(!obj)
  504. return false;
  505. //TODO: allow polling of remaining creatures in dwelling
  506. if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
  507. return rewardable->configuration.getResetDuration() == 7;
  508. if(dynamic_cast<const CGDwelling *>(obj))
  509. return true;
  510. switch(obj->ID)
  511. {
  512. case Obj::HILL_FORT:
  513. return true;
  514. case Obj::BORDER_GATE:
  515. case Obj::BORDERGUARD:
  516. return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(playerID); //FIXME: they could be revisited sooner than in a week
  517. }
  518. return false;
  519. }
  520. uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start)
  521. {
  522. auto end = std::chrono::high_resolution_clock::now();
  523. return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
  524. }
  525. int getDuplicatingSlots(const CArmedInstance * army)
  526. {
  527. int duplicatingSlots = 0;
  528. for(const auto & stack : army->Slots())
  529. {
  530. if(stack.second->getCreature() && army->getSlotFor(stack.second->getCreature()) != stack.first)
  531. duplicatingSlots++;
  532. }
  533. return duplicatingSlots;
  534. }
  535. // todo: move to obj manager
  536. bool shouldVisit(const Nullkiller * aiNk, const CGHeroInstance * hero, const CGObjectInstance * obj)
  537. {
  538. auto relations = aiNk->cc->getPlayerRelations(obj->tempOwner, hero->tempOwner);
  539. switch(obj->ID)
  540. {
  541. case Obj::TOWN:
  542. case Obj::HERO: //never visit our heroes at random
  543. return relations == PlayerRelations::ENEMIES; //do not visit our towns at random
  544. case Obj::BORDER_GATE:
  545. {
  546. for(auto q : aiNk->cc->getMyQuests())
  547. {
  548. if(q.obj == obj->id)
  549. {
  550. return false; // do not visit guards or gates when wandering
  551. }
  552. }
  553. return true; //we don't have this quest yet
  554. }
  555. case Obj::BORDERGUARD: //open borderguard if possible
  556. return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(aiNk->playerID);
  557. case Obj::SEER_HUT:
  558. {
  559. for(auto q : aiNk->cc->getMyQuests())
  560. {
  561. if(q.obj == obj->id)
  562. {
  563. if(q.getQuest(aiNk->cc.get())->checkQuest(hero))
  564. return true; //we completed the quest
  565. else
  566. return false; //we can't complete this quest
  567. }
  568. }
  569. return true; //we don't have this quest yet
  570. }
  571. case Obj::CREATURE_GENERATOR1:
  572. {
  573. if(relations == PlayerRelations::ENEMIES)
  574. return true; //flag just in case
  575. if(relations == PlayerRelations::ALLIES)
  576. return false;
  577. const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
  578. auto duplicatingSlotsCount = getDuplicatingSlots(hero);
  579. for(auto level : d->creatures)
  580. {
  581. for(auto c : level.second)
  582. {
  583. if(level.first
  584. && (hero->getSlotFor(CreatureID(c)) != SlotID() || duplicatingSlotsCount > 0)
  585. && aiNk->cc->getResourceAmount().canAfford(c.toCreature()->getFullRecruitCost()))
  586. {
  587. return true;
  588. }
  589. }
  590. }
  591. return false;
  592. }
  593. case Obj::HILL_FORT:
  594. {
  595. for(const auto & slot : hero->Slots())
  596. {
  597. if(slot.second->getType()->hasUpgrades())
  598. return true; //TODO: check price?
  599. }
  600. return false;
  601. }
  602. case Obj::MONOLITH_ONE_WAY_ENTRANCE:
  603. case Obj::MONOLITH_ONE_WAY_EXIT:
  604. case Obj::MONOLITH_TWO_WAY:
  605. case Obj::WHIRLPOOL:
  606. return false;
  607. case Obj::SCHOOL_OF_MAGIC:
  608. case Obj::SCHOOL_OF_WAR:
  609. {
  610. if(aiNk->getFreeGold() < 1000)
  611. return false;
  612. break;
  613. }
  614. case Obj::LIBRARY_OF_ENLIGHTENMENT:
  615. if(hero->level < 10)
  616. return false;
  617. break;
  618. case Obj::TREE_OF_KNOWLEDGE:
  619. {
  620. if(aiNk->heroManager->getHeroRole(hero) == HeroRole::SCOUT)
  621. return false;
  622. TResources myRes = aiNk->getFreeResources();
  623. if(myRes[EGameResID::GOLD] < 2000 || myRes[EGameResID::GEMS] < 10)
  624. return false;
  625. break;
  626. }
  627. case Obj::MAGIC_WELL:
  628. return hero->mana < hero->manaLimit();
  629. case Obj::PRISON:
  630. return !aiNk->heroManager->heroCapReached();
  631. case Obj::TAVERN:
  632. case Obj::EYE_OF_MAGI:
  633. case Obj::BOAT:
  634. case Obj::SIGN:
  635. return false;
  636. }
  637. if(obj->wasVisited(hero))
  638. return false;
  639. auto rewardable = dynamic_cast<const Rewardable::Interface *>(obj);
  640. if(rewardable && rewardable->getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT).empty())
  641. {
  642. return false;
  643. }
  644. return true;
  645. }
  646. bool townHasFreeTavern(const CGTownInstance * town)
  647. {
  648. if(!town->hasBuilt(BuildingID::TAVERN)) return false;
  649. if(!town->getVisitingHero()) return true;
  650. bool canMoveVisitingHeroToGarrison = !town->getUpperArmy()->stacksCount();
  651. return canMoveVisitingHeroToGarrison;
  652. }
  653. uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCreatureSet * heroArmy, int fortLevel)
  654. {
  655. auto armyStrength = heroArmy->getArmyStrength(fortLevel);
  656. if(hero && hero->getCommander() && hero->getCommander()->alive)
  657. {
  658. armyStrength += 100 * hero->getCommander()->level;
  659. }
  660. return armyStrength;
  661. }
  662. }