AIUtility.cpp 20 KB

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