1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

AI: separate hero chain recalculation

This commit is contained in:
Andrii Danylchenko
2021-05-15 21:01:48 +03:00
committed by Andrii Danylchenko
parent c1e521a544
commit a88181acd7
12 changed files with 226 additions and 101 deletions

View File

@@ -199,9 +199,9 @@ bool isSafeToVisit(HeroPtr h, crint3 tile)
return isSafeToVisit(h, fh->evaluateDanger(tile, h.get())); return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
} }
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength) bool isSafeToVisit(HeroPtr h, const CCreatureSet * heroArmy, uint64_t dangerStrength)
{ {
const ui64 heroStrength = h->getTotalStrength(); const ui64 heroStrength = h->getFightingStrength() * heroArmy->getArmyStrength();
if(dangerStrength) if(dangerStrength)
{ {
@@ -218,6 +218,11 @@ bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
return true; //there's no danger return true; //there's no danger
} }
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
{
return isSafeToVisit(h, h.get(), dangerStrength);
}
bool isObjectRemovable(const CGObjectInstance * obj) bool isObjectRemovable(const CGObjectInstance * obj)
{ {
//FIXME: move logic to object property! //FIXME: move logic to object property!

View File

@@ -168,6 +168,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property! bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength); bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
bool isSafeToVisit(HeroPtr h, const CCreatureSet *, uint64_t dangerStrength);
bool isSafeToVisit(HeroPtr h, crint3 tile); bool isSafeToVisit(HeroPtr h, crint3 tile);
bool compareHeroStrength(HeroPtr h1, HeroPtr h2); bool compareHeroStrength(HeroPtr h1, HeroPtr h2);

View File

@@ -94,11 +94,15 @@ namespace Goals
float movementCost; float movementCost;
int manaCost; int manaCost;
uint64_t danger; uint64_t danger;
uint64_t armyLoss;
uint64_t heroStrength;
EvaluationContext() EvaluationContext()
: movementCost(0.0), : movementCost(0.0),
manaCost(0), manaCost(0),
danger(0) danger(0),
armyLoss(0),
heroStrength(0)
{ {
} }
}; };

View File

@@ -28,8 +28,10 @@ AINodeStorage::~AINodeStorage() = default;
void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs) void AINodeStorage::initialize(const PathfinderOptions & options, const CGameState * gs)
{ {
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline if(heroChainPass)
return;
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
int3 pos; int3 pos;
const PlayerColor player = ai->playerID; const PlayerColor player = ai->playerID;
const int3 sizes = gs->getMapSize(); const int3 sizes = gs->getMapSize();
@@ -73,6 +75,7 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
void AINodeStorage::clear() void AINodeStorage::clear()
{ {
actors.clear(); actors.clear();
heroChainPass = false;
} }
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
@@ -114,6 +117,9 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
std::vector<CGPathNode *> AINodeStorage::getInitialNodes() std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
{ {
if(heroChainPass)
return heroChain;
std::vector<CGPathNode *> initialNodes; std::vector<CGPathNode *> initialNodes;
for(auto actorPtr : actors) for(auto actorPtr : actors)
@@ -152,6 +158,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat
heroNode.manaCost = 0; heroNode.manaCost = 0;
heroNode.specialAction.reset(); heroNode.specialAction.reset();
heroNode.armyLoss = 0; heroNode.armyLoss = 0;
heroNode.chainOther = nullptr;
heroNode.update(coord, layer, accessibility); heroNode.update(coord, layer, accessibility);
} }
} }
@@ -162,14 +169,7 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
updateAINode(destination.node, [&](AIPathNode * dstNode) updateAINode(destination.node, [&](AIPathNode * dstNode)
{ {
dstNode->moveRemains = destination.movementLeft; commit(dstNode, srcNode, destination.action, destination.turn, destination.movementLeft, destination.cost);
dstNode->turns = destination.turn;
dstNode->cost = destination.cost;
dstNode->danger = srcNode->danger;
dstNode->action = destination.action;
dstNode->theNodeBefore = srcNode->theNodeBefore;
dstNode->manaCost = srcNode->manaCost;
dstNode->armyLoss = srcNode->armyLoss;
if(dstNode->specialAction && dstNode->actor) if(dstNode->specialAction && dstNode->actor)
{ {
@@ -178,6 +178,24 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
}); });
} }
void AINodeStorage::commit(
AIPathNode * destination,
const AIPathNode * source,
CGPathNode::ENodeAction action,
int turn,
int movementLeft,
float cost) const
{
destination->action = source->action;
destination->cost = cost;
destination->moveRemains = movementLeft;
destination->turns = turn;
destination->armyLoss = source->armyLoss;
destination->manaCost = source->manaCost;
destination->danger = source->danger;
destination->theNodeBefore = source->theNodeBefore;
}
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours( std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
const PathNodeInfo & source, const PathNodeInfo & source,
const PathfinderConfig * pathfinderConfig, const PathfinderConfig * pathfinderConfig,
@@ -201,41 +219,55 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
} }
} }
if((source.node->layer == EPathfindingLayer::LAND || source.node->layer == EPathfindingLayer::SAIL)
&& source.node->turns < 1)
{
addHeroChain(neighbours, srcNode);
}
return neighbours; return neighbours;
} }
void AINodeStorage::addHeroChain(std::vector<CGPathNode *> & result, const AIPathNode * srcNode) bool AINodeStorage::calculateHeroChain()
{
heroChainPass = true;
heroChain.resize(0);
foreach_tile_pos([&](const int3 & pos) {
auto layer = EPathfindingLayer::LAND;
auto chains = nodes[pos.x][pos.y][pos.z][layer];
for(AIPathNode & node : chains)
{
if(node.locked && node.turns < 1)
addHeroChain(&node);
}
});
return heroChain.size();
}
void AINodeStorage::addHeroChain(AIPathNode * srcNode)
{ {
auto chains = nodes[srcNode->coord.x][srcNode->coord.y][srcNode->coord.z][srcNode->layer]; auto chains = nodes[srcNode->coord.x][srcNode->coord.y][srcNode->coord.z][srcNode->layer];
for(const AIPathNode & node : chains) for(AIPathNode & node : chains)
{ {
if(!node.locked || !node.actor || node.action == CGPathNode::ENodeAction::UNKNOWN && node.actor->hero) if(!node.locked || !node.actor || node.action == CGPathNode::ENodeAction::UNKNOWN && node.actor->hero)
{ {
continue; continue;
} }
addHeroChain(result, srcNode, &node); addHeroChain(srcNode, &node);
addHeroChain(result, &node, srcNode); addHeroChain(&node, srcNode);
} }
} }
void AINodeStorage::addHeroChain( void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
std::vector<CGPathNode *> & result,
const AIPathNode * carrier,
const AIPathNode * other)
{ {
if(carrier->actor->canExchange(other->actor)) if(carrier->actor->canExchange(other->actor))
{ {
bool hasLessMp = carrier->turns > other->turns || carrier->moveRemains < other->moveRemains; bool hasLessMp = carrier->turns > other->turns || carrier->moveRemains < other->moveRemains;
bool hasLessExperience = carrier->actor->hero->exp < other->actor->hero->exp; bool hasLessExperience = carrier->actor->hero->exp < other->actor->hero->exp;
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Check hero exhange at %s, %s -> %s", carrier->coord.toString(), other->actor->hero->name, carrier->actor->hero->name);
#endif
if(hasLessMp && hasLessExperience) if(hasLessMp && hasLessExperience)
return; return;
@@ -250,12 +282,59 @@ void AINodeStorage::addHeroChain(
if(chainNode->locked) if(chainNode->locked)
return; return;
chainNode->specialAction = newActor->getExchangeAction(); #ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Hero exhange at %s, %s -> %s", carrier->coord.toString(), other->actor->hero->name, carrier->actor->hero->name);
#endif
result.push_back(chainNode); commitExchange(chainNode, carrier, other);
heroChain.push_back(chainNode);
} }
} }
void AINodeStorage::commitExchange(
AIPathNode * exchangeNode,
AIPathNode * carrierParentNode,
AIPathNode * otherParentNode) const
{
auto carrierActor = carrierParentNode->actor;
auto exchangeActor = exchangeNode->actor;
auto otherActor = otherParentNode->actor;
auto armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
auto turns = carrierParentNode->turns;
auto cost = carrierParentNode->cost;
auto movementLeft = carrierParentNode->moveRemains;
if(carrierParentNode->turns < otherParentNode->turns)
{
int moveRemains = exchangeActor->hero->maxMovePoints(exchangeNode->layer);
float waitingCost = otherParentNode->turns - carrierParentNode->turns - 1
+ carrierParentNode->moveRemains / (float)moveRemains;
turns = otherParentNode->turns;
cost = waitingCost;
movementLeft = moveRemains;
}
if(exchangeNode->turns != 0xFF && exchangeNode->cost < cost)
return;
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace(
"Accepted hero exhange at %s, carrier %s, mp cost %f",
destination.coord.toString(),
carrierActor->hero->name,
destination.cost);
#endif
commit(exchangeNode, carrierParentNode, carrierParentNode->action, turns, movementLeft, cost);
exchangeNode->theNodeBefore = carrierParentNode;
exchangeNode->chainOther = otherParentNode;
exchangeNode->armyLoss = armyLoss;
exchangeNode->manaCost = carrierParentNode->manaCost;
}
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
{ {
auto aiNode = getAINode(node); auto aiNode = getAINode(node);
@@ -441,6 +520,9 @@ bool AINodeStorage::isTileAccessible(const HeroPtr & hero, const int3 & pos, con
std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
{ {
std::vector<AIPath> paths; std::vector<AIPath> paths;
paths.reserve(NUM_CHAINS / 4);
auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL]; auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
for(const AIPathNode & node : chains) for(const AIPathNode & node : chains)
@@ -451,33 +533,44 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
} }
AIPath path; AIPath path;
const AIPathNode * current = &node;
path.targetHero = node.actor->hero; path.targetHero = node.actor->hero;
auto initialPos = path.targetHero->visitablePos(); path.heroArmy = node.actor->creatureSet;
path.armyLoss = node.armyLoss;
while(current != nullptr && current->coord != initialPos)
{
AIPathNodeInfo pathNode;
pathNode.cost = current->cost;
pathNode.turns = current->turns;
pathNode.danger = current->danger;
pathNode.coord = current->coord;
path.nodes.push_back(pathNode);
path.specialAction = current->specialAction;
current = getAINode(current->theNodeBefore);
}
path.targetObjectDanger = evaluateDanger(pos, path.targetHero); path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
fillChainInfo(&node, path);
paths.push_back(path); paths.push_back(path);
} }
return paths; return paths;
} }
void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path) const
{
while(node != nullptr)
{
if(!node->actor->hero || node->coord == node->actor->hero->visitablePos())
return;
AIPathNodeInfo pathNode;
pathNode.cost = node->cost;
pathNode.targetHero = node->actor->hero;
pathNode.turns = node->turns;
pathNode.danger = node->danger;
pathNode.coord = node->coord;
path.nodes.push_back(pathNode);
path.specialAction = node->specialAction;
if(node->chainOther)
fillChainInfo(node->chainOther, path);
node = getAINode(node->theNodeBefore);
}
}
AIPath::AIPath() AIPath::AIPath()
: nodes({}) : nodes({})
{ {
@@ -514,6 +607,11 @@ float AIPath::movementCost() const
return 0.0; return 0.0;
} }
uint64_t AIPath::getHeroStrength() const
{
return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
}
uint64_t AIPath::getTotalDanger(HeroPtr hero) const uint64_t AIPath::getTotalDanger(HeroPtr hero) const
{ {
uint64_t pathDanger = getPathDanger(); uint64_t pathDanger = getPathDanger();

View File

@@ -23,6 +23,7 @@ struct AIPathNode : public CGPathNode
uint64_t danger; uint64_t danger;
uint64_t armyLoss; uint64_t armyLoss;
uint32_t manaCost; uint32_t manaCost;
const AIPathNode * chainOther;
std::shared_ptr<const ISpecialAction> specialAction; std::shared_ptr<const ISpecialAction> specialAction;
const ChainActor * actor; const ChainActor * actor;
}; };
@@ -33,6 +34,7 @@ struct AIPathNodeInfo
int turns; int turns;
int3 coord; int3 coord;
uint64_t danger; uint64_t danger;
const CGHeroInstance * targetHero;
}; };
struct AIPath struct AIPath
@@ -40,7 +42,9 @@ struct AIPath
std::vector<AIPathNodeInfo> nodes; std::vector<AIPathNodeInfo> nodes;
std::shared_ptr<const ISpecialAction> specialAction; std::shared_ptr<const ISpecialAction> specialAction;
uint64_t targetObjectDanger; uint64_t targetObjectDanger;
uint64_t armyLoss;
const CGHeroInstance * targetHero; const CGHeroInstance * targetHero;
const CCreatureSet * heroArmy;
AIPath(); AIPath();
@@ -53,6 +57,8 @@ struct AIPath
int3 firstTileToGet() const; int3 firstTileToGet() const;
float movementCost() const; float movementCost() const;
uint64_t getHeroStrength() const;
}; };
class AINodeStorage : public INodeStorage class AINodeStorage : public INodeStorage
@@ -66,11 +72,9 @@ private:
const VCAI * ai; const VCAI * ai;
std::unique_ptr<FuzzyHelper> dangerEvaluator; std::unique_ptr<FuzzyHelper> dangerEvaluator;
std::vector<std::shared_ptr<ChainActor>> actors; std::vector<std::shared_ptr<ChainActor>> actors;
std::vector<CGPathNode *> heroChain;
bool heroChainPass; // true if we need to calculate hero chain
STRONG_INLINE
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
void addHeroChain(std::vector<CGPathNode *> & result, const AIPathNode * srcNode);
void addHeroChain(std::vector<CGPathNode *> & result, const AIPathNode * carrier, const AIPathNode * other);
public: public:
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one. /// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
static const int NUM_CHAINS = 3 * GameConstants::MAX_HEROES_PER_PLAYER; static const int NUM_CHAINS = 3 * GameConstants::MAX_HEROES_PER_PLAYER;
@@ -101,14 +105,11 @@ public:
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor); boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const; std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const; bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
void setHeroes(std::vector<HeroPtr> heroes, const VCAI * ai); void setHeroes(std::vector<HeroPtr> heroes, const VCAI * ai);
const CGHeroInstance * getHero(const CGPathNode * node) const; const CGHeroInstance * getHero(const CGPathNode * node) const;
const std::set<const CGHeroInstance *> getAllHeroes() const; const std::set<const CGHeroInstance *> getAllHeroes() const;
void clear(); void clear();
bool calculateHeroChain();
uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero) const uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero) const
{ {
@@ -116,5 +117,22 @@ public:
} }
private: private:
STRONG_INLINE
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
void addHeroChain(AIPathNode * srcNode);
void addHeroChain(AIPathNode * carrier, AIPathNode * other);
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours); void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
void fillChainInfo(const AIPathNode * node, AIPath & path) const;
void commit(
AIPathNode * destination,
const AIPathNode * source,
CGPathNode::ENodeAction action,
int turn,
int movementLeft,
float cost) const;
void AINodeStorage::commitExchange(
AIPathNode * exchangeNode,
AIPathNode * carrierParentNode,
AIPathNode * otherParentNode) const;
}; };

