mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-15 20:03:15 +02:00
hero chain stabilisation
This commit is contained in:
committed by
Andrii Danylchenko
parent
87f1079c60
commit
774f531c4e
@@ -132,7 +132,8 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
|
||||
initialNode->turns = actor->initialTurn;
|
||||
initialNode->moveRemains = actor->initialMovement;
|
||||
initialNode->danger = 0;
|
||||
initialNode->cost = 0.0;
|
||||
initialNode->cost = actor->initialTurn;
|
||||
initialNode->action = CGPathNode::ENodeAction::NORMAL;
|
||||
|
||||
if(actor->isMovable)
|
||||
{
|
||||
@@ -175,6 +176,16 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
||||
{
|
||||
dstNode->specialAction->applyOnDestination(dstNode->actor->hero, destination, source, dstNode, srcNode);
|
||||
}
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace(
|
||||
"Commited %s -> %s, cost: %f, hero: %s, mask: %i",
|
||||
source.coord.toString(),
|
||||
destination.coord.toString(),
|
||||
destination.cost,
|
||||
dstNode->actor->hero->name,
|
||||
dstNode->actor->chainMask);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
|
||||
@@ -194,6 +205,7 @@ void AINodeStorage::commit(
|
||||
destination->manaCost = source->manaCost;
|
||||
destination->danger = source->danger;
|
||||
destination->theNodeBefore = source->theNodeBefore;
|
||||
destination->chainOther = nullptr;
|
||||
}
|
||||
|
||||
std::vector<CGPathNode *> AINodeStorage::calculateNeighbours(
|
||||
@@ -227,33 +239,53 @@ bool AINodeStorage::calculateHeroChain()
|
||||
heroChainPass = true;
|
||||
heroChain.resize(0);
|
||||
|
||||
std::vector<AIPathNode *> buffer;
|
||||
|
||||
buffer.reserve(NUM_CHAINS);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos) {
|
||||
auto layer = EPathfindingLayer::LAND;
|
||||
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
||||
|
||||
buffer.resize(0);
|
||||
|
||||
for(AIPathNode & node : chains)
|
||||
{
|
||||
if(node.locked && node.turns < 1)
|
||||
addHeroChain(&node);
|
||||
if(node.turns <= heroChainMaxTurns && node.action != CGPathNode::ENodeAction::UNKNOWN)
|
||||
buffer.push_back(&node);
|
||||
}
|
||||
|
||||
for(AIPathNode * node : buffer)
|
||||
{
|
||||
addHeroChain(node, buffer);
|
||||
}
|
||||
});
|
||||
|
||||
return heroChain.size();
|
||||
}
|
||||
|
||||
void AINodeStorage::addHeroChain(AIPathNode * srcNode)
|
||||
void AINodeStorage::addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *> variants)
|
||||
{
|
||||
auto chains = nodes[srcNode->coord.x][srcNode->coord.y][srcNode->coord.z][srcNode->layer];
|
||||
|
||||
for(AIPathNode & node : chains)
|
||||
for(AIPathNode * node : variants)
|
||||
{
|
||||
if(!node.locked || !node.actor || node.action == CGPathNode::ENodeAction::UNKNOWN && node.actor->hero)
|
||||
if(node == srcNode || !node->actor || node->turns > heroChainMaxTurns
|
||||
|| node->action == CGPathNode::ENodeAction::UNKNOWN && node->actor->hero)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
addHeroChain(srcNode, &node);
|
||||
addHeroChain(&node, srcNode);
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace(
|
||||
"Thy exchange %s[%i] -> %s[%i] at %s",
|
||||
node->actor->hero->name,
|
||||
node->actor->chainMask,
|
||||
srcNode->actor->hero->name,
|
||||
srcNode->actor->chainMask,
|
||||
srcNode->coord.toString());
|
||||
#endif
|
||||
|
||||
addHeroChain(srcNode, node);
|
||||
//addHeroChain(&node, srcNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,37 +293,65 @@ void AINodeStorage::addHeroChain(AIPathNode * carrier, AIPathNode * other)
|
||||
{
|
||||
if(carrier->actor->canExchange(other->actor))
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace(
|
||||
"Exchange allowed %s[%i] -> %s[%i] at %s",
|
||||
other->actor->hero->name,
|
||||
other->actor->chainMask,
|
||||
carrier->actor->hero->name,
|
||||
carrier->actor->chainMask,
|
||||
carrier->coord.toString());
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s is ineficient. Blocked.", carrier->coord.toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
auto newActor = carrier->actor->exchange(other->actor);
|
||||
auto chainNodeOptional = getOrCreateNode(carrier->coord, carrier->layer, newActor);
|
||||
|
||||
if(!chainNodeOptional)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s can not allocate node. Blocked.", carrier->coord.toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
auto chainNode = chainNodeOptional.get();
|
||||
|
||||
if(chainNode->locked)
|
||||
return;
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Hero exhange at %s, %s -> %s", carrier->coord.toString(), other->actor->hero->name, carrier->actor->hero->name);
|
||||
if(chainNode->action != CGPathNode::ENodeAction::UNKNOWN)
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s node is already in use. Blocked.", carrier->coord.toString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
commitExchange(chainNode, carrier, other);
|
||||
heroChain.push_back(chainNode);
|
||||
if(commitExchange(chainNode, carrier, other))
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace(
|
||||
"Chain accepted at %s %s -> %s, mask %i, cost %f",
|
||||
chainNode->coord.toString(),
|
||||
other->actor->hero->name,
|
||||
chainNode->actor->hero->name,
|
||||
chainNode->actor->chainMask,
|
||||
chainNode->cost);
|
||||
#endif
|
||||
heroChain.push_back(chainNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AINodeStorage::commitExchange(
|
||||
bool AINodeStorage::commitExchange(
|
||||
AIPathNode * exchangeNode,
|
||||
AIPathNode * carrierParentNode,
|
||||
AIPathNode * otherParentNode) const
|
||||
@@ -302,7 +362,7 @@ void AINodeStorage::commitExchange(
|
||||
|
||||
auto armyLoss = carrierParentNode->armyLoss + otherParentNode->armyLoss;
|
||||
auto turns = carrierParentNode->turns;
|
||||
auto cost = carrierParentNode->cost;
|
||||
auto cost = carrierParentNode->cost + otherParentNode->cost / 1000.0;
|
||||
auto movementLeft = carrierParentNode->moveRemains;
|
||||
|
||||
if(carrierParentNode->turns < otherParentNode->turns)
|
||||
@@ -312,27 +372,24 @@ void AINodeStorage::commitExchange(
|
||||
+ carrierParentNode->moveRemains / (float)moveRemains;
|
||||
|
||||
turns = otherParentNode->turns;
|
||||
cost = waitingCost;
|
||||
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",
|
||||
exchangeNode->coord.toString(),
|
||||
carrierActor->hero->name,
|
||||
cost);
|
||||
{
|
||||
#ifdef VCMI_TRACE_PATHFINDER_EX
|
||||
logAi->trace("Exchange at %s is is not effective enough. %f < %f", exchangeNode->coord.toString(), exchangeNode->cost, cost);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
commit(exchangeNode, carrierParentNode, carrierParentNode->action, turns, movementLeft, cost);
|
||||
|
||||
exchangeNode->theNodeBefore = carrierParentNode;
|
||||
exchangeNode->chainOther = otherParentNode;
|
||||
exchangeNode->armyLoss = armyLoss;
|
||||
exchangeNode->manaCost = carrierParentNode->manaCost;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
|
||||
@@ -538,6 +595,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
|
||||
path.heroArmy = node.actor->creatureSet;
|
||||
path.armyLoss = node.armyLoss;
|
||||
path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
|
||||
path.chainMask = node.actor->chainMask;
|
||||
|
||||
fillChainInfo(&node, path);
|
||||
|
||||
@@ -551,22 +609,26 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path) const
|
||||
{
|
||||
while(node != nullptr)
|
||||
{
|
||||
if(!node->actor->hero || node->coord == node->actor->hero->visitablePos())
|
||||
if(!node->actor->hero)
|
||||
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);
|
||||
|
||||
if(node->actor->hero->visitablePos() != node->coord)
|
||||
{
|
||||
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;
|
||||
|
||||
node = getAINode(node->theNodeBefore);
|
||||
}
|
||||
}
|
||||
@@ -586,6 +648,11 @@ int3 AIPath::firstTileToGet() const
|
||||
return int3(-1, -1, -1);
|
||||
}
|
||||
|
||||
const AIPathNodeInfo & AIPath::firstNode() const
|
||||
{
|
||||
return nodes.back();
|
||||
}
|
||||
|
||||
uint64_t AIPath::getPathDanger() const
|
||||
{
|
||||
if(nodes.size())
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#pragma once
|
||||
|
||||
#define VCMI_TRACE_PATHFINDER
|
||||
#define VCMI_TRACE_PATHFINDER_EX
|
||||
|
||||
#include "../../../lib/CPathfinder.h"
|
||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||
@@ -19,6 +20,7 @@
|
||||
#include "../Goals/AbstractGoal.h"
|
||||
#include "Actions/ISpecialAction.h"
|
||||
#include "Actors.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#define VCMI_TRACE_PATHFINDER
|
||||
|
||||
@@ -49,6 +51,7 @@ struct AIPath
|
||||
uint64_t armyLoss;
|
||||
const CGHeroInstance * targetHero;
|
||||
const CCreatureSet * heroArmy;
|
||||
uint64_t chainMask;
|
||||
|
||||
AIPath();
|
||||
|
||||
@@ -60,6 +63,8 @@ struct AIPath
|
||||
|
||||
int3 firstTileToGet() const;
|
||||
|
||||
const AIPathNodeInfo & firstNode() const;
|
||||
|
||||
float movementCost() const;
|
||||
|
||||
uint64_t getHeroStrength() const;
|
||||
@@ -78,6 +83,7 @@ private:
|
||||
std::vector<std::shared_ptr<ChainActor>> actors;
|
||||
std::vector<CGPathNode *> heroChain;
|
||||
bool heroChainPass; // true if we need to calculate hero chain
|
||||
int heroChainMaxTurns = 0;
|
||||
|
||||
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.
|
||||
@@ -123,7 +129,7 @@ public:
|
||||
private:
|
||||
STRONG_INLINE
|
||||
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
|
||||
void addHeroChain(AIPathNode * srcNode);
|
||||
void addHeroChain(AIPathNode * srcNode, std::vector<AIPathNode *> variants);
|
||||
void addHeroChain(AIPathNode * carrier, AIPathNode * other);
|
||||
void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
|
||||
void fillChainInfo(const AIPathNode * node, AIPath & path) const;
|
||||
@@ -135,7 +141,7 @@ private:
|
||||
int movementLeft,
|
||||
float cost) const;
|
||||
|
||||
void AINodeStorage::commitExchange(
|
||||
bool AINodeStorage::commitExchange(
|
||||
AIPathNode * exchangeNode,
|
||||
AIPathNode * carrierParentNode,
|
||||
AIPathNode * otherParentNode) const;
|
||||
|
@@ -51,18 +51,20 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||
}
|
||||
|
||||
logAi->debug("Recalculate all paths");
|
||||
int pass = 0;
|
||||
|
||||
storage->clear();
|
||||
storage->setHeroes(heroes, ai);
|
||||
|
||||
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);
|
||||
do {
|
||||
logAi->trace("Recalculate paths pass %" PRIi32, pass++);
|
||||
cb->calculatePaths(config);
|
||||
}
|
||||
|
||||
logAi->trace("Recalculate chain pass %" PRIi32, pass);
|
||||
useHeroChain = useHeroChain && storage->calculateHeroChain();
|
||||
} while(useHeroChain);
|
||||
}
|
||||
|
||||
void AIPathfinder::updatePaths(const HeroPtr & hero)
|
||||
|
@@ -13,7 +13,6 @@
|
||||
#include "Rules/AIMovementAfterDestinationRule.h"
|
||||
#include "Rules/AIMovementToDestinationRule.h"
|
||||
#include "Rules/AIPreviousNodeRule.h"
|
||||
#include "Rules/AIMovementCostRule.h"
|
||||
|
||||
namespace AIPathfinding
|
||||
{
|
||||
|
@@ -82,21 +82,16 @@ void ChainActor::setBaseActor(HeroActor * base)
|
||||
armyValue = base->armyValue;
|
||||
chainMask = base->chainMask;
|
||||
creatureSet = base->creatureSet;
|
||||
isMovable = base->isMovable;
|
||||
}
|
||||
|
||||
void HeroActor::setupSpecialActors()
|
||||
{
|
||||
auto allActors = std::vector<ChainActor *>{ this };
|
||||
|
||||
for(int i = 1; i <= SPECIAL_ACTORS_COUNT; i++)
|
||||
for(ChainActor & specialActor : specialActors)
|
||||
{
|
||||
ChainActor & specialActor = specialActors[i - 1];
|
||||
|
||||
specialActor.setBaseActor(this);
|
||||
specialActor.allowBattle = (i & 1) > 0;
|
||||
specialActor.allowSpellCast = (i & 2) > 0;
|
||||
specialActor.allowUseResources = (i & 4) > 0;
|
||||
|
||||
allActors.push_back(&specialActor);
|
||||
}
|
||||
|
||||
@@ -104,6 +99,9 @@ void HeroActor::setupSpecialActors()
|
||||
{
|
||||
ChainActor * actor = allActors[i];
|
||||
|
||||
actor->allowBattle = (i & 1) > 0;
|
||||
actor->allowSpellCast = (i & 2) > 0;
|
||||
actor->allowUseResources = (i & 4) > 0;
|
||||
actor->battleActor = allActors[i | 1];
|
||||
actor->castActor = allActors[i | 2];
|
||||
actor->resourceActor = allActors[i | 4];
|
||||
@@ -180,7 +178,7 @@ CCreatureSet * HeroActor::pickBestCreatures(const CCreatureSet * army1, const CC
|
||||
{
|
||||
for(auto & i : armyPtr->Slots())
|
||||
{
|
||||
creToPower[i.second->type] += i.second->getPower();
|
||||
creToPower[i.second->type] += i.second->count;
|
||||
}
|
||||
}
|
||||
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
|
||||
@@ -193,7 +191,7 @@ CCreatureSet * HeroActor::pickBestCreatures(const CCreatureSet * army1, const CC
|
||||
typedef const std::pair<const CCreature *, int> & CrePowerPair;
|
||||
auto creIt = boost::max_element(creToPower, [](CrePowerPair lhs, CrePowerPair rhs)
|
||||
{
|
||||
return lhs.second < rhs.second;
|
||||
return lhs.first->AIValue * lhs.second < rhs.first->AIValue * rhs.second;
|
||||
});
|
||||
|
||||
target->addToSlot(SlotID(i), creIt->first->idNumber, TQuantity(creIt->second));
|
||||
|
@@ -125,14 +125,23 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
||||
|
||||
for(auto path : chainInfo)
|
||||
{
|
||||
if(hero && hero.get() != path.targetHero)
|
||||
if(hero && hero.get() != path.targetHero || path.nodes.empty())
|
||||
continue;
|
||||
|
||||
int3 firstTileToGet = path.firstTileToGet();
|
||||
const AIPathNodeInfo & firstNode = path.firstNode();
|
||||
int3 firstTileToGet = firstNode.coord;
|
||||
|
||||
#ifdef VCMI_TRACE_PATHFINDER
|
||||
logAi->trace("Path found size=%i, first tile=%s", path.nodes.size(), firstTileToGet.toString());
|
||||
std::stringstream str;
|
||||
|
||||
str << "Path found ";
|
||||
|
||||
for(auto node : path.nodes)
|
||||
str << node.targetHero->name << "->" << node.coord.toString() << "; ";
|
||||
|
||||
logAi->trace(str.str());
|
||||
#endif
|
||||
if(firstTileToGet.valid() && ai->isTileNotReserved(hero.get(), firstTileToGet))
|
||||
if(ai->isTileNotReserved(hero.get(), firstTileToGet))
|
||||
{
|
||||
danger = path.getTotalDanger(hero);
|
||||
|
||||
@@ -148,7 +157,7 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
||||
{
|
||||
solution = dest == firstTileToGet
|
||||
? doVisitTile(firstTileToGet)
|
||||
: clearWayTo(hero, firstTileToGet);
|
||||
: clearWayTo(firstNode.targetHero, firstTileToGet);
|
||||
}
|
||||
|
||||
if(solution->invalid())
|
||||
@@ -161,7 +170,13 @@ Goals::TGoalVec PathfindingManager::findPaths(
|
||||
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());
|
||||
logAi->trace("It's safe for %s to visit tile %s with danger %s, loss %s, army strength %s, goal %s",
|
||||
hero->name,
|
||||
dest.toString(),
|
||||
std::to_string(danger),
|
||||
std::to_string(path.armyLoss),
|
||||
std::to_string(path.heroArmy->getArmyStrength()),
|
||||
solution->name());
|
||||
#endif
|
||||
result.push_back(solution);
|
||||
|
||||
@@ -210,9 +225,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
||||
{
|
||||
if(topObj != hero.get(true)) //the hero we want to free
|
||||
{
|
||||
logAi->error("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
|
||||
|
||||
return sptr(Goals::Invalid());
|
||||
logAi->warn("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,10 +253,10 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
|
||||
return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
|
||||
}
|
||||
|
||||
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes)
|
||||
void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
|
||||
{
|
||||
logAi->debug("AIPathfinder has been reseted.");
|
||||
pathfinder->updatePaths(heroes);
|
||||
pathfinder->updatePaths(heroes, useHeroChain);
|
||||
}
|
||||
|
||||
void PathfindingManager::updatePaths(const HeroPtr & hero)
|
||||
|
@@ -20,7 +20,7 @@ public:
|
||||
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
|
||||
virtual void setAI(VCAI * AI) = 0;
|
||||
|
||||
virtual void updatePaths(std::vector<HeroPtr> heroes) = 0;
|
||||
virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0;
|
||||
virtual void updatePaths(const HeroPtr & hero) = 0;
|
||||
virtual Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const = 0;
|
||||
virtual Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const = 0;
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
Goals::TGoalVec howToVisitTile(const int3 & tile, bool allowGatherArmy = true) const override;
|
||||
Goals::TGoalVec howToVisitObj(ObjectIdRef obj, bool allowGatherArmy = true) const override;
|
||||
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes) override;
|
||||
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
|
||||
void updatePaths(const HeroPtr & hero) override;
|
||||
|
||||
STRONG_INLINE
|
||||
|
@@ -38,39 +38,10 @@ namespace AIPathfinding
|
||||
|
||||
auto srcNode = nodeStorage->getAINode(source.node);
|
||||
|
||||
if(srcNode->specialAction)
|
||||
if(srcNode->specialAction || srcNode->chainOther)
|
||||
{
|
||||
// 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