mirror of
https://github.com/vcmi/vcmi.git
synced 2025-06-23 00:28:08 +02:00
NKAI: remove 5th dimension
This commit is contained in:
@ -24,7 +24,8 @@
|
|||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
|
|
||||||
std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
|
std::shared_ptr<boost::multi_array<AIPathNode, 4>> AISharedStorage::shared;
|
||||||
|
uint64_t AISharedStorage::version = 0;
|
||||||
boost::mutex AISharedStorage::locker;
|
boost::mutex AISharedStorage::locker;
|
||||||
std::set<int3> commitedTiles;
|
std::set<int3> commitedTiles;
|
||||||
std::set<int3> commitedTilesInitial;
|
std::set<int3> commitedTilesInitial;
|
||||||
@ -40,11 +41,24 @@ const bool DO_NOT_SAVE_TO_COMMITED_TILES = false;
|
|||||||
AISharedStorage::AISharedStorage(int3 sizes)
|
AISharedStorage::AISharedStorage(int3 sizes)
|
||||||
{
|
{
|
||||||
if(!shared){
|
if(!shared){
|
||||||
shared.reset(new boost::multi_array<AIPathNode, 5>(
|
shared.reset(new boost::multi_array<AIPathNode, 4>(
|
||||||
boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS]));
|
boost::extents[sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS]));
|
||||||
}
|
|
||||||
|
|
||||||
nodes = shared;
|
nodes = shared;
|
||||||
|
|
||||||
|
foreach_tile_pos([&](const int3 & pos)
|
||||||
|
{
|
||||||
|
for(auto i = 0; i < AIPathfinding::NUM_CHAINS; i++)
|
||||||
|
{
|
||||||
|
auto & node = get(pos)[i];
|
||||||
|
|
||||||
|
node.version = -1;
|
||||||
|
node.coord = pos;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nodes = shared;
|
||||||
}
|
}
|
||||||
|
|
||||||
AISharedStorage::~AISharedStorage()
|
AISharedStorage::~AISharedStorage()
|
||||||
@ -80,6 +94,9 @@ void AIPathNode::addSpecialAction(std::shared_ptr<const SpecialAction> action)
|
|||||||
AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
|
AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
|
||||||
: sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes)
|
: sizes(Sizes), ai(ai), cb(ai->cb.get()), nodes(Sizes)
|
||||||
{
|
{
|
||||||
|
accesibility = std::make_unique<boost::multi_array<EPathAccessibility, 4>>(
|
||||||
|
boost::extents[sizes.z][sizes.x][sizes.y][EPathfindingLayer::NUM_LAYERS]);
|
||||||
|
|
||||||
dangerEvaluator.reset(new FuzzyHelper(ai));
|
dangerEvaluator.reset(new FuzzyHelper(ai));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +107,8 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
|
|||||||
if(heroChainPass)
|
if(heroChainPass)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
AISharedStorage::version++;
|
||||||
|
|
||||||
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
|
//TODO: fix this code duplication with NodeStorage::initialize, problem is to keep `resetTile` inline
|
||||||
const PlayerColor fowPlayer = ai->playerID;
|
const PlayerColor fowPlayer = ai->playerID;
|
||||||
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
|
const auto & fow = static_cast<const CGameInfoCallback *>(gs)->getPlayerTeam(fowPlayer)->fogOfWarMap;
|
||||||
@ -152,9 +171,9 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
|||||||
{
|
{
|
||||||
int bucketIndex = ((uintptr_t)actor) % AIPathfinding::BUCKET_COUNT;
|
int bucketIndex = ((uintptr_t)actor) % AIPathfinding::BUCKET_COUNT;
|
||||||
int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE;
|
int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE;
|
||||||
auto chains = nodes.get(pos, layer);
|
auto chains = nodes.get(pos);
|
||||||
|
|
||||||
if(chains[0].blocked())
|
if(blocked(pos, layer))
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@ -163,15 +182,17 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
|
|||||||
{
|
{
|
||||||
AIPathNode & node = chains[i + bucketOffset];
|
AIPathNode & node = chains[i + bucketOffset];
|
||||||
|
|
||||||
if(node.actor == actor)
|
if(node.version != AISharedStorage::version)
|
||||||
{
|
{
|
||||||
|
node.reset(layer, getAccessibility(pos, layer));
|
||||||
|
node.version = AISharedStorage::version;
|
||||||
|
node.actor = actor;
|
||||||
|
|
||||||
return &node;
|
return &node;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!node.actor)
|
if(node.actor == actor && node.layer == layer)
|
||||||
{
|
{
|
||||||
node.actor = actor;
|
|
||||||
|
|
||||||
return &node;
|
return &node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -226,21 +247,6 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
|||||||
return initialNodes;
|
return initialNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, EPathAccessibility accessibility)
|
|
||||||
{
|
|
||||||
for(AIPathNode & heroNode : nodes.get(coord, layer))
|
|
||||||
{
|
|
||||||
heroNode.actor = nullptr;
|
|
||||||
heroNode.danger = 0;
|
|
||||||
heroNode.manaCost = 0;
|
|
||||||
heroNode.specialAction.reset();
|
|
||||||
heroNode.armyLoss = 0;
|
|
||||||
heroNode.chainOther = nullptr;
|
|
||||||
heroNode.dayFlags = DayFlags::NONE;
|
|
||||||
heroNode.update(coord, layer, accessibility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
|
void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
|
||||||
{
|
{
|
||||||
const AIPathNode * srcNode = getAINode(source.node);
|
const AIPathNode * srcNode = getAINode(source.node);
|
||||||
@ -306,30 +312,31 @@ void AINodeStorage::commit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
void AINodeStorage::calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<int3> accessibleNeighbourTiles;
|
||||||
neighbours.reserve(16);
|
|
||||||
|
result.clear();
|
||||||
|
accessibleNeighbourTiles.reserve(8);
|
||||||
|
|
||||||
|
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||||
|
|
||||||
const AIPathNode * srcNode = getAINode(source.node);
|
const AIPathNode * srcNode = getAINode(source.node);
|
||||||
auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source);
|
|
||||||
|
|
||||||
for(auto & neighbour : accessibleNeighbourTiles)
|
for(auto & neighbour : accessibleNeighbourTiles)
|
||||||
{
|
{
|
||||||
for(EPathfindingLayer i = EPathfindingLayer::LAND; i < EPathfindingLayer::NUM_LAYERS; i.advance(1))
|
auto nextNode = getOrCreateNode(neighbour, layer, srcNode->actor);
|
||||||
{
|
|
||||||
auto nextNode = getOrCreateNode(neighbour, i, srcNode->actor);
|
|
||||||
|
|
||||||
if(!nextNode || nextNode.value()->accessible == EPathAccessibility::NOT_SET)
|
if(!nextNode || nextNode.value()->accessible == EPathAccessibility::NOT_SET)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
neighbours.push_back(nextNode.value());
|
result.push_back(nextNode.value());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbours;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array phisycalLayers = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
|
constexpr std::array phisycalLayers = {EPathfindingLayer::LAND, EPathfindingLayer::SAIL};
|
||||||
@ -346,19 +353,16 @@ bool AINodeStorage::increaseHeroChainTurnLimit()
|
|||||||
{
|
{
|
||||||
foreach_tile_pos([&](const int3 & pos)
|
foreach_tile_pos([&](const int3 & pos)
|
||||||
{
|
{
|
||||||
auto chains = nodes.get(pos, layer);
|
iterateValidNodesUntil(pos, layer, [&](AIPathNode & node)
|
||||||
|
|
||||||
if(!chains[0].blocked())
|
|
||||||
{
|
|
||||||
for(AIPathNode & node : chains)
|
|
||||||
{
|
{
|
||||||
if(node.turns <= heroChainTurn && node.action != EPathNodeAction::UNKNOWN)
|
if(node.turns <= heroChainTurn && node.action != EPathNodeAction::UNKNOWN)
|
||||||
{
|
{
|
||||||
commitedTiles.insert(pos);
|
commitedTiles.insert(pos);
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
return false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,22 +378,17 @@ bool AINodeStorage::calculateHeroChainFinal()
|
|||||||
{
|
{
|
||||||
foreach_tile_pos([&](const int3 & pos)
|
foreach_tile_pos([&](const int3 & pos)
|
||||||
{
|
{
|
||||||
auto chains = nodes.get(pos, layer);
|
iterateValidNodes(pos, layer, [&](AIPathNode & node)
|
||||||
|
|
||||||
if(!chains[0].blocked())
|
|
||||||
{
|
|
||||||
for(AIPathNode & node : chains)
|
|
||||||
{
|
{
|
||||||
if(node.turns > heroChainTurn
|
if(node.turns > heroChainTurn
|
||||||
&& !node.locked
|
&& !node.locked
|
||||||
&& node.action != EPathNodeAction::UNKNOWN
|
&& node.action != EPathNodeAction::UNKNOWN
|
||||||
&& node.actor->actorExchangeCount > 1
|
&& node.actor->actorExchangeCount > 1
|
||||||
&& !hasBetterChain(&node, &node, chains))
|
&& !hasBetterChain(&node, node))
|
||||||
{
|
{
|
||||||
heroChain.push_back(&node);
|
heroChain.push_back(&node);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,21 +442,19 @@ public:
|
|||||||
|
|
||||||
for(auto layer : phisycalLayers)
|
for(auto layer : phisycalLayers)
|
||||||
{
|
{
|
||||||
auto chains = nodes.get(pos, layer);
|
existingChains.clear();
|
||||||
|
|
||||||
// fast cut inactive nodes
|
storage.iterateValidNodes(pos, layer, [this](AIPathNode & node)
|
||||||
if(chains[0].blocked())
|
{
|
||||||
|
if(node.turns <= heroChainTurn && node.action != EPathNodeAction::UNKNOWN)
|
||||||
|
existingChains.push_back(&node);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(existingChains.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
existingChains.clear();
|
|
||||||
newChains.clear();
|
newChains.clear();
|
||||||
|
|
||||||
for(AIPathNode & node : chains)
|
|
||||||
{
|
|
||||||
if(node.turns <= heroChainTurn && node.action != EPathNodeAction::UNKNOWN)
|
|
||||||
existingChains.push_back(&node);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shuffle(existingChains.begin(), existingChains.end(), randomEngine);
|
std::shuffle(existingChains.begin(), existingChains.end(), randomEngine);
|
||||||
|
|
||||||
for(AIPathNode * node : existingChains)
|
for(AIPathNode * node : existingChains)
|
||||||
@ -617,9 +614,8 @@ void HeroChainCalculationTask::cleanupInefectiveChains(std::vector<ExchangeCandi
|
|||||||
vstd::erase_if(result, [&](const ExchangeCandidate & chainInfo) -> bool
|
vstd::erase_if(result, [&](const ExchangeCandidate & chainInfo) -> bool
|
||||||
{
|
{
|
||||||
auto pos = chainInfo.coord;
|
auto pos = chainInfo.coord;
|
||||||
auto chains = nodes.get(pos, EPathfindingLayer::LAND);
|
auto isNotEffective = storage.hasBetterChain(chainInfo.carrierParent, chainInfo)
|
||||||
auto isNotEffective = storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, chains)
|
|| storage.hasBetterChain(chainInfo.carrierParent, chainInfo, result);
|
||||||
|| storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, result);
|
|
||||||
|
|
||||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||||
if(isNotEffective)
|
if(isNotEffective)
|
||||||
@ -645,7 +641,7 @@ void HeroChainCalculationTask::calculateHeroChain(
|
|||||||
{
|
{
|
||||||
for(AIPathNode * node : variants)
|
for(AIPathNode * node : variants)
|
||||||
{
|
{
|
||||||
if(node == srcNode || !node->actor)
|
if(node == srcNode || !node->actor || node->version != AISharedStorage::version)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
|
if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
|
||||||
@ -1195,95 +1191,116 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
|
|||||||
|
|
||||||
bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||||
{
|
{
|
||||||
auto pos = destination.coord;
|
auto candidateNode = getAINode(destination.node);
|
||||||
auto chains = nodes.get(pos, EPathfindingLayer::LAND);
|
|
||||||
|
|
||||||
return hasBetterChain(source.node, getAINode(destination.node), chains);
|
return hasBetterChain(source.node, *candidateNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AINodeStorage::hasBetterChain(
|
||||||
|
const CGPathNode * source,
|
||||||
|
const AIPathNode & candidateNode) const
|
||||||
|
{
|
||||||
|
return iterateValidNodesUntil(
|
||||||
|
candidateNode.coord,
|
||||||
|
candidateNode.layer,
|
||||||
|
[this, &source, candidateNode](const AIPathNode & node) -> bool
|
||||||
|
{
|
||||||
|
return isOtherChainBetter(source, candidateNode, node);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class NodeRange>
|
template<class NodeRange>
|
||||||
bool AINodeStorage::hasBetterChain(
|
bool AINodeStorage::hasBetterChain(
|
||||||
const CGPathNode * source,
|
const CGPathNode * source,
|
||||||
const AIPathNode * candidateNode,
|
const AIPathNode & candidateNode,
|
||||||
const NodeRange & chains) const
|
const NodeRange & nodes) const
|
||||||
{
|
{
|
||||||
auto candidateActor = candidateNode->actor;
|
for(const AIPathNode & node : nodes)
|
||||||
|
|
||||||
for(const AIPathNode & node : chains)
|
|
||||||
{
|
{
|
||||||
auto sameNode = node.actor == candidateNode->actor;
|
if(isOtherChainBetter(source, candidateNode, node))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if(sameNode || node.action == EPathNodeAction::UNKNOWN || !node.actor || !node.actor->hero)
|
return false;
|
||||||
{
|
}
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(node.danger <= candidateNode->danger && candidateNode->actor == node.actor->battleActor)
|
bool AINodeStorage::isOtherChainBetter(
|
||||||
{
|
const CGPathNode * source,
|
||||||
if(node.getCost() < candidateNode->getCost())
|
const AIPathNode & candidateNode,
|
||||||
{
|
const AIPathNode & other) const
|
||||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
{
|
||||||
logAi->trace(
|
auto sameNode = other.actor == candidateNode.actor;
|
||||||
"Block ineficient battle move %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
|
||||||
source->coord.toString(),
|
|
||||||
candidateNode->coord.toString(),
|
|
||||||
candidateNode->actor->hero->getNameTranslated(),
|
|
||||||
candidateNode->actor->chainMask,
|
|
||||||
candidateNode->actor->armyValue,
|
|
||||||
node.moveRemains - candidateNode->moveRemains);
|
|
||||||
#endif
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(candidateActor->chainMask != node.actor->chainMask && heroChainPass != EHeroChainPass::FINAL)
|
if(sameNode || other.action == EPathNodeAction::UNKNOWN || !other.actor || !other.actor->hero)
|
||||||
continue;
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
auto nodeActor = node.actor;
|
if(other.danger <= candidateNode.danger && candidateNode.actor == other.actor->battleActor)
|
||||||
auto nodeArmyValue = nodeActor->armyValue - node.armyLoss;
|
{
|
||||||
auto candidateArmyValue = candidateActor->armyValue - candidateNode->armyLoss;
|
if(other.getCost() < candidateNode.getCost())
|
||||||
|
|
||||||
if(nodeArmyValue > candidateArmyValue
|
|
||||||
&& node.getCost() <= candidateNode->getCost())
|
|
||||||
{
|
{
|
||||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Block ineficient move because of stronger army %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
"Block ineficient battle move %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
||||||
source->coord.toString(),
|
source->coord.toString(),
|
||||||
candidateNode->coord.toString(),
|
candidateNode.coord.toString(),
|
||||||
candidateNode->actor->hero->getNameTranslated(),
|
candidateNode.actor->hero->getNameTranslated(),
|
||||||
candidateNode->actor->chainMask,
|
candidateNode.actor->chainMask,
|
||||||
candidateNode->actor->armyValue,
|
candidateNode.actor->armyValue,
|
||||||
node.moveRemains - candidateNode->moveRemains);
|
other.moveRemains - candidateNode.moveRemains);
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(heroChainPass == EHeroChainPass::FINAL)
|
if(candidateNode.actor->chainMask != other.actor->chainMask && heroChainPass != EHeroChainPass::FINAL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto nodeActor = other.actor;
|
||||||
|
auto nodeArmyValue = nodeActor->armyValue - other.armyLoss;
|
||||||
|
auto candidateArmyValue = candidateNode.actor->armyValue - candidateNode.armyLoss;
|
||||||
|
|
||||||
|
if(nodeArmyValue > candidateArmyValue
|
||||||
|
&& other.getCost() <= candidateNode.getCost())
|
||||||
|
{
|
||||||
|
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||||
|
logAi->trace(
|
||||||
|
"Block ineficient move because of stronger army %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
||||||
|
source->coord.toString(),
|
||||||
|
candidateNode.coord.toString(),
|
||||||
|
candidateNode.actor->hero->getNameTranslated(),
|
||||||
|
candidateNode.actor->chainMask,
|
||||||
|
candidateNode.actor->armyValue,
|
||||||
|
other.moveRemains - candidateNode.moveRemains);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(heroChainPass == EHeroChainPass::FINAL)
|
||||||
|
{
|
||||||
|
if(nodeArmyValue == candidateArmyValue
|
||||||
|
&& nodeActor->heroFightingStrength >= candidateNode.actor->heroFightingStrength
|
||||||
|
&& other.getCost() <= candidateNode.getCost())
|
||||||
{
|
{
|
||||||
if(nodeArmyValue == candidateArmyValue
|
if(vstd::isAlmostEqual(nodeActor->heroFightingStrength, candidateNode.actor->heroFightingStrength)
|
||||||
&& nodeActor->heroFightingStrength >= candidateActor->heroFightingStrength
|
&& vstd::isAlmostEqual(other.getCost(), candidateNode.getCost())
|
||||||
&& node.getCost() <= candidateNode->getCost())
|
&& &other < &candidateNode)
|
||||||
{
|
{
|
||||||
if(vstd::isAlmostEqual(nodeActor->heroFightingStrength, candidateActor->heroFightingStrength)
|
return false;
|
||||||
&& vstd::isAlmostEqual(node.getCost(), candidateNode->getCost())
|
}
|
||||||
&& &node < candidateNode)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
"Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
|
||||||
source->coord.toString(),
|
source->coord.toString(),
|
||||||
candidateNode->coord.toString(),
|
candidateNode.coord.toString(),
|
||||||
candidateNode->actor->hero->getNameTranslated(),
|
candidateNode.actor->hero->getNameTranslated(),
|
||||||
candidateNode->actor->chainMask,
|
candidateNode.actor->chainMask,
|
||||||
candidateNode->actor->armyValue,
|
candidateNode.actor->armyValue,
|
||||||
node.moveRemains - candidateNode->moveRemains);
|
other.moveRemains - candidateNode.moveRemains);
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1292,12 +1309,15 @@ bool AINodeStorage::hasBetterChain(
|
|||||||
|
|
||||||
bool AINodeStorage::isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const
|
bool AINodeStorage::isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const
|
||||||
{
|
{
|
||||||
auto chains = nodes.get(pos, layer);
|
auto chains = nodes.get(pos);
|
||||||
|
|
||||||
for(const AIPathNode & node : chains)
|
for(const AIPathNode & node : chains)
|
||||||
{
|
{
|
||||||
if(node.action != EPathNodeAction::UNKNOWN
|
if(node.version == AISharedStorage::version
|
||||||
&& node.actor && node.actor->hero == hero.h)
|
&& node.layer == layer
|
||||||
|
&& node.action != EPathNodeAction::UNKNOWN
|
||||||
|
&& node.actor
|
||||||
|
&& node.actor->hero == hero.h)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1312,11 +1332,16 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
|||||||
|
|
||||||
paths.reserve(AIPathfinding::NUM_CHAINS / 4);
|
paths.reserve(AIPathfinding::NUM_CHAINS / 4);
|
||||||
|
|
||||||
auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
|
auto layer = isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL;
|
||||||
|
auto chains = nodes.get(pos);
|
||||||
|
|
||||||
for(const AIPathNode & node : chains)
|
for(const AIPathNode & node : chains)
|
||||||
{
|
{
|
||||||
if(node.action == EPathNodeAction::UNKNOWN || !node.actor || !node.actor->hero)
|
if(node.version != AISharedStorage::version
|
||||||
|
|| node.layer != layer
|
||||||
|
|| node.action == EPathNodeAction::UNKNOWN
|
||||||
|
|| !node.actor
|
||||||
|
|| !node.actor->hero)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1349,33 +1374,29 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa
|
|||||||
if(node->chainOther)
|
if(node->chainOther)
|
||||||
fillChainInfo(node->chainOther, path, parentIndex);
|
fillChainInfo(node->chainOther, path, parentIndex);
|
||||||
|
|
||||||
//if(node->actor->hero->visitablePos() != node->coord)
|
AIPathNodeInfo pathNode;
|
||||||
|
|
||||||
|
pathNode.cost = node->getCost();
|
||||||
|
pathNode.targetHero = node->actor->hero;
|
||||||
|
pathNode.chainMask = node->actor->chainMask;
|
||||||
|
pathNode.specialAction = node->specialAction;
|
||||||
|
pathNode.turns = node->turns;
|
||||||
|
pathNode.danger = node->danger;
|
||||||
|
pathNode.coord = node->coord;
|
||||||
|
pathNode.parentIndex = parentIndex;
|
||||||
|
pathNode.actionIsBlocked = false;
|
||||||
|
pathNode.layer = node->layer;
|
||||||
|
|
||||||
|
if(pathNode.specialAction)
|
||||||
{
|
{
|
||||||
AIPathNodeInfo pathNode;
|
auto targetNode =node->theNodeBefore ? getAINode(node->theNodeBefore) : node;
|
||||||
|
|
||||||
pathNode.cost = node->getCost();
|
pathNode.actionIsBlocked = !pathNode.specialAction->canAct(targetNode);
|
||||||
pathNode.targetHero = node->actor->hero;
|
|
||||||
pathNode.chainMask = node->actor->chainMask;
|
|
||||||
pathNode.specialAction = node->specialAction;
|
|
||||||
pathNode.turns = node->turns;
|
|
||||||
pathNode.danger = node->danger;
|
|
||||||
pathNode.coord = node->coord;
|
|
||||||
pathNode.parentIndex = parentIndex;
|
|
||||||
pathNode.actionIsBlocked = false;
|
|
||||||
pathNode.layer = node->layer;
|
|
||||||
|
|
||||||
if(pathNode.specialAction)
|
|
||||||
{
|
|
||||||
auto targetNode =node->theNodeBefore ? getAINode(node->theNodeBefore) : node;
|
|
||||||
|
|
||||||
pathNode.actionIsBlocked = !pathNode.specialAction->canAct(targetNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
parentIndex = path.nodes.size();
|
|
||||||
|
|
||||||
path.nodes.push_back(pathNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parentIndex = path.nodes.size();
|
||||||
|
|
||||||
|
path.nodes.push_back(pathNode);
|
||||||
node = getAINode(node->theNodeBefore);
|
node = getAINode(node->theNodeBefore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ namespace NKAI
|
|||||||
{
|
{
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
const int BUCKET_COUNT = 3;
|
const int BUCKET_COUNT = 5;
|
||||||
const int BUCKET_SIZE = 5;
|
const int BUCKET_SIZE = 6;
|
||||||
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
|
||||||
const int CHAIN_MAX_DEPTH = 4;
|
const int CHAIN_MAX_DEPTH = 4;
|
||||||
}
|
}
|
||||||
@ -49,15 +49,24 @@ struct AIPathNode : public CGPathNode
|
|||||||
const AIPathNode * chainOther;
|
const AIPathNode * chainOther;
|
||||||
std::shared_ptr<const SpecialAction> specialAction;
|
std::shared_ptr<const SpecialAction> specialAction;
|
||||||
const ChainActor * actor;
|
const ChainActor * actor;
|
||||||
|
uint64_t version;
|
||||||
STRONG_INLINE
|
|
||||||
bool blocked() const
|
|
||||||
{
|
|
||||||
return accessible == EPathAccessibility::NOT_SET
|
|
||||||
|| accessible == EPathAccessibility::BLOCKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addSpecialAction(std::shared_ptr<const SpecialAction> action);
|
void addSpecialAction(std::shared_ptr<const SpecialAction> action);
|
||||||
|
|
||||||
|
inline void reset(EPathfindingLayer layer, EPathAccessibility accessibility)
|
||||||
|
{
|
||||||
|
CGPathNode::reset();
|
||||||
|
|
||||||
|
actor = nullptr;
|
||||||
|
danger = 0;
|
||||||
|
manaCost = 0;
|
||||||
|
specialAction.reset();
|
||||||
|
armyLoss = 0;
|
||||||
|
chainOther = nullptr;
|
||||||
|
dayFlags = DayFlags::NONE;
|
||||||
|
this->layer = layer;
|
||||||
|
accessible = accessibility;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AIPathNodeInfo
|
struct AIPathNodeInfo
|
||||||
@ -133,21 +142,21 @@ enum EHeroChainPass
|
|||||||
|
|
||||||
class AISharedStorage
|
class AISharedStorage
|
||||||
{
|
{
|
||||||
// 1 - layer (air, water, land)
|
// 1-3 - position on map[z][x][y]
|
||||||
// 2-4 - position on map[z][x][y]
|
// 4 - chain + layer (normal, battle, spellcast and combinations, water, air)
|
||||||
// 5 - chain (normal, battle, spellcast and combinations)
|
static std::shared_ptr<boost::multi_array<AIPathNode, 4>> shared;
|
||||||
static std::shared_ptr<boost::multi_array<AIPathNode, 5>> shared;
|
std::shared_ptr<boost::multi_array<AIPathNode, 4>> nodes;
|
||||||
std::shared_ptr<boost::multi_array<AIPathNode, 5>> nodes;
|
|
||||||
public:
|
public:
|
||||||
static boost::mutex locker;
|
static boost::mutex locker;
|
||||||
|
static uint64_t version;
|
||||||
|
|
||||||
AISharedStorage(int3 mapSize);
|
AISharedStorage(int3 mapSize);
|
||||||
~AISharedStorage();
|
~AISharedStorage();
|
||||||
|
|
||||||
STRONG_INLINE
|
STRONG_INLINE
|
||||||
boost::detail::multi_array::sub_array<AIPathNode, 1> get(int3 tile, EPathfindingLayer layer) const
|
boost::detail::multi_array::sub_array<AIPathNode, 1> get(int3 tile) const
|
||||||
{
|
{
|
||||||
return (*nodes)[layer][tile.z][tile.x][tile.y];
|
return (*nodes)[tile.z][tile.x][tile.y];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,6 +165,8 @@ class AINodeStorage : public INodeStorage
|
|||||||
private:
|
private:
|
||||||
int3 sizes;
|
int3 sizes;
|
||||||
|
|
||||||
|
std::unique_ptr<boost::multi_array<EPathAccessibility, 4>> accesibility;
|
||||||
|
|
||||||
const CPlayerSpecificInfoCallback * cb;
|
const CPlayerSpecificInfoCallback * cb;
|
||||||
const Nullkiller * ai;
|
const Nullkiller * ai;
|
||||||
std::unique_ptr<FuzzyHelper> dangerEvaluator;
|
std::unique_ptr<FuzzyHelper> dangerEvaluator;
|
||||||
@ -182,8 +193,10 @@ public:
|
|||||||
|
|
||||||
std::vector<CGPathNode *> getInitialNodes() override;
|
std::vector<CGPathNode *> getInitialNodes() override;
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
virtual void calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper) override;
|
const CPathfinderHelper * pathfinderHelper) override;
|
||||||
|
|
||||||
@ -222,7 +235,27 @@ public:
|
|||||||
return aiNode->actor->hero;
|
return aiNode->actor->hero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool blocked(const int3 & tile, EPathfindingLayer layer) const
|
||||||
|
{
|
||||||
|
EPathAccessibility accessible = getAccessibility(tile, layer);
|
||||||
|
|
||||||
|
return accessible == EPathAccessibility::NOT_SET
|
||||||
|
|| accessible == EPathAccessibility::BLOCKED;
|
||||||
|
}
|
||||||
|
|
||||||
bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
||||||
|
bool hasBetterChain(const CGPathNode * source, const AIPathNode & candidateNode) const;
|
||||||
|
|
||||||
|
template<class NodeRange>
|
||||||
|
bool hasBetterChain(
|
||||||
|
const CGPathNode * source,
|
||||||
|
const AIPathNode & destinationNode,
|
||||||
|
const NodeRange & chains) const;
|
||||||
|
|
||||||
|
bool isOtherChainBetter(
|
||||||
|
const CGPathNode * source,
|
||||||
|
const AIPathNode & candidateNode,
|
||||||
|
const AIPathNode & other) const;
|
||||||
|
|
||||||
bool isMovementIneficient(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
bool isMovementIneficient(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||||
{
|
{
|
||||||
@ -231,12 +264,6 @@ public:
|
|||||||
|
|
||||||
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
||||||
|
|
||||||
template<class NodeRange>
|
|
||||||
bool hasBetterChain(
|
|
||||||
const CGPathNode * source,
|
|
||||||
const AIPathNode * destinationNode,
|
|
||||||
const NodeRange & chains) const;
|
|
||||||
|
|
||||||
std::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
|
std::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;
|
||||||
@ -263,10 +290,17 @@ public:
|
|||||||
return (uint64_t)(armyValue * ratio * ratio);
|
return (uint64_t)(armyValue * ratio * ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
STRONG_INLINE
|
inline EPathAccessibility getAccessibility(const int3 & tile, EPathfindingLayer layer) const
|
||||||
void resetTile(const int3 & tile, EPathfindingLayer layer, EPathAccessibility accessibility);
|
{
|
||||||
|
return (*this->accesibility)[tile.z][tile.x][tile.y][layer];
|
||||||
|
}
|
||||||
|
|
||||||
STRONG_INLINE int getBucket(const ChainActor * actor) const
|
inline void resetTile(const int3 & tile, EPathfindingLayer layer, EPathAccessibility tileAccessibility)
|
||||||
|
{
|
||||||
|
(*this->accesibility)[tile.z][tile.x][tile.y][layer] = tileAccessibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getBucket(const ChainActor * actor) const
|
||||||
{
|
{
|
||||||
return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;
|
return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;
|
||||||
}
|
}
|
||||||
@ -274,6 +308,44 @@ public:
|
|||||||
void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours);
|
void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours);
|
||||||
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
|
void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
|
||||||
|
|
||||||
|
template<typename Fn>
|
||||||
|
void iterateValidNodes(const int3 & pos, EPathfindingLayer layer, Fn fn)
|
||||||
|
{
|
||||||
|
if(blocked(pos, layer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto chains = nodes.get(pos);
|
||||||
|
|
||||||
|
for(AIPathNode & node : chains)
|
||||||
|
{
|
||||||
|
if(node.version != AISharedStorage::version || node.layer != layer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fn(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Fn>
|
||||||
|
bool iterateValidNodesUntil(const int3 & pos, EPathfindingLayer layer, Fn predicate) const
|
||||||
|
{
|
||||||
|
if(blocked(pos, layer))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto chains = nodes.get(pos);
|
||||||
|
|
||||||
|
for(AIPathNode & node : chains)
|
||||||
|
{
|
||||||
|
if(node.version != AISharedStorage::version || node.layer != layer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(predicate(node))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<class TVector>
|
template<class TVector>
|
||||||
void calculateTownPortal(
|
void calculateTownPortal(
|
||||||
|
@ -73,6 +73,36 @@ public:
|
|||||||
removeExtraConnections();
|
removeExtraConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getNeighborConnectionsCost(const int3 & pos)
|
||||||
|
{
|
||||||
|
float neighborCost = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
|
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
|
||||||
|
{
|
||||||
|
logAi->trace("Checking junction %s", pos.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach_neighbour(
|
||||||
|
ai->cb.get(),
|
||||||
|
pos,
|
||||||
|
[this, &neighborCost](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
|
||||||
|
{
|
||||||
|
auto costTotal = this->getConnectionsCost(neighbor);
|
||||||
|
|
||||||
|
if(costTotal.connectionsCount > 2 && costTotal.avg < neighborCost)
|
||||||
|
{
|
||||||
|
neighborCost = costTotal.avg;
|
||||||
|
|
||||||
|
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
|
||||||
|
{
|
||||||
|
logAi->trace("Better node found at %s", neighbor.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return neighborCost;
|
||||||
|
}
|
||||||
|
|
||||||
void addMinimalDistanceJunctions()
|
void addMinimalDistanceJunctions()
|
||||||
{
|
{
|
||||||
foreach_tile_pos(ai->cb.get(), [this](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
foreach_tile_pos(ai->cb.get(), [this](const CPlayerSpecificInfoCallback * cb, const int3 & pos)
|
||||||
@ -88,30 +118,7 @@ public:
|
|||||||
if(currentCost.connectionsCount <= 2)
|
if(currentCost.connectionsCount <= 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float neighborCost = currentCost.avg + 0.001f;
|
float neighborCost = getNeighborConnectionsCost(pos);
|
||||||
|
|
||||||
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
|
|
||||||
{
|
|
||||||
logAi->trace("Checking junction %s", pos.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach_neighbour(
|
|
||||||
ai->cb.get(),
|
|
||||||
pos,
|
|
||||||
[this, &neighborCost](const CPlayerSpecificInfoCallback * cb, const int3 & neighbor)
|
|
||||||
{
|
|
||||||
auto costTotal = this->getConnectionsCost(neighbor);
|
|
||||||
|
|
||||||
if(costTotal.avg < neighborCost)
|
|
||||||
{
|
|
||||||
neighborCost = costTotal.avg;
|
|
||||||
|
|
||||||
if(NKAI_GRAPH_TRACE_LEVEL >= 2)
|
|
||||||
{
|
|
||||||
logAi->trace("Better node found at %s", neighbor.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(currentCost.avg < neighborCost)
|
if(currentCost.avg < neighborCost)
|
||||||
{
|
{
|
||||||
|
@ -155,15 +155,21 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
void AINodeStorage::calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<int3> accessibleNeighbourTiles;
|
||||||
neighbours.reserve(16);
|
|
||||||
|
result.clear();
|
||||||
|
accessibleNeighbourTiles.reserve(8);
|
||||||
|
|
||||||
|
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||||
|
|
||||||
const AIPathNode * srcNode = getAINode(source.node);
|
const AIPathNode * srcNode = getAINode(source.node);
|
||||||
auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source);
|
|
||||||
|
|
||||||
for(auto & neighbour : accessibleNeighbourTiles)
|
for(auto & neighbour : accessibleNeighbourTiles)
|
||||||
{
|
{
|
||||||
@ -174,11 +180,9 @@ std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
|||||||
if(!nextNode || nextNode.value()->accessible == EPathAccessibility::NOT_SET)
|
if(!nextNode || nextNode.value()->accessible == EPathAccessibility::NOT_SET)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
neighbours.push_back(nextNode.value());
|
result.push_back(nextNode.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbours;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AINodeStorage::setHero(HeroPtr heroPtr, const VCAI * _ai)
|
void AINodeStorage::setHero(HeroPtr heroPtr, const VCAI * _ai)
|
||||||
|
@ -89,8 +89,10 @@ public:
|
|||||||
|
|
||||||
std::vector<CGPathNode *> getInitialNodes() override;
|
std::vector<CGPathNode *> getInitialNodes() override;
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
virtual void calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper) override;
|
const CPathfinderHelper * pathfinderHelper) override;
|
||||||
|
|
||||||
|
@ -49,30 +49,27 @@ bool CPathfinderHelper::canMoveFromNode(const PathNodeInfo & source) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int3> CPathfinderHelper::getNeighbourTiles(const PathNodeInfo & source) const
|
void CPathfinderHelper::calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const
|
||||||
{
|
{
|
||||||
std::vector<int3> neighbourTiles;
|
result.clear();
|
||||||
|
|
||||||
if (!canMoveFromNode(source))
|
if (!canMoveFromNode(source))
|
||||||
return neighbourTiles;
|
return;
|
||||||
|
|
||||||
neighbourTiles.reserve(8);
|
|
||||||
getNeighbours(
|
getNeighbours(
|
||||||
*source.tile,
|
*source.tile,
|
||||||
source.node->coord,
|
source.node->coord,
|
||||||
neighbourTiles,
|
result,
|
||||||
boost::logic::indeterminate,
|
boost::logic::indeterminate,
|
||||||
source.node->layer == EPathfindingLayer::SAIL);
|
source.node->layer == EPathfindingLayer::SAIL);
|
||||||
|
|
||||||
if(source.isNodeObjectVisitable())
|
if(source.isNodeObjectVisitable())
|
||||||
{
|
{
|
||||||
vstd::erase_if(neighbourTiles, [&](const int3 & tile) -> bool
|
vstd::erase_if(result, [&](const int3 & tile) -> bool
|
||||||
{
|
{
|
||||||
return !canMoveBetween(tile, source.nodeObject->visitablePos());
|
return !canMoveBetween(tile, source.nodeObject->visitablePos());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbourTiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CPathfinder::CPathfinder(CGameState * _gs, std::shared_ptr<PathfinderConfig> config):
|
CPathfinder::CPathfinder(CGameState * _gs, std::shared_ptr<PathfinderConfig> config):
|
||||||
@ -129,6 +126,8 @@ void CPathfinder::calculatePaths()
|
|||||||
pq.push(initialNode);
|
pq.push(initialNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<CGPathNode *> neighbourNodes;
|
||||||
|
|
||||||
while(!pq.empty())
|
while(!pq.empty())
|
||||||
{
|
{
|
||||||
counter++;
|
counter++;
|
||||||
@ -158,43 +157,47 @@ void CPathfinder::calculatePaths()
|
|||||||
source.updateInfo(hlp, gamestate);
|
source.updateInfo(hlp, gamestate);
|
||||||
|
|
||||||
//add accessible neighbouring nodes to the queue
|
//add accessible neighbouring nodes to the queue
|
||||||
auto neighbourNodes = config->nodeStorage->calculateNeighbours(source, config.get(), hlp);
|
for(EPathfindingLayer layer = EPathfindingLayer::LAND; layer < EPathfindingLayer::NUM_LAYERS; layer.advance(1))
|
||||||
for(CGPathNode * neighbour : neighbourNodes)
|
|
||||||
{
|
{
|
||||||
if(neighbour->locked)
|
if(!hlp->isLayerAvailable(layer))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(!hlp->isLayerAvailable(neighbour->layer))
|
config->nodeStorage->calculateNeighbours(neighbourNodes, source, layer, config.get(), hlp);
|
||||||
continue;
|
|
||||||
|
|
||||||
destination.setNode(gamestate, neighbour);
|
for(CGPathNode * neighbour : neighbourNodes)
|
||||||
hlp = config->getOrCreatePathfinderHelper(destination, gamestate);
|
|
||||||
|
|
||||||
if(!hlp->isPatrolMovementAllowed(neighbour->coord))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/// Check transition without tile accessability rules
|
|
||||||
if(source.node->layer != neighbour->layer && !isLayerTransitionPossible())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
destination.turn = turn;
|
|
||||||
destination.movementLeft = movement;
|
|
||||||
destination.cost = cost;
|
|
||||||
destination.updateInfo(hlp, gamestate);
|
|
||||||
destination.isGuardianTile = destination.guarded && isDestinationGuardian();
|
|
||||||
|
|
||||||
for(const auto & rule : config->rules)
|
|
||||||
{
|
{
|
||||||
rule->process(source, destination, config.get(), hlp);
|
if(neighbour->locked)
|
||||||
|
continue;
|
||||||
|
|
||||||
if(destination.blocked)
|
destination.setNode(gamestate, neighbour);
|
||||||
break;
|
hlp = config->getOrCreatePathfinderHelper(destination, gamestate);
|
||||||
}
|
|
||||||
|
|
||||||
if(!destination.blocked)
|
if(!hlp->isPatrolMovementAllowed(neighbour->coord))
|
||||||
push(destination.node);
|
continue;
|
||||||
|
|
||||||
} //neighbours loop
|
/// Check transition without tile accessability rules
|
||||||
|
if(source.node->layer != neighbour->layer && !isLayerTransitionPossible())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
destination.turn = turn;
|
||||||
|
destination.movementLeft = movement;
|
||||||
|
destination.cost = cost;
|
||||||
|
destination.updateInfo(hlp, gamestate);
|
||||||
|
destination.isGuardianTile = destination.guarded && isDestinationGuardian();
|
||||||
|
|
||||||
|
for(const auto & rule : config->rules)
|
||||||
|
{
|
||||||
|
rule->process(source, destination, config.get(), hlp);
|
||||||
|
|
||||||
|
if(destination.blocked)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!destination.blocked)
|
||||||
|
push(destination.node);
|
||||||
|
|
||||||
|
} //neighbours loop
|
||||||
|
}
|
||||||
|
|
||||||
//just add all passable teleport exits
|
//just add all passable teleport exits
|
||||||
hlp = config->getOrCreatePathfinderHelper(source, gamestate);
|
hlp = config->getOrCreatePathfinderHelper(source, gamestate);
|
||||||
|
@ -96,7 +96,7 @@ public:
|
|||||||
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
||||||
bool canMoveBetween(const int3 & a, const int3 & b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
bool canMoveBetween(const int3 & a, const int3 & b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
||||||
|
|
||||||
std::vector<int3> getNeighbourTiles(const PathNodeInfo & source) const;
|
void calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const;
|
||||||
std::vector<int3> getTeleportExits(const PathNodeInfo & source) const;
|
std::vector<int3> getTeleportExits(const PathNodeInfo & source) const;
|
||||||
|
|
||||||
void getNeighbours(
|
void getNeighbours(
|
||||||
|
@ -31,8 +31,10 @@ public:
|
|||||||
|
|
||||||
virtual std::vector<CGPathNode *> getInitialNodes() = 0;
|
virtual std::vector<CGPathNode *> getInitialNodes() = 0;
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
virtual void calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper) = 0;
|
const CPathfinderHelper * pathfinderHelper) = 0;
|
||||||
|
|
||||||
|
@ -60,29 +60,29 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> NodeStorage::calculateNeighbours(
|
void NodeStorage::calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper)
|
const CPathfinderHelper * pathfinderHelper)
|
||||||
{
|
{
|
||||||
std::vector<CGPathNode *> neighbours;
|
std::vector<int3> accessibleNeighbourTiles;
|
||||||
neighbours.reserve(16);
|
|
||||||
auto accessibleNeighbourTiles = pathfinderHelper->getNeighbourTiles(source);
|
result.clear();
|
||||||
|
accessibleNeighbourTiles.reserve(8);
|
||||||
|
|
||||||
|
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||||
|
|
||||||
for(auto & neighbour : accessibleNeighbourTiles)
|
for(auto & neighbour : accessibleNeighbourTiles)
|
||||||
{
|
{
|
||||||
for(EPathfindingLayer i = EPathfindingLayer::LAND; i < EPathfindingLayer::NUM_LAYERS; i.advance(1))
|
auto * node = getNode(neighbour, layer);
|
||||||
{
|
|
||||||
auto * node = getNode(neighbour, i);
|
|
||||||
|
|
||||||
if(node->accessible == EPathAccessibility::NOT_SET)
|
if(node->accessible == EPathAccessibility::NOT_SET)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
neighbours.push_back(node);
|
result.push_back(node);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbours;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CGPathNode *> NodeStorage::calculateTeleportations(
|
std::vector<CGPathNode *> NodeStorage::calculateTeleportations(
|
||||||
|
@ -36,8 +36,10 @@ public:
|
|||||||
|
|
||||||
std::vector<CGPathNode *> getInitialNodes() override;
|
std::vector<CGPathNode *> getInitialNodes() override;
|
||||||
|
|
||||||
virtual std::vector<CGPathNode *> calculateNeighbours(
|
virtual void calculateNeighbours(
|
||||||
|
std::vector<CGPathNode *> & result,
|
||||||
const PathNodeInfo & source,
|
const PathNodeInfo & source,
|
||||||
|
EPathfindingLayer layer,
|
||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
const CPathfinderHelper * pathfinderHelper) override;
|
const CPathfinderHelper * pathfinderHelper) override;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user