View File

@@ -43,7 +43,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 &
return storage->getChainInfo(tile, !tileInfo->isWater()); return storage->getChainInfo(tile, !tileInfo->isWater());
} }
void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes) void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
{ {
if(!storage) if(!storage)
{ {
@@ -55,8 +55,14 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
storage->clear(); storage->clear();
storage->setHeroes(heroes, ai); storage->setHeroes(heroes, ai);
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage); auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
cb->calculatePaths(config);
while(useHeroChain && storage->calculateHeroChain())
{
config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);
cb->calculatePaths(config); cb->calculatePaths(config);
}
} }
void AIPathfinder::updatePaths(const HeroPtr & hero) void AIPathfinder::updatePaths(const HeroPtr & hero)

View File

@@ -25,7 +25,7 @@ public:
AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai); AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
std::vector<AIPath> getPathInfo(const HeroPtr & hero, const int3 & tile) const; std::vector<AIPath> getPathInfo(const HeroPtr & hero, const int3 & tile) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const; bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
void updatePaths(std::vector<HeroPtr> heroes); void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false);
void updatePaths(const HeroPtr & heroes); void updatePaths(const HeroPtr & heroes);
void init(); void init();
}; };

View File

@@ -13,6 +13,7 @@
#include "Rules/AIMovementAfterDestinationRule.h" #include "Rules/AIMovementAfterDestinationRule.h"
#include "Rules/AIMovementToDestinationRule.h" #include "Rules/AIMovementToDestinationRule.h"
#include "Rules/AIPreviousNodeRule.h" #include "Rules/AIPreviousNodeRule.h"
#include "Rules/AIMovementCostRule.h"
namespace AIPathfinding namespace AIPathfinding
{ {
@@ -25,7 +26,7 @@ namespace AIPathfinding
std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage), std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
std::make_shared<DestinationActionRule>(), std::make_shared<DestinationActionRule>(),
std::make_shared<AIMovementToDestinationRule>(nodeStorage), std::make_shared<AIMovementToDestinationRule>(nodeStorage),
std::make_shared<MovementCostRule>(), std::make_shared<AIMovementCostRule>(nodeStorage),
std::make_shared<AIPreviousNodeRule>(nodeStorage), std::make_shared<AIPreviousNodeRule>(nodeStorage),
std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage) std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage)
}; };

