mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
NKAI: remove static ai field
This commit is contained in:
parent
d6f1a5c2b3
commit
728f673763
@ -566,7 +566,7 @@ void AIGateway::initGameInterface(std::shared_ptr<Environment> env, std::shared_
|
|||||||
myCb->waitTillRealize = true;
|
myCb->waitTillRealize = true;
|
||||||
myCb->unlockGsWhenWaiting = true;
|
myCb->unlockGsWhenWaiting = true;
|
||||||
|
|
||||||
nullkiller->init(CB, playerID);
|
nullkiller->init(CB, this);
|
||||||
|
|
||||||
retrieveVisitableObjs();
|
retrieveVisitableObjs();
|
||||||
}
|
}
|
||||||
@ -828,7 +828,7 @@ void AIGateway::makeTurn()
|
|||||||
{
|
{
|
||||||
for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs)
|
for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs)
|
||||||
{
|
{
|
||||||
if(isWeeklyRevisitable(obj))
|
if(isWeeklyRevisitable(nullkiller.get(), obj))
|
||||||
{
|
{
|
||||||
nullkiller->memory->markObjectUnvisited(obj);
|
nullkiller->memory->markObjectUnvisited(obj);
|
||||||
}
|
}
|
||||||
@ -1152,6 +1152,11 @@ void AIGateway::retrieveVisitableObjs()
|
|||||||
{
|
{
|
||||||
for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
|
for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
|
||||||
{
|
{
|
||||||
|
if(!obj->appearance)
|
||||||
|
{
|
||||||
|
logAi->error("Bad!");
|
||||||
|
}
|
||||||
|
|
||||||
addVisitableObj(obj);
|
addVisitableObj(obj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -163,14 +163,6 @@ bool HeroPtr::operator==(const HeroPtr & rhs) const
|
|||||||
return h == rhs.get(true);
|
return h == rhs.get(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
|
|
||||||
{
|
|
||||||
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
|
|
||||||
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
|
|
||||||
|
|
||||||
return ln->getCost() < rn->getCost();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet * heroArmy, uint64_t dangerStrength)
|
bool isSafeToVisit(const CGHeroInstance * h, const CCreatureSet * heroArmy, uint64_t dangerStrength)
|
||||||
{
|
{
|
||||||
const ui64 heroStrength = h->getFightingStrength() * heroArmy->getArmyStrength();
|
const ui64 heroStrength = h->getFightingStrength() * heroArmy->getArmyStrength();
|
||||||
@ -235,11 +227,6 @@ bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj)
|
|||||||
return isObjectPassable(obj, ai->playerID, ai->cb->getPlayerRelations(obj->tempOwner, ai->playerID));
|
return isObjectPassable(obj, ai->playerID, ai->cb->getPlayerRelations(obj->tempOwner, ai->playerID));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isObjectPassable(const CGObjectInstance * obj)
|
|
||||||
{
|
|
||||||
return isObjectPassable(obj, ai->playerID, ai->myCb->getPlayerRelations(obj->tempOwner, ai->playerID));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pathfinder internal helper
|
// Pathfinder internal helper
|
||||||
bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations)
|
bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations)
|
||||||
{
|
{
|
||||||
@ -306,7 +293,7 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
|||||||
return art1->getPrice() > art2->getPrice();
|
return art1->getPrice() > art2->getPrice();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWeeklyRevisitable(const CGObjectInstance * obj)
|
bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj)
|
||||||
{
|
{
|
||||||
if(!obj)
|
if(!obj)
|
||||||
return false;
|
return false;
|
||||||
|
@ -70,7 +70,6 @@ extern const float SAFE_ATTACK_CONSTANT;
|
|||||||
extern const int GOLD_RESERVE;
|
extern const int GOLD_RESERVE;
|
||||||
|
|
||||||
extern thread_local CCallback * cb;
|
extern thread_local CCallback * cb;
|
||||||
extern thread_local AIGateway * ai;
|
|
||||||
|
|
||||||
enum HeroRole
|
enum HeroRole
|
||||||
{
|
{
|
||||||
@ -114,11 +113,11 @@ public:
|
|||||||
bool validAndSet() const;
|
bool validAndSet() const;
|
||||||
|
|
||||||
|
|
||||||
template<typename Handler> void serialize(Handler & h)
|
template<typename Handler> void serialize(Handler & handler)
|
||||||
{
|
{
|
||||||
h & this->h;
|
handler & h;
|
||||||
h & hid;
|
handler & hid;
|
||||||
h & name;
|
handler & name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -224,12 +223,11 @@ void foreach_neighbour(CCallback * cbp, const int3 & pos, const Func & foo) // a
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
|
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
|
||||||
bool isObjectPassable(const CGObjectInstance * obj);
|
|
||||||
bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj);
|
bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj);
|
||||||
bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations);
|
bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations);
|
||||||
bool isBlockVisitObj(const int3 & pos);
|
bool isBlockVisitObj(const int3 & pos);
|
||||||
|
|
||||||
bool isWeeklyRevisitable(const CGObjectInstance * obj);
|
bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj);
|
||||||
|
|
||||||
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
|
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
|
||||||
bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength);
|
bool isSafeToVisit(const CGHeroInstance * h, uint64_t dangerStrength);
|
||||||
@ -245,18 +243,6 @@ uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock>
|
|||||||
// todo: move to obj manager
|
// todo: move to obj manager
|
||||||
bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj);
|
bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj);
|
||||||
|
|
||||||
class CDistanceSorter
|
|
||||||
{
|
|
||||||
const CGHeroInstance * hero;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CDistanceSorter(const CGHeroInstance * hero)
|
|
||||||
: hero(hero)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
class SharedPool
|
class SharedPool
|
||||||
{
|
{
|
||||||
|
@ -180,7 +180,7 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
|
|||||||
|
|
||||||
auto playerRelations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
|
auto playerRelations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
|
||||||
|
|
||||||
if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
|
if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(ai, obj))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -27,14 +27,14 @@ std::string BuildingBehavior::toString() const
|
|||||||
return "Build";
|
return "Build";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec BuildingBehavior::decompose() const
|
Goals::TGoalVec BuildingBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
TResources resourcesRequired = ai->nullkiller->buildAnalyzer->getResourcesRequiredNow();
|
TResources resourcesRequired = ai->buildAnalyzer->getResourcesRequiredNow();
|
||||||
TResources totalDevelopmentCost = ai->nullkiller->buildAnalyzer->getTotalResourcesRequired();
|
TResources totalDevelopmentCost = ai->buildAnalyzer->getTotalResourcesRequired();
|
||||||
TResources availableResources = ai->nullkiller->getFreeResources();
|
TResources availableResources = ai->getFreeResources();
|
||||||
TResources dailyIncome = ai->nullkiller->buildAnalyzer->getDailyIncome();
|
TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
|
||||||
|
|
||||||
logAi->trace("Free resources amount: %s", availableResources.toString());
|
logAi->trace("Free resources amount: %s", availableResources.toString());
|
||||||
|
|
||||||
@ -46,8 +46,8 @@ Goals::TGoalVec BuildingBehavior::decompose() const
|
|||||||
resourcesRequired.toString(),
|
resourcesRequired.toString(),
|
||||||
totalDevelopmentCost.toString());
|
totalDevelopmentCost.toString());
|
||||||
|
|
||||||
auto & developmentInfos = ai->nullkiller->buildAnalyzer->getDevelopmentInfo();
|
auto & developmentInfos = ai->buildAnalyzer->getDevelopmentInfo();
|
||||||
auto isGoldPreasureLow = !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh();
|
auto isGoldPreasureLow = !ai->buildAnalyzer->isGoldPreasureHigh();
|
||||||
|
|
||||||
for(auto & developmentInfo : developmentInfos)
|
for(auto & developmentInfo : developmentInfos)
|
||||||
{
|
{
|
||||||
@ -57,7 +57,7 @@ Goals::TGoalVec BuildingBehavior::decompose() const
|
|||||||
{
|
{
|
||||||
if(buildingInfo.notEnoughRes)
|
if(buildingInfo.notEnoughRes)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->getLockedResources().canAfford(buildingInfo.buildCost))
|
if(ai->getLockedResources().canAfford(buildingInfo.buildCost))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Composition composition;
|
Composition composition;
|
||||||
|
@ -25,7 +25,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec decompose() const override;
|
Goals::TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
bool operator==(const BuildingBehavior & other) const override
|
bool operator==(const BuildingBehavior & other) const override
|
||||||
{
|
{
|
||||||
|
@ -24,11 +24,11 @@ std::string BuyArmyBehavior::toString() const
|
|||||||
return "Buy army";
|
return "Buy army";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec BuyArmyBehavior::decompose() const
|
Goals::TGoalVec BuyArmyBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
if(cb->getDate(Date::DAY) == 1)
|
if(ai->cb->getDate(Date::DAY) == 1)
|
||||||
return tasks;
|
return tasks;
|
||||||
|
|
||||||
auto heroes = cb->getHeroesInfo();
|
auto heroes = cb->getHeroesInfo();
|
||||||
@ -40,26 +40,26 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
|
|||||||
|
|
||||||
for(auto town : cb->getTownsInfo())
|
for(auto town : cb->getTownsInfo())
|
||||||
{
|
{
|
||||||
auto townArmyAvailableToBuy = ai->nullkiller->armyManager->getArmyAvailableToBuyAsCCreatureSet(
|
auto townArmyAvailableToBuy = ai->armyManager->getArmyAvailableToBuyAsCCreatureSet(
|
||||||
town,
|
town,
|
||||||
ai->nullkiller->getFreeResources());
|
ai->getFreeResources());
|
||||||
|
|
||||||
for(const CGHeroInstance * targetHero : heroes)
|
for(const CGHeroInstance * targetHero : heroes)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->buildAnalyzer->isGoldPreasureHigh() && !town->hasBuilt(BuildingID::CITY_HALL))
|
if(ai->buildAnalyzer->isGoldPreasureHigh() && !town->hasBuilt(BuildingID::CITY_HALL))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ai->nullkiller->heroManager->getHeroRole(targetHero) == HeroRole::MAIN)
|
if(ai->heroManager->getHeroRole(targetHero) == HeroRole::MAIN)
|
||||||
{
|
{
|
||||||
auto reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanGet(
|
auto reinforcement = ai->armyManager->howManyReinforcementsCanGet(
|
||||||
targetHero,
|
targetHero,
|
||||||
targetHero,
|
targetHero,
|
||||||
&*townArmyAvailableToBuy);
|
&*townArmyAvailableToBuy);
|
||||||
|
|
||||||
if(reinforcement)
|
if(reinforcement)
|
||||||
vstd::amin(reinforcement, ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
|
vstd::amin(reinforcement, ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
|
||||||
|
|
||||||
if(reinforcement)
|
if(reinforcement)
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,7 @@ namespace Goals
|
|||||||
class BuyArmyBehavior : public CGoal<BuyArmyBehavior>
|
class BuyArmyBehavior : public CGoal<BuyArmyBehavior>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuyArmyBehavior()
|
Goals::TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Goals::TGoalVec decompose() const override;
|
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
bool operator==(const BuyArmyBehavior & other) const override
|
bool operator==(const BuyArmyBehavior & other) const override
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,7 @@ bool CaptureObjectsBehavior::operator==(const CaptureObjectsBehavior & other) co
|
|||||||
&& vectorEquals(objectSubTypes, other.objectSubTypes);
|
&& vectorEquals(objectSubTypes, other.objectSubTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath> & paths, Nullkiller * nullkiller, const CGObjectInstance * objToVisit)
|
Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath> & paths, const Nullkiller * nullkiller, const CGObjectInstance * objToVisit)
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
|
|||||||
auto firstBlockedAction = path.getFirstBlockedAction();
|
auto firstBlockedAction = path.getFirstBlockedAction();
|
||||||
if(firstBlockedAction)
|
if(firstBlockedAction)
|
||||||
{
|
{
|
||||||
auto subGoal = firstBlockedAction->decompose(path.targetHero);
|
auto subGoal = firstBlockedAction->decompose(nullkiller, path.targetHero);
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
|
logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
|
||||||
@ -167,83 +167,80 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
|
|||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
void CaptureObjectsBehavior::decomposeObjects(
|
||||||
|
Goals::TGoalVec & result,
|
||||||
|
const std::vector<const CGObjectInstance *> & objs,
|
||||||
|
const Nullkiller * nullkiller) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
if(objs.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::mutex sync;
|
std::mutex sync;
|
||||||
|
|
||||||
Nullkiller * nullkiller = ai->nullkiller.get();
|
logAi->debug("Scanning objects, count %d", objs.size());
|
||||||
|
|
||||||
auto captureObjects = [&](const std::vector<const CGObjectInstance *> & objs) -> void
|
// tbb::blocked_range<size_t> r(0, objs.size());
|
||||||
{
|
tbb::parallel_for(
|
||||||
if(objs.empty())
|
tbb::blocked_range<size_t>(0, objs.size()),
|
||||||
|
[this, &objs, &sync, &result, nullkiller](const tbb::blocked_range<size_t> & r)
|
||||||
{
|
{
|
||||||
return;
|
std::vector<AIPath> paths;
|
||||||
}
|
Goals::TGoalVec tasksLocal;
|
||||||
|
|
||||||
logAi->debug("Scanning objects, count %d", objs.size());
|
for(auto i = r.begin(); i != r.end(); i++)
|
||||||
|
|
||||||
tbb::parallel_for(
|
|
||||||
tbb::blocked_range<size_t>(0, objs.size()),
|
|
||||||
[this, &objs, &sync, &tasks, nullkiller](const tbb::blocked_range<size_t> & r)
|
|
||||||
{
|
{
|
||||||
std::vector<AIPath> paths;
|
auto objToVisit = objs[i];
|
||||||
Goals::TGoalVec tasksLocal;
|
|
||||||
|
|
||||||
for(auto i = r.begin(); i != r.end(); i++)
|
if(!objectMatchesFilter(objToVisit))
|
||||||
{
|
continue;
|
||||||
auto objToVisit = objs[i];
|
|
||||||
|
|
||||||
if(!objectMatchesFilter(objToVisit))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
|
logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const int3 pos = objToVisit->visitablePos();
|
nullkiller->pathfinder->calculatePathInfo(paths, objToVisit->visitablePos(), nullkiller->settings->isObjectGraphAllowed());
|
||||||
bool useObjectGraph = nullkiller->settings->isObjectGraphAllowed()
|
|
||||||
&& nullkiller->getScanDepth() != ScanDepth::SMALL;
|
|
||||||
|
|
||||||
nullkiller->pathfinder->calculatePathInfo(paths, pos, useObjectGraph);
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
|
||||||
std::shared_ptr<ExecuteHeroChain> closestWay;
|
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Found %d paths", paths.size());
|
logAi->trace("Found %d paths", paths.size());
|
||||||
#endif
|
#endif
|
||||||
vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit));
|
vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit));
|
||||||
}
|
}
|
||||||
|
|
||||||
vstd::erase_if(tasksLocal, [](TSubgoal task) -> bool
|
std::lock_guard<std::mutex> lock(sync);
|
||||||
{
|
vstd::concatenate(result, tasksLocal);
|
||||||
return task->invalid();
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(sync);
|
Goals::TGoalVec CaptureObjectsBehavior::decompose(const Nullkiller * ai) const
|
||||||
|
{
|
||||||
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
vstd::concatenate(tasks, tasksLocal);
|
vstd::erase_if(tasks, [](TSubgoal task) -> bool
|
||||||
});
|
{
|
||||||
};
|
return task->invalid();
|
||||||
|
});
|
||||||
|
|
||||||
if(specificObjects)
|
if(specificObjects)
|
||||||
{
|
{
|
||||||
captureObjects(objectsToCapture);
|
decomposeObjects(tasks, objectsToCapture, ai);
|
||||||
}
|
}
|
||||||
else if(objectTypes.size())
|
else if(objectTypes.size())
|
||||||
{
|
{
|
||||||
captureObjects(
|
decomposeObjects(
|
||||||
|
tasks,
|
||||||
std::vector<const CGObjectInstance *>(
|
std::vector<const CGObjectInstance *>(
|
||||||
nullkiller->memory->visitableObjs.begin(),
|
ai->memory->visitableObjs.begin(),
|
||||||
nullkiller->memory->visitableObjs.end()));
|
ai->memory->visitableObjs.end()),
|
||||||
|
ai);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
captureObjects(nullkiller->objectClusterizer->getNearbyObjects());
|
decomposeObjects(tasks, ai->objectClusterizer->getNearbyObjects(), ai);
|
||||||
|
|
||||||
if(tasks.empty() || nullkiller->getScanDepth() != ScanDepth::SMALL)
|
if(tasks.empty() || ai->getScanDepth() != ScanDepth::SMALL)
|
||||||
captureObjects(nullkiller->objectClusterizer->getFarObjects());
|
decomposeObjects(tasks, ai->objectClusterizer->getFarObjects(), ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tasks;
|
return tasks;
|
||||||
|
@ -25,6 +25,7 @@ namespace Goals
|
|||||||
std::vector<int> objectSubTypes;
|
std::vector<int> objectSubTypes;
|
||||||
std::vector<const CGObjectInstance *> objectsToCapture;
|
std::vector<const CGObjectInstance *> objectsToCapture;
|
||||||
bool specificObjects;
|
bool specificObjects;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CaptureObjectsBehavior()
|
CaptureObjectsBehavior()
|
||||||
:CGoal(CAPTURE_OBJECTS)
|
:CGoal(CAPTURE_OBJECTS)
|
||||||
@ -48,7 +49,7 @@ namespace Goals
|
|||||||
specificObjects = true;
|
specificObjects = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec decompose() const override;
|
Goals::TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
CaptureObjectsBehavior & ofType(int type)
|
CaptureObjectsBehavior & ofType(int type)
|
||||||
@ -67,10 +68,14 @@ namespace Goals
|
|||||||
|
|
||||||
bool operator==(const CaptureObjectsBehavior & other) const override;
|
bool operator==(const CaptureObjectsBehavior & other) const override;
|
||||||
|
|
||||||
static Goals::TGoalVec getVisitGoals(const std::vector<AIPath> & paths, Nullkiller * nullkiller, const CGObjectInstance * objToVisit = nullptr);
|
static Goals::TGoalVec getVisitGoals(const std::vector<AIPath> & paths, const Nullkiller * nullkiller, const CGObjectInstance * objToVisit = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool objectMatchesFilter(const CGObjectInstance * obj) const;
|
bool objectMatchesFilter(const CGObjectInstance * obj) const;
|
||||||
|
void decomposeObjects(
|
||||||
|
Goals::TGoalVec & result,
|
||||||
|
const std::vector<const CGObjectInstance *> & objs,
|
||||||
|
const Nullkiller * nullkiller) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,23 +26,23 @@ std::string ClusterBehavior::toString() const
|
|||||||
return "Unlock Clusters";
|
return "Unlock Clusters";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec ClusterBehavior::decompose() const
|
Goals::TGoalVec ClusterBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
auto clusters = ai->nullkiller->objectClusterizer->getLockedClusters();
|
auto clusters = ai->objectClusterizer->getLockedClusters();
|
||||||
|
|
||||||
for(auto cluster : clusters)
|
for(auto cluster : clusters)
|
||||||
{
|
{
|
||||||
vstd::concatenate(tasks, decomposeCluster(cluster));
|
vstd::concatenate(tasks, decomposeCluster(ai, cluster));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster> cluster) const
|
Goals::TGoalVec ClusterBehavior::decomposeCluster(const Nullkiller * ai, std::shared_ptr<ObjectCluster> cluster) const
|
||||||
{
|
{
|
||||||
auto center = cluster->calculateCenter();
|
auto center = cluster->calculateCenter();
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(center->visitablePos(), ai->nullkiller->settings->isObjectGraphAllowed());
|
auto paths = ai->pathfinder->getPathInfo(center->visitablePos(), ai->settings->isObjectGraphAllowed());
|
||||||
|
|
||||||
auto blockerPos = cluster->blocker->visitablePos();
|
auto blockerPos = cluster->blocker->visitablePos();
|
||||||
std::vector<AIPath> blockerPaths;
|
std::vector<AIPath> blockerPaths;
|
||||||
@ -65,7 +65,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
|
|||||||
logAi->trace("Checking path %s", path->toString());
|
logAi->trace("Checking path %s", path->toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto blocker = ai->nullkiller->objectClusterizer->getBlocker(*path);
|
auto blocker = ai->objectClusterizer->getBlocker(*path);
|
||||||
|
|
||||||
if(blocker != cluster->blocker)
|
if(blocker != cluster->blocker)
|
||||||
{
|
{
|
||||||
@ -83,7 +83,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
|
|||||||
{
|
{
|
||||||
clonedPath.nodes.insert(clonedPath.nodes.begin(), *node);
|
clonedPath.nodes.insert(clonedPath.nodes.begin(), *node);
|
||||||
|
|
||||||
if(node->coord == blockerPos || cb->getGuardingCreaturePosition(node->coord) == blockerPos)
|
if(node->coord == blockerPos || ai->cb->getGuardingCreaturePosition(node->coord) == blockerPos)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
|
|||||||
logAi->trace("Decompose unlock paths");
|
logAi->trace("Decompose unlock paths");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto unlockTasks = CaptureObjectsBehavior::getVisitGoals(blockerPaths, ai->nullkiller.get());
|
auto unlockTasks = CaptureObjectsBehavior::getVisitGoals(blockerPaths, ai);
|
||||||
|
|
||||||
for(int i = 0; i < paths.size(); i++)
|
for(int i = 0; i < paths.size(); i++)
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec decompose() const override;
|
TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
bool operator==(const ClusterBehavior & other) const override
|
bool operator==(const ClusterBehavior & other) const override
|
||||||
@ -37,7 +37,7 @@ namespace Goals
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Goals::TGoalVec decomposeCluster(std::shared_ptr<ObjectCluster> cluster) const;
|
Goals::TGoalVec decomposeCluster(const Nullkiller * ai, std::shared_ptr<ObjectCluster> cluster) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,21 +34,21 @@ std::string DefenceBehavior::toString() const
|
|||||||
return "Defend towns";
|
return "Defend towns";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec DefenceBehavior::decompose() const
|
Goals::TGoalVec DefenceBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
for(auto town : cb->getTownsInfo())
|
for(auto town : ai->cb->getTownsInfo())
|
||||||
{
|
{
|
||||||
evaluateDefence(tasks, town);
|
evaluateDefence(tasks, town, ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isThreatUnderControl(const CGTownInstance * town, const HitMapInfo & threat, const std::vector<AIPath> & paths)
|
bool isThreatUnderControl(const CGTownInstance * town, const HitMapInfo & threat, const Nullkiller * ai, const std::vector<AIPath> & paths)
|
||||||
{
|
{
|
||||||
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
int dayOfWeek = ai->cb->getDate(Date::DAY_OF_WEEK);
|
||||||
|
|
||||||
for(const AIPath & path : paths)
|
for(const AIPath & path : paths)
|
||||||
{
|
{
|
||||||
@ -81,14 +81,15 @@ void handleCounterAttack(
|
|||||||
const CGTownInstance * town,
|
const CGTownInstance * town,
|
||||||
const HitMapInfo & threat,
|
const HitMapInfo & threat,
|
||||||
const HitMapInfo & maximumDanger,
|
const HitMapInfo & maximumDanger,
|
||||||
|
const Nullkiller * ai,
|
||||||
Goals::TGoalVec & tasks)
|
Goals::TGoalVec & tasks)
|
||||||
{
|
{
|
||||||
if(threat.hero.validAndSet()
|
if(threat.hero.validAndSet()
|
||||||
&& threat.turn <= 1
|
&& threat.turn <= 1
|
||||||
&& (threat.danger == maximumDanger.danger || threat.turn < maximumDanger.turn))
|
&& (threat.danger == maximumDanger.danger || threat.turn < maximumDanger.turn))
|
||||||
{
|
{
|
||||||
auto heroCapturingPaths = ai->nullkiller->pathfinder->getPathInfo(threat.hero->visitablePos());
|
auto heroCapturingPaths = ai->pathfinder->getPathInfo(threat.hero->visitablePos());
|
||||||
auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, ai->nullkiller.get(), threat.hero.get());
|
auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, ai, threat.hero.get());
|
||||||
|
|
||||||
for(int i = 0; i < heroCapturingPaths.size(); i++)
|
for(int i = 0; i < heroCapturingPaths.size(); i++)
|
||||||
{
|
{
|
||||||
@ -106,9 +107,9 @@ void handleCounterAttack(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoalVec & tasks)
|
bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoalVec & tasks, const Nullkiller * ai)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->isHeroLocked(town->garrisonHero.get()))
|
if(ai->isHeroLocked(town->garrisonHero.get()))
|
||||||
{
|
{
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Hero %s in garrison of town %s is supposed to defend the town",
|
"Hero %s in garrison of town %s is supposed to defend the town",
|
||||||
@ -120,7 +121,7 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
|
|||||||
|
|
||||||
if(!town->visitingHero)
|
if(!town->visitingHero)
|
||||||
{
|
{
|
||||||
if(cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
|
if(ai->cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
|
||||||
{
|
{
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
"Extracting hero %s from garrison of town %s",
|
"Extracting hero %s from garrison of town %s",
|
||||||
@ -131,10 +132,10 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if(ai->nullkiller->heroManager->getHeroRole(town->garrisonHero.get()) == HeroRole::MAIN)
|
else if(ai->heroManager->getHeroRole(town->garrisonHero.get()) == HeroRole::MAIN)
|
||||||
{
|
{
|
||||||
auto armyDismissLimit = 1000;
|
auto armyDismissLimit = 1000;
|
||||||
auto heroToDismiss = ai->nullkiller->heroManager->findWeakHeroToDismiss(armyDismissLimit);
|
auto heroToDismiss = ai->heroManager->findWeakHeroToDismiss(armyDismissLimit);
|
||||||
|
|
||||||
if(heroToDismiss)
|
if(heroToDismiss)
|
||||||
{
|
{
|
||||||
@ -148,16 +149,16 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
|
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town, const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
logAi->trace("Evaluating defence for %s", town->getNameTranslated());
|
logAi->trace("Evaluating defence for %s", town->getNameTranslated());
|
||||||
|
|
||||||
auto threatNode = ai->nullkiller->dangerHitMap->getObjectThreat(town);
|
auto threatNode = ai->dangerHitMap->getObjectThreat(town);
|
||||||
std::vector<HitMapInfo> threats = ai->nullkiller->dangerHitMap->getTownThreats(town);
|
std::vector<HitMapInfo> threats = ai->dangerHitMap->getTownThreats(town);
|
||||||
|
|
||||||
threats.push_back(threatNode.fastestDanger); // no guarantee that fastest danger will be there
|
threats.push_back(threatNode.fastestDanger); // no guarantee that fastest danger will be there
|
||||||
|
|
||||||
if(town->garrisonHero && handleGarrisonHeroFromPreviousTurn(town, tasks))
|
if(town->garrisonHero && handleGarrisonHeroFromPreviousTurn(town, tasks, ai))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -169,7 +170,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
uint64_t reinforcement = ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
||||||
|
|
||||||
if(reinforcement)
|
if(reinforcement)
|
||||||
{
|
{
|
||||||
@ -177,7 +178,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
|
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
|
auto paths = ai->pathfinder->getPathInfo(town->visitablePos());
|
||||||
|
|
||||||
for(auto & threat : threats)
|
for(auto & threat : threats)
|
||||||
{
|
{
|
||||||
@ -188,14 +189,14 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
std::to_string(threat.turn),
|
std::to_string(threat.turn),
|
||||||
threat.hero ? threat.hero->getNameTranslated() : std::string("<no hero>"));
|
threat.hero ? threat.hero->getNameTranslated() : std::string("<no hero>"));
|
||||||
|
|
||||||
handleCounterAttack(town, threat, threatNode.maximumDanger, tasks);
|
handleCounterAttack(town, threat, threatNode.maximumDanger, ai, tasks);
|
||||||
|
|
||||||
if(isThreatUnderControl(town, threat, paths))
|
if(isThreatUnderControl(town, threat, ai, paths))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateRecruitingHero(tasks, threat, town);
|
evaluateRecruitingHero(tasks, threat, town, ai);
|
||||||
|
|
||||||
if(paths.empty())
|
if(paths.empty())
|
||||||
{
|
{
|
||||||
@ -274,7 +275,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
// main without army and visiting scout with army, very specific case
|
// main without army and visiting scout with army, very specific case
|
||||||
if(town->visitingHero && town->getUpperArmy()->stacksCount() == 0
|
if(town->visitingHero && town->getUpperArmy()->stacksCount() == 0
|
||||||
&& path.targetHero != town->visitingHero.get() && path.exchangeCount == 1 && path.turn() == 0
|
&& path.targetHero != town->visitingHero.get() && path.exchangeCount == 1 && path.turn() == 0
|
||||||
&& ai->nullkiller->heroManager->evaluateHero(path.targetHero) > ai->nullkiller->heroManager->evaluateHero(town->visitingHero.get())
|
&& ai->heroManager->evaluateHero(path.targetHero) > ai->heroManager->evaluateHero(town->visitingHero.get())
|
||||||
&& 10 * path.targetHero->getTotalStrength() < town->visitingHero->getTotalStrength())
|
&& 10 * path.targetHero->getTotalStrength() < town->visitingHero->getTotalStrength())
|
||||||
{
|
{
|
||||||
path.heroArmy = town->visitingHero.get();
|
path.heroArmy = town->visitingHero.get();
|
||||||
@ -293,7 +294,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
if(threat.turn == 0 || (path.turn() <= threat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= threat.danger))
|
if(threat.turn == 0 || (path.turn() <= threat.turn && path.getHeroStrength() * SAFE_ATTACK_CONSTANT >= threat.danger))
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->arePathHeroesLocked(path))
|
if(ai->arePathHeroesLocked(path))
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Can not move %s to defend town %s. Path is locked.",
|
logAi->trace("Can not move %s to defend town %s. Path is locked.",
|
||||||
@ -344,7 +345,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
{
|
{
|
||||||
if(town->garrisonHero)
|
if(town->garrisonHero)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->heroManager->getHeroRole(town->visitingHero.get()) == HeroRole::SCOUT
|
if(ai->heroManager->getHeroRole(town->visitingHero.get()) == HeroRole::SCOUT
|
||||||
&& town->visitingHero->getArmyStrength() < path.heroArmy->getArmyStrength() / 20)
|
&& town->visitingHero->getArmyStrength() < path.heroArmy->getArmyStrength() / 20)
|
||||||
{
|
{
|
||||||
if(path.turn() == 0)
|
if(path.turn() == 0)
|
||||||
@ -378,7 +379,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
auto firstBlockedAction = path.getFirstBlockedAction();
|
auto firstBlockedAction = path.getFirstBlockedAction();
|
||||||
if(firstBlockedAction)
|
if(firstBlockedAction)
|
||||||
{
|
{
|
||||||
auto subGoal = firstBlockedAction->decompose(path.targetHero);
|
auto subGoal = firstBlockedAction->decompose(ai, path.targetHero);
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
|
logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
|
||||||
@ -402,19 +403,19 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
logAi->debug("Found %d tasks", tasks.size());
|
logAi->debug("Found %d tasks", tasks.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town) const
|
void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town, const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
if(town->hasBuilt(BuildingID::TAVERN)
|
if(town->hasBuilt(BuildingID::TAVERN)
|
||||||
&& cb->getResourceAmount(EGameResID::GOLD) > GameConstants::HERO_GOLD_COST)
|
&& ai->cb->getResourceAmount(EGameResID::GOLD) > GameConstants::HERO_GOLD_COST)
|
||||||
{
|
{
|
||||||
auto heroesInTavern = cb->getAvailableHeroes(town);
|
auto heroesInTavern = ai->cb->getAvailableHeroes(town);
|
||||||
|
|
||||||
for(auto hero : heroesInTavern)
|
for(auto hero : heroesInTavern)
|
||||||
{
|
{
|
||||||
if(hero->getTotalStrength() < threat.danger)
|
if(hero->getTotalStrength() < threat.danger)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto myHeroes = cb->getHeroesInfo();
|
auto myHeroes = ai->cb->getHeroesInfo();
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Hero %s can be recruited to defend %s", hero->getObjectName(), town->getObjectName());
|
logAi->trace("Hero %s can be recruited to defend %s", hero->getObjectName(), town->getObjectName());
|
||||||
@ -448,9 +449,9 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
|
|||||||
if(heroToDismiss && heroToDismiss->getArmyStrength() + 500 > hero->getArmyStrength())
|
if(heroToDismiss && heroToDismiss->getArmyStrength() + 500 > hero->getArmyStrength())
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if(ai->nullkiller->heroManager->heroCapReached())
|
else if(ai->heroManager->heroCapReached())
|
||||||
{
|
{
|
||||||
heroToDismiss = ai->nullkiller->heroManager->findWeakHeroToDismiss(hero->getArmyStrength());
|
heroToDismiss = ai->heroManager->findWeakHeroToDismiss(hero->getArmyStrength());
|
||||||
|
|
||||||
if(!heroToDismiss)
|
if(!heroToDismiss)
|
||||||
continue;
|
continue;
|
||||||
|
@ -20,7 +20,6 @@ struct HitMapInfo;
|
|||||||
|
|
||||||
namespace Goals
|
namespace Goals
|
||||||
{
|
{
|
||||||
|
|
||||||
class DefenceBehavior : public CGoal<DefenceBehavior>
|
class DefenceBehavior : public CGoal<DefenceBehavior>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -29,7 +28,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec decompose() const override;
|
Goals::TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
bool operator==(const DefenceBehavior & other) const override
|
bool operator==(const DefenceBehavior & other) const override
|
||||||
@ -38,8 +37,8 @@ namespace Goals
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const;
|
void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town, const Nullkiller * ai) const;
|
||||||
void evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town) const;
|
void evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town, const Nullkiller * ai) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,11 @@ std::string GatherArmyBehavior::toString() const
|
|||||||
return "Gather army";
|
return "Gather army";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec GatherArmyBehavior::decompose() const
|
Goals::TGoalVec GatherArmyBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
|
|
||||||
auto heroes = cb->getHeroesInfo();
|
auto heroes = ai->cb->getHeroesInfo();
|
||||||
|
|
||||||
if(heroes.empty())
|
if(heroes.empty())
|
||||||
{
|
{
|
||||||
@ -43,33 +43,33 @@ Goals::TGoalVec GatherArmyBehavior::decompose() const
|
|||||||
|
|
||||||
for(const CGHeroInstance * hero : heroes)
|
for(const CGHeroInstance * hero : heroes)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::MAIN)
|
if(ai->heroManager->getHeroRole(hero) == HeroRole::MAIN)
|
||||||
{
|
{
|
||||||
vstd::concatenate(tasks, deliverArmyToHero(hero));
|
vstd::concatenate(tasks, deliverArmyToHero(ai, hero));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto towns = cb->getTownsInfo();
|
auto towns = ai->cb->getTownsInfo();
|
||||||
|
|
||||||
for(const CGTownInstance * town : towns)
|
for(const CGTownInstance * town : towns)
|
||||||
{
|
{
|
||||||
vstd::concatenate(tasks, upgradeArmy(town));
|
vstd::concatenate(tasks, upgradeArmy(ai, town));
|
||||||
}
|
}
|
||||||
|
|
||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const
|
Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const Nullkiller * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
const int3 pos = hero->visitablePos();
|
const int3 pos = hero->visitablePos();
|
||||||
auto targetHeroScore = ai->nullkiller->heroManager->evaluateHero(hero);
|
auto targetHeroScore = ai->heroManager->evaluateHero(hero);
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
|
logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos, ai->nullkiller->settings->isObjectGraphAllowed());
|
auto paths = ai->pathfinder->getPathInfo(pos, ai->settings->isObjectGraphAllowed());
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Gather army found %d paths", paths.size());
|
logAi->trace("Gather army found %d paths", paths.size());
|
||||||
@ -89,7 +89,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(path.turn() > 0 && ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
if(path.turn() > 0 && ai->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
||||||
@ -97,7 +97,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ai->nullkiller->arePathHeroesLocked(path))
|
if(ai->arePathHeroesLocked(path))
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Ignore path because of locked hero");
|
logAi->trace("Ignore path because of locked hero");
|
||||||
@ -107,7 +107,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
|
|
||||||
HeroExchange heroExchange(hero, path);
|
HeroExchange heroExchange(hero, path);
|
||||||
|
|
||||||
uint64_t armyValue = heroExchange.getReinforcementArmyStrength();
|
uint64_t armyValue = heroExchange.getReinforcementArmyStrength(ai);
|
||||||
float armyRatio = (float)armyValue / hero->getArmyStrength();
|
float armyRatio = (float)armyValue / hero->getArmyStrength();
|
||||||
|
|
||||||
// avoid transferring very small amount of army
|
// avoid transferring very small amount of army
|
||||||
@ -126,11 +126,11 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
{
|
{
|
||||||
if(!node.targetHero) continue;
|
if(!node.targetHero) continue;
|
||||||
|
|
||||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(node.targetHero);
|
auto heroRole = ai->heroManager->getHeroRole(node.targetHero);
|
||||||
|
|
||||||
if(heroRole == HeroRole::MAIN)
|
if(heroRole == HeroRole::MAIN)
|
||||||
{
|
{
|
||||||
auto score = ai->nullkiller->heroManager->evaluateHero(node.targetHero);
|
auto score = ai->heroManager->evaluateHero(node.targetHero);
|
||||||
|
|
||||||
if(score >= targetHeroScore)
|
if(score >= targetHeroScore)
|
||||||
{
|
{
|
||||||
@ -174,7 +174,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
|
|
||||||
if(hero->inTownGarrison && path.turn() == 0)
|
if(hero->inTownGarrison && path.turn() == 0)
|
||||||
{
|
{
|
||||||
auto lockReason = ai->nullkiller->getHeroLockedReason(hero);
|
auto lockReason = ai->getHeroLockedReason(hero);
|
||||||
|
|
||||||
if(path.targetHero->visitedTown == hero->visitedTown)
|
if(path.targetHero->visitedTown == hero->visitedTown)
|
||||||
{
|
{
|
||||||
@ -201,7 +201,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Action is blocked. Considering decomposition.");
|
logAi->trace("Action is blocked. Considering decomposition.");
|
||||||
#endif
|
#endif
|
||||||
auto subGoal = blockedAction->decompose(path.targetHero);
|
auto subGoal = blockedAction->decompose(ai, path.targetHero);
|
||||||
|
|
||||||
if(subGoal->invalid())
|
if(subGoal->invalid())
|
||||||
{
|
{
|
||||||
@ -221,18 +221,18 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
|
|||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) const
|
Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGTownInstance * upgrader) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
const int3 pos = upgrader->visitablePos();
|
const int3 pos = upgrader->visitablePos();
|
||||||
TResources availableResources = ai->nullkiller->getFreeResources();
|
TResources availableResources = ai->getFreeResources();
|
||||||
|
|
||||||
#if NKAI_TRACE_LEVEL >= 1
|
#if NKAI_TRACE_LEVEL >= 1
|
||||||
logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
|
logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos, ai->nullkiller->settings->isObjectGraphAllowed());
|
auto paths = ai->pathfinder->getPathInfo(pos, ai->settings->isObjectGraphAllowed());
|
||||||
auto goals = CaptureObjectsBehavior::getVisitGoals(paths, ai->nullkiller.get());
|
auto goals = CaptureObjectsBehavior::getVisitGoals(paths, ai);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
|
||||||
|
|
||||||
@ -244,9 +244,9 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
|||||||
|
|
||||||
for(const AIPath & path : paths)
|
for(const AIPath & path : paths)
|
||||||
{
|
{
|
||||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
|
auto heroRole = ai->heroManager->getHeroRole(path.targetHero);
|
||||||
|
|
||||||
if(heroRole == HeroRole::MAIN && path.turn() < ai->nullkiller->settings->getScoutHeroTurnDistanceLimit())
|
if(heroRole == HeroRole::MAIN && path.turn() < ai->settings->getScoutHeroTurnDistanceLimit())
|
||||||
hasMainAround = true;
|
hasMainAround = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +275,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ai->nullkiller->arePathHeroesLocked(path))
|
if(ai->arePathHeroesLocked(path))
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Ignore path because of locked hero");
|
logAi->trace("Ignore path because of locked hero");
|
||||||
@ -292,10 +292,10 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
|
auto heroRole = ai->heroManager->getHeroRole(path.targetHero);
|
||||||
|
|
||||||
if(heroRole == HeroRole::SCOUT
|
if(heroRole == HeroRole::SCOUT
|
||||||
&& ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
&& ai->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
|
||||||
{
|
{
|
||||||
#if NKAI_TRACE_LEVEL >= 2
|
#if NKAI_TRACE_LEVEL >= 2
|
||||||
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
|
||||||
@ -303,17 +303,17 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto upgrade = ai->nullkiller->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
|
auto upgrade = ai->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
|
||||||
|
|
||||||
if(!upgrader->garrisonHero
|
if(!upgrader->garrisonHero
|
||||||
&& (
|
&& (
|
||||||
hasMainAround
|
hasMainAround
|
||||||
|| ai->nullkiller->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))
|
|| ai->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))
|
||||||
{
|
{
|
||||||
ArmyUpgradeInfo armyToGetOrBuy;
|
ArmyUpgradeInfo armyToGetOrBuy;
|
||||||
|
|
||||||
armyToGetOrBuy.addArmyToGet(
|
armyToGetOrBuy.addArmyToGet(
|
||||||
ai->nullkiller->armyManager->getBestArmy(
|
ai->armyManager->getBestArmy(
|
||||||
path.targetHero,
|
path.targetHero,
|
||||||
path.heroArmy,
|
path.heroArmy,
|
||||||
upgrader->getUpperArmy()));
|
upgrader->getUpperArmy()));
|
||||||
@ -321,11 +321,11 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
|||||||
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
|
||||||
|
|
||||||
armyToGetOrBuy.addArmyToBuy(
|
armyToGetOrBuy.addArmyToBuy(
|
||||||
ai->nullkiller->armyManager->toSlotInfo(
|
ai->armyManager->toSlotInfo(
|
||||||
ai->nullkiller->armyManager->getArmyAvailableToBuy(
|
ai->armyManager->getArmyAvailableToBuy(
|
||||||
path.heroArmy,
|
path.heroArmy,
|
||||||
upgrader,
|
upgrader,
|
||||||
ai->nullkiller->getFreeResources(),
|
ai->getFreeResources(),
|
||||||
path.turn())));
|
path.turn())));
|
||||||
|
|
||||||
upgrade.upgradeValue += armyToGetOrBuy.upgradeValue;
|
upgrade.upgradeValue += armyToGetOrBuy.upgradeValue;
|
||||||
@ -334,17 +334,17 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
|
|||||||
|
|
||||||
if(!upgrade.upgradeValue
|
if(!upgrade.upgradeValue
|
||||||
&& armyToGetOrBuy.upgradeValue > 20000
|
&& armyToGetOrBuy.upgradeValue > 20000
|
||||||
&& ai->nullkiller->heroManager->canRecruitHero(town)
|
&& ai->heroManager->canRecruitHero(town)
|
||||||
&& path.turn() < ai->nullkiller->settings->getScoutHeroTurnDistanceLimit())
|
&& path.turn() < ai->settings->getScoutHeroTurnDistanceLimit())
|
||||||
{
|
{
|
||||||
for(auto hero : cb->getAvailableHeroes(town))
|
for(auto hero : cb->getAvailableHeroes(town))
|
||||||
{
|
{
|
||||||
auto scoutReinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(hero, town)
|
auto scoutReinforcement = ai->armyManager->howManyReinforcementsCanBuy(hero, town)
|
||||||
+ ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero, town);
|
+ ai->armyManager->howManyReinforcementsCanGet(hero, town);
|
||||||
|
|
||||||
if(scoutReinforcement >= armyToGetOrBuy.upgradeValue
|
if(scoutReinforcement >= armyToGetOrBuy.upgradeValue
|
||||||
&& ai->nullkiller->getFreeGold() >20000
|
&& ai->getFreeGold() >20000
|
||||||
&& !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh())
|
&& !ai->buildAnalyzer->isGoldPreasureHigh())
|
||||||
{
|
{
|
||||||
Composition recruitHero;
|
Composition recruitHero;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec decompose() const override;
|
TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
bool operator==(const GatherArmyBehavior & other) const override
|
bool operator==(const GatherArmyBehavior & other) const override
|
||||||
@ -34,8 +34,8 @@ namespace Goals
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const;
|
TGoalVec deliverArmyToHero(const Nullkiller * ai, const CGHeroInstance * hero) const;
|
||||||
TGoalVec upgradeArmy(const CGTownInstance * upgrader) const;
|
TGoalVec upgradeArmy(const Nullkiller * ai, const CGTownInstance * upgrader) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,12 +24,12 @@ std::string RecruitHeroBehavior::toString() const
|
|||||||
return "Recruit hero";
|
return "Recruit hero";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
auto towns = cb->getTownsInfo();
|
auto towns = ai->cb->getTownsInfo();
|
||||||
|
|
||||||
auto ourHeroes = ai->nullkiller->heroManager->getHeroRoles();
|
auto ourHeroes = ai->heroManager->getHeroRoles();
|
||||||
auto minScoreToHireMain = std::numeric_limits<float>::max();
|
auto minScoreToHireMain = std::numeric_limits<float>::max();
|
||||||
|
|
||||||
for(auto hero : ourHeroes)
|
for(auto hero : ourHeroes)
|
||||||
@ -37,7 +37,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
|||||||
if(hero.second != HeroRole::MAIN)
|
if(hero.second != HeroRole::MAIN)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto newScore = ai->nullkiller->heroManager->evaluateHero(hero.first.get());
|
auto newScore = ai->heroManager->evaluateHero(hero.first.get());
|
||||||
|
|
||||||
if(minScoreToHireMain > newScore)
|
if(minScoreToHireMain > newScore)
|
||||||
{
|
{
|
||||||
@ -48,13 +48,13 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
|||||||
|
|
||||||
for(auto town : towns)
|
for(auto town : towns)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->heroManager->canRecruitHero(town))
|
if(ai->heroManager->canRecruitHero(town))
|
||||||
{
|
{
|
||||||
auto availableHeroes = cb->getAvailableHeroes(town);
|
auto availableHeroes = ai->cb->getAvailableHeroes(town);
|
||||||
|
|
||||||
for(auto hero : availableHeroes)
|
for(auto hero : availableHeroes)
|
||||||
{
|
{
|
||||||
auto score = ai->nullkiller->heroManager->evaluateHero(hero);
|
auto score = ai->heroManager->evaluateHero(hero);
|
||||||
|
|
||||||
if(score > minScoreToHireMain)
|
if(score > minScoreToHireMain)
|
||||||
{
|
{
|
||||||
@ -65,16 +65,16 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
|||||||
|
|
||||||
int treasureSourcesCount = 0;
|
int treasureSourcesCount = 0;
|
||||||
|
|
||||||
for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects())
|
for(auto obj : ai->objectClusterizer->getNearbyObjects())
|
||||||
{
|
{
|
||||||
if((obj->ID == Obj::RESOURCE)
|
if((obj->ID == Obj::RESOURCE)
|
||||||
|| obj->ID == Obj::TREASURE_CHEST
|
|| obj->ID == Obj::TREASURE_CHEST
|
||||||
|| obj->ID == Obj::CAMPFIRE
|
|| obj->ID == Obj::CAMPFIRE
|
||||||
|| isWeeklyRevisitable(obj)
|
|| isWeeklyRevisitable(ai, obj)
|
||||||
|| obj->ID ==Obj::ARTIFACT)
|
|| obj->ID ==Obj::ARTIFACT)
|
||||||
{
|
{
|
||||||
auto tile = obj->visitablePos();
|
auto tile = obj->visitablePos();
|
||||||
auto closestTown = ai->nullkiller->dangerHitMap->getClosestTown(tile);
|
auto closestTown = ai->dangerHitMap->getClosestTown(tile);
|
||||||
|
|
||||||
if(town == closestTown)
|
if(town == closestTown)
|
||||||
treasureSourcesCount++;
|
treasureSourcesCount++;
|
||||||
@ -84,8 +84,8 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
|||||||
if(treasureSourcesCount < 5 && (town->garrisonHero || town->getUpperArmy()->getArmyStrength() < 10000))
|
if(treasureSourcesCount < 5 && (town->garrisonHero || town->getUpperArmy()->getArmyStrength() < 10000))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
|
if(ai->cb->getHeroesInfo().size() < ai->cb->getTownsInfo().size() + 1
|
||||||
|| (ai->nullkiller->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh()))
|
|| (ai->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->buildAnalyzer->isGoldPreasureHigh()))
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(Goals::RecruitHero(town).setpriority(3)));
|
tasks.push_back(Goals::sptr(Goals::RecruitHero(town).setpriority(3)));
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec decompose() const override;
|
TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
bool operator==(const RecruitHeroBehavior & other) const override
|
bool operator==(const RecruitHeroBehavior & other) const override
|
||||||
|
@ -41,9 +41,9 @@ const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPa
|
|||||||
return shortestPath;
|
return shortestPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CGHeroInstance * getNearestHero(const CGTownInstance * town)
|
const CGHeroInstance * getNearestHero(const Nullkiller * ai, const CGTownInstance * town)
|
||||||
{
|
{
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
|
auto paths = ai->pathfinder->getPathInfo(town->visitablePos());
|
||||||
|
|
||||||
if(paths.empty())
|
if(paths.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -59,9 +59,9 @@ const CGHeroInstance * getNearestHero(const CGTownInstance * town)
|
|||||||
return shortestPath.targetHero;
|
return shortestPath.targetHero;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool needToRecruitHero(const CGTownInstance * startupTown)
|
bool needToRecruitHero(const Nullkiller * ai, const CGTownInstance * startupTown)
|
||||||
{
|
{
|
||||||
if(!ai->nullkiller->heroManager->canRecruitHero(startupTown))
|
if(!ai->heroManager->canRecruitHero(startupTown))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!startupTown->garrisonHero && !startupTown->visitingHero)
|
if(!startupTown->garrisonHero && !startupTown->visitingHero)
|
||||||
@ -69,7 +69,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
|
|||||||
|
|
||||||
int treasureSourcesCount = 0;
|
int treasureSourcesCount = 0;
|
||||||
|
|
||||||
for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects())
|
for(auto obj : ai->objectClusterizer->getNearbyObjects())
|
||||||
{
|
{
|
||||||
if((obj->ID == Obj::RESOURCE && dynamic_cast<const CGResource *>(obj)->resourceID() == EGameResID::GOLD)
|
if((obj->ID == Obj::RESOURCE && dynamic_cast<const CGResource *>(obj)->resourceID() == EGameResID::GOLD)
|
||||||
|| obj->ID == Obj::TREASURE_CHEST
|
|| obj->ID == Obj::TREASURE_CHEST
|
||||||
@ -91,10 +91,10 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
|
|||||||
return cb->getHeroCount(ai->playerID, true) < basicCount + boost;
|
return cb->getHeroCount(ai->playerID, true) < basicCount + boost;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec StartupBehavior::decompose() const
|
Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
auto towns = cb->getTownsInfo();
|
auto towns = ai->cb->getTownsInfo();
|
||||||
|
|
||||||
if(!towns.size())
|
if(!towns.size())
|
||||||
return tasks;
|
return tasks;
|
||||||
@ -103,38 +103,38 @@ Goals::TGoalVec StartupBehavior::decompose() const
|
|||||||
|
|
||||||
if(towns.size() > 1)
|
if(towns.size() > 1)
|
||||||
{
|
{
|
||||||
startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
|
startupTown = *vstd::maxElementByFun(towns, [ai](const CGTownInstance * town) -> float
|
||||||
{
|
{
|
||||||
if(town->garrisonHero)
|
if(town->garrisonHero)
|
||||||
return ai->nullkiller->heroManager->evaluateHero(town->garrisonHero.get());
|
return ai->heroManager->evaluateHero(town->garrisonHero.get());
|
||||||
|
|
||||||
auto closestHero = getNearestHero(town);
|
auto closestHero = getNearestHero(ai, town);
|
||||||
|
|
||||||
if(closestHero)
|
if(closestHero)
|
||||||
return ai->nullkiller->heroManager->evaluateHero(closestHero);
|
return ai->heroManager->evaluateHero(closestHero);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!startupTown->hasBuilt(BuildingID::TAVERN)
|
if(!startupTown->hasBuilt(BuildingID::TAVERN)
|
||||||
&& cb->canBuildStructure(startupTown, BuildingID::TAVERN) == EBuildingState::ALLOWED)
|
&& ai->cb->canBuildStructure(startupTown, BuildingID::TAVERN) == EBuildingState::ALLOWED)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(Goals::BuildThis(BuildingID::TAVERN, startupTown).setpriority(100)));
|
tasks.push_back(Goals::sptr(Goals::BuildThis(BuildingID::TAVERN, startupTown).setpriority(100)));
|
||||||
|
|
||||||
return tasks;
|
return tasks;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canRecruitHero = needToRecruitHero(startupTown);
|
bool canRecruitHero = needToRecruitHero(ai, startupTown);
|
||||||
auto closestHero = getNearestHero(startupTown);
|
auto closestHero = getNearestHero(ai, startupTown);
|
||||||
|
|
||||||
if(closestHero)
|
if(closestHero)
|
||||||
{
|
{
|
||||||
if(!startupTown->visitingHero)
|
if(!startupTown->visitingHero)
|
||||||
{
|
{
|
||||||
if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero) > 200)
|
if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero) > 200)
|
||||||
{
|
{
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(startupTown->visitablePos());
|
auto paths = ai->pathfinder->getPathInfo(startupTown->visitablePos());
|
||||||
|
|
||||||
if(paths.size())
|
if(paths.size())
|
||||||
{
|
{
|
||||||
@ -147,22 +147,22 @@ Goals::TGoalVec StartupBehavior::decompose() const
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto visitingHero = startupTown->visitingHero.get();
|
auto visitingHero = startupTown->visitingHero.get();
|
||||||
auto visitingHeroScore = ai->nullkiller->heroManager->evaluateHero(visitingHero);
|
auto visitingHeroScore = ai->heroManager->evaluateHero(visitingHero);
|
||||||
|
|
||||||
if(startupTown->garrisonHero)
|
if(startupTown->garrisonHero)
|
||||||
{
|
{
|
||||||
auto garrisonHero = startupTown->garrisonHero.get();
|
auto garrisonHero = startupTown->garrisonHero.get();
|
||||||
auto garrisonHeroScore = ai->nullkiller->heroManager->evaluateHero(garrisonHero);
|
auto garrisonHeroScore = ai->heroManager->evaluateHero(garrisonHero);
|
||||||
|
|
||||||
if(visitingHeroScore > garrisonHeroScore
|
if(visitingHeroScore > garrisonHeroScore
|
||||||
|| (ai->nullkiller->heroManager->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->nullkiller->heroManager->getHeroRole(visitingHero) == HeroRole::MAIN))
|
|| (ai->heroManager->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->heroManager->getHeroRole(visitingHero) == HeroRole::MAIN))
|
||||||
{
|
{
|
||||||
if(canRecruitHero || ai->nullkiller->armyManager->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
|
if(canRecruitHero || ai->armyManager->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero, HeroLockedReason::STARTUP).setpriority(100)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero, HeroLockedReason::STARTUP).setpriority(100)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
|
else if(ai->armyManager->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero, HeroLockedReason::STARTUP).setpriority(100)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero, HeroLockedReason::STARTUP).setpriority(100)));
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
|
|||||||
else if(canRecruitHero)
|
else if(canRecruitHero)
|
||||||
{
|
{
|
||||||
auto canPickTownArmy = startupTown->stacksCount() == 0
|
auto canPickTownArmy = startupTown->stacksCount() == 0
|
||||||
|| ai->nullkiller->armyManager->howManyReinforcementsCanGet(startupTown->visitingHero, startupTown) > 0;
|
|| ai->armyManager->howManyReinforcementsCanGet(startupTown->visitingHero, startupTown) > 0;
|
||||||
|
|
||||||
if(canPickTownArmy)
|
if(canPickTownArmy)
|
||||||
{
|
{
|
||||||
@ -189,7 +189,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
|
|||||||
{
|
{
|
||||||
for(auto town : towns)
|
for(auto town : towns)
|
||||||
{
|
{
|
||||||
if(!town->visitingHero && needToRecruitHero(town))
|
if(!town->visitingHero && needToRecruitHero(ai, town))
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(Goals::RecruitHero(town)));
|
tasks.push_back(Goals::sptr(Goals::RecruitHero(town)));
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
|
|||||||
if(town->garrisonHero
|
if(town->garrisonHero
|
||||||
&& town->garrisonHero->movementPointsRemaining()
|
&& town->garrisonHero->movementPointsRemaining()
|
||||||
&& !town->visitingHero
|
&& !town->visitingHero
|
||||||
&& ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
|
&& ai->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
|
||||||
{
|
{
|
||||||
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(MIN_PRIORITY)));
|
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(MIN_PRIORITY)));
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec decompose() const override;
|
TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
bool operator==(const StartupBehavior & other) const override
|
bool operator==(const StartupBehavior & other) const override
|
||||||
|
@ -27,10 +27,10 @@ std::string StayAtTownBehavior::toString() const
|
|||||||
return "StayAtTownBehavior";
|
return "StayAtTownBehavior";
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec StayAtTownBehavior::decompose() const
|
Goals::TGoalVec StayAtTownBehavior::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
Goals::TGoalVec tasks;
|
Goals::TGoalVec tasks;
|
||||||
auto towns = cb->getTownsInfo();
|
auto towns = ai->cb->getTownsInfo();
|
||||||
|
|
||||||
if(!towns.size())
|
if(!towns.size())
|
||||||
return tasks;
|
return tasks;
|
||||||
@ -42,7 +42,7 @@ Goals::TGoalVec StayAtTownBehavior::decompose() const
|
|||||||
if(!town->hasBuilt(BuildingID::MAGES_GUILD_1))
|
if(!town->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ai->nullkiller->pathfinder->calculatePathInfo(paths, town->visitablePos());
|
ai->pathfinder->calculatePathInfo(paths, town->visitablePos());
|
||||||
|
|
||||||
for(auto & path : paths)
|
for(auto & path : paths)
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec decompose() const override;
|
TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
bool operator==(const StayAtTownBehavior & other) const override
|
bool operator==(const StayAtTownBehavior & other) const override
|
||||||
|
@ -26,6 +26,11 @@ namespace NKAI
|
|||||||
|
|
||||||
using namespace Goals;
|
using namespace Goals;
|
||||||
|
|
||||||
|
DeepDecomposer::DeepDecomposer(const Nullkiller * ai)
|
||||||
|
:ai(ai), depth(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void DeepDecomposer::reset()
|
void DeepDecomposer::reset()
|
||||||
{
|
{
|
||||||
decompositionCache.clear();
|
decompositionCache.clear();
|
||||||
@ -136,7 +141,7 @@ Goals::TSubgoal DeepDecomposer::aggregateGoals(int startDepth, TSubgoal last)
|
|||||||
|
|
||||||
Goals::TSubgoal DeepDecomposer::unwrapComposition(Goals::TSubgoal goal)
|
Goals::TSubgoal DeepDecomposer::unwrapComposition(Goals::TSubgoal goal)
|
||||||
{
|
{
|
||||||
return goal->goalType == Goals::COMPOSITION ? goal->decompose().back() : goal;
|
return goal->goalType == Goals::COMPOSITION ? goal->decompose(ai).back() : goal;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEquivalentGoals(TSubgoal goal1, TSubgoal goal2)
|
bool isEquivalentGoals(TSubgoal goal1, TSubgoal goal2)
|
||||||
@ -156,7 +161,7 @@ bool isEquivalentGoals(TSubgoal goal1, TSubgoal goal2)
|
|||||||
|
|
||||||
bool DeepDecomposer::isCompositionLoop(TSubgoal goal)
|
bool DeepDecomposer::isCompositionLoop(TSubgoal goal)
|
||||||
{
|
{
|
||||||
auto goalsToTest = goal->goalType == Goals::COMPOSITION ? goal->decompose() : TGoalVec{goal};
|
auto goalsToTest = goal->goalType == Goals::COMPOSITION ? goal->decompose(ai) : TGoalVec{goal};
|
||||||
|
|
||||||
for(auto goalToTest : goalsToTest)
|
for(auto goalToTest : goalsToTest)
|
||||||
{
|
{
|
||||||
@ -206,7 +211,7 @@ TGoalVec DeepDecomposer::decomposeCached(TSubgoal goal, bool & fromCache)
|
|||||||
|
|
||||||
fromCache = false;
|
fromCache = false;
|
||||||
|
|
||||||
return goal->decompose();
|
return goal->decompose(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeepDecomposer::addToCache(TSubgoal goal)
|
void DeepDecomposer::addToCache(TSubgoal goal)
|
||||||
|
@ -30,8 +30,10 @@ private:
|
|||||||
std::vector<Goals::TGoalVec> goals;
|
std::vector<Goals::TGoalVec> goals;
|
||||||
std::vector<TGoalHashSet> decompositionCache;
|
std::vector<TGoalHashSet> decompositionCache;
|
||||||
int depth;
|
int depth;
|
||||||
|
const Nullkiller * ai;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
DeepDecomposer(const Nullkiller * ai);
|
||||||
void reset();
|
void reset();
|
||||||
Goals::TGoalVec decompose(Goals::TSubgoal behavior, int depthLimit);
|
Goals::TGoalVec decompose(Goals::TSubgoal behavior, int depthLimit);
|
||||||
|
|
||||||
|
@ -37,10 +37,11 @@ Nullkiller::Nullkiller()
|
|||||||
settings = std::make_unique<Settings>();
|
settings = std::make_unique<Settings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
|
void Nullkiller::init(std::shared_ptr<CCallback> cb, AIGateway * gateway)
|
||||||
{
|
{
|
||||||
this->cb = cb;
|
this->cb = cb;
|
||||||
this->playerID = playerID;
|
this->gateway = gateway;
|
||||||
|
this->playerID = gateway->playerID;
|
||||||
|
|
||||||
baseGraph.reset();
|
baseGraph.reset();
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
|
|||||||
pathfinder.reset(new AIPathfinder(cb.get(), this));
|
pathfinder.reset(new AIPathfinder(cb.get(), this));
|
||||||
armyManager.reset(new ArmyManager(cb.get(), this));
|
armyManager.reset(new ArmyManager(cb.get(), this));
|
||||||
heroManager.reset(new HeroManager(cb.get(), this));
|
heroManager.reset(new HeroManager(cb.get(), this));
|
||||||
decomposer.reset(new DeepDecomposer());
|
decomposer.reset(new DeepDecomposer(this));
|
||||||
armyFormation.reset(new ArmyFormation(cb, this));
|
armyFormation.reset(new ArmyFormation(cb, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,12 +121,11 @@ void Nullkiller::resetAiState()
|
|||||||
|
|
||||||
lockedResources = TResources();
|
lockedResources = TResources();
|
||||||
scanDepth = ScanDepth::MAIN_FULL;
|
scanDepth = ScanDepth::MAIN_FULL;
|
||||||
playerID = ai->playerID;
|
|
||||||
lockedHeroes.clear();
|
lockedHeroes.clear();
|
||||||
dangerHitMap->reset();
|
dangerHitMap->reset();
|
||||||
useHeroChain = true;
|
useHeroChain = true;
|
||||||
|
|
||||||
if(!baseGraph && ai->nullkiller->settings->isObjectGraphAllowed())
|
if(!baseGraph && settings->isObjectGraphAllowed())
|
||||||
{
|
{
|
||||||
baseGraph = std::make_unique<ObjectGraph>();
|
baseGraph = std::make_unique<ObjectGraph>();
|
||||||
baseGraph->updateGraph(this);
|
baseGraph->updateGraph(this);
|
||||||
@ -253,6 +253,7 @@ void Nullkiller::makeTurn()
|
|||||||
|
|
||||||
for(int i = 1; i <= settings->getMaxPass(); i++)
|
for(int i = 1; i <= settings->getMaxPass(); i++)
|
||||||
{
|
{
|
||||||
|
auto start = std::chrono::high_resolution_clock::now();
|
||||||
updateAiState(i);
|
updateAiState(i);
|
||||||
|
|
||||||
Goals::TTask bestTask = taskptr(Goals::Invalid());
|
Goals::TTask bestTask = taskptr(Goals::Invalid());
|
||||||
@ -342,6 +343,8 @@ void Nullkiller::makeTurn()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logAi->debug("Decission madel in %ld", timeElapsed(start));
|
||||||
|
|
||||||
executeTask(bestTask);
|
executeTask(bestTask);
|
||||||
|
|
||||||
if(i == settings->getMaxPass())
|
if(i == settings->getMaxPass())
|
||||||
@ -361,7 +364,7 @@ void Nullkiller::executeTask(Goals::TTask task)
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
task->accept(ai);
|
task->accept(gateway);
|
||||||
logAi->trace("Task %s completed in %lld", taskDescr, timeElapsed(start));
|
logAi->trace("Task %s completed in %lld", taskDescr, timeElapsed(start));
|
||||||
}
|
}
|
||||||
catch(goalFulfilledException &)
|
catch(goalFulfilledException &)
|
||||||
|
@ -57,6 +57,7 @@ private:
|
|||||||
ScanDepth scanDepth;
|
ScanDepth scanDepth;
|
||||||
TResources lockedResources;
|
TResources lockedResources;
|
||||||
bool useHeroChain;
|
bool useHeroChain;
|
||||||
|
AIGateway * gateway;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<ObjectGraph> baseGraph;
|
static std::unique_ptr<ObjectGraph> baseGraph;
|
||||||
@ -79,7 +80,7 @@ public:
|
|||||||
std::mutex aiStateMutex;
|
std::mutex aiStateMutex;
|
||||||
|
|
||||||
Nullkiller();
|
Nullkiller();
|
||||||
void init(std::shared_ptr<CCallback> cb, PlayerColor playerID);
|
void init(std::shared_ptr<CCallback> cb, AIGateway * gateway);
|
||||||
void makeTurn();
|
void makeTurn();
|
||||||
bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
|
bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
|
||||||
bool isHeroLocked(const CGHeroInstance * hero) const;
|
bool isHeroLocked(const CGHeroInstance * hero) const;
|
||||||
|
@ -709,7 +709,7 @@ public:
|
|||||||
|
|
||||||
Goals::HeroExchange & heroExchange = dynamic_cast<Goals::HeroExchange &>(*task);
|
Goals::HeroExchange & heroExchange = dynamic_cast<Goals::HeroExchange &>(*task);
|
||||||
|
|
||||||
uint64_t armyStrength = heroExchange.getReinforcementArmyStrength();
|
uint64_t armyStrength = heroExchange.getReinforcementArmyStrength(evaluationContext.evaluator.ai);
|
||||||
|
|
||||||
evaluationContext.addNonCriticalStrategicalValue(2.0f * armyStrength / (float)heroExchange.hero.get()->getArmyStrength());
|
evaluationContext.addNonCriticalStrategicalValue(2.0f * armyStrength / (float)heroExchange.hero.get()->getArmyStrength());
|
||||||
evaluationContext.heroRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(heroExchange.hero.get());
|
evaluationContext.heroRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(heroExchange.hero.get());
|
||||||
@ -1064,7 +1064,7 @@ EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal
|
|||||||
|
|
||||||
if(goal->goalType == Goals::COMPOSITION)
|
if(goal->goalType == Goals::COMPOSITION)
|
||||||
{
|
{
|
||||||
parts = goal->decompose();
|
parts = goal->decompose(ai);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -21,6 +21,7 @@ namespace NKAI
|
|||||||
struct HeroPtr;
|
struct HeroPtr;
|
||||||
class AIGateway;
|
class AIGateway;
|
||||||
class FuzzyHelper;
|
class FuzzyHelper;
|
||||||
|
class Nullkiller;
|
||||||
|
|
||||||
namespace Goals
|
namespace Goals
|
||||||
{
|
{
|
||||||
@ -128,7 +129,7 @@ namespace Goals
|
|||||||
return const_cast<AbstractGoal *>(this);
|
return const_cast<AbstractGoal *>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual TGoalVec decompose() const
|
virtual TGoalVec decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
return TGoalVec();
|
return TGoalVec();
|
||||||
}
|
}
|
||||||
|
@ -54,9 +54,9 @@ namespace Goals
|
|||||||
|
|
||||||
virtual bool operator==(const T & other) const = 0;
|
virtual bool operator==(const T & other) const = 0;
|
||||||
|
|
||||||
TGoalVec decompose() const override
|
TGoalVec decompose(const Nullkiller * ai) const override
|
||||||
{
|
{
|
||||||
TSubgoal single = decomposeSingle();
|
TSubgoal single = decomposeSingle(ai);
|
||||||
|
|
||||||
if(!single || single->invalid())
|
if(!single || single->invalid())
|
||||||
return {};
|
return {};
|
||||||
@ -65,7 +65,7 @@ namespace Goals
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual TSubgoal decomposeSingle() const
|
virtual TSubgoal decomposeSingle(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
return TSubgoal();
|
return TSubgoal();
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ std::string CaptureObject::toString() const
|
|||||||
return "Capture " + name + " at " + tile.toString();
|
return "Capture " + name + " at " + tile.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CaptureObject::decompose() const
|
TGoalVec CaptureObject::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
return CaptureObjectsBehavior(cb->getObj(ObjectInstanceID(objid))).decompose();
|
return CaptureObjectsBehavior(ai->cb->getObj(ObjectInstanceID(objid))).decompose(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ namespace Goals
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const CaptureObject & other) const override;
|
bool operator==(const CaptureObject & other) const override;
|
||||||
Goals::TGoalVec decompose() const override;
|
Goals::TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
bool hasHash() const override { return true; }
|
bool hasHash() const override { return true; }
|
||||||
uint64_t getHash() const override;
|
uint64_t getHash() const override;
|
||||||
|
@ -29,36 +29,36 @@ std::string CompleteQuest::toString() const
|
|||||||
return "Complete quest " + questToString();
|
return "Complete quest " + questToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::decompose() const
|
TGoalVec CompleteQuest::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
if(isKeyMaster(q))
|
if(isKeyMaster(q))
|
||||||
{
|
{
|
||||||
return missionKeymaster();
|
return missionKeymaster(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
logAi->debug("Trying to realize quest: %s", questToString());
|
logAi->debug("Trying to realize quest: %s", questToString());
|
||||||
|
|
||||||
if(!q.quest->mission.artifacts.empty())
|
if(!q.quest->mission.artifacts.empty())
|
||||||
return missionArt();
|
return missionArt(ai);
|
||||||
|
|
||||||
if(!q.quest->mission.heroes.empty())
|
if(!q.quest->mission.heroes.empty())
|
||||||
return missionHero();
|
return missionHero(ai);
|
||||||
|
|
||||||
if(!q.quest->mission.creatures.empty())
|
if(!q.quest->mission.creatures.empty())
|
||||||
return missionArmy();
|
return missionArmy(ai);
|
||||||
|
|
||||||
if(q.quest->mission.resources.nonZero())
|
if(q.quest->mission.resources.nonZero())
|
||||||
return missionResources();
|
return missionResources(ai);
|
||||||
|
|
||||||
if(q.quest->killTarget != ObjectInstanceID::NONE)
|
if(q.quest->killTarget != ObjectInstanceID::NONE)
|
||||||
return missionDestroyObj();
|
return missionDestroyObj(ai);
|
||||||
|
|
||||||
for(auto & s : q.quest->mission.primary)
|
for(auto & s : q.quest->mission.primary)
|
||||||
if(s)
|
if(s)
|
||||||
return missionIncreasePrimaryStat();
|
return missionIncreasePrimaryStat(ai);
|
||||||
|
|
||||||
if(q.quest->mission.heroLevel > 0)
|
if(q.quest->mission.heroLevel > 0)
|
||||||
return missionLevel();
|
return missionLevel(ai);
|
||||||
|
|
||||||
return TGoalVec();
|
return TGoalVec();
|
||||||
}
|
}
|
||||||
@ -103,21 +103,21 @@ std::string CompleteQuest::questToString() const
|
|||||||
return ms.toString();
|
return ms.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::tryCompleteQuest() const
|
TGoalVec CompleteQuest::tryCompleteQuest(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(q.obj->visitablePos());
|
auto paths = ai->pathfinder->getPathInfo(q.obj->visitablePos());
|
||||||
|
|
||||||
vstd::erase_if(paths, [&](const AIPath & path) -> bool
|
vstd::erase_if(paths, [&](const AIPath & path) -> bool
|
||||||
{
|
{
|
||||||
return !q.quest->checkQuest(path.targetHero);
|
return !q.quest->checkQuest(path.targetHero);
|
||||||
});
|
});
|
||||||
|
|
||||||
return CaptureObjectsBehavior::getVisitGoals(paths, ai->nullkiller.get(), q.obj);
|
return CaptureObjectsBehavior::getVisitGoals(paths, ai, q.obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionArt() const
|
TGoalVec CompleteQuest::missionArt(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
TGoalVec solutions = tryCompleteQuest();
|
TGoalVec solutions = tryCompleteQuest(ai);
|
||||||
|
|
||||||
if(!solutions.empty())
|
if(!solutions.empty())
|
||||||
return solutions;
|
return solutions;
|
||||||
@ -132,9 +132,9 @@ TGoalVec CompleteQuest::missionArt() const
|
|||||||
return solutions;
|
return solutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionHero() const
|
TGoalVec CompleteQuest::missionHero(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
TGoalVec solutions = tryCompleteQuest();
|
TGoalVec solutions = tryCompleteQuest(ai);
|
||||||
|
|
||||||
if(solutions.empty())
|
if(solutions.empty())
|
||||||
{
|
{
|
||||||
@ -145,43 +145,43 @@ TGoalVec CompleteQuest::missionHero() const
|
|||||||
return solutions;
|
return solutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionArmy() const
|
TGoalVec CompleteQuest::missionArmy(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
auto paths = ai->nullkiller->pathfinder->getPathInfo(q.obj->visitablePos());
|
auto paths = ai->pathfinder->getPathInfo(q.obj->visitablePos());
|
||||||
|
|
||||||
vstd::erase_if(paths, [&](const AIPath & path) -> bool
|
vstd::erase_if(paths, [&](const AIPath & path) -> bool
|
||||||
{
|
{
|
||||||
return !CQuest::checkMissionArmy(q.quest, path.heroArmy);
|
return !CQuest::checkMissionArmy(q.quest, path.heroArmy);
|
||||||
});
|
});
|
||||||
|
|
||||||
return CaptureObjectsBehavior::getVisitGoals(paths, ai->nullkiller.get(), q.obj);
|
return CaptureObjectsBehavior::getVisitGoals(paths, ai, q.obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionIncreasePrimaryStat() const
|
TGoalVec CompleteQuest::missionIncreasePrimaryStat(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
return tryCompleteQuest();
|
return tryCompleteQuest(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionLevel() const
|
TGoalVec CompleteQuest::missionLevel(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
return tryCompleteQuest();
|
return tryCompleteQuest(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionKeymaster() const
|
TGoalVec CompleteQuest::missionKeymaster(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
if(isObjectPassable(q.obj))
|
if(isObjectPassable(ai, q.obj))
|
||||||
{
|
{
|
||||||
return CaptureObjectsBehavior(q.obj).decompose();
|
return CaptureObjectsBehavior(q.obj).decompose(ai);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return CaptureObjectsBehavior().ofType(Obj::KEYMASTER, q.obj->subID).decompose();
|
return CaptureObjectsBehavior().ofType(Obj::KEYMASTER, q.obj->subID).decompose(ai);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionResources() const
|
TGoalVec CompleteQuest::missionResources(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
TGoalVec solutions = tryCompleteQuest();
|
TGoalVec solutions = tryCompleteQuest(ai);
|
||||||
|
|
||||||
/*auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
|
/*auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
|
||||||
|
|
||||||
@ -208,14 +208,14 @@ TGoalVec CompleteQuest::missionResources() const
|
|||||||
return solutions;
|
return solutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec CompleteQuest::missionDestroyObj() const
|
TGoalVec CompleteQuest::missionDestroyObj(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
auto obj = cb->getObj(q.quest->killTarget);
|
auto obj = ai->cb->getObj(q.quest->killTarget);
|
||||||
|
|
||||||
if(!obj)
|
if(!obj)
|
||||||
return CaptureObjectsBehavior(q.obj).decompose();
|
return CaptureObjectsBehavior(q.obj).decompose(ai);
|
||||||
|
|
||||||
auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
|
auto relations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
|
||||||
|
|
||||||
//if(relations == PlayerRelations::SAME_PLAYER)
|
//if(relations == PlayerRelations::SAME_PLAYER)
|
||||||
//{
|
//{
|
||||||
@ -226,7 +226,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
|
|||||||
//else
|
//else
|
||||||
if(relations == PlayerRelations::ENEMIES)
|
if(relations == PlayerRelations::ENEMIES)
|
||||||
{
|
{
|
||||||
return CaptureObjectsBehavior(obj).decompose();
|
return CaptureObjectsBehavior(obj).decompose(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TGoalVec();
|
return TGoalVec();
|
||||||
|
@ -29,7 +29,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TGoalVec decompose() const override;
|
Goals::TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
bool hasHash() const override { return true; }
|
bool hasHash() const override { return true; }
|
||||||
uint64_t getHash() const override;
|
uint64_t getHash() const override;
|
||||||
@ -37,15 +37,15 @@ namespace Goals
|
|||||||
bool operator==(const CompleteQuest & other) const override;
|
bool operator==(const CompleteQuest & other) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TGoalVec tryCompleteQuest() const;
|
TGoalVec tryCompleteQuest(const Nullkiller * ai) const;
|
||||||
TGoalVec missionArt() const;
|
TGoalVec missionArt(const Nullkiller * ai) const;
|
||||||
TGoalVec missionHero() const;
|
TGoalVec missionHero(const Nullkiller * ai) const;
|
||||||
TGoalVec missionArmy() const;
|
TGoalVec missionArmy(const Nullkiller * ai) const;
|
||||||
TGoalVec missionResources() const;
|
TGoalVec missionResources(const Nullkiller * ai) const;
|
||||||
TGoalVec missionDestroyObj() const;
|
TGoalVec missionDestroyObj(const Nullkiller * ai) const;
|
||||||
TGoalVec missionIncreasePrimaryStat() const;
|
TGoalVec missionIncreasePrimaryStat(const Nullkiller * ai) const;
|
||||||
TGoalVec missionLevel() const;
|
TGoalVec missionLevel(const Nullkiller * ai) const;
|
||||||
TGoalVec missionKeymaster() const;
|
TGoalVec missionKeymaster(const Nullkiller * ai) const;
|
||||||
std::string questToString() const;
|
std::string questToString() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ void Composition::accept(AIGateway * ai)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TGoalVec Composition::decompose() const
|
TGoalVec Composition::decompose(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
TGoalVec result;
|
TGoalVec result;
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ namespace Goals
|
|||||||
Composition & addNext(const AbstractGoal & goal);
|
Composition & addNext(const AbstractGoal & goal);
|
||||||
Composition & addNext(TSubgoal goal);
|
Composition & addNext(TSubgoal goal);
|
||||||
Composition & addNextSequence(const TGoalVec & taskSequence);
|
Composition & addNextSequence(const TGoalVec & taskSequence);
|
||||||
TGoalVec decompose() const override;
|
TGoalVec decompose(const Nullkiller * ai) const override;
|
||||||
bool isElementar() const override;
|
bool isElementar() const override;
|
||||||
int getHeroExchangeCount() const override;
|
int getHeroExchangeCount() const override;
|
||||||
};
|
};
|
||||||
|
@ -100,7 +100,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
|||||||
throw cannotFulfillGoalException("Path is nondeterministic.");
|
throw cannotFulfillGoalException("Path is nondeterministic.");
|
||||||
}
|
}
|
||||||
|
|
||||||
node->specialAction->execute(hero);
|
node->specialAction->execute(ai, hero);
|
||||||
|
|
||||||
if(!heroPtr.validAndSet())
|
if(!heroPtr.validAndSet())
|
||||||
{
|
{
|
||||||
@ -156,7 +156,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(moveHeroToTile(hero, node->coord))
|
if(moveHeroToTile(ai, hero, node->coord))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -224,9 +224,9 @@ std::string ExecuteHeroChain::toString() const
|
|||||||
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->getNameTranslated();
|
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->getNameTranslated();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile)
|
bool ExecuteHeroChain::moveHeroToTile(AIGateway * ai, const CGHeroInstance * hero, const int3 & tile)
|
||||||
{
|
{
|
||||||
if(tile == hero->visitablePos() && cb->getVisitableObjs(hero->visitablePos()).size() < 2)
|
if(tile == hero->visitablePos() && ai->myCb->getVisitableObjs(hero->visitablePos()).size() < 2)
|
||||||
{
|
{
|
||||||
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", hero->getNameTranslated(), tile.toString());
|
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", hero->getNameTranslated(), tile.toString());
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ namespace Goals
|
|||||||
|
|
||||||
ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
|
ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
|
||||||
|
|
||||||
|
|
||||||
void accept(AIGateway * ai) override;
|
void accept(AIGateway * ai) override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
bool operator==(const ExecuteHeroChain & other) const override;
|
bool operator==(const ExecuteHeroChain & other) const override;
|
||||||
@ -36,7 +35,7 @@ namespace Goals
|
|||||||
int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
|
int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
|
bool moveHeroToTile(AIGateway * ai, const CGHeroInstance * hero, const int3 & tile);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ namespace Goals
|
|||||||
{
|
{
|
||||||
priority = -1;
|
priority = -1;
|
||||||
}
|
}
|
||||||
TGoalVec decompose() const override
|
TGoalVec decompose(const Nullkiller * ai) const override
|
||||||
{
|
{
|
||||||
return TGoalVec();
|
return TGoalVec();
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,9 @@ std::string HeroExchange::toString() const
|
|||||||
return "Hero exchange for " +hero.get()->getObjectName() + " by " + exchangePath.toString();
|
return "Hero exchange for " +hero.get()->getObjectName() + " by " + exchangePath.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t HeroExchange::getReinforcementArmyStrength() const
|
uint64_t HeroExchange::getReinforcementArmyStrength(const Nullkiller * ai) const
|
||||||
{
|
{
|
||||||
uint64_t armyValue = ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero.get(), exchangePath.heroArmy);
|
uint64_t armyValue = ai->armyManager->howManyReinforcementsCanGet(hero.get(), exchangePath.heroArmy);
|
||||||
|
|
||||||
return armyValue;
|
return armyValue;
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ namespace Goals
|
|||||||
bool operator==(const HeroExchange & other) const override;
|
bool operator==(const HeroExchange & other) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
|
|
||||||
uint64_t getReinforcementArmyStrength() const;
|
uint64_t getReinforcementArmyStrength(const Nullkiller * ai) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1389,7 +1389,7 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa
|
|||||||
{
|
{
|
||||||
auto targetNode =node->theNodeBefore ? getAINode(node->theNodeBefore) : node;
|
auto targetNode =node->theNodeBefore ? getAINode(node->theNodeBefore) : node;
|
||||||
|
|
||||||
pathNode.actionIsBlocked = !pathNode.specialAction->canAct(targetNode);
|
pathNode.actionIsBlocked = !pathNode.specialAction->canAct(ai, targetNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
parentIndex = path.nodes.size();
|
parentIndex = path.nodes.size();
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
|
|
||||||
|
std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>> AIPathfinder::heroGraphs;
|
||||||
|
|
||||||
AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai)
|
AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai)
|
||||||
:cb(cb), ai(ai)
|
:cb(cb), ai(ai)
|
||||||
{
|
{
|
||||||
|
@ -40,7 +40,7 @@ private:
|
|||||||
std::shared_ptr<AINodeStorage> storage;
|
std::shared_ptr<AINodeStorage> storage;
|
||||||
CPlayerSpecificInfoCallback * cb;
|
CPlayerSpecificInfoCallback * cb;
|
||||||
Nullkiller * ai;
|
Nullkiller * ai;
|
||||||
std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>> heroGraphs;
|
static std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>> heroGraphs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
|
||||||
|
@ -33,7 +33,7 @@ namespace AIPathfinding
|
|||||||
std::make_shared<AIMovementToDestinationRule>(nodeStorage, allowBypassObjects),
|
std::make_shared<AIMovementToDestinationRule>(nodeStorage, allowBypassObjects),
|
||||||
std::make_shared<MovementCostRule>(),
|
std::make_shared<MovementCostRule>(),
|
||||||
std::make_shared<AIPreviousNodeRule>(nodeStorage),
|
std::make_shared<AIPreviousNodeRule>(nodeStorage),
|
||||||
std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage, allowBypassObjects)
|
std::make_shared<AIMovementAfterDestinationRule>(ai, cb, nodeStorage, allowBypassObjects)
|
||||||
};
|
};
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
|
@ -49,14 +49,14 @@ namespace AIPathfinding
|
|||||||
dstNode->dayFlags = static_cast<DayFlags>(dstNode->dayFlags | flagsToAdd);
|
dstNode->dayFlags = static_cast<DayFlags>(dstNode->dayFlags | flagsToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AdventureCastAction::execute(const CGHeroInstance * hero) const
|
void AdventureCastAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
assert(hero == this->hero);
|
assert(hero == this->hero);
|
||||||
|
|
||||||
Goals::AdventureSpellCast(hero, spellToCast).accept(ai);
|
Goals::AdventureSpellCast(hero, spellToCast).accept(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AdventureCastAction::canAct(const AIPathNode * source) const
|
bool AdventureCastAction::canAct(const Nullkiller * ai, const AIPathNode * source) const
|
||||||
{
|
{
|
||||||
assert(hero == this->hero);
|
assert(hero == this->hero);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace AIPathfinding
|
|||||||
public:
|
public:
|
||||||
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd = DayFlags::NONE);
|
AdventureCastAction(SpellID spellToCast, const CGHeroInstance * hero, DayFlags flagsToAdd = DayFlags::NONE);
|
||||||
|
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
virtual void applyOnDestination(
|
virtual void applyOnDestination(
|
||||||
const CGHeroInstance * hero,
|
const CGHeroInstance * hero,
|
||||||
@ -38,7 +38,7 @@ namespace AIPathfinding
|
|||||||
AIPathNode * dstMode,
|
AIPathNode * dstMode,
|
||||||
const AIPathNode * srcNode) const override;
|
const AIPathNode * srcNode) const override;
|
||||||
|
|
||||||
bool canAct(const AIPathNode * source) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNode * source) const override;
|
||||||
|
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,7 @@ namespace NKAI
|
|||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
void BattleAction::execute(const CGHeroInstance * hero) const
|
void BattleAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
ai->moveHeroToTile(targetTile, hero);
|
ai->moveHeroToTile(targetTile, hero);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
};
|
};
|
||||||
|
@ -22,12 +22,12 @@ namespace NKAI
|
|||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
void BuildBoatAction::execute(const CGHeroInstance * hero) const
|
void BuildBoatAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
return Goals::BuildBoat(shipyard).accept(ai);
|
return Goals::BuildBoat(shipyard).accept(ai);
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TSubgoal BuildBoatAction::decompose(const CGHeroInstance * hero) const
|
Goals::TSubgoal BuildBoatAction::decompose(const Nullkiller * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
|
if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
|
||||||
{
|
{
|
||||||
@ -37,7 +37,7 @@ namespace AIPathfinding
|
|||||||
return Goals::sptr(Goals::Invalid());
|
return Goals::sptr(Goals::Invalid());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildBoatAction::canAct(const CGHeroInstance * hero, const TResources & reservedResources) const
|
bool BuildBoatAction::canAct(const Nullkiller * ai, const CGHeroInstance * hero, const TResources & reservedResources) const
|
||||||
{
|
{
|
||||||
if(cb->getPlayerRelations(hero->tempOwner, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
|
if(cb->getPlayerRelations(hero->tempOwner, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
|
||||||
{
|
{
|
||||||
@ -63,16 +63,16 @@ namespace AIPathfinding
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildBoatAction::canAct(const AIPathNode * source) const
|
bool BuildBoatAction::canAct(const Nullkiller * ai, const AIPathNode * source) const
|
||||||
{
|
{
|
||||||
return canAct(source->actor->hero, source->actor->armyCost);
|
return canAct(ai, source->actor->hero, source->actor->armyCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BuildBoatAction::canAct(const AIPathNodeInfo & source) const
|
bool BuildBoatAction::canAct(const Nullkiller * ai, const AIPathNodeInfo & source) const
|
||||||
{
|
{
|
||||||
TResources res;
|
TResources res;
|
||||||
|
|
||||||
return canAct(source.targetHero, res);
|
return canAct(ai, source.targetHero, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CGObjectInstance * BuildBoatAction::targetObject() const
|
const CGObjectInstance * BuildBoatAction::targetObject() const
|
||||||
@ -90,7 +90,7 @@ namespace AIPathfinding
|
|||||||
return std::make_shared<BuildBoatAction>(ai->cb.get(), dynamic_cast<const IShipyard * >(ai->cb->getObj(shipyard)));
|
return std::make_shared<BuildBoatAction>(ai->cb.get(), dynamic_cast<const IShipyard * >(ai->cb->getObj(shipyard)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SummonBoatAction::execute(const CGHeroInstance * hero) const
|
void SummonBoatAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai);
|
Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai);
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ namespace AIPathfinding
|
|||||||
return "Build Boat at " + shipyard->getObject()->visitablePos().toString();
|
return "Build Boat at " + shipyard->getObject()->visitablePos().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SummonBoatAction::canAct(const AIPathNode * source) const
|
bool SummonBoatAction::canAct(const Nullkiller * ai, const AIPathNode * source) const
|
||||||
{
|
{
|
||||||
auto hero = source->actor->hero;
|
auto hero = source->actor->hero;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ namespace AIPathfinding
|
|||||||
class SummonBoatAction : public VirtualBoatAction
|
class SummonBoatAction : public VirtualBoatAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
virtual void applyOnDestination(
|
virtual void applyOnDestination(
|
||||||
const CGHeroInstance * hero,
|
const CGHeroInstance * hero,
|
||||||
@ -34,7 +34,7 @@ namespace AIPathfinding
|
|||||||
AIPathNode * dstMode,
|
AIPathNode * dstMode,
|
||||||
const AIPathNode * srcNode) const override;
|
const AIPathNode * srcNode) const override;
|
||||||
|
|
||||||
bool canAct(const AIPathNode * source) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNode * source) const override;
|
||||||
|
|
||||||
const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||||
|
|
||||||
@ -56,13 +56,13 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canAct(const AIPathNode * source) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNode * source) const override;
|
||||||
bool canAct(const AIPathNodeInfo & source) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNodeInfo & source) const override;
|
||||||
bool canAct(const CGHeroInstance * hero, const TResources & reservedResources) const;
|
bool canAct(const Nullkiller * ai, const CGHeroInstance * hero, const TResources & reservedResources) const;
|
||||||
|
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
Goals::TSubgoal decompose(const Nullkiller * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
const ChainActor * getActor(const ChainActor * sourceActor) const override;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace NKAI
|
|||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
void BuyArmyAction::execute(const CGHeroInstance * hero) const
|
void BuyArmyAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
if(!hero->visitedTown)
|
if(!hero->visitedTown)
|
||||||
{
|
{
|
||||||
|
@ -21,12 +21,12 @@ namespace AIPathfinding
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool canAct(const AIPathNode * source) const override
|
bool canAct(const Nullkiller * ai, const AIPathNode * source) const override
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,17 @@ namespace NKAI
|
|||||||
|
|
||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
bool QuestAction::canAct(const AIPathNode * node) const
|
bool QuestAction::canAct(const Nullkiller * ai, const AIPathNode * node) const
|
||||||
{
|
{
|
||||||
return canAct(node->actor->hero);
|
return canAct(ai, node->actor->hero);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuestAction::canAct(const AIPathNodeInfo & node) const
|
bool QuestAction::canAct(const Nullkiller * ai, const AIPathNodeInfo & node) const
|
||||||
{
|
{
|
||||||
return canAct(node.targetHero);
|
return canAct(ai, node.targetHero);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QuestAction::canAct(const CGHeroInstance * hero) const
|
bool QuestAction::canAct(const Nullkiller * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
if(questInfo.obj->ID == Obj::BORDER_GATE || questInfo.obj->ID == Obj::BORDERGUARD)
|
if(questInfo.obj->ID == Obj::BORDER_GATE || questInfo.obj->ID == Obj::BORDERGUARD)
|
||||||
{
|
{
|
||||||
@ -39,12 +39,12 @@ namespace AIPathfinding
|
|||||||
|| questInfo.quest->checkQuest(hero);
|
|| questInfo.quest->checkQuest(hero);
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TSubgoal QuestAction::decompose(const CGHeroInstance * hero) const
|
Goals::TSubgoal QuestAction::decompose(const Nullkiller * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
return Goals::sptr(Goals::CompleteQuest(questInfo));
|
return Goals::sptr(Goals::CompleteQuest(questInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuestAction::execute(const CGHeroInstance * hero) const
|
void QuestAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
ai->moveHeroToTile(questInfo.obj->visitablePos(), hero);
|
ai->moveHeroToTile(questInfo.obj->visitablePos(), hero);
|
||||||
}
|
}
|
||||||
|
@ -28,13 +28,13 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canAct(const AIPathNode * node) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNode * node) const override;
|
||||||
bool canAct(const AIPathNodeInfo & node) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNodeInfo & node) const override;
|
||||||
bool canAct(const CGHeroInstance * hero) const;
|
bool canAct(const Nullkiller * ai, const CGHeroInstance * hero) const;
|
||||||
|
|
||||||
Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
Goals::TSubgoal decompose(const Nullkiller * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
};
|
};
|
||||||
|
@ -17,43 +17,43 @@
|
|||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
|
|
||||||
Goals::TSubgoal SpecialAction::decompose(const CGHeroInstance * hero) const
|
Goals::TSubgoal SpecialAction::decompose(const Nullkiller * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
return Goals::sptr(Goals::Invalid());
|
return Goals::sptr(Goals::Invalid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpecialAction::execute(const CGHeroInstance * hero) const
|
void SpecialAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
throw cannotFulfillGoalException("Can not execute " + toString());
|
throw cannotFulfillGoalException("Can not execute " + toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompositeAction::canAct(const AIPathNode * source) const
|
bool CompositeAction::canAct(const Nullkiller * ai, const AIPathNode * source) const
|
||||||
{
|
{
|
||||||
for(auto part : parts)
|
for(auto part : parts)
|
||||||
{
|
{
|
||||||
if(!part->canAct(source)) return false;
|
if(!part->canAct(ai, source)) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goals::TSubgoal CompositeAction::decompose(const CGHeroInstance * hero) const
|
Goals::TSubgoal CompositeAction::decompose(const Nullkiller * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
for(auto part : parts)
|
for(auto part : parts)
|
||||||
{
|
{
|
||||||
auto goal = part->decompose(hero);
|
auto goal = part->decompose(ai, hero);
|
||||||
|
|
||||||
if(!goal->invalid()) return goal;
|
if(!goal->invalid()) return goal;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpecialAction::decompose(hero);
|
return SpecialAction::decompose(ai, hero);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompositeAction::execute(const CGHeroInstance * hero) const
|
void CompositeAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
for(auto part : parts)
|
for(auto part : parts)
|
||||||
{
|
{
|
||||||
part->execute(hero);
|
part->execute(ai, hero);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,19 +30,19 @@ class SpecialAction
|
|||||||
public:
|
public:
|
||||||
virtual ~SpecialAction() = default;
|
virtual ~SpecialAction() = default;
|
||||||
|
|
||||||
virtual bool canAct(const AIPathNode * source) const
|
virtual bool canAct(const Nullkiller * ai, const AIPathNode * source) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool canAct(const AIPathNodeInfo & source) const
|
virtual bool canAct(const Nullkiller * ai, const AIPathNodeInfo & source) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const;
|
virtual Goals::TSubgoal decompose(const Nullkiller * ai, const CGHeroInstance * hero) const;
|
||||||
|
|
||||||
virtual void execute(const CGHeroInstance * hero) const;
|
virtual void execute(AIGateway * ai, const CGHeroInstance * hero) const;
|
||||||
|
|
||||||
virtual void applyOnDestination(
|
virtual void applyOnDestination(
|
||||||
const CGHeroInstance * hero,
|
const CGHeroInstance * hero,
|
||||||
@ -76,11 +76,11 @@ private:
|
|||||||
public:
|
public:
|
||||||
CompositeAction(std::vector<std::shared_ptr<const SpecialAction>> parts) : parts(parts) {}
|
CompositeAction(std::vector<std::shared_ptr<const SpecialAction>> parts) : parts(parts) {}
|
||||||
|
|
||||||
bool canAct(const AIPathNode * source) const override;
|
bool canAct(const Nullkiller * ai, const AIPathNode * source) const override;
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
const CGObjectInstance * targetObject() const override;
|
const CGObjectInstance * targetObject() const override;
|
||||||
Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
|
Goals::TSubgoal decompose(const Nullkiller * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const SpecialAction>> getParts() const override
|
std::vector<std::shared_ptr<const SpecialAction>> getParts() const override
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ namespace NKAI
|
|||||||
|
|
||||||
using namespace AIPathfinding;
|
using namespace AIPathfinding;
|
||||||
|
|
||||||
void TownPortalAction::execute(const CGHeroInstance * hero) const
|
void TownPortalAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
auto goal = Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL);
|
auto goal = Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(const CGHeroInstance * hero) const override;
|
void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
|
||||||
|
|
||||||
std::string toString() const override;
|
std::string toString() const override;
|
||||||
};
|
};
|
||||||
|
@ -602,7 +602,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
|||||||
|
|
||||||
auto questAction = std::make_shared<AIPathfinding::QuestAction>(questInfo);
|
auto questAction = std::make_shared<AIPathfinding::QuestAction>(questInfo);
|
||||||
|
|
||||||
if(!questAction->canAct(targetHero))
|
if(!questAction->canAct(ai, targetHero))
|
||||||
{
|
{
|
||||||
transitionAction = questAction;
|
transitionAction = questAction;
|
||||||
}
|
}
|
||||||
@ -762,7 +762,7 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
|
|||||||
|
|
||||||
if(n.specialAction)
|
if(n.specialAction)
|
||||||
{
|
{
|
||||||
n.actionIsBlocked = !n.specialAction->canAct(n);
|
n.actionIsBlocked = !n.specialAction->canAct(ai, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto & node : path.nodes)
|
for(auto & node : path.nodes)
|
||||||
@ -883,7 +883,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector<AIPath> & paths, int3
|
|||||||
|
|
||||||
if(n.specialAction)
|
if(n.specialAction)
|
||||||
{
|
{
|
||||||
n.actionIsBlocked = !n.specialAction->canAct(n);
|
n.actionIsBlocked = !n.specialAction->canAct(ai, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto blocker = ai->objectClusterizer->getBlocker(n);
|
auto blocker = ai->objectClusterizer->getBlocker(n);
|
||||||
|
@ -177,7 +177,7 @@ namespace AIPathfinding
|
|||||||
const CGHeroInstance * hero = nodeStorage->getHero(source.node);
|
const CGHeroInstance * hero = nodeStorage->getHero(source.node);
|
||||||
|
|
||||||
if(vstd::contains(summonableVirtualBoats, hero)
|
if(vstd::contains(summonableVirtualBoats, hero)
|
||||||
&& summonableVirtualBoats.at(hero)->canAct(nodeStorage->getAINode(source.node)))
|
&& summonableVirtualBoats.at(hero)->canAct(ai, nodeStorage->getAINode(source.node)))
|
||||||
{
|
{
|
||||||
virtualBoat = summonableVirtualBoats.at(hero);
|
virtualBoat = summonableVirtualBoats.at(hero);
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,11 @@ namespace NKAI
|
|||||||
namespace AIPathfinding
|
namespace AIPathfinding
|
||||||
{
|
{
|
||||||
AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
|
AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
|
||||||
|
const Nullkiller * ai,
|
||||||
CPlayerSpecificInfoCallback * cb,
|
CPlayerSpecificInfoCallback * cb,
|
||||||
std::shared_ptr<AINodeStorage> nodeStorage,
|
std::shared_ptr<AINodeStorage> nodeStorage,
|
||||||
bool allowBypassObjects)
|
bool allowBypassObjects)
|
||||||
:cb(cb), nodeStorage(nodeStorage), allowBypassObjects(allowBypassObjects)
|
:ai(ai), cb(cb), nodeStorage(nodeStorage), allowBypassObjects(allowBypassObjects)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +172,7 @@ namespace AIPathfinding
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!questAction.canAct(destinationNode))
|
if(!questAction.canAct(ai, destinationNode))
|
||||||
{
|
{
|
||||||
if(!destinationNode->actor->allowUseResources)
|
if(!destinationNode->actor->allowUseResources)
|
||||||
{
|
{
|
||||||
|
@ -24,11 +24,13 @@ namespace AIPathfinding
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
CPlayerSpecificInfoCallback * cb;
|
CPlayerSpecificInfoCallback * cb;
|
||||||
|
const Nullkiller * ai;
|
||||||
std::shared_ptr<AINodeStorage> nodeStorage;
|
std::shared_ptr<AINodeStorage> nodeStorage;
|
||||||
bool allowBypassObjects;
|
bool allowBypassObjects;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AIMovementAfterDestinationRule(
|
AIMovementAfterDestinationRule(
|
||||||
|
const Nullkiller * ai,
|
||||||
CPlayerSpecificInfoCallback * cb,
|
CPlayerSpecificInfoCallback * cb,
|
||||||
std::shared_ptr<AINodeStorage> nodeStorage,
|
std::shared_ptr<AINodeStorage> nodeStorage,
|
||||||
bool allowBypassObjects);
|
bool allowBypassObjects);
|
||||||
|
Loading…
Reference in New Issue
Block a user