Browse Source

NKAI: remove static ai field

Andrii Danylchenko 1 year ago
parent
commit
728f673763
62 changed files with 368 additions and 376 deletions
  1. 7 2
      AI/Nullkiller/AIGateway.cpp
  2. 1 14
      AI/Nullkiller/AIUtility.cpp
  3. 5 19
      AI/Nullkiller/AIUtility.h
  4. 1 1
      AI/Nullkiller/Analyzers/ObjectClusterizer.cpp
  5. 8 8
      AI/Nullkiller/Behaviors/BuildingBehavior.cpp
  6. 1 1
      AI/Nullkiller/Behaviors/BuildingBehavior.h
  7. 8 8
      AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp
  8. 1 5
      AI/Nullkiller/Behaviors/BuyArmyBehavior.h
  9. 47 50
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp
  10. 7 2
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h
  11. 8 8
      AI/Nullkiller/Behaviors/ClusterBehavior.cpp
  12. 2 2
      AI/Nullkiller/Behaviors/ClusterBehavior.h
  13. 32 31
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  14. 3 4
      AI/Nullkiller/Behaviors/DefenceBehavior.h
  15. 37 37
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  16. 3 3
      AI/Nullkiller/Behaviors/GatherArmyBehavior.h
  17. 12 12
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp
  18. 1 1
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.h
  19. 24 24
      AI/Nullkiller/Behaviors/StartupBehavior.cpp
  20. 1 1
      AI/Nullkiller/Behaviors/StartupBehavior.h
  21. 3 3
      AI/Nullkiller/Behaviors/StayAtTownBehavior.cpp
  22. 1 1
      AI/Nullkiller/Behaviors/StayAtTownBehavior.h
  23. 8 3
      AI/Nullkiller/Engine/DeepDecomposer.cpp
  24. 2 0
      AI/Nullkiller/Engine/DeepDecomposer.h
  25. 9 6
      AI/Nullkiller/Engine/Nullkiller.cpp
  26. 2 1
      AI/Nullkiller/Engine/Nullkiller.h
  27. 2 2
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  28. 2 1
      AI/Nullkiller/Goals/AbstractGoal.h
  29. 3 3
      AI/Nullkiller/Goals/CGoal.h
  30. 2 2
      AI/Nullkiller/Goals/CaptureObject.cpp
  31. 1 1
      AI/Nullkiller/Goals/CaptureObject.h
  32. 34 34
      AI/Nullkiller/Goals/CompleteQuest.cpp
  33. 10 10
      AI/Nullkiller/Goals/CompleteQuest.h
  34. 1 1
      AI/Nullkiller/Goals/Composition.cpp
  35. 1 1
      AI/Nullkiller/Goals/Composition.h
  36. 4 4
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  37. 1 2
      AI/Nullkiller/Goals/ExecuteHeroChain.h
  38. 1 1
      AI/Nullkiller/Goals/Invalid.h
  39. 2 2
      AI/Nullkiller/Markers/HeroExchange.cpp
  40. 1 1
      AI/Nullkiller/Markers/HeroExchange.h
  41. 1 1
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  42. 2 0
      AI/Nullkiller/Pathfinding/AIPathfinder.cpp
  43. 1 1
      AI/Nullkiller/Pathfinding/AIPathfinder.h
  44. 1 1
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp
  45. 2 2
      AI/Nullkiller/Pathfinding/Actions/AdventureSpellCastMovementActions.cpp
  46. 2 2
      AI/Nullkiller/Pathfinding/Actions/AdventureSpellCastMovementActions.h
  47. 1 1
      AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp
  48. 1 1
      AI/Nullkiller/Pathfinding/Actions/BattleAction.h
  49. 9 9
      AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp
  50. 7 7
      AI/Nullkiller/Pathfinding/Actions/BoatActions.h
  51. 1 1
      AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.cpp
  52. 2 2
      AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.h
  53. 7 7
      AI/Nullkiller/Pathfinding/Actions/QuestAction.cpp
  54. 5 5
      AI/Nullkiller/Pathfinding/Actions/QuestAction.h
  55. 9 9
      AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp
  56. 7 7
      AI/Nullkiller/Pathfinding/Actions/SpecialAction.h
  57. 1 1
      AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp
  58. 1 1
      AI/Nullkiller/Pathfinding/Actions/TownPortalAction.h
  59. 3 3
      AI/Nullkiller/Pathfinding/ObjectGraph.cpp
  60. 1 1
      AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp
  61. 3 2
      AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
  62. 2 0
      AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.h

+ 7 - 2
AI/Nullkiller/AIGateway.cpp

@@ -566,7 +566,7 @@ void AIGateway::initGameInterface(std::shared_ptr<Environment> env, std::shared_
 	myCb->waitTillRealize = true;
 	myCb->unlockGsWhenWaiting = true;
 
-	nullkiller->init(CB, playerID);
+	nullkiller->init(CB, this);
 	
 	retrieveVisitableObjs();
 }
@@ -828,7 +828,7 @@ void AIGateway::makeTurn()
 	{
 		for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs)
 		{
-			if(isWeeklyRevisitable(obj))
+			if(isWeeklyRevisitable(nullkiller.get(), obj))
 			{
 				nullkiller->memory->markObjectUnvisited(obj);
 			}
@@ -1152,6 +1152,11 @@ void AIGateway::retrieveVisitableObjs()
 	{
 		for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
 		{
+			if(!obj->appearance)
+			{
+				logAi->error("Bad!");
+			}
+
 			addVisitableObj(obj);
 		}
 	});

+ 1 - 14
AI/Nullkiller/AIUtility.cpp

@@ -163,14 +163,6 @@ bool HeroPtr::operator==(const HeroPtr & rhs) const
 	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)
 {
 	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));
 }
 
-bool isObjectPassable(const CGObjectInstance * obj)
-{
-	return isObjectPassable(obj, ai->playerID, ai->myCb->getPlayerRelations(obj->tempOwner, ai->playerID));
-}
-
 // Pathfinder internal helper
 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();
 }
 
-bool isWeeklyRevisitable(const CGObjectInstance * obj)
+bool isWeeklyRevisitable(const Nullkiller * ai, const CGObjectInstance * obj)
 {
 	if(!obj)
 		return false;

+ 5 - 19
AI/Nullkiller/AIUtility.h

@@ -70,7 +70,6 @@ extern const float SAFE_ATTACK_CONSTANT;
 extern const int GOLD_RESERVE;
 
 extern thread_local CCallback * cb;
-extern thread_local AIGateway * ai;
 
 enum HeroRole
 {
@@ -114,11 +113,11 @@ public:
 	bool validAndSet() const;
 
 
-	template<typename Handler> void serialize(Handler & h)
+	template<typename Handler> void serialize(Handler & handler)
 	{
-		h & this->h;
-		h & hid;
-		h & name;
+		handler & h;
+		handler & hid;
+		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 isObjectPassable(const CGObjectInstance * obj);
 bool isObjectPassable(const Nullkiller * ai, const CGObjectInstance * obj);
 bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, PlayerRelations objectRelations);
 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 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
 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>
 class SharedPool
 {

+ 1 - 1
AI/Nullkiller/Analyzers/ObjectClusterizer.cpp

@@ -180,7 +180,7 @@ bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
 
 	auto playerRelations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
 
-	if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
+	if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(ai, obj))
 	{
 		return false;
 	}

+ 8 - 8
AI/Nullkiller/Behaviors/BuildingBehavior.cpp

@@ -27,14 +27,14 @@ std::string BuildingBehavior::toString() const
 	return "Build";
 }
 
-Goals::TGoalVec BuildingBehavior::decompose() const
+Goals::TGoalVec BuildingBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
 
-	TResources resourcesRequired = ai->nullkiller->buildAnalyzer->getResourcesRequiredNow();
-	TResources totalDevelopmentCost = ai->nullkiller->buildAnalyzer->getTotalResourcesRequired();
-	TResources availableResources = ai->nullkiller->getFreeResources();
-	TResources dailyIncome = ai->nullkiller->buildAnalyzer->getDailyIncome();
+	TResources resourcesRequired = ai->buildAnalyzer->getResourcesRequiredNow();
+	TResources totalDevelopmentCost = ai->buildAnalyzer->getTotalResourcesRequired();
+	TResources availableResources = ai->getFreeResources();
+	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
 
 	logAi->trace("Free resources amount: %s", availableResources.toString());
 
@@ -46,8 +46,8 @@ Goals::TGoalVec BuildingBehavior::decompose() const
 		resourcesRequired.toString(),
 		totalDevelopmentCost.toString());
 