View File

@@ -48,7 +48,7 @@ ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, con
carrierParent(carrier), otherParent(other), chainMask(carrier->chainMask | other->chainMask) carrierParent(carrier), otherParent(other), chainMask(carrier->chainMask | other->chainMask)
{ {
baseActor = static_cast<HeroActor *>(this); baseActor = static_cast<HeroActor *>(this);
armyValue = heroArmy->getArmyStrength(); armyValue = hero->getFightingStrength() * heroArmy->getArmyStrength();
} }
HeroActor::HeroActor(const CGHeroInstance * hero, int chainMask) HeroActor::HeroActor(const CGHeroInstance * hero, int chainMask)

View File

@@ -136,7 +136,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
{ {
danger = path.getTotalDanger(hero); danger = path.getTotalDanger(hero);
if(isSafeToVisit(hero, danger)) if(isSafeToVisit(hero, path.heroArmy, danger))
{ {
Goals::TSubgoal solution; Goals::TSubgoal solution;
@@ -158,6 +158,8 @@ Goals::TGoalVec PathfindingManager::findPaths(
solution->evaluationContext.danger = danger; solution->evaluationContext.danger = danger;
solution->evaluationContext.movementCost += path.movementCost(); solution->evaluationContext.movementCost += path.movementCost();
solution->evaluationContext.armyLoss += path.armyLoss;
solution->evaluationContext.heroStrength = path.getHeroStrength();
#ifdef VCMI_TRACE_PATHFINDER #ifdef VCMI_TRACE_PATHFINDER
logAi->trace("It's safe for %s to visit tile %s with danger %s, goal %s", hero->name, dest.toString(), std::to_string(danger), solution->name()); logAi->trace("It's safe for %s to visit tile %s with danger %s, goal %s", hero->name, dest.toString(), std::to_string(danger), solution->name());
#endif #endif

View File

@@ -23,44 +23,5 @@ namespace AIPathfinding
const PathfinderConfig * pathfinderConfig, const PathfinderConfig * pathfinderConfig,
CPathfinderHelper * pathfinderHelper) const CPathfinderHelper * pathfinderHelper) const
{ {
auto srcNode = nodeStorage->getAINode(source.node);
auto dstNode = nodeStorage->getAINode(destination.node);
auto srcActor = srcNode->actor;
auto dstActor = dstNode->actor;
if(srcActor == dstActor)
{
MovementCostRule::process(source, destination, pathfinderConfig, pathfinderHelper);
return;
}
auto carrierActor = dstActor->carrierParent;
auto otherActor = dstActor->otherParent;
if(source.coord == destination.coord)
{
auto carrierNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, carrierActor).get();
auto otherNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, otherActor).get();
if(carrierNode->turns >= otherNode->turns)
{
destination.turn = carrierNode->turns;
destination.cost = carrierNode->cost;
return;
}
double waitingCost = otherNode->turns - carrierNode->turns - 1.0
+ carrierNode->moveRemains / (double)pathfinderHelper->getMaxMovePoints(carrierNode->layer);
destination.turn = otherNode->turns;
destination.cost = waitingCost;
}
else
{
// TODO: exchange through sail->land border might be more sofisticated
destination.blocked = true;
}
} }
} }

