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()));
|
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!
|
||||||
|
@@ -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);
|
||||||
|
@@ -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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -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();
|
||||||
|
@@ -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;
|
||||||
};
|
};
|
||||||
|
@@ -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)
|
||||||
|
@@ -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();
|
||||||
};
|
};
|
||||||
|
@@ -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)
|
||||||
};
|
};
|
||||||
|
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user