-	auto & developmentInfos = ai->nullkiller->buildAnalyzer->getDevelopmentInfo();
-	auto isGoldPreasureLow = !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh();
+	auto & developmentInfos = ai->buildAnalyzer->getDevelopmentInfo();
+	auto isGoldPreasureLow = !ai->buildAnalyzer->isGoldPreasureHigh();
 
 	for(auto & developmentInfo : developmentInfos)
 	{
@@ -57,7 +57,7 @@ Goals::TGoalVec BuildingBehavior::decompose() const
 			{
 				if(buildingInfo.notEnoughRes)
 				{
-					if(ai->nullkiller->getLockedResources().canAfford(buildingInfo.buildCost))
+					if(ai->getLockedResources().canAfford(buildingInfo.buildCost))
 						continue;
 
 					Composition composition;

+ 1 - 1
AI/Nullkiller/Behaviors/BuildingBehavior.h

@@ -25,7 +25,7 @@ namespace Goals
 		{
 		}
 
-		Goals::TGoalVec decompose() const override;
+		Goals::TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 		bool operator==(const BuildingBehavior & other) const override
 		{

+ 8 - 8
AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp

@@ -24,11 +24,11 @@ std::string BuyArmyBehavior::toString() const
 	return "Buy army";
 }
 
-Goals::TGoalVec BuyArmyBehavior::decompose() const
+Goals::TGoalVec BuyArmyBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
 
-	if(cb->getDate(Date::DAY) == 1)
+	if(ai->cb->getDate(Date::DAY) == 1)
 		return tasks;
 		
 	auto heroes = cb->getHeroesInfo();
@@ -40,26 +40,26 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
 
 	for(auto town : cb->getTownsInfo())
 	{
-		auto townArmyAvailableToBuy = ai->nullkiller->armyManager->getArmyAvailableToBuyAsCCreatureSet(
+		auto townArmyAvailableToBuy = ai->armyManager->getArmyAvailableToBuyAsCCreatureSet(
 			town,
-			ai->nullkiller->getFreeResources());
+			ai->getFreeResources());
 
 		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;
 			}
 
-			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,
 					&*townArmyAvailableToBuy);
 
 				if(reinforcement)
-					vstd::amin(reinforcement, ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
+					vstd::amin(reinforcement, ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town));
 
 				if(reinforcement)
 				{

+ 1 - 5
AI/Nullkiller/Behaviors/BuyArmyBehavior.h

@@ -20,11 +20,7 @@ namespace Goals
 	class BuyArmyBehavior : public CGoal<BuyArmyBehavior>
 	{
 	public:
-		BuyArmyBehavior()
-		{
-		}
-
-		Goals::TGoalVec decompose() const override;
+		Goals::TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 		bool operator==(const BuyArmyBehavior & other) const override
 		{

+ 47 - 50
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -47,7 +47,7 @@ bool CaptureObjectsBehavior::operator==(const CaptureObjectsBehavior & other) co
 		&& 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;
 
@@ -96,7 +96,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
 		auto firstBlockedAction = path.getFirstBlockedAction();
 		if(firstBlockedAction)
 		{
-			auto subGoal = firstBlockedAction->decompose(path.targetHero);
+			auto subGoal = firstBlockedAction->decompose(nullkiller, path.targetHero);
 
 #if NKAI_TRACE_LEVEL >= 2
 			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;
 }
 
-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;
 
-	Nullkiller * nullkiller = ai->nullkiller.get();
+	logAi->debug("Scanning objects, count %d", objs.size());
 
-	auto captureObjects = [&](const std::vector<const CGObjectInstance *> & objs) -> void
-	{
-		if(objs.empty())
+	// tbb::blocked_range<size_t> r(0, objs.size());
+	tbb::parallel_for(
+		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());
-
-		tbb::parallel_for(
-			tbb::blocked_range<size_t>(0, objs.size()),
-			[this, &objs, &sync, &tasks, nullkiller](const tbb::blocked_range<size_t> & r)
+			for(auto i = r.begin(); i != r.end(); i++)
 			{
-				std::vector<AIPath> paths;
-				Goals::TGoalVec tasksLocal;
-
-				for(auto i = r.begin(); i != r.end(); i++)
-				{
-					auto objToVisit = objs[i];
+				auto objToVisit = objs[i];
 
-					if(!objectMatchesFilter(objToVisit))
-						continue;
+				if(!objectMatchesFilter(objToVisit))
+					continue;
 
 #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
 
-					const int3 pos = objToVisit->visitablePos();
-					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;
+				nullkiller->pathfinder->calculatePathInfo(paths, objToVisit->visitablePos(), nullkiller->settings->isObjectGraphAllowed());
 
 #if NKAI_TRACE_LEVEL >= 1
-					logAi->trace("Found %d paths", paths.size());
+				logAi->trace("Found %d paths", paths.size());
 #endif
-					vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit));
-				}
+				vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit));
+			}
 
-				vstd::erase_if(tasksLocal, [](TSubgoal task) -> bool
-				{
-					return task->invalid();
-				});
+			std::lock_guard<std::mutex> lock(sync);
+			vstd::concatenate(result, tasksLocal);
+		});
+}
 
-				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)
 	{
-		captureObjects(objectsToCapture);
+		decomposeObjects(tasks, objectsToCapture, ai);
 	}
 	else if(objectTypes.size())
 	{
-		captureObjects(
+		decomposeObjects(
+			tasks,
 			std::vector<const CGObjectInstance *>(
-				nullkiller->memory->visitableObjs.begin(),
-				nullkiller->memory->visitableObjs.end()));
+				ai->memory->visitableObjs.begin(),
+				ai->memory->visitableObjs.end()),
+			ai);
 	}
 	else
 	{
-		captureObjects(nullkiller->objectClusterizer->getNearbyObjects());
+		decomposeObjects(tasks, ai->objectClusterizer->getNearbyObjects(), ai);
 
-		if(tasks.empty() || nullkiller->getScanDepth() != ScanDepth::SMALL)
-			captureObjects(nullkiller->objectClusterizer->getFarObjects());
+		if(tasks.empty() || ai->getScanDepth() != ScanDepth::SMALL)
+			decomposeObjects(tasks, ai->objectClusterizer->getFarObjects(), ai);
 	}
 
 	return tasks;

+ 7 - 2
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h

@@ -25,6 +25,7 @@ namespace Goals
 		std::vector<int> objectSubTypes;
 		std::vector<const CGObjectInstance *> objectsToCapture;
 		bool specificObjects;
+
 	public:
 		CaptureObjectsBehavior()
 			:CGoal(CAPTURE_OBJECTS)
@@ -48,7 +49,7 @@ namespace Goals
 			specificObjects = true;
 		}
 
