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:
committed by
Andrii Danylchenko
parent
c1e521a544
commit
a88181acd7
@@ -199,9 +199,9 @@ bool isSafeToVisit(HeroPtr h, crint3 tile)
|
||||
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)
|
||||
{
|
||||
@@ -218,6 +218,11 @@ bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
|
||||
return true; //there's no danger
|
||||
}
|
||||
|
||||
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
|
||||
{
|
||||
return isSafeToVisit(h, h.get(), dangerStrength);
|
||||
}
|
||||
|
||||
bool isObjectRemovable(const CGObjectInstance * obj)
|
||||
{
|
||||
//FIXME: move logic to object property!
|
||||
|
@@ -168,6 +168,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
|
||||
|
||||
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
|
||||
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
|
||||
bool isSafeToVisit(HeroPtr h, const CCreatureSet *, uint64_t dangerStrength);
|
||||
bool isSafeToVisit(HeroPtr h, crint3 tile);
|
||||
|
||||
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
||||
|
@@ -94,11 +94,15 @@ namespace Goals
|
||||
float movementCost;
|
||||
int manaCost;
|
||||
uint64_t danger;
|
||||
uint64_t armyLoss;
|
||||
uint64_t heroStrength;
|
||||
|
||||
EvaluationContext()
|
||||
: movementCost(0.0),
|
||||
manaCost(0),
|
||||
danger(0)
|
||||
danger(0),
|
||||
armyLoss(0),
|
||||
heroStrength(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@@ -28,8 +28,10 @@ AINodeStorage::~AINodeStorage() = default;
|
||||
|
||||
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;
|
||||
const PlayerColor player = ai->playerID;
|
||||
const int3 sizes = gs->getMapSize();
|
||||
@@ -73,6 +75,7 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
|
||||
void AINodeStorage::clear()
|
||||
{
|
||||
actors.clear();
|
||||
heroChainPass = false;
|
||||
}
|
||||
|
||||
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
|
||||
@@ -114,6 +117,9 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
||||
|
||||
std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
||||
{
|
||||
if(heroChainPass)
|
||||
return heroChain;
|
||||
|
||||
std::vector<CGPathNode *> initialNodes;
|
||||
|
||||
for(auto actorPtr : actors)
|
||||
@@ -152,6 +158,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, CGPat
|
||||
heroNode.manaCost = 0;
|
||||
heroNode.specialAction.reset();
|
||||
heroNode.armyLoss = 0;
|
||||
heroNode.chainOther = nullptr;
|
||||
heroNode.update(coord, layer, accessibility);
|
||||
}
|
||||
}
|
||||
@@ -162,14 +169,7 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
||||
|
||||
updateAINode(destination.node, [&](AIPathNode * dstNode)
|
||||
{
|
||||
dstNode->moveRemains = destination.movementLeft;
|
||||
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;
|
||||
commit(dstNode, srcNode, destination.action, destination.turn, destination.movementLeft, destination.cost);
|
||||
|
||||
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(
|
||||
const PathNodeInfo & source,
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
for(const AIPathNode & node : chains)
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
if(!node.locked || !node.actor || node.action == CGPathNode::ENodeAction::UNKNOWN && node.actor->hero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
addHeroChain(result, srcNode, &node);
|
||||
addHeroChain(result, &node, srcNode);
|
||||
addHeroChain(srcNode, &node);
|
||||
addHeroChain(&node, srcNode);
|
||||
}
|
||||
}
|
||||
|
||||
void AINodeStorage::addHeroChain(
|
||||
std::vector<CGPathNode *> & result,
|
||||
const AIPathNode * carrier,
|
||||
const AIPathNode * other)
|
||||
void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
|
||||
{
|
||||
if(carrier->actor->canExchange(other->actor))
|
||||
{
|
||||
bool hasLessMp = carrier->turns > other->turns || carrier->moveRemains < other->moveRemains;
|
||||
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)
|
||||
return;
|
||||
|
||||
@@ -250,12 +282,59 @@ void AINodeStorage::addHeroChain(
|
||||
if(chainNode->locked)
|
||||
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
|
||||
{
|
||||
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> paths;
|
||||
|
||||
paths.reserve(NUM_CHAINS / 4);
|
||||
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL];
|
||||
|
||||
for(const AIPathNode & node : chains)
|
||||
@@ -451,33 +533,44 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
||||
}
|
||||
|
||||
AIPath path;
|
||||
const AIPathNode * current = &node;
|
||||
|
||||
path.targetHero = node.actor->hero;
|
||||
auto initialPos = path.targetHero->visitablePos();
|
||||
|
||||
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.heroArmy = node.actor->creatureSet;
|
||||
path.armyLoss = node.armyLoss;
|
||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
|
||||
|
||||
fillChainInfo(&node, path);
|
||||
|
||||
paths.push_back(path);
|
||||
}
|
||||
|
||||
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()
|
||||
: nodes({})
|
||||
{
|
||||
@@ -514,6 +607,11 @@ float AIPath::movementCost() const
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
uint64_t AIPath::getHeroStrength() const
|
||||
{
|
||||
return targetHero->getFightingStrength() * heroArmy->getArmyStrength();
|
||||
}
|
||||
|
||||
uint64_t AIPath::getTotalDanger(HeroPtr hero) const
|
||||
{
|
||||
uint64_t pathDanger = getPathDanger();
|
||||
|
@@ -23,6 +23,7 @@ struct AIPathNode : public CGPathNode
|
||||
uint64_t danger;
|
||||
uint64_t armyLoss;
|
||||
uint32_t manaCost;
|
||||
const AIPathNode * chainOther;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
const ChainActor * actor;
|
||||
};
|
||||
@@ -33,6 +34,7 @@ struct AIPathNodeInfo
|
||||
int turns;
|
||||
int3 coord;
|
||||
uint64_t danger;
|
||||
const CGHeroInstance * targetHero;
|
||||
};
|
||||
|
||||
struct AIPath
|
||||
@@ -40,7 +42,9 @@ struct AIPath
|
||||
std::vector<AIPathNodeInfo> nodes;
|
||||
std::shared_ptr<const ISpecialAction> specialAction;
|
||||
uint64_t targetObjectDanger;
|
||||
uint64_t armyLoss;
|
||||
const CGHeroInstance * targetHero;
|
||||
const CCreatureSet * heroArmy;
|
||||
|
||||
AIPath();
|
||||
|
||||
@@ -53,6 +57,8 @@ struct AIPath
|
||||
int3 firstTileToGet() const;
|
||||
|
||||
float movementCost() const;
|
||||
|
||||
uint64_t getHeroStrength() const;
|
||||
};
|
||||
|
||||
class AINodeStorage : public INodeStorage
|
||||
@@ -66,11 +72,9 @@ private:
|
||||
const VCAI * ai;
|
||||
std::unique_ptr<FuzzyHelper> dangerEvaluator;
|
||||
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:
|
||||
/// 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;
|
||||
@@ -101,14 +105,11 @@ public:
|
||||
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
|
||||
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
|
||||
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
|
||||
|
||||
void setHeroes(std::vector<HeroPtr> heroes, const VCAI * ai);
|
||||
|
||||
const CGHeroInstance * getHero(const CGPathNode * node) const;
|
||||
|
||||
const std::set<const CGHeroInstance *> getAllHeroes() const;
|
||||
|
||||
void clear();
|
||||
bool calculateHeroChain();
|
||||
|
||||
uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero) const
|
||||
{
|
||||
@@ -116,5 +117,22 @@ public:
|
||||
}
|
||||
|
||||
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 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;
|
||||
};
|
||||
|
@@ -43,7 +43,7 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 &
|
||||
return storage->getChainInfo(tile, !tileInfo->isWater());
|
||||
}
|
||||
|
||||
void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
|
||||
void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||
{
|
||||
if(!storage)
|
||||
{
|
||||
@@ -55,8 +55,14 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
|
||||
storage->clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void AIPathfinder::updatePaths(const HeroPtr & hero)
|
||||
|
@@ -25,7 +25,7 @@ public:
|
||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
|
||||
std::vector<AIPath> getPathInfo(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 init();
|
||||
};
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "Rules/AIMovementAfterDestinationRule.h"
|
||||
#include "Rules/AIMovementToDestinationRule.h"
|
||||
#include "Rules/AIPreviousNodeRule.h"
|
||||
#include "Rules/AIMovementCostRule.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
@@ -25,7 +26,7 @@ namespace AIPathfinding
|
||||
std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
|
||||
std::make_shared<DestinationActionRule>(),
|
||||
std::make_shared<AIMovementToDestinationRule>(nodeStorage),
|
||||
std::make_shared<MovementCostRule>(),
|
||||
std::make_shared<AIMovementCostRule>(nodeStorage),
|
||||
std::make_shared<AIPreviousNodeRule>(nodeStorage),
|
||||
std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage)
|
||||
};
|
||||
|
@@ -48,7 +48,7 @@ ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, con
|
||||
carrierParent(carrier), otherParent(other), chainMask(carrier->chainMask | other->chainMask)
|
||||
{
|
||||
baseActor = static_cast<HeroActor *>(this);
|
||||
armyValue = heroArmy->getArmyStrength();
|
||||
armyValue = hero->getFightingStrength() * heroArmy->getArmyStrength();
|
||||
}
|
||||
|
||||
HeroActor::HeroActor(const CGHeroInstance * hero, int chainMask)
|
||||
|
@@ -136,7 +136,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
||||
{
|
||||
danger = path.getTotalDanger(hero);
|
||||
|
||||
if(isSafeToVisit(hero, danger))
|
||||
if(isSafeToVisit(hero, path.heroArmy, danger))
|
||||
{
|
||||
Goals::TSubgoal solution;
|
||||
|
||||
@@ -158,6 +158,8 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
||||
solution->evaluationContext.danger = danger;
|
||||
|
||||
solution->evaluationContext.movementCost += path.movementCost();
|
||||
solution->evaluationContext.armyLoss += path.armyLoss;
|
||||
solution->evaluationContext.heroStrength = path.getHeroStrength();
|
||||
#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());
|
||||
#endif
|
||||
|
@@ -23,44 +23,5 @@ namespace AIPathfinding
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,12 +36,41 @@ namespace AIPathfinding
|
||||
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
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user