View File

@@ -36,12 +36,41 @@ namespace AIPathfinding
return; return;
} }
auto aiSourceNode = nodeStorage->getAINode(source.node); auto srcNode = nodeStorage->getAINode(source.node);
if(aiSourceNode->specialAction) if(srcNode->specialAction)
{ {
// there is some action on source tile which should be performed before we can bypass it // there is some action on source tile which should be performed before we can bypass it
destination.node->theNodeBefore = source.node; destination.node->theNodeBefore = source.node;
} }
auto dstNode = nodeStorage->getAINode(destination.node);
auto srcActor = srcNode->actor;
auto dstActor = dstNode->actor;
if(srcActor == dstActor)
{
return;
}
auto carrierActor = dstActor->carrierParent;
auto otherActor = dstActor->otherParent;
nodeStorage->updateAINode(destination.node, [&](AIPathNode * dstNode) {
if(source.coord == destination.coord)
{
auto carrierNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, carrierActor).get();
auto otherNode = nodeStorage->getOrCreateNode(source.coord, source.node->layer, otherActor).get();
if(destination.coord != carrierNode->coord)
dstNode->theNodeBefore = carrierNode;
dstNode->chainOther = otherNode;
#ifdef VCMI_TRACE_PATHFINDER
logAi->trace("Link Hero exhange at %s, %s -> %s", dstNode->coord.toString(), otherNode->actor->hero->name, carrierNode->actor->hero->name);
#endif
}
});
} }
} }