-		Goals::TGoalVec decompose() const override;
+		Goals::TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 
 		CaptureObjectsBehavior & ofType(int type)
@@ -67,10 +68,14 @@ namespace Goals
 
 		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:
 		bool objectMatchesFilter(const CGObjectInstance * obj) const;
+		void decomposeObjects(
+			Goals::TGoalVec & result,
+			const std::vector<const CGObjectInstance *> & objs,
+			const Nullkiller * nullkiller) const;
 	};
 }
 

+ 8 - 8
AI/Nullkiller/Behaviors/ClusterBehavior.cpp

@@ -26,23 +26,23 @@ std::string ClusterBehavior::toString() const
 	return "Unlock Clusters";
 }
 
-Goals::TGoalVec ClusterBehavior::decompose() const
+Goals::TGoalVec ClusterBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
-	auto clusters = ai->nullkiller->objectClusterizer->getLockedClusters();
+	auto clusters = ai->objectClusterizer->getLockedClusters();
 
 	for(auto cluster : clusters)
 	{
-		vstd::concatenate(tasks, decomposeCluster(cluster));
+		vstd::concatenate(tasks, decomposeCluster(ai, cluster));
 	}
 
 	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 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();
 	std::vector<AIPath> blockerPaths;
@@ -65,7 +65,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 		logAi->trace("Checking path %s", path->toString());
 #endif
 
-		auto blocker = ai->nullkiller->objectClusterizer->getBlocker(*path);
+		auto blocker = ai->objectClusterizer->getBlocker(*path);
 
 		if(blocker != cluster->blocker)
 		{
@@ -83,7 +83,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 		{
 			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;
 		}
 
@@ -100,7 +100,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 	logAi->trace("Decompose unlock paths");
 #endif
 
-	auto unlockTasks = CaptureObjectsBehavior::getVisitGoals(blockerPaths, ai->nullkiller.get());
+	auto unlockTasks = CaptureObjectsBehavior::getVisitGoals(blockerPaths, ai);
 
 	for(int i = 0; i < paths.size(); i++)
 	{

+ 2 - 2
AI/Nullkiller/Behaviors/ClusterBehavior.h

@@ -28,7 +28,7 @@ namespace Goals
 		{
 		}
 
-		TGoalVec decompose() const override;
+		TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 
 		bool operator==(const ClusterBehavior & other) const override
@@ -37,7 +37,7 @@ namespace Goals
 		}
 
 	private:
-		Goals::TGoalVec decomposeCluster(std::shared_ptr<ObjectCluster> cluster) const;
+		Goals::TGoalVec decomposeCluster(const Nullkiller * ai, std::shared_ptr<ObjectCluster> cluster) const;
 	};
 }
 

+ 32 - 31
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -34,21 +34,21 @@ std::string DefenceBehavior::toString() const
 	return "Defend towns";
 }
 
-Goals::TGoalVec DefenceBehavior::decompose() const
+Goals::TGoalVec DefenceBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
 		
-	for(auto town : cb->getTownsInfo())
+	for(auto town : ai->cb->getTownsInfo())
 	{
-		evaluateDefence(tasks, town);
+		evaluateDefence(tasks, town, ai);
 	}
 
 	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)
 	{
@@ -81,14 +81,15 @@ void handleCounterAttack(
 	const CGTownInstance * town,
 	const HitMapInfo & threat,
 	const HitMapInfo & maximumDanger,
+	const Nullkiller * ai,
 	Goals::TGoalVec & tasks)
 {
 	if(threat.hero.validAndSet()
 		&& threat.turn <= 1
 		&& (threat.danger == maximumDanger.danger || threat.turn < maximumDanger.turn))
 	{
-		auto heroCapturingPaths = ai->nullkiller->pathfinder->getPathInfo(threat.hero->visitablePos());
-		auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, ai->nullkiller.get(), threat.hero.get());
+		auto heroCapturingPaths = ai->pathfinder->getPathInfo(threat.hero->visitablePos());
+		auto goals = CaptureObjectsBehavior::getVisitGoals(heroCapturingPaths, ai, threat.hero.get());
 
 		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(
 			"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(cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
+		if(ai->cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
 		{
 			logAi->trace(
 				"Extracting hero %s from garrison of town %s",
@@ -131,10 +132,10 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
 
 			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 heroToDismiss = ai->nullkiller->heroManager->findWeakHeroToDismiss(armyDismissLimit);
+			auto heroToDismiss = ai->heroManager->findWeakHeroToDismiss(armyDismissLimit);
 
 			if(heroToDismiss)
 			{
@@ -148,16 +149,16 @@ bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoa
 	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());
 
-	auto threatNode = ai->nullkiller->dangerHitMap->getObjectThreat(town);
-	std::vector<HitMapInfo> threats = ai->nullkiller->dangerHitMap->getTownThreats(town);
+	auto threatNode = ai->dangerHitMap->getObjectThreat(town);
+	std::vector<HitMapInfo> threats = ai->dangerHitMap->getTownThreats(town);
 	
 	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;
 	}
@@ -169,7 +170,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 		return;
 	}
 	
-	uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
+	uint64_t reinforcement = ai->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
 
 	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)));
 	}
 
-	auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
+	auto paths = ai->pathfinder->getPathInfo(town->visitablePos());
 
 	for(auto & threat : threats)
 	{
@@ -188,14 +189,14 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			std::to_string(threat.turn),
 			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;
 		}
 
-		evaluateRecruitingHero(tasks, threat, town);
+		evaluateRecruitingHero(tasks, threat, town, ai);
 
 		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
 			if(town->visitingHero && town->getUpperArmy()->stacksCount() == 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())
 			{
 				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(ai->nullkiller->arePathHeroesLocked(path))
+				if(ai->arePathHeroesLocked(path))
 				{
 #if NKAI_TRACE_LEVEL >= 1
 					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(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)
 					{
 						if(path.turn() == 0)
@@ -378,7 +379,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			auto firstBlockedAction = path.getFirstBlockedAction();
 			if(firstBlockedAction)
 			{
-				auto subGoal = firstBlockedAction->decompose(path.targetHero);
+				auto subGoal = firstBlockedAction->decompose(ai, path.targetHero);
 
 #if NKAI_TRACE_LEVEL >= 2
 				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());
 }
 
-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)
-		&& 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)
 		{
 			if(hero->getTotalStrength() < threat.danger)
 				continue;
 
-			auto myHeroes = cb->getHeroesInfo();
+			auto myHeroes = ai->cb->getHeroesInfo();
 
 #if NKAI_TRACE_LEVEL >= 1
 			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())
 					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)
 					continue;

+ 3 - 4
AI/Nullkiller/Behaviors/DefenceBehavior.h

@@ -20,7 +20,6 @@ struct HitMapInfo;
 
 namespace Goals
 {
-
 	class DefenceBehavior : public CGoal<DefenceBehavior>
 	{
 	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;
 
 		bool operator==(const DefenceBehavior & other) const override
@@ -38,8 +37,8 @@ namespace Goals
 		}
 
 	private:
-		void evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const;
-		void evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, 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 Nullkiller * ai) const;
 	};
 }
 

+ 37 - 37
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -30,11 +30,11 @@ std::string GatherArmyBehavior::toString() const
 	return "Gather army";
 }
 
-Goals::TGoalVec GatherArmyBehavior::decompose() const
+Goals::TGoalVec GatherArmyBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
 
-	auto heroes = cb->getHeroesInfo();
+	auto heroes = ai->cb->getHeroesInfo();
 
 	if(heroes.empty())
 	{
@@ -43,33 +43,33 @@ Goals::TGoalVec GatherArmyBehavior::decompose() const
 
 	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)
 	{
-		vstd::concatenate(tasks, upgradeArmy(town));
+		vstd::concatenate(tasks, upgradeArmy(ai, town));
 	}
 
 	return tasks;
 }
 
-Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * hero) const
+Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const Nullkiller * ai, const CGHeroInstance * hero) const
 {
 	Goals::TGoalVec tasks;
 	const int3 pos = hero->visitablePos();
-	auto targetHeroScore = ai->nullkiller->heroManager->evaluateHero(hero);
+	auto targetHeroScore = ai->heroManager->evaluateHero(hero);
 
 #if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
 #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
 	logAi->trace("Gather army found %d paths", paths.size());
@@ -89,7 +89,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 			continue;
 		}
 
-		if(path.turn() > 0 && ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
+		if(path.turn() > 0 && ai->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			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;
 		}
 
-		if(ai->nullkiller->arePathHeroesLocked(path))
+		if(ai->arePathHeroesLocked(path))
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path because of locked hero");
@@ -107,7 +107,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 		HeroExchange heroExchange(hero, path);
 
-		uint64_t armyValue = heroExchange.getReinforcementArmyStrength();
+		uint64_t armyValue = heroExchange.getReinforcementArmyStrength(ai);
 		float armyRatio = (float)armyValue / hero->getArmyStrength();
 
 		// avoid transferring very small amount of army
@@ -126,11 +126,11 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		{
 			if(!node.targetHero) continue;
 
-			auto heroRole = ai->nullkiller->heroManager->getHeroRole(node.targetHero);
+			auto heroRole = ai->heroManager->getHeroRole(node.targetHero);
 
 			if(heroRole == HeroRole::MAIN)
 			{
-				auto score = ai->nullkiller->heroManager->evaluateHero(node.targetHero);
+				auto score = ai->heroManager->evaluateHero(node.targetHero);
 
 				if(score >= targetHeroScore)
 				{
@@ -174,7 +174,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 			if(hero->inTownGarrison && path.turn() == 0)
 			{
-				auto lockReason = ai->nullkiller->getHeroLockedReason(hero);
+				auto lockReason = ai->getHeroLockedReason(hero);
 
 				if(path.targetHero->visitedTown == hero->visitedTown)
 				{
@@ -201,7 +201,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 	#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Action is blocked. Considering decomposition.");
 	#endif
-				auto subGoal = blockedAction->decompose(path.targetHero);
+				auto subGoal = blockedAction->decompose(ai, path.targetHero);
 
 				if(subGoal->invalid())
 				{
@@ -221,18 +221,18 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 	return tasks;
 }
 
-Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) const
+Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGTownInstance * upgrader) const
 {
 	Goals::TGoalVec tasks;
 	const int3 pos = upgrader->visitablePos();
-	TResources availableResources = ai->nullkiller->getFreeResources();
+	TResources availableResources = ai->getFreeResources();
 
 #if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
 #endif
 	
-	auto paths = ai->nullkiller->pathfinder->getPathInfo(pos, ai->nullkiller->settings->isObjectGraphAllowed());
-	auto goals = CaptureObjectsBehavior::getVisitGoals(paths, ai->nullkiller.get());
+	auto paths = ai->pathfinder->getPathInfo(pos, ai->settings->isObjectGraphAllowed());
+	auto goals = CaptureObjectsBehavior::getVisitGoals(paths, ai);
 
 	std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
 
@@ -244,9 +244,9 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 	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;
 	}
 
@@ -275,7 +275,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 			continue;
 		}
 
-		if(ai->nullkiller->arePathHeroesLocked(path))
+		if(ai->arePathHeroesLocked(path))
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path because of locked hero");
@@ -292,10 +292,10 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 			continue;
 		}
 
-		auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);
+		auto heroRole = ai->heroManager->getHeroRole(path.targetHero);
 
 		if(heroRole == HeroRole::SCOUT
-			&& ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
+			&& ai->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 		{
 #if NKAI_TRACE_LEVEL >= 2
 			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;
 		}
 
-		auto upgrade = ai->nullkiller->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
+		auto upgrade = ai->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
 
 		if(!upgrader->garrisonHero
 			&& (
 				hasMainAround
-				|| ai->nullkiller->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))
+				|| ai->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))
 		{
 			ArmyUpgradeInfo armyToGetOrBuy;
 
 			armyToGetOrBuy.addArmyToGet(
-				ai->nullkiller->armyManager->getBestArmy(
+				ai->armyManager->getBestArmy(
 					path.targetHero,
 					path.heroArmy,
 					upgrader->getUpperArmy()));
@@ -321,11 +321,11 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 			armyToGetOrBuy.upgradeValue -= path.heroArmy->getArmyStrength();
 
 			armyToGetOrBuy.addArmyToBuy(
-				ai->nullkiller->armyManager->toSlotInfo(
-					ai->nullkiller->armyManager->getArmyAvailableToBuy(
+				ai->armyManager->toSlotInfo(
+					ai->armyManager->getArmyAvailableToBuy(
 						path.heroArmy,
 						upgrader,
-						ai->nullkiller->getFreeResources(),
+						ai->getFreeResources(),
 						path.turn())));
 
 			upgrade.upgradeValue += armyToGetOrBuy.upgradeValue;
@@ -334,17 +334,17 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 			if(!upgrade.upgradeValue
 				&& armyToGetOrBuy.upgradeValue > 20000
-				&& ai->nullkiller->heroManager->canRecruitHero(town)
-				&& path.turn() < ai->nullkiller->settings->getScoutHeroTurnDistanceLimit())
+				&& ai->heroManager->canRecruitHero(town)
+				&& path.turn() < ai->settings->getScoutHeroTurnDistanceLimit())
 			{
 				for(auto hero : cb->getAvailableHeroes(town))
 				{
-					auto scoutReinforcement =  ai->nullkiller->armyManager->howManyReinforcementsCanBuy(hero, town)
-						+ ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero, town);
+					auto scoutReinforcement =  ai->armyManager->howManyReinforcementsCanBuy(hero, town)
+						+ ai->armyManager->howManyReinforcementsCanGet(hero, town);
 
 					if(scoutReinforcement >= armyToGetOrBuy.upgradeValue
-						&& ai->nullkiller->getFreeGold() >20000
-						&& !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh())
+						&& ai->getFreeGold() >20000
+						&& !ai->buildAnalyzer->isGoldPreasureHigh())
 					{
 						Composition recruitHero;
 

+ 3 - 3
AI/Nullkiller/Behaviors/GatherArmyBehavior.h

@@ -25,7 +25,7 @@ namespace Goals
 		{
 		}
 
-		TGoalVec decompose() const override;
+		TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 
 		bool operator==(const GatherArmyBehavior & other) const override
@@ -34,8 +34,8 @@ namespace Goals
 		}
 
 	private:
-		TGoalVec deliverArmyToHero(const CGHeroInstance * hero) const;
-		TGoalVec upgradeArmy(const CGTownInstance * upgrader) const;
+		TGoalVec deliverArmyToHero(const Nullkiller * ai, const CGHeroInstance * hero) const;
+		TGoalVec upgradeArmy(const Nullkiller * ai, const CGTownInstance * upgrader) const;
 	};
 }
 

+ 12 - 12
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -24,12 +24,12 @@ std::string RecruitHeroBehavior::toString() const
 	return "Recruit hero";
 }
 
