mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-15 20:03:15 +02:00
Nullkiller: small optimization of AIPathfinder for big maps
This commit is contained in:
committed by
Andrii Danylchenko
parent
07b6b0605c
commit
5bfe71c8f3
@@ -351,4 +351,11 @@ bool isWeeklyRevisitable(const CGObjectInstance * obj)
|
|||||||
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
|
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t timeElapsed(boost::chrono::time_point<boost::chrono::steady_clock> start)
|
||||||
|
{
|
||||||
|
auto end = boost::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
return boost::chrono::duration_cast<boost::chrono::milliseconds>(end - start).count();
|
||||||
}
|
}
|
@@ -182,6 +182,8 @@ bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
|||||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
||||||
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
||||||
|
|
||||||
|
uint64_t timeElapsed(boost::chrono::time_point<boost::chrono::steady_clock> start);
|
||||||
|
|
||||||
class CDistanceSorter
|
class CDistanceSorter
|
||||||
{
|
{
|
||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
|
@@ -16,14 +16,17 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
if(upToDate)
|
if(upToDate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
logAi->trace("Update danger hitmap");
|
||||||
|
|
||||||
upToDate = true;
|
upToDate = true;
|
||||||
|
auto start = boost::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
auto cb = ai->cb.get();
|
auto cb = ai->cb.get();
|
||||||
auto mapSize = ai->cb->getMapSize();
|
auto mapSize = ai->cb->getMapSize();
|
||||||
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
|
||||||
enemyHeroAccessibleObjects.clear();
|
enemyHeroAccessibleObjects.clear();
|
||||||
|
|
||||||
std::map<PlayerColor, std::vector<const CGHeroInstance *>> heroes;
|
std::map<PlayerColor, std::map<const CGHeroInstance *, HeroRole>> heroes;
|
||||||
|
|
||||||
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
|
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
|
||||||
{
|
{
|
||||||
@@ -31,7 +34,7 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
{
|
{
|
||||||
auto hero = dynamic_cast<const CGHeroInstance *>(obj);
|
auto hero = dynamic_cast<const CGHeroInstance *>(obj);
|
||||||
|
|
||||||
heroes[hero->tempOwner].push_back(hero);
|
heroes[hero->tempOwner][hero] = HeroRole::MAIN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +86,8 @@ void DangerHitMapAnalyzer::updateHitMap()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logAi->trace("Danger hit map updated in %ld", timeElapsed(start));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath & path) const
|
uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath & path) const
|
||||||
|
@@ -172,6 +172,8 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
|
|||||||
|
|
||||||
void ObjectClusterizer::clusterize()
|
void ObjectClusterizer::clusterize()
|
||||||
{
|
{
|
||||||
|
auto start = boost::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
nearObjects.reset();
|
nearObjects.reset();
|
||||||
farObjects.reset();
|
farObjects.reset();
|
||||||
blockedObjects.clear();
|
blockedObjects.clear();
|
||||||
@@ -276,4 +278,6 @@ void ObjectClusterizer::clusterize()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logAi->trace("Clusterization complete in %ld", timeElapsed(start));
|
||||||
}
|
}
|
@@ -62,6 +62,7 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
|
|||||||
Goals::TGoalVec goals[MAX_DEPTH + 1];
|
Goals::TGoalVec goals[MAX_DEPTH + 1];
|
||||||
Goals::TTaskVec tasks;
|
Goals::TTaskVec tasks;
|
||||||
std::map<Goals::TSubgoal, Goals::TSubgoal> decompositionMap;
|
std::map<Goals::TSubgoal, Goals::TSubgoal> decompositionMap;
|
||||||
|
auto start = boost::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
goals[0] = {behavior};
|
goals[0] = {behavior};
|
||||||
|
|
||||||
@@ -127,14 +128,19 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
|
|||||||
|
|
||||||
if(tasks.empty())
|
if(tasks.empty())
|
||||||
{
|
{
|
||||||
logAi->debug("Behavior %s found no tasks", behavior->toString());
|
logAi->debug("Behavior %s found no tasks. Time taken %ld", behavior->toString(), timeElapsed(start));
|
||||||
|
|
||||||
return Goals::taskptr(Goals::Invalid());
|
return Goals::taskptr(Goals::Invalid());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto task = choseBestTask(tasks);
|
auto task = choseBestTask(tasks);
|
||||||
|
|
||||||
logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority);
|
logAi->debug(
|
||||||
|
"Behavior %s returns %s, priority %f. Time taken %ld",
|
||||||
|
behavior->toString(),
|
||||||
|
task->toString(),
|
||||||
|
task->priority,
|
||||||
|
timeElapsed(start));
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@@ -148,26 +154,34 @@ void Nullkiller::resetAiState()
|
|||||||
|
|
||||||
void Nullkiller::updateAiState()
|
void Nullkiller::updateAiState()
|
||||||
{
|
{
|
||||||
|
auto start = boost::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
activeHero = nullptr;
|
activeHero = nullptr;
|
||||||
|
|
||||||
memory->removeInvisibleObjects(cb.get());
|
memory->removeInvisibleObjects(cb.get());
|
||||||
dangerHitMap->updateHitMap();
|
dangerHitMap->updateHitMap();
|
||||||
|
|
||||||
auto activeHeroes = cb->getHeroesInfo();
|
heroManager->update();
|
||||||
|
logAi->trace("Updating paths");
|
||||||
|
|
||||||
vstd::erase_if(activeHeroes, [this](const CGHeroInstance * hero) -> bool
|
std::map<const CGHeroInstance *, HeroRole> activeHeroes;
|
||||||
|
|
||||||
|
for(auto hero : cb->getHeroesInfo())
|
||||||
{
|
{
|
||||||
auto lockedReason = getHeroLockedReason(hero);
|
if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
|
||||||
|
continue;
|
||||||
|
|
||||||
return lockedReason == HeroLockedReason::DEFENCE;
|
activeHeroes[hero] = heroManager->getHeroRole(hero);
|
||||||
});
|
}
|
||||||
|
|
||||||
pathfinder->updatePaths(activeHeroes, true);
|
pathfinder->updatePaths(activeHeroes, true);
|
||||||
heroManager->update();
|
|
||||||
armyManager->update();
|
armyManager->update();
|
||||||
|
|
||||||
objectClusterizer->clusterize();
|
objectClusterizer->clusterize();
|
||||||
buildAnalyzer->update();
|
buildAnalyzer->update();
|
||||||
|
|
||||||
|
logAi->debug("AI state updated in %ld", timeElapsed(start));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Nullkiller::isHeroLocked(const CGHeroInstance * hero) const
|
bool Nullkiller::isHeroLocked(const CGHeroInstance * hero) const
|
||||||
|
@@ -640,13 +640,6 @@ EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// distance
|
|
||||||
/// nearest hero?
|
|
||||||
/// gold income
|
|
||||||
/// army income
|
|
||||||
/// hero strength - hero skills
|
|
||||||
/// danger
|
|
||||||
/// importance
|
|
||||||
float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
||||||
{
|
{
|
||||||
auto evaluationContext = buildEvaluationContext(task);
|
auto evaluationContext = buildEvaluationContext(task);
|
||||||
@@ -677,7 +670,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
|
|||||||
fearVariable->setValue(evaluationContext.enemyHeroDangerRatio);
|
fearVariable->setValue(evaluationContext.enemyHeroDangerRatio);
|
||||||
|
|
||||||
engine->process();
|
engine->process();
|
||||||
//engine.process(VISIT_TILE); //TODO: Process only Visit_Tile
|
|
||||||
result = value->getValue();
|
result = value->getValue();
|
||||||
}
|
}
|
||||||
catch(fl::Exception & fe)
|
catch(fl::Exception & fe)
|
||||||
|
@@ -614,22 +614,45 @@ const std::set<const CGHeroInstance *> AINodeStorage::getAllHeroes() const
|
|||||||
return heroes;
|
return heroes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AINodeStorage::setHeroes(std::vector<const CGHeroInstance *> heroes)
|
bool AINodeStorage::isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
||||||
|
{
|
||||||
|
if(heroChainPass == EHeroChainPass::CHAIN && destination.node->turns > heroChainTurn)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto aiNode = getAINode(destination.node);
|
||||||
|
|
||||||
|
if(heroChainPass == EHeroChainPass::FINAL)
|
||||||
|
{
|
||||||
|
if(aiNode->actor->heroRole == HeroRole::SCOUT && destination.node->turns > 3)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if(heroChainPass == EHeroChainPass::INITIAL)
|
||||||
|
{
|
||||||
|
if(aiNode->actor->heroRole == HeroRole::SCOUT && destination.node->turns > 5)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
|
||||||
{
|
{
|
||||||
playerID = ai->playerID;
|
playerID = ai->playerID;
|
||||||
|
|
||||||
for(auto & hero : heroes)
|
for(auto & hero : heroes)
|
||||||
{
|
{
|
||||||
uint64_t mask = 1 << actors.size();
|
uint64_t mask = 1 << actors.size();
|
||||||
auto actor = std::make_shared<HeroActor>(hero, mask, ai);
|
auto actor = std::make_shared<HeroActor>(hero.first, hero.second, mask, ai);
|
||||||
|
|
||||||
if(hero->tempOwner != ai->playerID)
|
if(actor->hero->tempOwner != ai->playerID)
|
||||||
{
|
{
|
||||||
bool onLand = !actor->hero->boat;
|
bool onLand = !actor->hero->boat;
|
||||||
actor->initialMovement = actor->hero->maxMovePoints(onLand);
|
actor->initialMovement = actor->hero->maxMovePoints(onLand);
|
||||||
}
|
}
|
||||||
|
|
||||||
playerID = hero->tempOwner;
|
playerID = actor->hero->tempOwner;
|
||||||
|
|
||||||
actors.push_back(actor);
|
actors.push_back(actor);
|
||||||
}
|
}
|
||||||
@@ -926,7 +949,7 @@ bool AINodeStorage::hasBetterChain(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(candidateActor->chainMask != node.actor->chainMask)
|
if(candidateActor->chainMask != node.actor->chainMask && heroChainPass == EHeroChainPass::CHAIN)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto nodeActor = node.actor;
|
auto nodeActor = node.actor;
|
||||||
@@ -949,29 +972,32 @@ bool AINodeStorage::hasBetterChain(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if(nodeArmyValue == candidateArmyValue
|
if(heroChainPass == EHeroChainPass::FINAL)
|
||||||
&& nodeActor->heroFightingStrength >= candidateActor->heroFightingStrength
|
|
||||||
&& node.cost <= candidateNode->cost)
|
|
||||||
{
|
{
|
||||||
if(nodeActor->heroFightingStrength == candidateActor->heroFightingStrength
|
if(nodeArmyValue == candidateArmyValue
|
||||||
&& node.cost == candidateNode->cost
|
&& nodeActor->heroFightingStrength >= candidateActor->heroFightingStrength
|
||||||
&& &node < candidateNode)
|
&& node.cost <= candidateNode->cost)
|
||||||
{
|
{
|
||||||
continue;
|
if(nodeActor->heroFightingStrength == candidateActor->heroFightingStrength
|
||||||
}
|
&& node.cost == candidateNode->cost
|
||||||
|
&& &node < candidateNode)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
#if AI_TRACE_LEVEL >= 2
|
#if AI_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->name,
|
candidateNode->actor->hero->name,
|
||||||
candidateNode->actor->chainMask,
|
candidateNode->actor->chainMask,
|
||||||
candidateNode->actor->armyValue,
|
candidateNode->actor->armyValue,
|
||||||
node.moveRemains - candidateNode->moveRemains);
|
node.moveRemains - candidateNode->moveRemains);
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}*/
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1170,7 +1196,7 @@ std::string AIPath::toString() const
|
|||||||
{
|
{
|
||||||
std::stringstream str;
|
std::stringstream str;
|
||||||
|
|
||||||
str << targetHero->name << "[" << std::hex << chainMask << std::dec << "]" << ": ";
|
str << targetHero->name << "[" << std::hex << chainMask << std::dec << "]" << ", turn " << (int)(turn()) << ": ";
|
||||||
|
|
||||||
for(auto node : nodes)
|
for(auto node : nodes)
|
||||||
str << node.targetHero->name << "[" << std::hex << node.chainMask << std::dec << "]" << "->" << node.coord.toString() << "; ";
|
str << node.targetHero->name << "[" << std::hex << node.chainMask << std::dec << "]" << "->" << node.coord.toString() << "; ";
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define PATHFINDER_TRACE_LEVEL 0
|
#define PATHFINDER_TRACE_LEVEL 0
|
||||||
#define AI_TRACE_LEVEL 1
|
#define AI_TRACE_LEVEL 0
|
||||||
|
|
||||||
#include "../../../lib/CPathfinder.h"
|
#include "../../../lib/CPathfinder.h"
|
||||||
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
#include "../../../lib/mapObjects/CGHeroInstance.h"
|
||||||
@@ -162,10 +162,7 @@ public:
|
|||||||
return hasBetterChain(source, destination);
|
return hasBetterChain(source, destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
|
bool isDistanceLimitReached(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
|
||||||
{
|
|
||||||
return heroChainPass == EHeroChainPass::CHAIN && destination.node->turns > heroChainTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class NodeRange>
|
template<class NodeRange>
|
||||||
bool hasBetterChain(
|
bool hasBetterChain(
|
||||||
@@ -176,7 +173,7 @@ 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<const CGHeroInstance *> heroes);
|
void setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes);
|
||||||
void setTownsAndDwellings(
|
void setTownsAndDwellings(
|
||||||
const std::vector<const CGTownInstance *> & towns,
|
const std::vector<const CGTownInstance *> & towns,
|
||||||
const std::set<const CGObjectInstance *> & visitableObjs);
|
const std::set<const CGObjectInstance *> & visitableObjs);
|
||||||
|
@@ -42,13 +42,14 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile) const
|
|||||||
return storage->getChainInfo(tile, !tileInfo->isWater());
|
return storage->getChainInfo(tile, !tileInfo->isWater());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIPathfinder::updatePaths(std::vector<const CGHeroInstance *> heroes, bool useHeroChain)
|
void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, bool useHeroChain)
|
||||||
{
|
{
|
||||||
if(!storage)
|
if(!storage)
|
||||||
{
|
{
|
||||||
storage.reset(new AINodeStorage(ai, cb->getMapSize()));
|
storage.reset(new AINodeStorage(ai, cb->getMapSize()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto start = boost::chrono::high_resolution_clock::now();
|
||||||
logAi->debug("Recalculate all paths");
|
logAi->debug("Recalculate all paths");
|
||||||
int pass = 0;
|
int pass = 0;
|
||||||
|
|
||||||
@@ -89,4 +90,6 @@ void AIPathfinder::updatePaths(std::vector<const CGHeroInstance *> heroes, bool
|
|||||||
cb->calculatePaths(config);
|
cb->calculatePaths(config);
|
||||||
}
|
}
|
||||||
} while(storage->increaseHeroChainTurnLimit());
|
} while(storage->increaseHeroChainTurnLimit());
|
||||||
|
|
||||||
|
logAi->trace("Recalculated paths in %ld", timeElapsed(start));
|
||||||
}
|
}
|
||||||
|
@@ -26,6 +26,6 @@ public:
|
|||||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
||||||
std::vector<AIPath> getPathInfo(const int3 & tile) const;
|
std::vector<AIPath> getPathInfo(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<const CGHeroInstance *> heroes, bool useHeroChain = false);
|
void updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, bool useHeroChain = false);
|
||||||
void init();
|
void init();
|
||||||
};
|
};
|
||||||
|
@@ -22,8 +22,8 @@ bool HeroExchangeArmy::needsLastStack() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChainActor::ChainActor(const CGHeroInstance * hero, uint64_t chainMask)
|
ChainActor::ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask)
|
||||||
:hero(hero), isMovable(true), chainMask(chainMask), creatureSet(hero),
|
:hero(hero), heroRole(heroRole), isMovable(true), chainMask(chainMask), creatureSet(hero),
|
||||||
baseActor(this), carrierParent(nullptr), otherParent(nullptr), actorExchangeCount(1), armyCost()
|
baseActor(this), carrierParent(nullptr), otherParent(nullptr), actorExchangeCount(1), armyCost()
|
||||||
{
|
{
|
||||||
initialPosition = hero->visitablePos();
|
initialPosition = hero->visitablePos();
|
||||||
@@ -35,7 +35,7 @@ ChainActor::ChainActor(const CGHeroInstance * hero, uint64_t chainMask)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
|
ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy)
|
||||||
:hero(carrier->hero), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
|
:hero(carrier->hero), heroRole(carrier->heroRole), isMovable(true), creatureSet(heroArmy), chainMask(carrier->chainMask | other->chainMask),
|
||||||
baseActor(this), carrierParent(carrier), otherParent(other), heroFightingStrength(carrier->heroFightingStrength),
|
baseActor(this), carrierParent(carrier), otherParent(other), heroFightingStrength(carrier->heroFightingStrength),
|
||||||
actorExchangeCount(carrier->actorExchangeCount + other->actorExchangeCount), armyCost(carrier->armyCost + other->armyCost)
|
actorExchangeCount(carrier->actorExchangeCount + other->actorExchangeCount), armyCost(carrier->armyCost + other->armyCost)
|
||||||
{
|
{
|
||||||
@@ -43,7 +43,7 @@ ChainActor::ChainActor(const ChainActor * carrier, const ChainActor * other, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
ChainActor::ChainActor(const CGObjectInstance * obj, const CCreatureSet * creatureSet, uint64_t chainMask, int initialTurn)
|
ChainActor::ChainActor(const CGObjectInstance * obj, const CCreatureSet * creatureSet, uint64_t chainMask, int initialTurn)
|
||||||
:hero(nullptr), isMovable(false), creatureSet(creatureSet), chainMask(chainMask),
|
:hero(nullptr), heroRole(HeroRole::MAIN), isMovable(false), creatureSet(creatureSet), chainMask(chainMask),
|
||||||
baseActor(this), carrierParent(nullptr), otherParent(nullptr), initialTurn(initialTurn), initialMovement(0),
|
baseActor(this), carrierParent(nullptr), otherParent(nullptr), initialTurn(initialTurn), initialMovement(0),
|
||||||
heroFightingStrength(0), actorExchangeCount(1), armyCost()
|
heroFightingStrength(0), actorExchangeCount(1), armyCost()
|
||||||
{
|
{
|
||||||
@@ -72,8 +72,8 @@ std::string ObjectActor::toString() const
|
|||||||
return object->getObjectName() + " at " + object->visitablePos().toString();
|
return object->getObjectName() + " at " + object->visitablePos().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const Nullkiller * ai)
|
HeroActor::HeroActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask, const Nullkiller * ai)
|
||||||
:ChainActor(hero, chainMask)
|
:ChainActor(hero, heroRole, chainMask)
|
||||||
{
|
{
|
||||||
exchangeMap = new HeroExchangeMap(this, ai);
|
exchangeMap = new HeroExchangeMap(this, ai);
|
||||||
setupSpecialActors();
|
setupSpecialActors();
|
||||||
@@ -94,6 +94,7 @@ void ChainActor::setBaseActor(HeroActor * base)
|
|||||||
{
|
{
|
||||||
baseActor = base;
|
baseActor = base;
|
||||||
hero = base->hero;
|
hero = base->hero;
|
||||||
|
heroRole = base->heroRole;
|
||||||
layer = base->layer;
|
layer = base->layer;
|
||||||
initialMovement = base->initialMovement;
|
initialMovement = base->initialMovement;
|
||||||
initialTurn = base->initialTurn;
|
initialTurn = base->initialTurn;
|
||||||
|
@@ -27,7 +27,7 @@ public:
|
|||||||
class ChainActor
|
class ChainActor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
ChainActor(const CGHeroInstance * hero, uint64_t chainMask);
|
ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask);
|
||||||
ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy);
|
ChainActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * heroArmy);
|
||||||
ChainActor(const CGObjectInstance * obj, const CCreatureSet * army, uint64_t chainMask, int initialTurn);
|
ChainActor(const CGObjectInstance * obj, const CCreatureSet * army, uint64_t chainMask, int initialTurn);
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ public:
|
|||||||
bool allowBattle;
|
bool allowBattle;
|
||||||
bool allowSpellCast;
|
bool allowSpellCast;
|
||||||
const CGHeroInstance * hero;
|
const CGHeroInstance * hero;
|
||||||
|
HeroRole heroRole;
|
||||||
const CCreatureSet * creatureSet;
|
const CCreatureSet * creatureSet;
|
||||||
const ChainActor * battleActor;
|
const ChainActor * battleActor;
|
||||||
const ChainActor * castActor;
|
const ChainActor * castActor;
|
||||||
@@ -105,7 +106,7 @@ public:
|
|||||||
std::shared_ptr<SpecialAction> exchangeAction;
|
std::shared_ptr<SpecialAction> exchangeAction;
|
||||||
// chain flags, can be combined meaning hero exchange and so on
|
// chain flags, can be combined meaning hero exchange and so on
|
||||||
|
|
||||||
HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const Nullkiller * ai);
|
HeroActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t chainMask, const Nullkiller * ai);
|
||||||
HeroActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * army, const Nullkiller * ai);
|
HeroActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * army, const Nullkiller * ai);
|
||||||
|
|
||||||
virtual bool canExchange(const ChainActor * other) const override;
|
virtual bool canExchange(const ChainActor * other) const override;
|
||||||
|
@@ -38,7 +38,11 @@ namespace AIPathfinding
|
|||||||
|
|
||||||
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
|
||||||
if(blocker == BlockingReason::NONE)
|
if(blocker == BlockingReason::NONE)
|
||||||
|
{
|
||||||
|
destination.blocked = nodeStorage->isDistanceLimitReached(source, destination);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto destGuardians = cb->getGuardingCreatures(destination.coord);
|
auto destGuardians = cb->getGuardingCreatures(destination.coord);
|
||||||
bool allowBypass = false;
|
bool allowBypass = false;
|
||||||
|
Reference in New Issue
Block a user