AINodeStorage.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392
  1. /*
  2. * AINodeStorage.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 <tbb/tbb.h>
  12. #include "AINodeStorage.h"
  13. #include "Actions/TownPortalAction.h"
  14. #include "../Goals/Goals.h"
  15. #include "../VCAI.h"
  16. #include "../Engine/Nullkiller.h"
  17. #include "../../../CCallback.h"
  18. #include "../../../lib/mapping/CMap.h"
  19. #include "../../../lib/mapObjects/MapObjects.h"
  20. #include "../../../lib/PathfinderUtil.h"
  21. #include "../../../lib/CPlayerState.h"
  22. using namespace tbb;
  23. std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
  24. std::set<int3> commitedTiles;
  25. std::set<int3> commitedTilesInitial;
  26. const uint64_t FirstActorMask = 1;
  27. const int BUCKET_COUNT = 11;
  28. const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
  29. const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
  30. AISharedStorage::AISharedStorage(int3 sizes)
  31. {
  32. if(!shared){
  33. shared.reset(new boost::multi_array<AIPathNode, 5>(
  34. boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]));
  35. }
  36. nodes = shared;
  37. }
  38. AISharedStorage::~AISharedStorage()
  39. {
  40. nodes.reset();
  41. if(shared && shared.use_count() == 1)
  42. {
  43. shared.reset();
  44. }
  45. }
  46. AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
  47. : sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes)
  48. {
  49. dangerEvaluator.reset(new FuzzyHelper(ai));
  50. }
  51. AINodeStorage::~AINodeStorage() = default;
  52. void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
  53. {
  54. if(heroChainPass)
  55. return;
  56. //TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
  57. const PlayerColor fowPlayer = ai->playerID;
  58. const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
  59. const int3 sizes = gs->getMapSize();
  60. parallel_for(blocked_range<size_t>(0, sizes.x), [&](const blocked_range<size_t>& r)
  61. {
  62. //make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
  63. const bool useFlying = options.useFlying;
  64. const bool useWaterWalking = options.useWaterWalking;
  65. const PlayerColor player = playerID;
  66. int3 pos;
  67. for(pos.x = r.begin(); pos.x != r.end(); ++pos.x)
  68. {
  69. for(pos.y = 0; pos.y < sizes.y; ++pos.y)
  70. {
  71. for(pos.z = 0; pos.z < sizes.z; ++pos.z)
  72. {
  73. const TerrainTile * tile = &gs->map->getTile(pos);
  74. switch(tile->terType)
  75. {
  76. case ETerrainType::ROCK:
  77. break;
  78. case ETerrainType::WATER:
  79. resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
  80. if(useFlying)
  81. resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
  82. if(useWaterWalking)
  83. resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, tile, fow, player, gs));
  84. break;
  85. default:
  86. resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, tile, fow, player, gs));
  87. if(useFlying)
  88. resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, tile, fow, player, gs));
  89. break;
  90. }
  91. }
  92. }
  93. }
  94. });
  95. }
  96. void AINodeStorage::clear()
  97. {
  98. CCreature::DisableChildLinkage = true;
  99. actors.clear();
  100. CCreature::DisableChildLinkage = false;
  101. heroChainPass = EHeroChainPass::INITIAL;
  102. heroChainTurn = 0;
  103. heroChainMaxTurns = 1;
  104. turnDistanceLimit[HeroRole::MAIN] = 255;
  105. turnDistanceLimit[HeroRole::SCOUT] = 255;
  106. }
  107. const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
  108. {
  109. return static_cast<const AIPathNode *>(node);
  110. }
  111. void AINodeStorage::updateAINode(CGPathNode * node, std::function<void(AIPathNode *)> updater)
  112. {
  113. auto aiNode = static_cast<AIPathNode *>(node);
  114. updater(aiNode);
  115. }
  116. boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
  117. const int3 & pos,
  118. const EPathfindingLayer layer,
  119. const ChainActor * actor)
  120. {
  121. int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
  122. int bucketOffset = bucketIndex * BUCKET_SIZE;
  123. auto chains = nodes.get(pos, layer);
  124. if(chains[0].blocked())
  125. {
  126. return boost::none;
  127. }
  128. for(auto i = BUCKET_SIZE - 1; i >= 0; i--)
  129. {
  130. AIPathNode & node = chains[i + bucketOffset];
  131. if(node.actor == actor)
  132. {
  133. return &node;
  134. }
  135. if(!node.actor)
  136. {
  137. node.actor = actor;
  138. return &node;
  139. }
  140. }
  141. return boost::none;
  142. }
  143. std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
  144. {
  145. if(heroChainPass)
  146. {
  147. calculateTownPortalTeleportations(heroChain);
  148. return heroChain;
  149. }
  150. std::vector<CGPathNode *> initialNodes;
  151. for(auto actorPtr : actors)
  152. {
  153. ChainActor * actor = actorPtr.get();
  154. AIPathNode * initialNode =
  155. getOrCreateNode(actor->initialPosition, actor->layer, actor)
  156. .get();
  157. if(!initialNode)
  158. continue;
  159. initialNode->inPQ = false;
  160. initialNode->pq = nullptr;
  161. initialNode->turns = actor->initialTurn;
  162. initialNode->moveRemains = actor->initialMovement;
  163. initialNode->danger = 0;
  164. initialNode->setCost(actor->initialTurn);
  165. initialNode->action = CGPathNode::ENodeAction::NORMAL;
  166. if(actor->isMovable)
  167. {
  168. initialNodes.push_back(initialNode);
  169. }
  170. else
  171. {
  172. initialNode->locked = true;
  173. }
  174. }
  175. calculateTownPortalTeleportations(initialNodes);
  176. return initialNodes;
  177. }
  178. void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility)
  179. {
  180. for(AIPathNode & heroNode : nodes.get(coord, layer))
  181. {
  182. heroNode.actor = nullptr;
  183. heroNode.danger = 0;
  184. heroNode.manaCost = 0;
  185. heroNode.specialAction.reset();
  186. heroNode.armyLoss = 0;
  187. heroNode.chainOther = nullptr;
  188. heroNode.update(coord, layer, accessibility);
  189. }
  190. }
  191. void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
  192. {
  193. const AIPathNode * srcNode = getAINode(source.node);
  194. updateAINode(destination.node, [&](AIPathNode * dstNode)
  195. {
  196. commit(dstNode, srcNode, destination.action, destination.turn, destination.movementLeft, destination.cost);
  197. if(srcNode->specialAction || srcNode->chainOther)
  198. {
  199. // there is some action on source tile which should be performed before we can bypass it
  200. destination.node->theNodeBefore = source.node;
  201. }
  202. if(dstNode->specialAction && dstNode->actor)
  203. {
  204. dstNode->specialAction->applyOnDestination(dstNode->actor->hero, destination, source, dstNode, srcNode);
  205. }
  206. });
  207. }
  208. void AINodeStorage::commit(
  209. AIPathNode * destination,
  210. const AIPathNode * source,
  211. CGPathNode::ENodeAction action,
  212. int turn,
  213. int movementLeft,
  214. float cost) const
  215. {
  216. destination->action = action;
  217. destination->setCost(cost);
  218. destination->moveRemains = movementLeft;
  219. destination->turns = turn;
  220. destination->armyLoss = source->armyLoss;
  221. destination->manaCost = source->manaCost;
  222. destination->danger = source->danger;
  223. destination->theNodeBefore = source->theNodeBefore;
  224. destination->chainOther = nullptr;
  225. #if PATHFINDER_TRACE_LEVEL >= 2
  226. logAi->trace(
  227. "Commited %s -> %s, cost: %f, turn: %s, mp: %d, hero: %s, mask: %x, army: %lld",
  228. source->coord.toString(),
  229. destination->coord.toString(),
  230. destination->getCost(),
  231. std::to_string(destination->turns),
  232. destination->moveRemains,
  233. destination->actor->toString(),
  234. destination->actor->chainMask,
  235. destination->actor->armyValue);
  236. #endif
  237. if(destination->turns <= heroChainTurn)
  238. {
  239. commitedTiles.insert(destination->coord);
  240. }
  241. }
  242. std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
  243. const PathNodeInfo & source,
  244. const PathfinderConfig * pathfinderConfig,
  245. const CPathfinderHelper * pathfinderHelper)
  246. {
  247. std::vector<CGPathNode *> neighbours;
  248. neighbours.reserve(16);
  249. const AIPathNode * srcNode = getAINode(source.node);
  250. auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source);
  251. for(auto & neighbour : accessibleNeighbourTiles)
  252. {
  253. for(EPathfindingLayer i = EPathfindingLayer::LAND; i <= EPathfindingLayer::AIR; i.advance(1))
  254. {
  255. auto nextNode = getOrCreateNode(neighbour, i, srcNode->actor);
  256. if(!nextNode || nextNode.get()->accessible == CGPathNode::NOT_SET)
  257. continue;
  258. neighbours.push_back(nextNode.get());
  259. }
  260. }
  261. return neighbours;
  262. }
  263. EPathfindingLayer phisycalLayers[2] = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
  264. bool AINodeStorage::increaseHeroChainTurnLimit()
  265. {
  266. if(heroChainTurn >= heroChainMaxTurns)
  267. return false;
  268. heroChainTurn++;
  269. commitedTiles.clear();
  270. for(auto layer : phisycalLayers)
  271. {
  272. foreach_tile_pos([&](const int3 & pos)
  273. {
  274. auto chains = nodes.get(pos, layer);
  275. if(!chains[0].blocked())
  276. {
  277. for(AIPathNode & node : chains)
  278. {
  279. if(node.turns <= heroChainTurn && node.action != CGPathNode::ENodeAction::UNKNOWN)
  280. {
  281. commitedTiles.insert(pos);
  282. break;
  283. }
  284. }
  285. }
  286. });
  287. }
  288. return true;
  289. }
  290. bool AINodeStorage::calculateHeroChainFinal()
  291. {
  292. heroChainPass = EHeroChainPass::FINAL;
  293. heroChain.resize(0);
  294. for(auto layer : phisycalLayers)
  295. {
  296. foreach_tile_pos([&](const int3 & pos)
  297. {
  298. auto chains = nodes.get(pos, layer);
  299. if(!chains[0].blocked())
  300. {
  301. for(AIPathNode & node : chains)
  302. {
  303. if(node.turns > heroChainTurn
  304. && !node.locked
  305. && node.action != CGPathNode::ENodeAction::UNKNOWN
  306. && node.actor->actorExchangeCount > 1
  307. && !hasBetterChain(&node, &node, chains))
  308. {
  309. heroChain.push_back(&node);
  310. }
  311. }
  312. }
  313. });
  314. }
  315. return heroChain.size();
  316. }
  317. struct DelayedWork
  318. {
  319. AIPathNode * carrier;
  320. AIPathNode * other;
  321. DelayedWork()
  322. {
  323. }
  324. DelayedWork(AIPathNode * carrier, AIPathNode * other) : carrier(carrier), other(other)
  325. {
  326. }
  327. };
  328. class HeroChainCalculationTask
  329. {
  330. private:
  331. AISharedStorage & nodes;
  332. AINodeStorage & storage;
  333. std::vector<AIPathNode *> existingChains;
  334. std::vector<ExchangeCandidate> newChains;
  335. uint64_t chainMask;
  336. int heroChainTurn;
  337. std::vector<CGPathNode *> heroChain;
  338. const std::vector<int3> & tiles;
  339. public:
  340. HeroChainCalculationTask(
  341. AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
  342. :existingChains(), newChains(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
  343. {
  344. existingChains.reserve(NUM_CHAINS);
  345. newChains.reserve(NUM_CHAINS);
  346. }
  347. void execute(const blocked_range<size_t>& r)
  348. {
  349. for(int i = r.begin(); i != r.end(); i++)
  350. {
  351. auto & pos = tiles[i];
  352. for(auto layer : phisycalLayers)
  353. {
  354. auto chains = nodes.get(pos, layer);
  355. // fast cut inactive nodes
  356. if(chains[0].blocked())
  357. continue;
  358. existingChains.clear();
  359. newChains.clear();
  360. for(AIPathNode & node : chains)
  361. {
  362. if(node.turns <= heroChainTurn && node.action != CGPathNode::ENodeAction::UNKNOWN)
  363. existingChains.push_back(&node);
  364. }
  365. std::random_shuffle(existingChains.begin(), existingChains.end());
  366. for(AIPathNode * node : existingChains)
  367. {
  368. if(node->actor->isMovable)
  369. {
  370. calculateHeroChain(node, existingChains, newChains);
  371. }
  372. }
  373. cleanupInefectiveChains(newChains);
  374. addHeroChain(newChains);
  375. }
  376. }
  377. }
  378. void calculateHeroChain(
  379. AIPathNode * srcNode,
  380. const std::vector<AIPathNode *> & variants,
  381. std::vector<ExchangeCandidate> & result);
  382. void calculateHeroChain(
  383. AIPathNode * carrier,
  384. AIPathNode * other,
  385. std::vector<ExchangeCandidate> & result);
  386. void cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const;
  387. void addHeroChain(const std::vector<ExchangeCandidate> & result);
  388. ExchangeCandidate calculateExchange(
  389. ChainActor * exchangeActor,
  390. AIPathNode * carrierParentNode,
  391. AIPathNode * otherParentNode) const;
  392. void flushResult(std::vector<CGPathNode *> & result)
  393. {
  394. vstd::concatenate(result, heroChain);
  395. }
  396. };
  397. bool AINodeStorage::calculateHeroChain()
  398. {
  399. heroChainPass = EHeroChainPass::CHAIN;
  400. heroChain.clear();
  401. std::vector<int3> data(commitedTiles.begin(), commitedTiles.end());
  402. CCreature::DisableChildLinkage = true;
  403. auto r = blocked_range<size_t>(0, data.size());
  404. HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
  405. task.execute(r);
  406. task.flushResult(heroChain);
  407. CCreature::DisableChildLinkage = false;
  408. commitedTiles.clear();
  409. return !heroChain.empty();
  410. }
  411. bool AINodeStorage::selectFirstActor()
  412. {
  413. if(actors.empty())
  414. return false;
  415. auto strongest = *vstd::maxElementByFun(actors, [](std::shared_ptr<ChainActor> actor) -> uint64_t
  416. {
  417. return actor->armyValue;
  418. });
  419. chainMask = strongest->chainMask;
  420. commitedTilesInitial = commitedTiles;
  421. return true;
  422. }
  423. bool AINodeStorage::selectNextActor()
  424. {
  425. auto currentActor = std::find_if(actors.begin(), actors.end(), [&](std::shared_ptr<ChainActor> actor)-> bool
  426. {
  427. return actor->chainMask == chainMask;
  428. });
  429. auto nextActor = actors.end();
  430. for(auto actor = actors.begin(); actor != actors.end(); actor++)
  431. {
  432. if(actor->get()->armyValue > currentActor->get()->armyValue
  433. || actor->get()->armyValue == currentActor->get()->armyValue && actor <= currentActor)
  434. {
  435. continue;
  436. }
  437. if(nextActor == actors.end()
  438. || actor->get()->armyValue > nextActor->get()->armyValue)
  439. {
  440. nextActor = actor;
  441. }
  442. }
  443. if(nextActor != actors.end())
  444. {
  445. if(nextActor->get()->armyValue < 1000)
  446. return false;
  447. chainMask = nextActor->get()->chainMask;
  448. commitedTiles = commitedTilesInitial;
  449. return true;
  450. }
  451. return false;
  452. }
  453. void HeroChainCalculationTask::cleanupInefectiveChains(std::vector<ExchangeCandidate> & result) const
  454. {
  455. vstd::erase_if(result, [&](const ExchangeCandidate & chainInfo) -> bool
  456. {
  457. auto pos = chainInfo.coord;
  458. auto chains = nodes.get(pos, EPathfindingLayer::LAND);
  459. auto isNotEffective = storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, chains)
  460. || storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, result);
  461. #if PATHFINDER_TRACE_LEVEL >= 2
  462. if(isNotEffective)
  463. {
  464. logAi->trace(
  465. "Skip exchange %s[%x] -> %s[%x] at %s is ineficient",
  466. chainInfo.otherParent->actor->toString(),
  467. chainInfo.otherParent->actor->chainMask,
  468. chainInfo.carrierParent->actor->toString(),
  469. chainInfo.carrierParent->actor->chainMask,
  470. chainInfo.carrierParent->coord.toString());
  471. }
  472. #endif
  473. return isNotEffective;
  474. });
  475. }
  476. void HeroChainCalculationTask::calculateHeroChain(
  477. AIPathNode * srcNode,
  478. const std::vector<AIPathNode *> & variants,
  479. std::vector<ExchangeCandidate> & result)
  480. {
  481. for(AIPathNode * node : variants)
  482. {
  483. if(node == srcNode || !node->actor)
  484. continue;
  485. if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
  486. continue;
  487. if(node->action == CGPathNode::ENodeAction::BATTLE
  488. || node->action == CGPathNode::ENodeAction::TELEPORT_BATTLE
  489. || node->action == CGPathNode::ENodeAction::TELEPORT_NORMAL
  490. || node->action == CGPathNode::ENodeAction::TELEPORT_BLOCKING_VISIT)
  491. {
  492. continue;
  493. }
  494. if(node->turns > heroChainTurn
  495. || (node->action == CGPathNode::ENodeAction::UNKNOWN && node->actor->hero)
  496. || (node->actor->chainMask & srcNode->actor->chainMask) != 0)
  497. {
  498. #if PATHFINDER_TRACE_LEVEL >= 2
  499. logAi->trace(
  500. "Skip exchange %s[%x] -> %s[%x] at %s because of %s",
  501. node->actor->toString(),
  502. node->actor->chainMask,
  503. srcNode->actor->toString(),
  504. srcNode->actor->chainMask,
  505. srcNode->coord.toString(),
  506. (node->turns > heroChainTurn
  507. ? "turn limit"
  508. : (node->action == CGPathNode::ENodeAction::UNKNOWN && node->actor->hero)
  509. ? "action unknown"
  510. : "chain mask"));
  511. #endif
  512. continue;
  513. }
  514. #if PATHFINDER_TRACE_LEVEL >= 2
  515. logAi->trace(
  516. "Thy exchange %s[%x] -> %s[%x] at %s",
  517. node->actor->toString(),
  518. node->actor->chainMask,
  519. srcNode->actor->toString(),
  520. srcNode->actor->chainMask,
  521. srcNode->coord.toString());
  522. #endif
  523. calculateHeroChain(srcNode, node, result);
  524. }
  525. }
  526. void HeroChainCalculationTask::calculateHeroChain(
  527. AIPathNode * carrier,
  528. AIPathNode * other,
  529. std::vector<ExchangeCandidate> & result)
  530. {
  531. if(carrier->armyLoss < carrier->actor->armyValue
  532. && (carrier->action != CGPathNode::BATTLE || (carrier->actor->allowBattle && carrier->specialAction))
  533. && carrier->action != CGPathNode::BLOCKING_VISIT
  534. && (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue))
  535. {
  536. #if PATHFINDER_TRACE_LEVEL >= 2
  537. logAi->trace(
  538. "Exchange allowed %s[%x] -> %s[%x] at %s",
  539. other->actor->toString(),
  540. other->actor->chainMask,
  541. carrier->actor->toString(),
  542. carrier->actor->chainMask,
  543. carrier->coord.toString());
  544. #endif
  545. if(other->actor->isMovable)
  546. {
  547. bool hasLessMp = carrier->turns > other->turns || (carrier->turns == other->turns && carrier->moveRemains < other->moveRemains);
  548. bool hasLessExperience = carrier->actor->hero->exp < other->actor->hero->exp;
  549. if(hasLessMp && hasLessExperience)
  550. {
  551. #if PATHFINDER_TRACE_LEVEL >= 2
  552. logAi->trace("Exchange at %s is ineficient. Blocked.", carrier->coord.toString());
  553. #endif
  554. return;
  555. }
  556. }
  557. auto newActor = carrier->actor->tryExchange(other->actor);
  558. if(newActor) result.push_back(calculateExchange(newActor, carrier, other));
  559. }
  560. }
  561. void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate> & result)
  562. {
  563. for(const ExchangeCandidate & chainInfo : result)
  564. {
  565. auto carrier = chainInfo.carrierParent;
  566. auto newActor = chainInfo.actor;
  567. auto other = chainInfo.otherParent;
  568. auto chainNodeOptional = storage.getOrCreateNode(carrier->coord, carrier->layer, newActor);
  569. if(!chainNodeOptional)
  570. {
  571. #if PATHFINDER_TRACE_LEVEL >= 2
  572. logAi->trace("Exchange at %s can not allocate node. Blocked.", carrier->coord.toString());
  573. #endif
  574. continue;
  575. }
  576. auto exchangeNode = chainNodeOptional.get();
  577. if(exchangeNode->action != CGPathNode::ENodeAction::UNKNOWN)
  578. {
  579. #if PATHFINDER_TRACE_LEVEL >= 2
  580. logAi->trace(
  581. "Skip exchange %s[%x] -> %s[%x] at %s because node is in use",
  582. other->actor->toString(),
  583. other->actor->chainMask,
  584. carrier->actor->toString(),
  585. carrier->actor->chainMask,
  586. carrier->coord.toString());
  587. #endif
  588. continue;
  589. }
  590. if(exchangeNode->turns != 0xFF && exchangeNode->getCost() < chainInfo.getCost())
  591. {
  592. #if PATHFINDER_TRACE_LEVEL >= 2
  593. logAi->trace(
  594. "Skip exchange %s[%x] -> %s[%x] at %s because not effective enough. %f < %f",
  595. other->actor->toString(),
  596. other->actor->chainMask,
  597. carrier->actor->toString(),
  598. carrier->actor->chainMask,
  599. carrier->coord.toString(),
  600. exchangeNode->getCost(),
  601. chainInfo.getCost());
  602. #endif
  603. continue;
  604. }
  605. storage.commit(exchangeNode, carrier, carrier->action, chainInfo.turns, chainInfo.moveRemains, chainInfo.getCost());
  606. if(carrier->specialAction || carrier->chainOther)
  607. {
  608. // there is some action on source tile which should be performed before we can bypass it
  609. exchangeNode->theNodeBefore = carrier;
  610. }
  611. if(exchangeNode->actor->actorAction)
  612. {
  613. exchangeNode->theNodeBefore = carrier;
  614. exchangeNode->specialAction = exchangeNode->actor->actorAction;
  615. }
  616. exchangeNode->chainOther = other;
  617. exchangeNode->armyLoss = chainInfo.armyLoss;
  618. #if PATHFINDER_TRACE_LEVEL >= 2
  619. logAi->trace(
  620. "Chain accepted at %s %s -> %s, mask %x, cost %f, turn: %s, mp: %d, army %i",
  621. exchangeNode->coord.toString(),
  622. other->actor->toString(),
  623. exchangeNode->actor->toString(),
  624. exchangeNode->actor->chainMask,
  625. exchangeNode->getCost(),
  626. std::to_string(exchangeNode->turns),
  627. exchangeNode->moveRemains,
  628. exchangeNode->actor->armyValue);
  629. #endif
  630. heroChain.push_back(exchangeNode);
  631. }
  632. }
  633. ExchangeCandidate HeroChainCalculationTask::calculateExchange(
  634. ChainActor * exchangeActor,
  635. AIPathNode * carrierParentNode,
  636. AIPathNode * otherParentNode) const
  637. {
  638. ExchangeCandidate candidate;
  639. candidate.layer = carrierParentNode->layer;
  640. candidate.coord = carrierParentNode->coord;
  641. candidate.carrierParent = carrierParentNode;
  642. candidate.otherParent = otherParentNode;
  643. candidate.actor = exchangeActor;
  644. candidate.armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
  645. candidate.turns = carrierParentNode->turns;
  646. candidate.setCost(carrierParentNode->getCost() + otherParentNode->getCost() / 1000.0);
  647. candidate.moveRemains = carrierParentNode->moveRemains;
  648. if(carrierParentNode->turns < otherParentNode->turns)
  649. {
  650. int moveRemains = exchangeActor->hero->maxMovePoints(carrierParentNode->layer);
  651. float waitingCost = otherParentNode->turns - carrierParentNode->turns - 1
  652. + carrierParentNode->moveRemains / (float)moveRemains;
  653. candidate.turns = otherParentNode->turns;
  654. candidate.setCost(candidate.getCost() + waitingCost);
  655. candidate.moveRemains = moveRemains;
  656. }
  657. return candidate;
  658. }
  659. const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
  660. {
  661. auto aiNode = getAINode(node);
  662. return aiNode->actor->hero;
  663. }
  664. const std::set<const CGHeroInstance *> AINodeStorage::getAllHeroes() const
  665. {
  666. std::set<const CGHeroInstance *> heroes;
  667. for(auto actor : actors)
  668. {
  669. if(actor->hero)
  670. heroes.insert(actor->hero);
  671. }
  672. return heroes;
  673. }
  674. bool AINodeStorage::isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
  675. {
  676. if(heroChainPass == EHeroChainPass::CHAIN && destination.node->turns > heroChainTurn)
  677. {
  678. return true;
  679. }
  680. auto aiNode = getAINode(destination.node);
  681. if(heroChainPass != EHeroChainPass::CHAIN
  682. && destination.node->turns > turnDistanceLimit[aiNode->actor->heroRole])
  683. {
  684. return true;
  685. }
  686. return false;
  687. }
  688. void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
  689. {
  690. playerID = ai->playerID;
  691. for(auto & hero : heroes)
  692. {
  693. uint64_t mask = FirstActorMask << actors.size();
  694. auto actor = std::make_shared<HeroActor>(hero.first, hero.second, mask, ai);
  695. if(actor->hero->tempOwner != ai->playerID)
  696. {
  697. bool onLand = !actor->hero->boat;
  698. actor->initialMovement = actor->hero->maxMovePoints(onLand);
  699. }
  700. playerID = actor->hero->tempOwner;
  701. actors.push_back(actor);
  702. }
  703. }
  704. void AINodeStorage::setTownsAndDwellings(
  705. const std::vector<const CGTownInstance *> & towns,
  706. const std::set<const CGObjectInstance *> & visitableObjs)
  707. {
  708. for(auto town : towns)
  709. {
  710. uint64_t mask = FirstActorMask << actors.size();
  711. // TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE
  712. // check defence imrove
  713. if(!town->garrisonHero)
  714. {
  715. actors.push_back(std::make_shared<TownGarrisonActor>(town, mask));
  716. }
  717. }
  718. /*auto dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
  719. auto waitForGrowth = dayOfWeek > 4;*/
  720. for(auto obj: visitableObjs)
  721. {
  722. if(obj->ID == Obj::HILL_FORT)
  723. {
  724. uint64_t mask = FirstActorMask << actors.size();
  725. actors.push_back(std::make_shared<HillFortActor>(obj, mask));
  726. }
  727. /*const CGDwelling * dwelling = dynamic_cast<const CGDwelling *>(obj);
  728. if(dwelling)
  729. {
  730. uint64_t mask = 1 << actors.size();
  731. auto dwellingActor = std::make_shared<DwellingActor>(dwelling, mask, false, dayOfWeek);
  732. if(dwellingActor->creatureSet->getArmyStrength())
  733. {
  734. actors.push_back(dwellingActor);
  735. }
  736. if(waitForGrowth)
  737. {
  738. mask = 1 << actors.size();
  739. dwellingActor = std::make_shared<DwellingActor>(dwelling, mask, waitForGrowth, dayOfWeek);
  740. if(dwellingActor->creatureSet->getArmyStrength())
  741. {
  742. actors.push_back(dwellingActor);
  743. }
  744. }
  745. }*/
  746. }
  747. }
  748. std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
  749. const PathNodeInfo & source,
  750. const PathfinderConfig * pathfinderConfig,
  751. const CPathfinderHelper * pathfinderHelper)
  752. {
  753. std::vector<CGPathNode *> neighbours;
  754. if(source.isNodeObjectVisitable())
  755. {
  756. auto accessibleExits = pathfinderHelper->getTeleportExits(source);
  757. auto srcNode = getAINode(source.node);
  758. for(auto & neighbour : accessibleExits)
  759. {
  760. auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->actor);
  761. if(!node)
  762. continue;
  763. neighbours.push_back(node.get());
  764. }
  765. }
  766. return neighbours;
  767. }
  768. struct TowmPortalFinder
  769. {
  770. const std::vector<CGPathNode *> & initialNodes;
  771. SecSkillLevel::SecSkillLevel townPortalSkillLevel;
  772. uint64_t movementNeeded;
  773. const ChainActor * actor;
  774. const CGHeroInstance * hero;
  775. std::vector<const CGTownInstance *> targetTowns;
  776. AINodeStorage * nodeStorage;
  777. SpellID spellID;
  778. const CSpell * townPortal;
  779. TowmPortalFinder(
  780. const ChainActor * actor,
  781. const std::vector<CGPathNode *> & initialNodes,
  782. std::vector<const CGTownInstance *> targetTowns,
  783. AINodeStorage * nodeStorage)
  784. :actor(actor), initialNodes(initialNodes), hero(actor->hero),
  785. targetTowns(targetTowns), nodeStorage(nodeStorage)
  786. {
  787. spellID = SpellID::TOWN_PORTAL;
  788. townPortal = spellID.toSpell();
  789. // TODO: Copy/Paste from TownPortalMechanics
  790. townPortalSkillLevel = SecSkillLevel::SecSkillLevel(hero->getSpellSchoolLevel(townPortal));
  791. movementNeeded = GameConstants::BASE_MOVEMENT_COST * (townPortalSkillLevel >= SecSkillLevel::EXPERT ? 2 : 3);
  792. }
  793. bool actorCanCastTownPortal()
  794. {
  795. return hero->canCastThisSpell(townPortal) && hero->mana >= hero->getSpellCost(townPortal);
  796. }
  797. CGPathNode * getBestInitialNodeForTownPortal(const CGTownInstance * targetTown)
  798. {
  799. CGPathNode * bestNode = nullptr;
  800. for(CGPathNode * node : initialNodes)
  801. {
  802. auto aiNode = nodeStorage->getAINode(node);
  803. if(aiNode->actor->baseActor != actor
  804. || node->layer != EPathfindingLayer::LAND
  805. || node->moveRemains < movementNeeded)
  806. {
  807. continue;
  808. }
  809. if(townPortalSkillLevel < SecSkillLevel::ADVANCED)
  810. {
  811. const CGTownInstance * nearestTown = *vstd::minElementByFun(targetTowns, [&](const CGTownInstance * t) -> int
  812. {
  813. return node->coord.dist2dSQ(t->visitablePos());
  814. });
  815. if(targetTown != nearestTown)
  816. continue;
  817. }
  818. if(!bestNode || bestNode->getCost() > node->getCost())
  819. bestNode = node;
  820. }
  821. return bestNode;
  822. }
  823. boost::optional<AIPathNode *> createTownPortalNode(const CGTownInstance * targetTown)
  824. {
  825. auto bestNode = getBestInitialNodeForTownPortal(targetTown);
  826. if(!bestNode)
  827. return boost::none;
  828. auto nodeOptional = nodeStorage->getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, actor->castActor);
  829. if(!nodeOptional)
  830. return boost::none;
  831. AIPathNode * node = nodeOptional.get();
  832. float movementCost = (float)movementNeeded / (float)hero->maxMovePoints(EPathfindingLayer::LAND);
  833. movementCost += bestNode->getCost();
  834. if(node->action == CGPathNode::UNKNOWN || node->getCost() > movementCost)
  835. {
  836. nodeStorage->commit(
  837. node,
  838. nodeStorage->getAINode(bestNode),
  839. CGPathNode::TELEPORT_NORMAL,
  840. bestNode->turns,
  841. bestNode->moveRemains - movementNeeded,
  842. movementCost);
  843. node->theNodeBefore = bestNode;
  844. node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown));
  845. }
  846. return nodeOptional;
  847. }
  848. };
  849. void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *> & initialNodes)
  850. {
  851. std::set<const ChainActor *> actorsOfInitial;
  852. for(const CGPathNode * node : initialNodes)
  853. {
  854. auto aiNode = getAINode(node);
  855. actorsOfInitial.insert(aiNode->actor->baseActor);
  856. }
  857. std::map<const CGHeroInstance *, int> maskMap;
  858. for(std::shared_ptr<ChainActor> basicActor : actors)
  859. {
  860. if(basicActor->hero)
  861. maskMap[basicActor->hero] = basicActor->chainMask;
  862. }
  863. for(const ChainActor * actor : actorsOfInitial)
  864. {
  865. if(!actor->hero)
  866. continue;
  867. auto towns = cb->getTownsInfo(false);
  868. vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
  869. {
  870. return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
  871. });
  872. if(!towns.size())
  873. {
  874. return; // no towns no need to run loop further
  875. }
  876. TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
  877. if(townPortalFinder.actorCanCastTownPortal())
  878. {
  879. for(const CGTownInstance * targetTown : towns)
  880. {
  881. // TODO: allow to hide visiting hero in garrison
  882. if(targetTown->visitingHero)
  883. {
  884. auto basicMask = maskMap[targetTown->visitingHero.get()];
  885. bool heroIsInChain = (actor->chainMask & basicMask) != 0;
  886. bool sameActorInTown = actor->chainMask == basicMask;
  887. if(sameActorInTown || !heroIsInChain)
  888. continue;
  889. }
  890. auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
  891. if(nodeOptional)
  892. {
  893. #if PATHFINDER_TRACE_LEVEL >= 1
  894. logAi->trace("Adding town portal node at %s", targetTown->name);
  895. #endif
  896. initialNodes.push_back(nodeOptional.get());
  897. }
  898. }
  899. }
  900. }
  901. }
  902. bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
  903. {
  904. auto pos = destination.coord;
  905. auto chains = nodes.get(pos, EPathfindingLayer::LAND);
  906. return hasBetterChain(source.node, getAINode(destination.node), chains);
  907. }
  908. template<class NodeRange>
  909. bool AINodeStorage::hasBetterChain(
  910. const CGPathNode * source,
  911. const AIPathNode * candidateNode,
  912. const NodeRange & chains) const
  913. {
  914. auto candidateActor = candidateNode->actor;
  915. for(const AIPathNode & node : chains)
  916. {
  917. auto sameNode = node.actor == candidateNode->actor;
  918. if(sameNode || node.action == CGPathNode::ENodeAction::UNKNOWN || !node.actor->hero)
  919. {
  920. continue;
  921. }
  922. if(node.danger <= candidateNode->danger && candidateNode->actor == node.actor->battleActor)
  923. {
  924. if(node.getCost() < candidateNode->getCost())
  925. {
  926. #if PATHFINDER_TRACE_LEVEL >= 2
  927. logAi->trace(
  928. "Block ineficient battle move %s->%s, hero: %s[%X], army %lld, mp diff: %i",
  929. source->coord.toString(),
  930. candidateNode->coord.toString(),
  931. candidateNode->actor->hero->name,
  932. candidateNode->actor->chainMask,
  933. candidateNode->actor->armyValue,
  934. node.moveRemains - candidateNode->moveRemains);
  935. #endif
  936. return true;
  937. }
  938. }
  939. if(candidateActor->chainMask != node.actor->chainMask && heroChainPass != EHeroChainPass::FINAL)
  940. continue;
  941. auto nodeActor = node.actor;
  942. auto nodeArmyValue = nodeActor->armyValue - node.armyLoss;
  943. auto candidateArmyValue = candidateActor->armyValue - candidateNode->armyLoss;
  944. if(nodeArmyValue > candidateArmyValue
  945. && node.getCost() <= candidateNode->getCost())
  946. {
  947. #if PATHFINDER_TRACE_LEVEL >= 2
  948. logAi->trace(
  949. "Block ineficient move because of stronger army %s->%s, hero: %s[%X], army %lld, mp diff: %i",
  950. source->coord.toString(),
  951. candidateNode->coord.toString(),
  952. candidateNode->actor->hero->name,
  953. candidateNode->actor->chainMask,
  954. candidateNode->actor->armyValue,
  955. node.moveRemains - candidateNode->moveRemains);
  956. #endif
  957. return true;
  958. }
  959. if(heroChainPass == EHeroChainPass::FINAL)
  960. {
  961. if(nodeArmyValue == candidateArmyValue
  962. && nodeActor->heroFightingStrength >= candidateActor->heroFightingStrength
  963. && node.getCost() <= candidateNode->getCost())
  964. {
  965. if(nodeActor->heroFightingStrength == candidateActor->heroFightingStrength
  966. && node.getCost() == candidateNode->getCost()
  967. && &node < candidateNode)
  968. {
  969. continue;
  970. }
  971. #if AI_TRACE_LEVEL >= 2
  972. logAi->trace(
  973. "Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
  974. source->coord.toString(),
  975. candidateNode->coord.toString(),
  976. candidateNode->actor->hero->name,
  977. candidateNode->actor->chainMask,
  978. candidateNode->actor->armyValue,
  979. node.moveRemains - candidateNode->moveRemains);
  980. #endif
  981. return true;
  982. }
  983. }
  984. }
  985. return false;
  986. }
  987. bool AINodeStorage::isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const
  988. {
  989. auto chains = nodes.get(pos, layer);
  990. for(const AIPathNode & node : chains)
  991. {
  992. if(node.action != CGPathNode::ENodeAction::UNKNOWN
  993. && node.actor && node.actor->hero == hero.h)
  994. {
  995. return true;
  996. }
  997. }
  998. return false;
  999. }
  1000. std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
  1001. {
  1002. std::vector<AIPath> paths;
  1003. paths.reserve(NUM_CHAINS / 4);
  1004. auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
  1005. for(const AIPathNode & node : chains)
  1006. {
  1007. if(node.action == CGPathNode::ENodeAction::UNKNOWN || !node.actor || !node.actor->hero)
  1008. {
  1009. continue;
  1010. }
  1011. AIPath path;
  1012. path.targetHero = node.actor->hero;
  1013. path.heroArmy = node.actor->creatureSet;
  1014. path.armyLoss = node.armyLoss;
  1015. path.targetObjectDanger = evaluateDanger(pos, path.targetHero, false);
  1016. path.targetObjectArmyLoss = evaluateArmyLoss(path.targetHero, path.heroArmy->getArmyStrength(), path.targetObjectDanger);
  1017. path.chainMask = node.actor->chainMask;
  1018. path.exchangeCount = node.actor->actorExchangeCount;
  1019. fillChainInfo(&node, path, -1);
  1020. paths.push_back(path);
  1021. }
  1022. return paths;
  1023. }
  1024. void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const
  1025. {
  1026. while(node != nullptr)
  1027. {
  1028. if(!node->actor->hero)
  1029. return;
  1030. if(node->chainOther)
  1031. fillChainInfo(node->chainOther, path, parentIndex);
  1032. //if(node->actor->hero->visitablePos() != node->coord)
  1033. {
  1034. AIPathNodeInfo pathNode;
  1035. pathNode.cost = node->getCost();
  1036. pathNode.targetHero = node->actor->hero;
  1037. pathNode.chainMask = node->actor->chainMask;
  1038. pathNode.specialAction = node->specialAction;
  1039. pathNode.turns = node->turns;
  1040. pathNode.danger = node->danger;
  1041. pathNode.coord = node->coord;
  1042. pathNode.parentIndex = parentIndex;
  1043. pathNode.actionIsBlocked = false;
  1044. if(pathNode.specialAction)
  1045. {
  1046. auto targetNode =node->theNodeBefore ? getAINode(node->theNodeBefore) : node;
  1047. pathNode.actionIsBlocked = !pathNode.specialAction->canAct(targetNode);
  1048. }
  1049. parentIndex = path.nodes.size();
  1050. path.nodes.push_back(pathNode);
  1051. }
  1052. node = getAINode(node->theNodeBefore);
  1053. }
  1054. }
  1055. AIPath::AIPath()
  1056. : nodes({})
  1057. {
  1058. }
  1059. std::shared_ptr<const SpecialAction> AIPath::getFirstBlockedAction() const
  1060. {
  1061. for(auto node = nodes.rbegin(); node != nodes.rend(); node++)
  1062. {
  1063. if(node->specialAction && node->actionIsBlocked)
  1064. return node->specialAction;
  1065. }
  1066. return std::shared_ptr<const SpecialAction>();
  1067. }
  1068. int3 AIPath::firstTileToGet() const
  1069. {
  1070. if(nodes.size())
  1071. {
  1072. return nodes.back().coord;
  1073. }
  1074. return int3(-1, -1, -1);
  1075. }
  1076. int3 AIPath::targetTile() const
  1077. {
  1078. if(nodes.size())
  1079. {
  1080. return targetNode().coord;
  1081. }
  1082. return int3(-1, -1, -1);
  1083. }
  1084. const AIPathNodeInfo & AIPath::firstNode() const
  1085. {
  1086. return nodes.back();
  1087. }
  1088. const AIPathNodeInfo & AIPath::targetNode() const
  1089. {
  1090. auto & node = nodes.front();
  1091. return targetHero == node.targetHero ? node : nodes.at(1);
  1092. }
  1093. uint64_t AIPath::getPathDanger() const
  1094. {
  1095. if(nodes.empty())
  1096. return 0;
  1097. return targetNode().danger;
  1098. }
  1099. float AIPath::movementCost() const
  1100. {
  1101. if(nodes.empty())
  1102. return 0.0f;
  1103. return targetNode().cost;
  1104. }
  1105. uint8_t AIPath::turn() const
  1106. {
  1107. if(nodes.empty())
  1108. return 0;
  1109. return targetNode().turns;
  1110. }
  1111. uint64_t AIPath::getHeroStrength() const
  1112. {
  1113. return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
  1114. }
  1115. uint64_t AIPath::getTotalDanger() const
  1116. {
  1117. uint64_t pathDanger = getPathDanger();
  1118. uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger;
  1119. return danger;
  1120. }
  1121. bool AIPath::containsHero(const CGHeroInstance * hero) const
  1122. {
  1123. if(targetHero == hero)
  1124. return true;
  1125. for(auto node : nodes)
  1126. {
  1127. if(node.targetHero == hero)
  1128. return true;
  1129. }
  1130. return false;
  1131. }
  1132. uint64_t AIPath::getTotalArmyLoss() const
  1133. {
  1134. return armyLoss + targetObjectArmyLoss;
  1135. }
  1136. std::string AIPath::toString() const
  1137. {
  1138. std::stringstream str;
  1139. str << targetHero->name << "[" << std::hex << chainMask << std::dec << "]" << ", turn " << (int)(turn()) << ": ";
  1140. for(auto node : nodes)
  1141. str << node.targetHero->name << "[" << std::hex << node.chainMask << std::dec << "]" << "->" << node.coord.toString() << "; ";
  1142. return str.str();
  1143. }