-Goals::TGoalVec RecruitHeroBehavior::decompose() const
+Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
 {
 	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();
 
 	for(auto hero : ourHeroes)
@@ -37,7 +37,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 		if(hero.second != HeroRole::MAIN)
 			continue;
 
-		auto newScore = ai->nullkiller->heroManager->evaluateHero(hero.first.get());
+		auto newScore = ai->heroManager->evaluateHero(hero.first.get());
 
 		if(minScoreToHireMain > newScore)
 		{
@@ -48,13 +48,13 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 
 	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)
 			{
-				auto score = ai->nullkiller->heroManager->evaluateHero(hero);
+				auto score = ai->heroManager->evaluateHero(hero);
 
 				if(score > minScoreToHireMain)
 				{
@@ -65,16 +65,16 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 
 			int treasureSourcesCount = 0;
 
-			for(auto obj : ai->nullkiller->objectClusterizer->getNearbyObjects())
+			for(auto obj : ai->objectClusterizer->getNearbyObjects())
 			{
 				if((obj->ID == Obj::RESOURCE)
 					|| obj->ID == Obj::TREASURE_CHEST
 					|| obj->ID == Obj::CAMPFIRE
-					|| isWeeklyRevisitable(obj)
+					|| isWeeklyRevisitable(ai, obj)
 					|| obj->ID ==Obj::ARTIFACT)
 				{
 					auto tile = obj->visitablePos();
-					auto closestTown = ai->nullkiller->dangerHitMap->getClosestTown(tile);
+					auto closestTown = ai->dangerHitMap->getClosestTown(tile);
 
 					if(town == closestTown)
 						treasureSourcesCount++;
@@ -84,8 +84,8 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 			if(treasureSourcesCount < 5 && (town->garrisonHero || town->getUpperArmy()->getArmyStrength() < 10000))
 				continue;
 
-			if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
-				|| (ai->nullkiller->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->nullkiller->buildAnalyzer->isGoldPreasureHigh()))
+			if(ai->cb->getHeroesInfo().size() < ai->cb->getTownsInfo().size() + 1
+				|| (ai->getFreeResources()[EGameResID::GOLD] > 10000 && !ai->buildAnalyzer->isGoldPreasureHigh()))
 			{
 				tasks.push_back(Goals::sptr(Goals::RecruitHero(town).setpriority(3)));
 			}

+ 1 - 1
AI/Nullkiller/Behaviors/RecruitHeroBehavior.h

@@ -25,7 +25,7 @@ namespace Goals
 		{
 		}
 
-		TGoalVec decompose() const override;
+		TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 
 		bool operator==(const RecruitHeroBehavior & other) const override

+ 24 - 24
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -41,9 +41,9 @@ const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPa
 	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())
 		return nullptr;
@@ -59,9 +59,9 @@ const CGHeroInstance * getNearestHero(const CGTownInstance * town)
 	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;
 
 	if(!startupTown->garrisonHero && !startupTown->visitingHero)
@@ -69,7 +69,7 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
 
 	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)
 			|| obj->ID == Obj::TREASURE_CHEST
@@ -91,10 +91,10 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
 	return cb->getHeroCount(ai->playerID, true) < basicCount + boost;
 }
 
-Goals::TGoalVec StartupBehavior::decompose() const
+Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
-	auto towns = cb->getTownsInfo();
+	auto towns = ai->cb->getTownsInfo();
 
 	if(!towns.size())
 		return tasks;
@@ -103,38 +103,38 @@ Goals::TGoalVec StartupBehavior::decompose() const
 
 	if(towns.size() > 1)
 	{
-		startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
+		startupTown = *vstd::maxElementByFun(towns, [ai](const CGTownInstance * town) -> float
 		{
 			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)
-				return ai->nullkiller->heroManager->evaluateHero(closestHero);
+				return ai->heroManager->evaluateHero(closestHero);
 
 			return 0;
 		});
 	}
 
 	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)));
 
 		return tasks;
 	}
 
-	bool canRecruitHero = needToRecruitHero(startupTown);
-	auto closestHero = getNearestHero(startupTown);
+	bool canRecruitHero = needToRecruitHero(ai, startupTown);
+	auto closestHero = getNearestHero(ai, startupTown);
 
 	if(closestHero)
 	{
 		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())
 				{
@@ -147,22 +147,22 @@ Goals::TGoalVec StartupBehavior::decompose() const
 		else
 		{
 			auto visitingHero = startupTown->visitingHero.get();
-			auto visitingHeroScore = ai->nullkiller->heroManager->evaluateHero(visitingHero);
+			auto visitingHeroScore = ai->heroManager->evaluateHero(visitingHero);
 				
 			if(startupTown->garrisonHero)
 			{
 				auto garrisonHero = startupTown->garrisonHero.get();
-				auto garrisonHeroScore = ai->nullkiller->heroManager->evaluateHero(garrisonHero);
+				auto garrisonHeroScore = ai->heroManager->evaluateHero(garrisonHero);
 
 				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)));
 					}
 				}
-				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)));
 				}
@@ -170,7 +170,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
 			else if(canRecruitHero)
 			{
 				auto canPickTownArmy = startupTown->stacksCount() == 0
-					|| ai->nullkiller->armyManager->howManyReinforcementsCanGet(startupTown->visitingHero, startupTown) > 0;
+					|| ai->armyManager->howManyReinforcementsCanGet(startupTown->visitingHero, startupTown) > 0;
 
 				if(canPickTownArmy)
 				{
@@ -189,7 +189,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
 	{
 		for(auto town : towns)
 		{
-			if(!town->visitingHero && needToRecruitHero(town))
+			if(!town->visitingHero && needToRecruitHero(ai, town))
 			{
 				tasks.push_back(Goals::sptr(Goals::RecruitHero(town)));
 
@@ -205,7 +205,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
 			if(town->garrisonHero
 				&& town->garrisonHero->movementPointsRemaining()
 				&& !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)));
 			}

+ 1 - 1
AI/Nullkiller/Behaviors/StartupBehavior.h

@@ -25,7 +25,7 @@ namespace Goals
 		{
 		}
 
-		TGoalVec decompose() const override;
+		TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 
 		bool operator==(const StartupBehavior & other) const override

+ 3 - 3
AI/Nullkiller/Behaviors/StayAtTownBehavior.cpp

@@ -27,10 +27,10 @@ std::string StayAtTownBehavior::toString() const
 	return "StayAtTownBehavior";
 }
 
-Goals::TGoalVec StayAtTownBehavior::decompose() const
+Goals::TGoalVec StayAtTownBehavior::decompose(const Nullkiller * ai) const
 {
 	Goals::TGoalVec tasks;
-	auto towns = cb->getTownsInfo();
+	auto towns = ai->cb->getTownsInfo();
 
 	if(!towns.size())
 		return tasks;
@@ -42,7 +42,7 @@ Goals::TGoalVec StayAtTownBehavior::decompose() const
 		if(!town->hasBuilt(BuildingID::MAGES_GUILD_1))
 			continue;
 
-		ai->nullkiller->pathfinder->calculatePathInfo(paths, town->visitablePos());
+		ai->pathfinder->calculatePathInfo(paths, town->visitablePos());
 
 		for(auto & path : paths)
 		{

+ 1 - 1
AI/Nullkiller/Behaviors/StayAtTownBehavior.h

@@ -25,7 +25,7 @@ namespace Goals
 		{
 		}
 
-		TGoalVec decompose() const override;
+		TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 
 		bool operator==(const StayAtTownBehavior & other) const override

+ 8 - 3
AI/Nullkiller/Engine/DeepDecomposer.cpp

@@ -26,6 +26,11 @@ namespace NKAI
 
 using namespace Goals;
 
+DeepDecomposer::DeepDecomposer(const Nullkiller * ai)
+	:ai(ai), depth(0)
+{
+}
+
 void DeepDecomposer::reset()
 {
 	decompositionCache.clear();
@@ -136,7 +141,7 @@ Goals::TSubgoal DeepDecomposer::aggregateGoals(int startDepth, TSubgoal last)
 
 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)
@@ -156,7 +161,7 @@ bool isEquivalentGoals(TSubgoal goal1, TSubgoal goal2)
 
 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)
 	{
@@ -206,7 +211,7 @@ TGoalVec DeepDecomposer::decomposeCached(TSubgoal goal, bool & fromCache)
 
 	fromCache = false;
 
-	return goal->decompose();
+	return goal->decompose(ai);
 }
 
 void DeepDecomposer::addToCache(TSubgoal goal)

+ 2 - 0
AI/Nullkiller/Engine/DeepDecomposer.h

@@ -30,8 +30,10 @@ private:
 	std::vector<Goals::TGoalVec> goals;
 	std::vector<TGoalHashSet> decompositionCache;
 	int depth;
+	const Nullkiller * ai;
 
 public:
+	DeepDecomposer(const Nullkiller * ai);
 	void reset();
 	Goals::TGoalVec decompose(Goals::TSubgoal behavior, int depthLimit);
 

+ 9 - 6
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -37,10 +37,11 @@ Nullkiller::Nullkiller()
 	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->playerID = playerID;
+	this->gateway = gateway;
+	this->playerID = gateway->playerID;
 
 	baseGraph.reset();
 
@@ -59,7 +60,7 @@ void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
 	pathfinder.reset(new AIPathfinder(cb.get(), this));
 	armyManager.reset(new ArmyManager(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));
 }
 
@@ -120,12 +121,11 @@ void Nullkiller::resetAiState()
 
 	lockedResources = TResources();
 	scanDepth = ScanDepth::MAIN_FULL;
-	playerID = ai->playerID;
 	lockedHeroes.clear();
 	dangerHitMap->reset();
 	useHeroChain = true;
 
-	if(!baseGraph && ai->nullkiller->settings->isObjectGraphAllowed())
+	if(!baseGraph && settings->isObjectGraphAllowed())
 	{
 		baseGraph = std::make_unique<ObjectGraph>();
 		baseGraph->updateGraph(this);
@@ -253,6 +253,7 @@ void Nullkiller::makeTurn()
 
 	for(int i = 1; i <= settings->getMaxPass(); i++)
 	{
+		auto start = std::chrono::high_resolution_clock::now();
 		updateAiState(i);
 
 		Goals::TTask bestTask = taskptr(Goals::Invalid());
@@ -342,6 +343,8 @@ void Nullkiller::makeTurn()
 			return;
 		}
 
+		logAi->debug("Decission madel in %ld", timeElapsed(start));
+
 		executeTask(bestTask);
 
 		if(i == settings->getMaxPass())
@@ -361,7 +364,7 @@ void Nullkiller::executeTask(Goals::TTask task)
 
 	try
 	{
-		task->accept(ai);
+		task->accept(gateway);
 		logAi->trace("Task %s completed in %lld", taskDescr, timeElapsed(start));
 	}
 	catch(goalFulfilledException &)

+ 2 - 1
AI/Nullkiller/Engine/Nullkiller.h

@@ -57,6 +57,7 @@ private:
 	ScanDepth scanDepth;
 	TResources lockedResources;
 	bool useHeroChain;
+	AIGateway * gateway;
 
 public:
 	static std::unique_ptr<ObjectGraph> baseGraph;
@@ -79,7 +80,7 @@ public:
 	std::mutex aiStateMutex;
 
 	Nullkiller();
-	void init(std::shared_ptr<CCallback> cb, PlayerColor playerID);
+	void init(std::shared_ptr<CCallback> cb, AIGateway * gateway);
 	void makeTurn();
 	bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
 	bool isHeroLocked(const CGHeroInstance * hero) const;

+ 2 - 2
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -709,7 +709,7 @@ public:
 
 		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.heroRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(heroExchange.hero.get());
@@ -1064,7 +1064,7 @@ EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal
 
 	if(goal->goalType == Goals::COMPOSITION)
 	{
-		parts = goal->decompose();
+		parts = goal->decompose(ai);
 	}
 	else
 	{

+ 2 - 1
AI/Nullkiller/Goals/AbstractGoal.h

@@ -21,6 +21,7 @@ namespace NKAI
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
+class Nullkiller;
 
 namespace Goals
 {
@@ -128,7 +129,7 @@ namespace Goals
 			return const_cast<AbstractGoal *>(this);
 		}
 
-		virtual TGoalVec decompose() const
+		virtual TGoalVec decompose(const Nullkiller * ai) const
 		{
 			return TGoalVec();
 		}

+ 3 - 3
AI/Nullkiller/Goals/CGoal.h

@@ -54,9 +54,9 @@ namespace Goals
 
 		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())
 				return {};
@@ -65,7 +65,7 @@ namespace Goals
 		}
 
 	protected:
-		virtual TSubgoal decomposeSingle() const
+		virtual TSubgoal decomposeSingle(const Nullkiller * ai) const
 		{
 			return TSubgoal();
 		}

+ 2 - 2
AI/Nullkiller/Goals/CaptureObject.cpp

@@ -35,9 +35,9 @@ std::string CaptureObject::toString() const
 	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);
 }
 
 }

+ 1 - 1
AI/Nullkiller/Goals/CaptureObject.h

@@ -35,7 +35,7 @@ namespace Goals
 		}
 
 		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;
 		bool hasHash() const override { return true; }
 		uint64_t getHash() const override;

+ 34 - 34
AI/Nullkiller/Goals/CompleteQuest.cpp

@@ -29,36 +29,36 @@ std::string CompleteQuest::toString() const
 	return "Complete quest " + questToString();
 }
 
-TGoalVec CompleteQuest::decompose() const
+TGoalVec CompleteQuest::decompose(const Nullkiller * ai) const
 {
 	if(isKeyMaster(q))
 	{
-		return missionKeymaster();
+		return missionKeymaster(ai);
 	}
 
 	logAi->debug("Trying to realize quest: %s", questToString());
 	
 	if(!q.quest->mission.artifacts.empty())
-		return missionArt();
+		return missionArt(ai);
 
 	if(!q.quest->mission.heroes.empty())
-		return missionHero();
+		return missionHero(ai);
 
 	if(!q.quest->mission.creatures.empty())
-		return missionArmy();
+		return missionArmy(ai);
 
 	if(q.quest->mission.resources.nonZero())
-		return missionResources();
+		return missionResources(ai);
 
 	if(q.quest->killTarget != ObjectInstanceID::NONE)
-		return missionDestroyObj();
+		return missionDestroyObj(ai);
 
 	for(auto & s : q.quest->mission.primary)
 		if(s)
-			return missionIncreasePrimaryStat();
+			return missionIncreasePrimaryStat(ai);
 
 	if(q.quest->mission.heroLevel > 0)
-		return missionLevel();
+		return missionLevel(ai);
 
 	return TGoalVec();
 }
@@ -103,21 +103,21 @@ std::string CompleteQuest::questToString() const
 	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
 	{
 		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())
 		return solutions;
@@ -132,9 +132,9 @@ TGoalVec CompleteQuest::missionArt() const
 	return solutions;
 }
 
-TGoalVec CompleteQuest::missionHero() const
+TGoalVec CompleteQuest::missionHero(const Nullkiller * ai) const
 {
-	TGoalVec solutions = tryCompleteQuest();
+	TGoalVec solutions = tryCompleteQuest(ai);
 
 	if(solutions.empty())
 	{
@@ -145,43 +145,43 @@ TGoalVec CompleteQuest::missionHero() const
 	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
 	{
 		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
 	{
-		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?
 
@@ -208,14 +208,14 @@ TGoalVec CompleteQuest::missionResources() const
 	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)
-		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)
 	//{
@@ -226,7 +226,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
 	//else 
 	if(relations == PlayerRelations::ENEMIES)
 	{
-		return CaptureObjectsBehavior(obj).decompose();
+		return CaptureObjectsBehavior(obj).decompose(ai);
 	}
 
 	return TGoalVec();

+ 10 - 10
AI/Nullkiller/Goals/CompleteQuest.h

@@ -29,7 +29,7 @@ namespace Goals
 		{
 		}
 
-		Goals::TGoalVec decompose() const override;
+		Goals::TGoalVec decompose(const Nullkiller * ai) const override;
 		std::string toString() const override;
 		bool hasHash() const override { return true; }
 		uint64_t getHash() const override;
@@ -37,15 +37,15 @@ namespace Goals
 		bool operator==(const CompleteQuest & other) const override;
 
 	private:
-		TGoalVec tryCompleteQuest() const;
-		TGoalVec missionArt() const;
-		TGoalVec missionHero() const;
-		TGoalVec missionArmy() const;
-		TGoalVec missionResources() const;
-		TGoalVec missionDestroyObj() const;
-		TGoalVec missionIncreasePrimaryStat() const;
-		TGoalVec missionLevel() const;
-		TGoalVec missionKeymaster() const;
+		TGoalVec tryCompleteQuest(const Nullkiller * ai) const;
+		TGoalVec missionArt(const Nullkiller * ai) const;
+		TGoalVec missionHero(const Nullkiller * ai) const;
+		TGoalVec missionArmy(const Nullkiller * ai) const;
+		TGoalVec missionResources(const Nullkiller * ai) const;
+		TGoalVec missionDestroyObj(const Nullkiller * ai) const;
+		TGoalVec missionIncreasePrimaryStat(const Nullkiller * ai) const;
+		TGoalVec missionLevel(const Nullkiller * ai) const;
+		TGoalVec missionKeymaster(const Nullkiller * ai) const;
 		std::string questToString() const;
 	};
 }

+ 1 - 1
AI/Nullkiller/Goals/Composition.cpp

@@ -59,7 +59,7 @@ void Composition::accept(AIGateway * ai)
 	}
 }
 
-TGoalVec Composition::decompose() const
+TGoalVec Composition::decompose(const Nullkiller * ai) const
 {
 	TGoalVec result;
 

+ 1 - 1
AI/Nullkiller/Goals/Composition.h

@@ -32,7 +32,7 @@ namespace Goals
 		Composition & addNext(const AbstractGoal & goal);
 		Composition & addNext(TSubgoal goal);
 		Composition & addNextSequence(const TGoalVec & taskSequence);
-		TGoalVec decompose() const override;
+		TGoalVec decompose(const Nullkiller * ai) const override;
 		bool isElementar() const override;
 		int getHeroExchangeCount() const override;
 	};

+ 4 - 4
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -100,7 +100,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
 						throw cannotFulfillGoalException("Path is nondeterministic.");
 					}
 					
-					node->specialAction->execute(hero);
+					node->specialAction->execute(ai, hero);
 					
 					if(!heroPtr.validAndSet())
 					{
@@ -156,7 +156,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
 				{
 					try
 					{
-						if(moveHeroToTile(hero, node->coord))
+						if(moveHeroToTile(ai, hero, node->coord))
 						{
 							continue;
 						}
@@ -224,9 +224,9 @@ std::string ExecuteHeroChain::toString() const
 	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());
 

+ 1 - 2
AI/Nullkiller/Goals/ExecuteHeroChain.h

@@ -27,7 +27,6 @@ namespace Goals
 
 		ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
 
-		
 		void accept(AIGateway * ai) override;
 		std::string toString() const override;
 		bool operator==(const ExecuteHeroChain & other) const override;
@@ -36,7 +35,7 @@ namespace Goals
 		int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
 
 	private:
-		bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
+		bool moveHeroToTile(AIGateway * ai, const CGHeroInstance * hero, const int3 & tile);
 	};
 }
 

+ 1 - 1
AI/Nullkiller/Goals/Invalid.h

@@ -27,7 +27,7 @@ namespace Goals
 		{
 			priority = -1;
 		}
-		TGoalVec decompose() const override
+		TGoalVec decompose(const Nullkiller * ai) const override
 		{
 			return TGoalVec();
 		}

+ 2 - 2
AI/Nullkiller/Markers/HeroExchange.cpp

@@ -29,9 +29,9 @@ std::string HeroExchange::toString() const
 	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;
 }

+ 1 - 1
AI/Nullkiller/Markers/HeroExchange.h

@@ -31,7 +31,7 @@ namespace Goals
 		bool operator==(const HeroExchange & other) const override;
 		std::string toString() const override;
 
-		uint64_t getReinforcementArmyStrength() const;
+		uint64_t getReinforcementArmyStrength(const Nullkiller * ai) const;
 	};
 }
 

+ 1 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -1389,7 +1389,7 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa
 		{
 			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();

+ 2 - 0
AI/Nullkiller/Pathfinding/AIPathfinder.cpp

@@ -17,6 +17,8 @@
 namespace NKAI
 {
 
+std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>>  AIPathfinder::heroGraphs;
+
 AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai)
 	:cb(cb), ai(ai)
 {

+ 1 - 1
AI/Nullkiller/Pathfinding/AIPathfinder.h

@@ -40,7 +40,7 @@ private:
 	std::shared_ptr<AINodeStorage> storage;
 	CPlayerSpecificInfoCallback * cb;
 	Nullkiller * ai;
-	std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>>  heroGraphs;
+	static std::map<ObjectInstanceID, std::unique_ptr<GraphPaths>>  heroGraphs;
 
 public:
 	AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);

+ 1 - 1
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp

@@ -33,7 +33,7 @@ namespace AIPathfinding
 				std::make_shared<AIMovementToDestinationRule>(nodeStorage, allowBypassObjects),
 				std::make_shared<MovementCostRule>(),
 				std::make_shared<AIPreviousNodeRule>(nodeStorage),
-				std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage, allowBypassObjects)
+				std::make_shared<AIMovementAfterDestinationRule>(ai, cb, nodeStorage, allowBypassObjects)
 			};
 
 		return rules;

+ 2 - 2
AI/Nullkiller/Pathfinding/Actions/AdventureSpellCastMovementActions.cpp

@@ -49,14 +49,14 @@ namespace AIPathfinding
 		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);
 
 		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);
 

+ 2 - 2
AI/Nullkiller/Pathfinding/Actions/AdventureSpellCastMovementActions.h

@@ -29,7 +29,7 @@ namespace AIPathfinding
 	public:
 		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(
 			const CGHeroInstance * hero,
@@ -38,7 +38,7 @@ namespace AIPathfinding
 			AIPathNode * dstMode,
 			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;
 	};

+ 1 - 1
AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp

@@ -18,7 +18,7 @@ namespace NKAI
 
 namespace AIPathfinding
 {
-	void BattleAction::execute(const CGHeroInstance * hero) const
+	void BattleAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
 		ai->moveHeroToTile(targetTile, hero);
 	}

+ 1 - 1
AI/Nullkiller/Pathfinding/Actions/BattleAction.h

@@ -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;
 	};

+ 9 - 9
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp

@@ -22,12 +22,12 @@ namespace NKAI
 
 namespace AIPathfinding
 {
-	void BuildBoatAction::execute(const CGHeroInstance * hero) const
+	void BuildBoatAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
 		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)
 		{
@@ -37,7 +37,7 @@ namespace AIPathfinding
 		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)
 		{
@@ -63,16 +63,16 @@ namespace AIPathfinding
 		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;
 
-		return canAct(source.targetHero, res);
+		return canAct(ai, source.targetHero, res);
 	}
 
 	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)));
 	}
 
-	void SummonBoatAction::execute(const CGHeroInstance * hero) const
+	void SummonBoatAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
 		Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai);
 	}
@@ -116,7 +116,7 @@ namespace AIPathfinding
 		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;
 

+ 7 - 7
AI/Nullkiller/Pathfinding/Actions/BoatActions.h

@@ -25,7 +25,7 @@ namespace AIPathfinding
 	class SummonBoatAction : public VirtualBoatAction
 	{
 	public:
-		void execute(const CGHeroInstance * hero) const override;
+		void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
 
 		virtual void applyOnDestination(
 			const CGHeroInstance * hero,
@@ -34,7 +34,7 @@ namespace AIPathfinding
 			AIPathNode * dstMode,
 			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;
 
@@ -56,13 +56,13 @@ namespace AIPathfinding
 		{
 		}
 
-		bool canAct(const AIPathNode * source) const override;
-		bool canAct(const AIPathNodeInfo & source) const override;
-		bool canAct(const CGHeroInstance * hero, const TResources & reservedResources) const;
+		bool canAct(const Nullkiller * ai, const AIPathNode * source) const override;
+		bool canAct(const Nullkiller * ai, const AIPathNodeInfo & source) const override;
+		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;
 

+ 1 - 1
AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.cpp

@@ -18,7 +18,7 @@ namespace NKAI
 
 namespace AIPathfinding
 {
-	void BuyArmyAction::execute(const CGHeroInstance * hero) const
+	void BuyArmyAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
 		if(!hero->visitedTown)
 		{

+ 2 - 2
AI/Nullkiller/Pathfinding/Actions/BuyArmyAction.h

@@ -21,12 +21,12 @@ namespace AIPathfinding
 	private:
 
 	public:
-		bool canAct(const AIPathNode * source) const override
+		bool canAct(const Nullkiller * ai, const AIPathNode * source) const override
 		{
 			return true;
 		}
 
-		void execute(const CGHeroInstance * hero) const override;
+		void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
 		std::string toString() const override;
 	};
 }

+ 7 - 7
AI/Nullkiller/Pathfinding/Actions/QuestAction.cpp

@@ -18,17 +18,17 @@ namespace NKAI
 
 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)
 		{
@@ -39,12 +39,12 @@ namespace AIPathfinding
 			|| 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));
 	}
 
-	void QuestAction::execute(const CGHeroInstance * hero) const
+	void QuestAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 	{
 		ai->moveHeroToTile(questInfo.obj->visitablePos(), hero);
 	}

+ 5 - 5
AI/Nullkiller/Pathfinding/Actions/QuestAction.h

@@ -28,13 +28,13 @@ namespace AIPathfinding
 		{
 		}
 
-		bool canAct(const AIPathNode * node) const override;
-		bool canAct(const AIPathNodeInfo & node) const override;
-		bool canAct(const CGHeroInstance * hero) const;
+		bool canAct(const Nullkiller * ai, const AIPathNode * node) const override;
+		bool canAct(const Nullkiller * ai, const AIPathNodeInfo & node) const override;
+		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;
 	};

+ 9 - 9
AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp

@@ -17,43 +17,43 @@
 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());
 }
 
-void SpecialAction::execute(const CGHeroInstance * hero) const
+void SpecialAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
 {
 	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)
 	{
-		if(!part->canAct(source)) return false;
+		if(!part->canAct(ai, source)) return false;
 	}
 
 	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)
 	{
-		auto goal = part->decompose(hero);
+		auto goal = part->decompose(ai, hero);
 
 		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)
 	{
-		part->execute(hero);
+		part->execute(ai, hero);
 	}
 }
 

+ 7 - 7
AI/Nullkiller/Pathfinding/Actions/SpecialAction.h

@@ -30,19 +30,19 @@ class SpecialAction
 public:
 	virtual ~SpecialAction() = default;
 
-	virtual bool canAct(const AIPathNode * source) const
+	virtual bool canAct(const Nullkiller * ai, const AIPathNode * source) const
 	{
 		return true;
 	}
 
-	virtual bool canAct(const AIPathNodeInfo & source) const
+	virtual bool canAct(const Nullkiller * ai, const AIPathNodeInfo & source) const
 	{
 		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(
 		const CGHeroInstance * hero,
@@ -76,11 +76,11 @@ private:
 public:
 	CompositeAction(std::vector<std::shared_ptr<const SpecialAction>> parts) : parts(parts) {}
 
-	bool canAct(const AIPathNode * source) const override;
-	void execute(const CGHeroInstance * hero) const override;
+	bool canAct(const Nullkiller * ai, const AIPathNode * source) const override;
+	void execute(AIGateway * ai, const CGHeroInstance * hero) const override;
 	std::string toString() 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
 	{

+ 1 - 1
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp

@@ -18,7 +18,7 @@ namespace NKAI
 
 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);
 	

+ 1 - 1
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.h

@@ -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;
 	};

+ 3 - 3
AI/Nullkiller/Pathfinding/ObjectGraph.cpp

@@ -602,7 +602,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 
 				auto questAction = std::make_shared<AIPathfinding::QuestAction>(questInfo);
 
-				if(!questAction->canAct(targetHero))
+				if(!questAction->canAct(ai, targetHero))
 				{
 					transitionAction = questAction;
 				}
@@ -762,7 +762,7 @@ void GraphPaths::addChainInfo(std::vector<AIPath> & paths, int3 tile, const CGHe
 
 				if(n.specialAction)
 				{
-					n.actionIsBlocked = !n.specialAction->canAct(n);
+					n.actionIsBlocked = !n.specialAction->canAct(ai, n);
 				}
 
 				for(auto & node : path.nodes)
@@ -883,7 +883,7 @@ void GraphPaths::quickAddChainInfoWithBlocker(std::vector<AIPath> & paths, int3
 
 				if(n.specialAction)
 				{
-					n.actionIsBlocked = !n.specialAction->canAct(n);
+					n.actionIsBlocked = !n.specialAction->canAct(ai, n);
 				}
 
 				auto blocker = ai->objectClusterizer->getBlocker(n);

+ 1 - 1
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -177,7 +177,7 @@ namespace AIPathfinding
 			const CGHeroInstance * hero = nodeStorage->getHero(source.node);
 
 			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);
 			}

+ 3 - 2
AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -20,10 +20,11 @@ namespace NKAI
 namespace AIPathfinding
 {
 	AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
+		const Nullkiller * ai,
 		CPlayerSpecificInfoCallback * cb, 
 		std::shared_ptr<AINodeStorage> nodeStorage,
 		bool allowBypassObjects)
-		:cb(cb), nodeStorage(nodeStorage), allowBypassObjects(allowBypassObjects)
+		:ai(ai), cb(cb), nodeStorage(nodeStorage), allowBypassObjects(allowBypassObjects)
 	{
 	}
 
@@ -171,7 +172,7 @@ namespace AIPathfinding
 			return false;
 		}
 
-		if(!questAction.canAct(destinationNode))
+		if(!questAction.canAct(ai, destinationNode))
 		{
 			if(!destinationNode->actor->allowUseResources)
 			{

+ 2 - 0
AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.h

@@ -24,11 +24,13 @@ namespace AIPathfinding
 	{
 	private:
 		CPlayerSpecificInfoCallback * cb;
+		const Nullkiller * ai;
 		std::shared_ptr<AINodeStorage> nodeStorage;
 		bool allowBypassObjects;
 
 	public:
 		AIMovementAfterDestinationRule(
+			const Nullkiller * ai,
 			CPlayerSpecificInfoCallback * cb,
 			std::shared_ptr<AINodeStorage> nodeStorage,
 			bool allowBypassObjects);