Browse Source

Nullkiller: remove AIhelper + refactoring

Andrii Danylchenko 5 years ago
parent
commit
1806dd8447
67 changed files with 598 additions and 1721 deletions
  1. 24 10
      AI/Nullkiller/AIUtility.cpp
  2. 0 2
      AI/Nullkiller/AIUtility.h
  3. 0 136
      AI/Nullkiller/AIhelper.cpp
  4. 0 73
      AI/Nullkiller/AIhelper.h
  5. 1 11
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  6. 2 6
      AI/Nullkiller/Analyzers/ArmyManager.h
  7. 0 1
      AI/Nullkiller/Analyzers/BuildAnalyzer.cpp
  8. 8 9
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  9. 3 1
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h
  10. 5 15
      AI/Nullkiller/Analyzers/HeroManager.cpp
  11. 2 5
      AI/Nullkiller/Analyzers/HeroManager.h
  12. 11 14
      AI/Nullkiller/Analyzers/ObjectClusterizer.cpp
  13. 6 2
      AI/Nullkiller/Analyzers/ObjectClusterizer.h
  14. 1 2
      AI/Nullkiller/Behaviors/BuildingBehavior.cpp
  15. 3 5
      AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp
  16. 122 4
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp
  17. 1 0
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h
  18. 1 3
      AI/Nullkiller/Behaviors/ClusterBehavior.cpp
  19. 0 2
      AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp
  20. 5 6
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  21. 5 7
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  22. 0 2
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp
  23. 10 12
      AI/Nullkiller/Behaviors/StartupBehavior.cpp
  24. 10 14
      AI/Nullkiller/CMakeLists.txt
  25. 104 0
      AI/Nullkiller/Engine/AIMemory.cpp
  26. 36 0
      AI/Nullkiller/Engine/AIMemory.h
  27. 1 3
      AI/Nullkiller/Engine/FuzzyEngines.cpp
  28. 0 0
      AI/Nullkiller/Engine/FuzzyEngines.h
  29. 15 23
      AI/Nullkiller/Engine/FuzzyHelper.cpp
  30. 8 4
      AI/Nullkiller/Engine/FuzzyHelper.h
  31. 21 8
      AI/Nullkiller/Engine/Nullkiller.cpp
  32. 12 0
      AI/Nullkiller/Engine/Nullkiller.h
  33. 60 56
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  34. 29 3
      AI/Nullkiller/Engine/PriorityEvaluator.h
  35. 0 3
      AI/Nullkiller/Goals/AbstractGoal.cpp
  36. 0 3
      AI/Nullkiller/Goals/AdventureSpellCast.cpp
  37. 0 3
      AI/Nullkiller/Goals/BuildBoat.cpp
  38. 0 3
      AI/Nullkiller/Goals/BuildThis.cpp
  39. 3 4
      AI/Nullkiller/Goals/BuyArmy.cpp
  40. 5 8
      AI/Nullkiller/Goals/CGoal.h
  41. 0 3
      AI/Nullkiller/Goals/CollectRes.cpp
  42. 0 3
      AI/Nullkiller/Goals/Composition.cpp
  43. 0 1
      AI/Nullkiller/Goals/DigAtTile.cpp
  44. 0 3
      AI/Nullkiller/Goals/DismissHero.cpp
  45. 0 3
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp
  46. 0 3
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  47. 0 3
      AI/Nullkiller/Goals/GatherTroops.cpp
  48. 0 1
      AI/Nullkiller/Goals/GetArtOfType.cpp
  49. 0 3
      AI/Nullkiller/Goals/RecruitHero.cpp
  50. 0 1
      AI/Nullkiller/Goals/UnlockCluster.cpp
  51. 0 140
      AI/Nullkiller/MapObjectsEvaluator.cpp
  52. 0 26
      AI/Nullkiller/MapObjectsEvaluator.h
  53. 5 8
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  54. 5 5
      AI/Nullkiller/Pathfinding/AINodeStorage.h
  55. 6 5
      AI/Nullkiller/Pathfinding/AIPathfinder.cpp
  56. 5 4
      AI/Nullkiller/Pathfinding/AIPathfinder.h
  57. 3 2
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp
  58. 3 2
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.h
  59. 9 9
      AI/Nullkiller/Pathfinding/Actors.cpp
  60. 5 5
      AI/Nullkiller/Pathfinding/Actors.h
  61. 0 62
      AI/Nullkiller/Pathfinding/PathfindingManager.h
  62. 3 2
      AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp
  63. 2 2
      AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h
  64. 0 431
      AI/Nullkiller/SectorMap.cpp
  65. 0 70
      AI/Nullkiller/SectorMap.h
  66. 31 429
      AI/Nullkiller/VCAI.cpp
  67. 7 32
      AI/Nullkiller/VCAI.h

+ 24 - 10
AI/Nullkiller/AIUtility.cpp

@@ -10,20 +10,16 @@
 #include "StdInc.h"
 #include "AIUtility.h"
 #include "VCAI.h"
-#include "FuzzyHelper.h"
 #include "Goals/Goals.h"
 
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CHeroHandler.h"
-#include "../../lib/mapObjects/CBank.h"
-#include "../../lib/mapObjects/CGTownInstance.h"
-#include "../../lib/mapObjects/CQuest.h"
+#include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/mapping/CMapDefines.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 //extern static const int3 dirs[8];
 
@@ -194,11 +190,6 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns
 	return ln->cost < rn->cost;
 }
 
-bool isSafeToVisit(HeroPtr h, crint3 tile)
-{
-	return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
-}
-
 bool isSafeToVisit(HeroPtr h, const CCreatureSet * heroArmy, uint64_t dangerStrength)
 {
 	const ui64 heroStrength = h->getFightingStrength() * heroArmy->getArmyStrength();
@@ -338,3 +329,26 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
 	else
 		return art1->price > art2->price;
 }
+
+bool isWeeklyRevisitable(const CGObjectInstance * obj)
+{
+	//TODO: allow polling of remaining creatures in dwelling
+	if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
+		return true;
+	if(dynamic_cast<const CGDwelling *>(obj))
+		return true;
+	if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
+		return true;
+
+	switch(obj->ID)
+	{
+	case Obj::STABLES:
+	case Obj::MAGIC_WELL:
+	case Obj::HILL_FORT:
+		return true;
+	case Obj::BORDER_GATE:
+	case Obj::BORDERGUARD:
+		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
+	}
+	return false;
+}

+ 0 - 2
AI/Nullkiller/AIUtility.h

@@ -173,12 +173,10 @@ bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, Pla
 bool isBlockVisitObj(const int3 & pos);
 
 bool isWeeklyRevisitable(const CGObjectInstance * obj);
-bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
 
 bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
 bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
 bool isSafeToVisit(HeroPtr h, const CCreatureSet *, uint64_t dangerStrength);
-bool isSafeToVisit(HeroPtr h, crint3 tile);
 
 bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
 bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);

+ 0 - 136
AI/Nullkiller/AIhelper.cpp

@@ -1,136 +0,0 @@
-/*
-* AIhelper.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-#include "StdInc.h"
-
-#include "AIhelper.h"
-
-AIhelper::AIhelper()
-{
-	pathfindingManager.reset(new PathfindingManager());
-	armyManager.reset(new ArmyManager());
-	heroManager.reset(new HeroManager());
-}
-
-AIhelper::~AIhelper()
-{
-}
-
-void AIhelper::init(CPlayerSpecificInfoCallback * CB)
-{
-	pathfindingManager->init(CB);
-	armyManager->init(CB);
-	heroManager->init(CB);
-}
-
-void AIhelper::setAI(VCAI * AI)
-{
-	pathfindingManager->setAI(AI);
-	armyManager->setAI(AI);
-	heroManager->setAI(AI);
-}
-
-void AIhelper::update()
-{
-	armyManager->update();
-	heroManager->update();
-}
-
-std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
-{
-	return pathfindingManager->getPathsToTile(hero, tile);
-}
-
-std::vector<AIPath> AIhelper::getPathsToTile(const int3 & tile) const
-{
-	return pathfindingManager->getPathsToTile(tile);
-}
-
-void AIhelper::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
-{
-	pathfindingManager->updatePaths(heroes, useHeroChain);
-}
-
-uint64_t AIhelper::evaluateStackPower(const CCreature * creature, int count) const
-{
-	return armyManager->evaluateStackPower(creature, count);
-}
-
-SlotInfo AIhelper::getTotalCreaturesAvailable(CreatureID creatureID) const
-{
-	return armyManager->getTotalCreaturesAvailable(creatureID);
-}
-
-bool AIhelper::canGetArmy(const CArmedInstance * army, const CArmedInstance * source) const
-{
-	return armyManager->canGetArmy(army, source);
-}
-
-ui64 AIhelper::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
-{
-	return armyManager->howManyReinforcementsCanBuy(h, t);
-}
-
-ui64 AIhelper::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	return armyManager->howManyReinforcementsCanGet(target, source);
-}
-
-std::vector<SlotInfo> AIhelper::getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	return armyManager->getBestArmy(target, source);
-}
-
-std::vector<SlotInfo>::iterator AIhelper::getWeakestCreature(std::vector<SlotInfo> & army) const
-{
-	return armyManager->getWeakestCreature(army);
-}
-
-std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	return armyManager->getSortedSlots(target, source);
-}
-
-std::vector<creInfo> AIhelper::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const
-{
-	return armyManager->getArmyAvailableToBuy(hero, dwelling);
-}
-
-ArmyUpgradeInfo AIhelper::calculateCreateresUpgrade(
-	const CCreatureSet * army,
-	const CGObjectInstance * upgrader,
-	const TResources & availableResources) const
-{
-	return armyManager->calculateCreateresUpgrade(army, upgrader, availableResources);
-}
-
-int AIhelper::selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const
-{
-	return heroManager->selectBestSkill(hero, skills);
-}
-
-const std::map<HeroPtr, HeroRole> & AIhelper::getHeroRoles() const
-{
-	return heroManager->getHeroRoles();
-}
-
-HeroRole AIhelper::getHeroRole(const HeroPtr & hero) const
-{
-	return heroManager->getHeroRole(hero);
-}
-
-float AIhelper::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const
-{
-	return heroManager->evaluateSecSkill(skill, hero);
-}
-
-float AIhelper::evaluateHero(const CGHeroInstance * hero) const
-{
-	return heroManager->evaluateHero(hero);
-}

+ 0 - 73
AI/Nullkiller/AIhelper.h

@@ -1,73 +0,0 @@
-/*
-* AIhelper.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#pragma once
-
-/*
-	!!!	Note: Include THIS file at the end of include list to avoid "undefined base class" error
-*/
-
-#include "ArmyManager.h"
-#include "HeroManager.h"
-#include "Pathfinding/PathfindingManager.h"
-
-class ResourceManager;
-class BuildingManager;
-
-
-//TODO: remove class, put managers to engine
-class DLL_EXPORT AIhelper : public IPathfindingManager, public IArmyManager, public IHeroManager
-{
-	//std::shared_ptr<ResourceManager> resourceManager;
-	//std::shared_ptr<BuildingManager> buildingManager;
-	std::shared_ptr<PathfindingManager> pathfindingManager;
-	std::shared_ptr<ArmyManager> armyManager;
-	std::shared_ptr<HeroManager> heroManager;
-	//TODO: vector<IAbstractManager>
-public:
-	AIhelper();
-	~AIhelper();
-
-	std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
-	std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
-	void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
-
-	STRONG_INLINE
-	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
-	{
-		return pathfindingManager->isTileAccessible(hero, tile);
-	}
-
-	bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override;
-	ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override;
-	ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
-	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
-	uint64_t evaluateStackPower(const CCreature * creature, int count) const override;
-	SlotInfo getTotalCreaturesAvailable(CreatureID creatureID) const override;
-	ArmyUpgradeInfo calculateCreateresUpgrade(
-		const CCreatureSet * army,
-		const CGObjectInstance * upgrader,
-		const TResources & availableResources) const override;
-
-	const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
-	HeroRole getHeroRole(const HeroPtr & hero) const override;
-	int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
-	float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
-	float evaluateHero(const CGHeroInstance * hero) const override;
-
-	void update() override;
-
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
-};
-

+ 1 - 11
AI/Nullkiller/ArmyManager.cpp → AI/Nullkiller/Analyzers/ArmyManager.cpp

@@ -10,7 +10,7 @@
 
 #include "StdInc.h"
 #include "ArmyManager.h"
-
+#include "../Engine/Nullkiller.h"
 #include "../../CCallback.h"
 #include "../../lib/mapObjects/MapObjects.h"
 
@@ -31,16 +31,6 @@ public:
 	}
 };
 
-void ArmyManager::init(CPlayerSpecificInfoCallback * CB)
-{
-	cb = CB;
-}
-
-void ArmyManager::setAI(VCAI * AI)
-{
-	ai = AI;
-}
-
 std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
 {
 	const CCreatureSet * armies[] = { target, source };

+ 2 - 6
AI/Nullkiller/ArmyManager.h → AI/Nullkiller/Analyzers/ArmyManager.h

@@ -40,8 +40,6 @@ struct ArmyUpgradeInfo
 class DLL_EXPORT IArmyManager //: public: IAbstractManager
 {
 public:
-	virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
-	virtual void setAI(VCAI * AI) = 0;
 	virtual void update() = 0;
 	virtual bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const = 0;
 	virtual ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const = 0;
@@ -64,14 +62,12 @@ class DLL_EXPORT ArmyManager : public IArmyManager
 {
 private:
 	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
-	VCAI * ai;
+	const Nullkiller * ai;
 	std::map<CreatureID, SlotInfo> totalArmy;
 
 public:
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
+	ArmyManager(CPlayerSpecificInfoCallback * CB, const Nullkiller * ai): cb(CB), ai(ai) {}
 	void update() override;
-
 	bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override;
 	ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override;
 	ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override;

+ 0 - 1
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -12,7 +12,6 @@
 #include "lib/mapping/CMap.h" //for victory conditions
 
 extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
 
 void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
 {

+ 8 - 9
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp

@@ -10,9 +10,7 @@
 #include "StdInc.h"
 #include "DangerHitMapAnalyzer.h"
 #include "lib/mapping/CMap.h" //for victory conditions
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
+#include "../Engine/Nullkiller.h"
 
 void DangerHitMapAnalyzer::updateHitMap()
 {
@@ -21,17 +19,18 @@ void DangerHitMapAnalyzer::updateHitMap()
 
 	upToDate = true;
 
-	auto mapSize = cb->getMapSize();
+	auto cb = ai->cb.get();
+	auto mapSize = ai->cb->getMapSize();
 	hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
 	enemyHeroAccessibleObjects.clear();
 
-	std::map<PlayerColor, std::vector<HeroPtr>> heroes;
+	std::map<PlayerColor, std::vector<const CGHeroInstance *>> heroes;
 
-	for(const CGObjectInstance * obj : ai->visitableObjs)
+	for(const CGObjectInstance * obj : ai->memory->visitableObjs)
 	{
 		if(obj->ID == Obj::HERO)
 		{
-			HeroPtr hero = dynamic_cast<const CGHeroInstance *>(obj);
+			auto hero = dynamic_cast<const CGHeroInstance *>(obj);
 
 			heroes[hero->tempOwner].push_back(hero);
 		}
@@ -43,11 +42,11 @@ void DangerHitMapAnalyzer::updateHitMap()
 
 	for(auto pair : heroes)
 	{
-		ai->ah->updatePaths(pair.second, false);
+		ai->pathfinder->updatePaths(pair.second, false);
 
 		foreach_tile_pos([&](const int3 & pos)
 		{
-			for(AIPath & path : ai->ah->getPathsToTile(pos))
+			for(AIPath & path : ai->pathfinder->getPathInfo(pos))
 			{
 				if(path.getFirstBlockedAction())
 					continue;

+ 3 - 1
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "../VCAI.h"
-#include "../AIhelper.h"
 
 struct HitMapInfo
 {
@@ -44,8 +43,11 @@ private:
 	boost::multi_array<HitMapNode, 3> hitMap;
 	std::map<const CGHeroInstance *, std::set<const CGObjectInstance *>> enemyHeroAccessibleObjects;
 	bool upToDate;
+	const Nullkiller * ai;
 
 public:
+	DangerHitMapAnalyzer(const Nullkiller * ai) :ai(ai) {}
+
 	void updateHitMap();
 	uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
 	const HitMapNode & getObjectTreat(const CGObjectInstance * obj) const;

+ 5 - 15
AI/Nullkiller/HeroManager.cpp → AI/Nullkiller/Analyzers/HeroManager.cpp

@@ -10,7 +10,7 @@
 
 #include "StdInc.h"
 #include "HeroManager.h"
-
+#include "../Engine/Nullkiller.h"
 #include "../../CCallback.h"
 #include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/CHeroHandler.h"
@@ -57,16 +57,6 @@ SecondarySkillEvaluator HeroManager::scountSkillsScores = SecondarySkillEvaluato
 		std::make_shared<ExistingSkillRule>()
 	});
 
-void HeroManager::init(CPlayerSpecificInfoCallback * CB)
-{
-	cb = CB;
-}
-
-void HeroManager::setAI(VCAI * AI)
-{
-	ai = AI;
-}
-
 float HeroManager::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const
 {
 	auto role = getHeroRole(hero);
@@ -106,11 +96,11 @@ void HeroManager::update()
 	logAi->trace("Start analysing our heroes");
 
 	std::map<HeroPtr, float> scores;
-	auto myHeroes = ai->getMyHeroes();
+	auto myHeroes = cb->getHeroesInfo();
 
 	for(auto & hero : myHeroes)
 	{
-		scores[hero] = evaluateFightingStrength(hero.get());
+		scores[hero] = evaluateFightingStrength(hero);
 	}
 
 	std::sort(myHeroes.begin(), myHeroes.end(), [&](const HeroPtr & h1, const HeroPtr & h2) -> bool
@@ -120,10 +110,10 @@ void HeroManager::update()
 
 	int mainHeroCount = (myHeroes.size() + 2) / 3;
 
-	for(auto & hero : myHeroes)
+	for(auto hero : myHeroes)
 	{
 		heroRoles[hero] = (mainHeroCount--) > 0 ? HeroRole::MAIN : HeroRole::SCOUT;
-		logAi->trace("Hero %s has role %s", hero.name, heroRoles[hero] == HeroRole::MAIN ? "main" : "scout");
+		logAi->trace("Hero %s has role %s", hero->name, heroRoles[hero] == HeroRole::MAIN ? "main" : "scout");
 	}
 }
 

+ 2 - 5
AI/Nullkiller/HeroManager.h → AI/Nullkiller/Analyzers/HeroManager.h

@@ -21,8 +21,6 @@
 class DLL_EXPORT IHeroManager //: public: IAbstractManager
 {
 public:
-	virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
-	virtual void setAI(VCAI * AI) = 0;
 	virtual const std::map<HeroPtr, HeroRole> & getHeroRoles() const = 0;
 	virtual int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const = 0;
 	virtual HeroRole getHeroRole(const HeroPtr & hero) const = 0;
@@ -55,12 +53,11 @@ private:
 	static SecondarySkillEvaluator scountSkillsScores;
 
 	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
-	VCAI * ai;
+	const Nullkiller * ai;
 	std::map<HeroPtr, HeroRole> heroRoles;
 
 public:
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
+	HeroManager(CPlayerSpecificInfoCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {}
 	const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
 	HeroRole getHeroRole(const HeroPtr & hero) const override;
 	int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;

+ 11 - 14
AI/Nullkiller/Analyzers/ObjectClusterizer.cpp

@@ -14,9 +14,6 @@
 #include "../Engine/Nullkiller.h"
 #include "lib/mapping/CMap.h" //for victory conditions
 
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-
 void ObjectCluster::addObject(const CGObjectInstance * obj, const AIPath & path, float priority)
 {
 	auto & info = objects[obj];
@@ -93,12 +90,12 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
 {
 	for(auto node = path.nodes.rbegin(); node != path.nodes.rend(); node++)
 	{
-		auto guardPos = cb->getGuardingCreaturePosition(node->coord);
-		auto blockers = cb->getVisitableObjs(node->coord);
+		auto guardPos = ai->cb->getGuardingCreaturePosition(node->coord);
+		auto blockers = ai->cb->getVisitableObjs(node->coord);
 
 		if(guardPos.valid())
 		{
-			auto guard = cb->getTopObj(cb->getGuardingCreaturePosition(node->coord));
+			auto guard = ai->cb->getTopObj(ai->cb->getGuardingCreaturePosition(node->coord));
 
 			if(guard)
 			{
@@ -126,7 +123,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
 	return nullptr;
 }
 
-bool shouldVisitObject(const CGObjectInstance * obj)
+bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
 {
 	if(isObjectRemovable(obj))
 	{
@@ -135,13 +132,13 @@ bool shouldVisitObject(const CGObjectInstance * obj)
 
 	const int3 pos = obj->visitablePos();
 
-	if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->alreadyVisited, obj)
+	if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->memory->alreadyVisited, obj)
 		|| obj->wasVisited(ai->playerID))
 	{
 		return false;
 	}
 
-	auto playerRelations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
+	auto playerRelations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
 
 	if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
 	{
@@ -151,12 +148,12 @@ bool shouldVisitObject(const CGObjectInstance * obj)
 	//it may be hero visiting this obj
 	//we don't try visiting object on which allied or owned hero stands
 	// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
-	const CGObjectInstance * topObj = cb->getTopObj(pos);
+	const CGObjectInstance * topObj = ai->cb->getTopObj(pos);
 
 	if(!topObj)
 		return false; // partly visible obj but its visitable pos is not visible.
 
-	if(topObj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, topObj->tempOwner) != PlayerRelations::ENEMIES)
+	if(topObj->ID == Obj::HERO && ai->cb->getPlayerRelations(ai->playerID, topObj->tempOwner) != PlayerRelations::ENEMIES)
 		return false;
 	else
 		return true; //all of the following is met
@@ -180,12 +177,12 @@ void ObjectClusterizer::clusterize()
 
 	logAi->debug("Begin object clusterization");
 
-	for(const CGObjectInstance * obj : ai->visitableObjs)
+	for(const CGObjectInstance * obj : ai->memory->visitableObjs)
 	{
 		if(!shouldVisitObject(obj))
 			continue;
 
-		auto paths = ai->ah->getPathsToTile(obj->visitablePos());
+		auto paths = ai->pathfinder->getPathInfo(obj->visitablePos());
 
 		if(paths.empty())
 			continue;
@@ -223,7 +220,7 @@ void ObjectClusterizer::clusterize()
 
 					if(!vstd::contains(cluster->objects, obj))
 					{
-						float priority = ai->nullkiller->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
+						float priority = ai->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
 
 						cluster->addObject(obj, path, priority);
 

+ 6 - 2
AI/Nullkiller/Analyzers/ObjectClusterizer.h

@@ -51,6 +51,7 @@ private:
 	ObjectCluster nearObjects;
 	ObjectCluster farObjects;
 	std::map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>> blockedObjects;
+	const Nullkiller * ai;
 
 public:
 	void clusterize();
@@ -59,8 +60,11 @@ public:
 	std::vector<std::shared_ptr<ObjectCluster>> getLockedClusters() const;
 	const CGObjectInstance * getBlocker(const AIPath & path) const;
 
-	ObjectClusterizer()
-		:nearObjects(), farObjects(), blockedObjects()
+	ObjectClusterizer(const Nullkiller * ai)
+		:nearObjects(), farObjects(), blockedObjects(), ai(ai)
 	{
 	}
+
+private:
+	bool shouldVisitObject(const CGObjectInstance * obj) const;
 };

+ 1 - 2
AI/Nullkiller/Behaviors/BuildingBehavior.cpp

@@ -10,16 +10,15 @@
 #include "StdInc.h"
 #include "BuildingBehavior.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/BuyArmy.h"
+#include "../Goals/BuildThis.h"
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 3 - 5
AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "BuyArmyBehavior.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/BuyArmy.h"
 #include "../Engine/Nullkiller.h"
@@ -19,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -55,7 +53,7 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
 			{
 				targetHero = town->visitingHero.get();
 
-				if(ai->ah->howManyReinforcementsCanGet(targetHero, town->getUpperArmy()))
+				if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(targetHero, town->getUpperArmy()))
 				{
 					tasks.push_back(sptr(VisitTile(town->visitablePos()).sethero(targetHero).setpriority(5)));
 
@@ -63,10 +61,10 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
 				}
 			}*/
 
-			auto reinforcement = ai->ah->howManyReinforcementsCanBuy(targetHero, town);
+			auto reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(targetHero, town);
 
 			if(reinforcement)
-				reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
+				reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
 
 			if(reinforcement)
 			{

+ 122 - 4
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -10,17 +10,16 @@
 #include "StdInc.h"
 #include "../VCAI.h"
 #include "../Engine/Nullkiller.h"
-#include "../AIhelper.h"
 #include "../Goals/Composition.h"
 #include "../Goals/ExecuteHeroChain.h"
 #include "CaptureObjectsBehavior.h"
 #include "../AIUtility.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
+#include "../../../lib/CModHandler.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -81,7 +80,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
 		auto hero = path.targetHero;
 		auto danger = path.getTotalDanger();
 
-		if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
+		if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
 			continue;
 
 		auto firstBlockedAction = path.getFirstBlockedAction();
@@ -169,7 +168,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 
 			const int3 pos = objToVisit->visitablePos();
 
-			auto paths = ai->ah->getPathsToTile(pos);
+			auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
 			std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
 			std::shared_ptr<ExecuteHeroChain> closestWay;
 					
@@ -214,3 +213,122 @@ bool CaptureObjectsBehavior::shouldVisitObject(const CGObjectInstance * obj) con
 
 	return true;
 }
+
+bool CaptureObjectsBehavior::shouldVisit(HeroPtr h, const CGObjectInstance * obj)
+{
+	switch(obj->ID)
+	{
+	case Obj::TOWN:
+	case Obj::HERO: //never visit our heroes at random
+		return obj->tempOwner != h->tempOwner; //do not visit our towns at random
+	case Obj::BORDER_GATE:
+	{
+		for(auto q : ai->myCb->getMyQuests())
+		{
+			if(q.obj == obj)
+			{
+				return false; // do not visit guards or gates when wandering
+			}
+		}
+		return true; //we don't have this quest yet
+	}
+	case Obj::BORDERGUARD: //open borderguard if possible
+		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
+	case Obj::SEER_HUT:
+	case Obj::QUEST_GUARD:
+	{
+		for(auto q : ai->myCb->getMyQuests())
+		{
+			if(q.obj == obj)
+			{
+				if(q.quest->checkQuest(h.h))
+					return true; //we completed the quest
+				else
+					return false; //we can't complete this quest
+			}
+		}
+		return true; //we don't have this quest yet
+	}
+	case Obj::CREATURE_GENERATOR1:
+	{
+		if(obj->tempOwner != h->tempOwner)
+			return true; //flag just in case
+
+		const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
+
+		for(auto level : d->creatures)
+		{
+			for(auto c : level.second)
+			{
+				if(level.first
+					&& h->getSlotFor(CreatureID(c)) != SlotID()
+					&& cb->getResourceAmount().canAfford(c.toCreature()->cost))
+				{
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+	case Obj::HILL_FORT:
+	{
+		for(auto slot : h->Slots())
+		{
+			if(slot.second->type->upgrades.size())
+				return true; //TODO: check price?
+		}
+		return false;
+	}
+	case Obj::MONOLITH_ONE_WAY_ENTRANCE:
+	case Obj::MONOLITH_ONE_WAY_EXIT:
+	case Obj::MONOLITH_TWO_WAY:
+	case Obj::WHIRLPOOL:
+		return false;
+	case Obj::SCHOOL_OF_MAGIC:
+	case Obj::SCHOOL_OF_WAR:
+	{
+		if(cb->getResourceAmount(Res::GOLD) < 1000)
+			return false;
+		break;
+	}
+	case Obj::LIBRARY_OF_ENLIGHTENMENT:
+		if(h->level < 12)
+			return false;
+		break;
+	case Obj::TREE_OF_KNOWLEDGE:
+	{
+		if(ai->nullkiller->heroManager->getHeroRole(h) == HeroRole::SCOUT)
+			return false;
+
+		TResources myRes = cb->getResourceAmount();
+		if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
+			return false;
+		break;
+	}
+	case Obj::MAGIC_WELL:
+		return h->mana < h->manaLimit();
+	case Obj::PRISON:
+		return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
+	case Obj::TAVERN:
+	{
+		//TODO: make AI actually recruit heroes
+		//TODO: only on request
+		if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
+			return false;
+		else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
+			return false;
+		break;
+	}
+	case Obj::BOAT:
+		return false;
+		//Boats are handled by pathfinder
+	case Obj::EYE_OF_MAGI:
+		return false; //this object is useless to visit, but could be visited indefinitely
+	}
+
+	if(obj->wasVisited(*h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
+		return false;
+
+	return true;
+}

+ 1 - 0
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h

@@ -68,6 +68,7 @@ namespace Goals
 
 	private:
 		bool shouldVisitObject(const CGObjectInstance * obj) const;
+		static bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
 	};
 }
 

+ 1 - 3
AI/Nullkiller/Behaviors/ClusterBehavior.cpp

@@ -11,7 +11,6 @@
 #include "ClusterBehavior.h"
 #include "../VCAI.h"
 #include "../Engine/Nullkiller.h"
-#include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/UnlockCluster.h"
 #include "../Goals/Composition.h"
@@ -19,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -44,7 +42,7 @@ Goals::TGoalVec ClusterBehavior::decompose() const
 Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster> cluster) const
 {
 	auto center = cluster->calculateCenter();
-	auto paths = ai->ah->getPathsToTile(center->visitablePos());
+	auto paths = ai->nullkiller->pathfinder->getPathInfo(center->visitablePos());
 	auto blockerPos = cluster->blocker->visitablePos();
 	std::vector<AIPath> blockerPaths;
 

+ 0 - 2
AI/Nullkiller/Behaviors/CompleteQuestBehavior.cpp

@@ -11,13 +11,11 @@
 #include "CompleteQuestBehavior.h"
 #include "CaptureObjectsBehavior.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 5 - 6
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -11,10 +11,10 @@
 #include "DefenceBehavior.h"
 #include "../VCAI.h"
 #include "../Engine/Nullkiller.h"
-#include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/BuyArmy.h"
 #include "../Goals/ExecuteHeroChain.h"
+#include "../Goals/RecruitHero.h"
 #include "../Goals/DismissHero.h"
 #include "../Goals/ExchangeSwapTownHeroes.h"
 #include "lib/mapping/CMap.h" //for victory conditions
@@ -22,7 +22,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -98,7 +97,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 		return;
 	}
 	
-	uint64_t reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
+	uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
 
 	if(reinforcement)
 	{
@@ -106,7 +105,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 		tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
 	}
 
-	auto paths = ai->ah->getPathsToTile(town->visitablePos());
+	auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
 
 	for(auto & treat : treats)
 	{
@@ -168,7 +167,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 						{
 							if(ai->nullkiller->isHeroLocked(existingHero)
 								|| existingHero->getArmyStrength() > hero->getArmyStrength()
-								|| ai->ah->getHeroRole(existingHero) == HeroRole::MAIN
+								|| ai->nullkiller->heroManager->getHeroRole(existingHero) == HeroRole::MAIN
 								|| existingHero->movement
 								|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
 								continue;
@@ -262,7 +261,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 	/*for(auto & treat : treats)
 	{
-		auto paths = ai->ah->getPathsToTile(treat.hero->visitablePos());
+		auto paths = ai->nullkiller->pathfinder->getPathInfo(treat.hero->visitablePos());
 
 		for(AIPath & path : paths)
 		{

+ 5 - 7
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "../VCAI.h"
 #include "../Engine/Nullkiller.h"
-#include "../AIhelper.h"
 #include "../Goals/ExecuteHeroChain.h"
 #include "GatherArmyBehavior.h"
 #include "../AIUtility.h"
@@ -19,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -41,7 +39,7 @@ Goals::TGoalVec GatherArmyBehavior::decompose() const
 
 	for(const CGHeroInstance * hero : heroes)
 	{
-		if(ai->ah->getHeroRole(hero) == HeroRole::MAIN
+		if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::MAIN
 			&& hero->getArmyStrength() >= 300)
 		{
 			vstd::concatenate(tasks, deliverArmyToHero(hero));
@@ -74,7 +72,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		return tasks;
 	}
 
-	auto paths = ai->ah->getPathsToTile(pos);
+	auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
 	std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
 
 #if AI_TRACE_LEVEL >= 1
@@ -106,7 +104,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 			continue;
 		}
 
-		float armyValue = (float)ai->ah->howManyReinforcementsCanGet(hero, path.heroArmy) / hero->getArmyStrength();
+		float armyValue = (float)ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero, path.heroArmy) / hero->getArmyStrength();
 
 		// avoid transferring very small amount of army
 		if(armyValue < 0.1f)
@@ -166,7 +164,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 	logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
 #endif
 	
-	auto paths = ai->ah->getPathsToTile(pos);
+	auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
 	std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
 
 #if AI_TRACE_LEVEL >= 1
@@ -203,7 +201,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 			continue;
 		}
 
-		auto upgrade = ai->ah->calculateCreateresUpgrade(path.heroArmy, upgrader, availableResources);
+		auto upgrade = ai->nullkiller->armyManager->calculateCreateresUpgrade(path.heroArmy, upgrader, availableResources);
 		auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
 
 		if(armyValue < 0.1f || upgrade.upgradeValue < 300) // avoid small upgrades

+ 0 - 2
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "RecruitHeroBehavior.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/RecruitHero.h"
 #include "../Goals/ExecuteHeroChain.h"
@@ -19,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 10 - 12
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "StartupBehavior.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/RecruitHero.h"
 #include "../Goals/ExecuteHeroChain.h"
@@ -22,7 +21,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -46,7 +44,7 @@ const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPa
 
 const CGHeroInstance * getNearestHero(const CGTownInstance * town)
 {
-	auto paths = ai->ah->getPathsToTile(town->visitablePos());
+	auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
 
 	if(paths.empty())
 		return nullptr;
@@ -114,12 +112,12 @@ Goals::TGoalVec StartupBehavior::decompose() const
 		startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
 		{
 			if(town->garrisonHero)
-				return ai->ah->evaluateHero(town->garrisonHero.get());
+				return ai->nullkiller->heroManager->evaluateHero(town->garrisonHero.get());
 
 			auto closestHero = getNearestHero(town);
 
 			if(closestHero)
-				return ai->ah->evaluateHero(closestHero);
+				return ai->nullkiller->heroManager->evaluateHero(closestHero);
 
 			return 0;
 		});
@@ -132,9 +130,9 @@ Goals::TGoalVec StartupBehavior::decompose() const
 	{
 		if(!startupTown->visitingHero)
 		{
-			if(ai->ah->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
+			if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
 			{
-				auto paths = ai->ah->getPathsToTile(startupTown->visitablePos());
+				auto paths = ai->nullkiller->pathfinder->getPathInfo(startupTown->visitablePos());
 
 				if(paths.size())
 				{
@@ -147,22 +145,22 @@ Goals::TGoalVec StartupBehavior::decompose() const
 		else
 		{
 			auto visitingHero = startupTown->visitingHero.get();
-			auto visitingHeroScore = ai->ah->evaluateHero(visitingHero);
+			auto visitingHeroScore = ai->nullkiller->heroManager->evaluateHero(visitingHero);
 				
 			if(startupTown->garrisonHero)
 			{
 				auto garrisonHero = startupTown->garrisonHero.get();
-				auto garrisonHeroScore = ai->ah->evaluateHero(garrisonHero);
+				auto garrisonHeroScore = ai->nullkiller->heroManager->evaluateHero(garrisonHero);
 
 				if(visitingHeroScore > garrisonHeroScore
-					|| ai->ah->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->ah->getHeroRole(visitingHero) == HeroRole::MAIN)
+					|| ai->nullkiller->heroManager->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->nullkiller->heroManager->getHeroRole(visitingHero) == HeroRole::MAIN)
 				{
-					if(canRecruitHero || ai->ah->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
+					if(canRecruitHero || ai->nullkiller->armyManager->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
 					{
 						tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero, HeroLockedReason::STARTUP).setpriority(100)));
 					}
 				}
-				else if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
+				else if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
 				{
 					tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero, HeroLockedReason::STARTUP).setpriority(100)));
 				}

+ 10 - 14
AI/Nullkiller/CMakeLists.txt

@@ -4,7 +4,6 @@ set(VCAI_SRCS
 		Pathfinding/AIPathfinderConfig.cpp
 		Pathfinding/AIPathfinder.cpp
 		Pathfinding/AINodeStorage.cpp
-		Pathfinding/PathfindingManager.cpp
 		Pathfinding/Actors.cpp
 		Pathfinding/Actions/SpecialAction.cpp
 		Pathfinding/Actions/BattleAction.cpp
@@ -15,12 +14,11 @@ set(VCAI_SRCS
 		Pathfinding/Rules/AIMovementToDestinationRule.cpp
 		Pathfinding/Rules/AIPreviousNodeRule.cpp
 		AIUtility.cpp
-		AIhelper.cpp
-		ArmyManager.cpp
-		HeroManager.cpp
-		MapObjectsEvaluator.cpp
-		FuzzyEngines.cpp
-		FuzzyHelper.cpp
+		Analyzers/ArmyManager.cpp
+		Analyzers/HeroManager.cpp
+		Engine/FuzzyEngines.cpp
+		Engine/FuzzyHelper.cpp
+		Engine/AIMemory.cpp
 		Goals/AbstractGoal.cpp
 		Goals/Composition.cpp
 		Goals/BuildBoat.cpp
@@ -61,7 +59,6 @@ set(VCAI_HEADERS
 		Pathfinding/AIPathfinderConfig.h
 		Pathfinding/AIPathfinder.h
 		Pathfinding/AINodeStorage.h
-		Pathfinding/PathfindingManager.h
 		Pathfinding/Actors.h
 		Pathfinding/Actions/SpecialAction.h
 		Pathfinding/Actions/BattleAction.h
@@ -72,12 +69,11 @@ set(VCAI_HEADERS
 		Pathfinding/Rules/AIMovementToDestinationRule.h
 		Pathfinding/Rules/AIPreviousNodeRule.h
 		AIUtility.h
-		AIhelper.h
-		ArmyManager.h
-		HeroManager.h
-		MapObjectsEvaluator.h
-		FuzzyEngines.h
-		FuzzyHelper.h
+		Analyzers/ArmyManager.h
+		Analyzers/HeroManager.h
+		Engine/FuzzyEngines.h
+		Engine/FuzzyHelper.h
+		Engine/AIMemory.h
 		Goals/AbstractGoal.h
 		Goals/CGoal.h
 		Goals/Composition.h

+ 104 - 0
AI/Nullkiller/Engine/AIMemory.cpp

@@ -0,0 +1,104 @@
+/*
+* AIMemory.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "AIMemory.h"
+#include "../../../lib/mapObjects/MapObjects.h"
+
+void AIMemory::removeFromMemory(const CGObjectInstance * obj)
+{
+	vstd::erase_if_present(visitableObjs, obj);
+	vstd::erase_if_present(alreadyVisited, obj);
+
+	//TODO: Find better way to handle hero boat removal
+	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
+	{
+		if(hero->boat)
+		{
+			vstd::erase_if_present(visitableObjs, hero->boat);
+			vstd::erase_if_present(alreadyVisited, hero->boat);
+		}
+	}
+}
+
+void AIMemory::removeFromMemory(ObjectIdRef obj)
+{
+	auto matchesId = [&](const CGObjectInstance * hlpObj) -> bool
+	{
+		return hlpObj->id == obj.id;
+	};
+
+	vstd::erase_if(visitableObjs, matchesId);
+	vstd::erase_if(alreadyVisited, matchesId);
+}
+
+void AIMemory::addSubterraneanGate(const CGObjectInstance * entrance, const CGObjectInstance * exit)
+{
+	knownSubterraneanGates[entrance] = exit;
+	knownSubterraneanGates[exit] = entrance;
+
+	logAi->trace(
+		"Found a pair of subterranean gates between %s and %s!",
+		entrance->visitablePos().toString(),
+		exit->visitablePos().toString());
+}
+
+void AIMemory::addVisitableObject(const CGObjectInstance * obj)
+{
+	visitableObjs.insert(obj);
+
+	// All teleport objects seen automatically assigned to appropriate channels
+	auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
+	if(teleportObj)
+	{
+		CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
+	}
+}
+
+void AIMemory::markObjectVisited(const CGObjectInstance * obj)
+{
+	if(!obj)
+		return;
+	
+	// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
+	if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
+		return;
+	
+	if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
+		return;
+	
+	if(obj->ID == Obj::MONSTER)
+		return;
+
+	alreadyVisited.insert(obj);
+}
+
+void AIMemory::markObjectUnvisited(const CGObjectInstance * obj)
+{
+	vstd::erase_if_present(alreadyVisited, obj);
+}
+
+bool AIMemory::wasVisited(const CGObjectInstance * obj) const
+{
+	return vstd::contains(alreadyVisited, obj);
+}
+
+void AIMemory::removeInvisibleObjects(CCallback * cb)
+{
+	auto shouldBeErased = [&](const CGObjectInstance * obj) -> bool
+	{
+		if(obj)
+			return !cb->getObj(obj->id, false); // no verbose output needed as we check object visibility
+		else
+			return true;
+	};
+
+	vstd::erase_if(visitableObjs, shouldBeErased);
+	vstd::erase_if(alreadyVisited, shouldBeErased);
+}

+ 36 - 0
AI/Nullkiller/Engine/AIMemory.h

@@ -0,0 +1,36 @@
+/*
+* AIMemory.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "PriorityEvaluator.h"
+#include "../Analyzers/DangerHitMapAnalyzer.h"
+#include "../Analyzers/BuildAnalyzer.h"
+#include "../Analyzers/ArmyManager.h"
+#include "../Analyzers/HeroManager.h"
+#include "../Analyzers/ObjectClusterizer.h"
+#include "../Goals/AbstractGoal.h"
+
+class AIMemory
+{
+public:
+	std::set<const CGObjectInstance *> visitableObjs;
+	std::set<const CGObjectInstance *> alreadyVisited;
+	std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
+	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
+
+	void removeFromMemory(const CGObjectInstance * obj);
+	void removeFromMemory(ObjectIdRef obj);
+	void addSubterraneanGate(const CGObjectInstance * entrance, const CGObjectInstance * exit);
+	void addVisitableObject(const CGObjectInstance * obj);
+	void markObjectVisited(const CGObjectInstance * obj);
+	void markObjectUnvisited(const CGObjectInstance * obj);
+	bool wasVisited(const CGObjectInstance * obj) const;
+	void removeInvisibleObjects(CCallback * cb);
+};

+ 1 - 3
AI/Nullkiller/FuzzyEngines.cpp → AI/Nullkiller/Engine/FuzzyEngines.cpp

@@ -9,17 +9,15 @@
 */
 #include "StdInc.h"
 #include "FuzzyEngines.h"
-#include "Goals/Goals.h"
+#include "../Goals/Goals.h"
 
 #include "../../lib/mapObjects/MapObjects.h"
 #include "VCAI.h"
-#include "MapObjectsEvaluator.h"
 
 #define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
 #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
 
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 engineBase::engineBase()
 {

+ 0 - 0
AI/Nullkiller/FuzzyEngines.h → AI/Nullkiller/Engine/FuzzyEngines.h


+ 15 - 23
AI/Nullkiller/FuzzyHelper.cpp → AI/Nullkiller/Engine/FuzzyHelper.cpp

@@ -12,13 +12,7 @@
 
 #include "../../lib/mapObjects/CommonConstructors.h"
 #include "Goals/Goals.h"
-#include "VCAI.h"
-
-FuzzyHelper * fh;
-
-extern boost::thread_specific_ptr<VCAI> ai;
-extern boost::thread_specific_ptr<CCallback> cb;
-
+#include "Nullkiller.h"
 
 ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
 {
@@ -39,14 +33,9 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
 
 }
 
-ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
+ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, bool checkGuards)
 {
-	return evaluateDanger(tile, visitor, ai.get());
-}
-
-ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards)
-{
-	auto cb = ai->myCb;
+	auto cb = ai->cb.get();
 	const TerrainTile * t = cb->getTile(tile, false);
 	if(!t) //we can know about guard but can't check its tile (the edge of fow)
 		return 190000000; //MUCH
@@ -66,7 +55,7 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
 
 	if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
 	{
-		objectDanger = evaluateDanger(dangerousObject, ai); //unguarded objects can also be dangerous or unhandled
+		objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled
 		if(objectDanger)
 		{
 			//TODO: don't downcast objects AI shouldn't know about!
@@ -80,15 +69,18 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
 		if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
 		{
 			//check guard on the other side of the gate
-			auto it = ai->knownSubterraneanGates.find(dangerousObject);
-			if(it != ai->knownSubterraneanGates.end())
+			auto it = ai->memory->knownSubterraneanGates.find(dangerousObject);
+			if(it != ai->memory->knownSubterraneanGates.end())
 			{
 				auto guards = cb->getGuardingCreatures(it->second->visitablePos());
+
 				for(auto cre : guards)
 				{
-					float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
+					float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(
+						visitor, 
+						dynamic_cast<const CArmedInstance *>(cre));
 
-					vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage);
+					vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantage);
 				}
 			}
 		}
@@ -101,7 +93,7 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
 		{
 			float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
 
-			vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage); //we are interested in strongest monster around
+			vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantage); //we are interested in strongest monster around
 		}
 	}
 
@@ -109,9 +101,9 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
 	return std::max(objectDanger, guardDanger);
 }
 
-ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
+ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
 {
-	auto cb = ai->myCb;
+	auto cb = ai->cb.get();
 
 	if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat
 		return 0;
@@ -126,7 +118,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
 	case Obj::ARTIFACT:
 	case Obj::RESOURCE:
 	{
-		if(!vstd::contains(ai->alreadyVisited, obj))
+		if(!vstd::contains(ai->memory->alreadyVisited, obj))
 		{
 			return 0;
 		}

+ 8 - 4
AI/Nullkiller/FuzzyHelper.h → AI/Nullkiller/Engine/FuzzyHelper.h

@@ -11,15 +11,19 @@
 #include "FuzzyEngines.h"
 
 class CBank;
+class Nullkiller;
 
 class DLL_EXPORT FuzzyHelper
 {
-public:
+private:
+	const Nullkiller * ai;
 	TacticalAdvantageEngine tacticalAdvantageEngine;
 
+public:
+	FuzzyHelper(const Nullkiller * ai) : ai(ai), tacticalAdvantageEngine() {}
+
 	ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
 
-	ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
-	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true);
-	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
+	ui64 evaluateDanger(const CGObjectInstance * obj);
+	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, bool checkGuards = true);
 };

+ 21 - 8
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "Nullkiller.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../Behaviors/CaptureObjectsBehavior.h"
 #include "../Behaviors/RecruitHeroBehavior.h"
 #include "../Behaviors/BuyArmyBehavior.h"
@@ -28,10 +27,22 @@ using namespace Goals;
 
 Nullkiller::Nullkiller()
 {
-	priorityEvaluator.reset(new PriorityEvaluator());
-	dangerHitMap.reset(new DangerHitMapAnalyzer());
+	memory.reset(new AIMemory());
+}
+
+void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
+{
+	this->cb = cb;
+	this->playerID = playerID;
+
+	priorityEvaluator.reset(new PriorityEvaluator(this));
+	dangerHitMap.reset(new DangerHitMapAnalyzer(this));
 	buildAnalyzer.reset(new BuildAnalyzer());
-	objectClusterizer.reset(new ObjectClusterizer());
+	objectClusterizer.reset(new ObjectClusterizer(this));
+	dangerEvaluator.reset(new FuzzyHelper(this));
+	pathfinder.reset(new AIPathfinder(cb.get(), this));
+	armyManager.reset(new ArmyManager(cb.get(), this));
+	heroManager.reset(new HeroManager(cb.get(), this));
 }
 
 Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const
@@ -130,6 +141,7 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
 
 void Nullkiller::resetAiState()
 {
+	playerID = ai->playerID;
 	lockedHeroes.clear();
 	dangerHitMap->reset();
 }
@@ -138,11 +150,11 @@ void Nullkiller::updateAiState()
 {
 	activeHero = nullptr;
 
-	ai->validateVisitableObjs();
+	memory->removeInvisibleObjects(cb.get());
 	dangerHitMap->updateHitMap();
 
 	// TODO: move to hero manager
-	auto activeHeroes = ai->getMyHeroes();
+	auto activeHeroes = cb->getHeroesInfo();
 
 	vstd::erase_if(activeHeroes, [this](const HeroPtr & hero) -> bool
 	{
@@ -151,8 +163,9 @@ void Nullkiller::updateAiState()
 		return lockedReason == HeroLockedReason::DEFENCE;
 	});
 
-	ai->ah->updatePaths(activeHeroes, true);
-	ai->ah->update();
+	pathfinder->updatePaths(activeHeroes, true);
+	heroManager->update();
+	armyManager->update();
 
 	objectClusterizer->clusterize();
 	buildAnalyzer->update();

+ 12 - 0
AI/Nullkiller/Engine/Nullkiller.h

@@ -10,8 +10,12 @@
 #pragma once
 
 #include "PriorityEvaluator.h"
+#include "FuzzyHelper.h"
+#include "AIMemory.h"
 #include "../Analyzers/DangerHitMapAnalyzer.h"
 #include "../Analyzers/BuildAnalyzer.h"
+#include "../Analyzers/ArmyManager.h"
+#include "../Analyzers/HeroManager.h"
 #include "../Analyzers/ObjectClusterizer.h"
 #include "../Goals/AbstractGoal.h"
 
@@ -41,8 +45,16 @@ public:
 	std::unique_ptr<BuildAnalyzer> buildAnalyzer;
 	std::unique_ptr<ObjectClusterizer> objectClusterizer;
 	std::unique_ptr<PriorityEvaluator> priorityEvaluator;
+	std::unique_ptr<AIPathfinder> pathfinder;
+	std::unique_ptr<HeroManager> heroManager;
+	std::unique_ptr<ArmyManager> armyManager;
+	std::unique_ptr<AIMemory> memory;
+	std::unique_ptr<FuzzyHelper> dangerEvaluator;
+	PlayerColor playerID;
+	std::shared_ptr<CCallback> cb;
 
 	Nullkiller();
+	void init(std::shared_ptr<CCallback> cb, PlayerColor playerID);
 	void makeTurn();
 	bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
 	bool isHeroLocked(const CGHeroInstance * hero) const;

+ 60 - 56
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -19,25 +19,17 @@
 #include "../../../lib/VCMI_Lib.h"
 #include "../../../CCallback.h"
 #include "../../../lib/filesystem/Filesystem.h"
-#include "../VCAI.h"
-#include "../AIhelper.h"
 #include "../Engine/Nullkiller.h"
 #include "../Goals/ExecuteHeroChain.h"
 #include "../Goals/UnlockCluster.h"
+#include "../Goals/BuildThis.h"
 
 #define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
 #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
 
-struct BankConfig;
-class CBankInfo;
-class Engine;
-class InputVariable;
-class CGTownInstance;
-
 extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
 
-EvaluationContext::EvaluationContext()
+EvaluationContext::EvaluationContext(const Nullkiller * ai)
 	: movementCost(0.0),
 	manaCost(0),
 	danger(0),
@@ -50,7 +42,8 @@ EvaluationContext::EvaluationContext()
 	armyLossPersentage(0),
 	heroRole(HeroRole::SCOUT),
 	turn(0),
-	strategicalValue(0)
+	strategicalValue(0),
+	evaluator(ai)
 {
 }
 
@@ -194,7 +187,11 @@ uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
 	return statsValue > classValue ? statsValue : classValue;
 }
 
-uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army, bool checkGold)
+uint64_t RewardEvaluator::getArmyReward(
+	const CGObjectInstance * target,
+	const CGHeroInstance * hero,
+	const CCreatureSet * army,
+	bool checkGold) const
 {
 	const float enemyArmyEliminationRewardRatio = 0.5f;
 
@@ -206,7 +203,7 @@ uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * h
 	case Obj::TOWN:
 		return target->tempOwner == PlayerColor::NEUTRAL ? 1000 : 10000;
 	case Obj::HILL_FORT:
-		return ai->ah->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeValue;
+		return ai->armyManager->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeValue;
 	case Obj::CREATURE_BANK:
 		return getCreatureBankArmyReward(target, hero);
 	case Obj::CREATURE_GENERATOR1:
@@ -232,7 +229,7 @@ uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * h
 	}
 }
 
-int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army)
+int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const
 {
 	if(!target)
 		return 0;
@@ -240,7 +237,7 @@ int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, co
 	switch(target->ID)
 	{
 	case Obj::HILL_FORT:
-		return ai->ah->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeCost[Res::GOLD];
+		return ai->armyManager->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeCost[Res::GOLD];
 	case Obj::SCHOOL_OF_MAGIC:
 	case Obj::SCHOOL_OF_WAR:
 		return 1000;
@@ -256,11 +253,9 @@ int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, co
 	}
 }
 
-float getStrategicalValue(const CGObjectInstance * target);
-
-float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
+float RewardEvaluator::getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const
 {
-	auto objectsUnderTreat = ai->nullkiller->dangerHitMap->getOneTurnAccessibleObjects(enemy);
+	auto objectsUnderTreat = ai->dangerHitMap->getOneTurnAccessibleObjects(enemy);
 	float objectValue = 0;
 
 	for(auto obj : objectsUnderTreat)
@@ -271,10 +266,10 @@ float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
 	return objectValue / 2.0f + enemy->level / 15.0f;
 }
 
-float getResourceRequirementStrength(int resType)
+float RewardEvaluator::getResourceRequirementStrength(int resType) const
 {
-	TResources requiredResources = ai->nullkiller->buildAnalyzer->getResourcesRequiredNow();
-	TResources dailyIncome = ai->nullkiller->buildAnalyzer->getDailyIncome();
+	TResources requiredResources = ai->buildAnalyzer->getResourcesRequiredNow();
+	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
 
 	if(requiredResources[resType] == 0)
 		return 0;
@@ -287,10 +282,10 @@ float getResourceRequirementStrength(int resType)
 	return std::min(ratio, 1.0f);
 }
 
-float getTotalResourceRequirementStrength(int resType)
+float RewardEvaluator::getTotalResourceRequirementStrength(int resType) const
 {
-	TResources requiredResources = ai->nullkiller->buildAnalyzer->getTotalResourcesRequired();
-	TResources dailyIncome = ai->nullkiller->buildAnalyzer->getDailyIncome();
+	TResources requiredResources = ai->buildAnalyzer->getTotalResourcesRequired();
+	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
 
 	if(requiredResources[resType] == 0)
 		return 0;
@@ -302,7 +297,7 @@ float getTotalResourceRequirementStrength(int resType)
 	return std::min(ratio, 1.0f);
 }
 
-float getStrategicalValue(const CGObjectInstance * target)
+float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) const
 {
 	if(!target)
 		return 0;
@@ -310,7 +305,9 @@ float getStrategicalValue(const CGObjectInstance * target)
 	switch(target->ID)
 	{
 	case Obj::MINE:
-		return target->subID == Res::GOLD ? 0.5f : 0.02f * getTotalResourceRequirementStrength(target->subID) + 0.02f * getResourceRequirementStrength(target->subID);
+		return target->subID == Res::GOLD 
+			? 0.5f 
+			: 0.02f * getTotalResourceRequirementStrength(target->subID) + 0.02f * getResourceRequirementStrength(target->subID);
 
 	case Obj::RESOURCE:
 		return target->subID == Res::GOLD ? 0 : 0.1f * getResourceRequirementStrength(target->subID);
@@ -330,7 +327,7 @@ float getStrategicalValue(const CGObjectInstance * target)
 	}
 }
 
-float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role)
+float RewardEvaluator::evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role) const
 {
 	if(!hut->wasVisited(hero->tempOwner))
 		return role == HeroRole::SCOUT ? 2 : 0;
@@ -341,12 +338,12 @@ float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance *
 		|| hero->secSkills.size() >= GameConstants::SKILL_PER_HERO)
 		return 0;
 
-	auto score = ai->ah->evaluateSecSkill(skill, hero);
+	auto score = ai->heroManager->evaluateSecSkill(skill, hero);
 
 	return score >= 2 ? (role == HeroRole::MAIN ? 10 : 4) : score;
 }
 
-float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role)
+float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const
 {
 	const float enemyHeroEliminationSkillRewardRatio = 0.5f;
 
@@ -397,7 +394,7 @@ int32_t getArmyCost(const CArmedInstance * army)
 }
 
 /// Gets aproximated reward in gold. Daily income is multiplied by 5
-int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero)
+int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const
 {
 	if(!target)
 		return 0;
@@ -469,7 +466,7 @@ public:
 
 		for(auto pair : costsPerHero)
 		{
-			auto role = ai->ah->getHeroRole(pair.first);
+			auto role = evaluationContext.evaluator.ai->heroManager->getHeroRole(pair.first);
 
 			evaluationContext.movementCostByRole[role] += pair.second;
 		}
@@ -482,12 +479,12 @@ public:
 		auto army = path.heroArmy;
 
 		vstd::amax(evaluationContext.armyLossPersentage, path.getTotalArmyLoss() / (double)path.getHeroStrength());
-		evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
-		evaluationContext.goldReward += getGoldReward(target, hero);
-		evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold);
-		evaluationContext.skillReward += getSkillReward(target, hero, evaluationContext.heroRole);
-		evaluationContext.strategicalValue += getStrategicalValue(target);
-		evaluationContext.goldCost += getGoldCost(target, hero, army);
+		evaluationContext.heroRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(heroPtr);
+		evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero);
+		evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold);
+		evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, evaluationContext.heroRole);
+		evaluationContext.strategicalValue += evaluationContext.evaluator.getStrategicalValue(target);
+		evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
 		vstd::amax(evaluationContext.turn, path.turn());
 	}
 };
@@ -504,7 +501,7 @@ public:
 		std::shared_ptr<ObjectCluster> cluster = clusterGoal.getCluster();
 
 		auto hero = clusterGoal.hero.get();
-		auto role = ai->ah->getHeroRole(clusterGoal.hero);
+		auto role = evaluationContext.evaluator.ai->heroManager->getHeroRole(clusterGoal.hero);
 
 		std::vector<std::pair<const CGObjectInstance *, ObjectInfo>> objects(cluster->objects.begin(), cluster->objects.end());
 
@@ -522,11 +519,11 @@ public:
 			bool checkGold = objInfo.second.danger == 0;
 			auto army = hero;
 
-			evaluationContext.goldReward += getGoldReward(target, hero) / boost;
-			evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold) / boost;
-			evaluationContext.skillReward += getSkillReward(target, hero, role) / boost;
-			evaluationContext.strategicalValue += getStrategicalValue(target) / boost;
-			evaluationContext.goldCost += getGoldCost(target, hero, army) / boost;
+			evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero) / boost;
+			evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold) / boost;
+			evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, role) / boost;
+			evaluationContext.strategicalValue += evaluationContext.evaluator.getStrategicalValue(target) / boost;
+			evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army) / boost;
 			evaluationContext.movementCostByRole[role] += objInfo.second.movementCost / boost;
 			evaluationContext.movementCost += objInfo.second.movementCost / boost;
 
@@ -565,27 +562,34 @@ public:
 			if(bi.baseCreatureID == bi.creatureID)
 			{
 				evaluationContext.strategicalValue += 0.5f + 0.1f * bi.creatureLevel / (float)bi.prerequisitesCount;
-				evaluationContext.armyReward += ai->ah->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
+				evaluationContext.armyReward += evaluationContext.evaluator.ai->armyManager->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
 			}
 			else
 			{
-				auto creaturesToUpgrade = ai->ah->getTotalCreaturesAvailable(bi.baseCreatureID);
-				auto upgradedPower = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
-
 				evaluationContext.strategicalValue += 0.05f * bi.creatureLevel / (float)bi.prerequisitesCount;
-
-				if(!ai->nullkiller->buildAnalyzer->hasAnyBuilding(buildThis.town->alignment, bi.id))
-					evaluationContext.armyReward += upgradedPower - creaturesToUpgrade.power;
+				evaluationContext.armyReward += evaluationContext.evaluator.getUpgradeArmyReward(buildThis.town, bi);
 			}
 		}
 		else
 		{
-			evaluationContext.strategicalValue += ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
+			evaluationContext.strategicalValue += evaluationContext.evaluator.ai->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
 		}
 	}
 };
 
-PriorityEvaluator::PriorityEvaluator()
+uint64_t RewardEvaluator::getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const
+{
+	if(ai->buildAnalyzer->hasAnyBuilding(town->alignment, bi.id))
+		return 0;
+
+	auto creaturesToUpgrade = ai->armyManager->getTotalCreaturesAvailable(bi.baseCreatureID);
+	auto upgradedPower = ai->armyManager->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
+
+	return upgradedPower - creaturesToUpgrade.power;
+}
+
+PriorityEvaluator::PriorityEvaluator(const Nullkiller * ai)
+	:ai(ai)
 {
 	initVisitTile();
 	evaluationContextBuilders.push_back(std::make_shared<ExecuteHeroChainEvaluationContextBuilder>());
@@ -596,7 +600,7 @@ PriorityEvaluator::PriorityEvaluator()
 EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
 {
 	Goals::TGoalVec parts;
-	EvaluationContext context;
+	EvaluationContext context(ai);
 
 	if(goal->goalType == Goals::COMPOSITION)
 	{
@@ -652,8 +656,8 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
 		rewardTypeVariable->setValue(rewardType);
 		closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
 		strategicalValueVariable->setValue(evaluationContext.strategicalValue);
-		goldPreasureVariable->setValue(ai->nullkiller->buildAnalyzer->getGoldPreasure());
-		goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->nullkiller->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
+		goldPreasureVariable->setValue(ai->buildAnalyzer->getGoldPreasure());
+		goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
 		turnVariable->setValue(evaluationContext.turn);
 
 		engine->process();

+ 29 - 3
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -9,7 +9,28 @@
 */
 #pragma once
 #include "fl/Headers.h"
-#include "../Goals/Goals.h"
+#include "../Goals/CGoal.h"
+
+class BuildingInfo;
+
+class RewardEvaluator
+{
+public:
+	const Nullkiller * ai;
+
+	RewardEvaluator(const Nullkiller * ai) : ai(ai) {}
+
+	uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army, bool checkGold) const;
+	int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
+	float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const;
+	float getResourceRequirementStrength(int resType) const;
+	float getStrategicalValue(const CGObjectInstance * target) const;
+	float getTotalResourceRequirementStrength(int resType) const;
+	float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role) const;
+	float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;
+	int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;
+	uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;
+};
 
 struct DLL_EXPORT EvaluationContext
 {
@@ -26,8 +47,9 @@ struct DLL_EXPORT EvaluationContext
 	float strategicalValue;
 	HeroRole heroRole;
 	uint8_t turn;
+	RewardEvaluator evaluator;
 
-	EvaluationContext();
+	EvaluationContext(const Nullkiller * ai);
 };
 
 class IEvaluationContextBuilder
@@ -36,16 +58,20 @@ public:
 	virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal goal) const = 0;
 };
 
+class Nullkiller;
+
 class PriorityEvaluator
 {
 public:
-	PriorityEvaluator();
+	PriorityEvaluator(const Nullkiller * ai);
 	~PriorityEvaluator();
 	void initVisitTile();
 
 	float evaluate(Goals::TSubgoal task);
 
 private:
+	const Nullkiller * ai;
+
 	fl::Engine * engine;
 	fl::InputVariable * armyLossPersentageVariable;
 	fl::InputVariable * heroRoleVariable;

+ 0 - 3
AI/Nullkiller/Goals/AbstractGoal.cpp

@@ -10,15 +10,12 @@
 #include "StdInc.h"
 #include "AbstractGoal.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/AdventureSpellCast.cpp

@@ -10,14 +10,11 @@
 #include "StdInc.h"
 #include "AdventureSpellCast.h"
 #include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/BuildBoat.cpp

@@ -10,15 +10,12 @@
 #include "StdInc.h"
 #include "BuildBoat.h"
 #include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../Behaviors/CaptureObjectsBehavior.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/BuildThis.cpp

@@ -11,8 +11,6 @@
 #include "BuildThis.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
@@ -20,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 3 - 4
AI/Nullkiller/Goals/BuyArmy.cpp

@@ -9,14 +9,13 @@
 */
 #include "StdInc.h"
 #include "BuyArmy.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapObjects/CGTownInstance.h"
+#include "VCAI.h"
+#include "Engine/Nullkiller.h"
 
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 
@@ -37,7 +36,7 @@ void BuyArmy::accept(VCAI * ai)
 
 	auto upgradeSuccessfull = ai->makePossibleUpgrades(town);
 
-	auto armyToBuy = ai->ah->getArmyAvailableToBuy(town->getUpperArmy(), town);
+	auto armyToBuy = ai->nullkiller->armyManager->getArmyAvailableToBuy(town->getUpperArmy(), town);
 
 	if(armyToBuy.empty())
 	{

+ 5 - 8
AI/Nullkiller/Goals/CGoal.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "AbstractGoal.h"
-#include "../FuzzyHelper.h"
 #include "../VCAI.h"
 
 struct HeroPtr;
@@ -92,13 +91,6 @@ namespace Goals
 		{
 		}
 
-		///Visitor pattern
-		//TODO: make accept work for std::shared_ptr... somehow
-		virtual void accept(VCAI * ai) override //unhandled goal will report standard error
-		{
-			ai->tryRealize(*((T *)this));
-		}
-
 		T & setpriority(float p)
 		{
 			ITask::priority = p;
@@ -134,5 +126,10 @@ namespace Goals
 		{
 			return "Invalid";
 		}
+
+		virtual void accept(VCAI * ai) override
+		{
+			throw cannotFulfillGoalException("Can not fulfill Invalid goal!");
+		}
 	};
 }

+ 0 - 3
AI/Nullkiller/Goals/CollectRes.cpp

@@ -11,8 +11,6 @@
 #include "Goals.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
@@ -20,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/Composition.cpp

@@ -11,8 +11,6 @@
 #include "Composition.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
@@ -20,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 1
AI/Nullkiller/Goals/DigAtTile.cpp

@@ -15,7 +15,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/DismissHero.cpp

@@ -10,14 +10,11 @@
 #include "StdInc.h"
 #include "DismissHero.h"
 #include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp

@@ -11,15 +11,12 @@
 #include "ExchangeSwapTownHeroes.h"
 #include "ExecuteHeroChain.h"
 #include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -10,15 +10,12 @@
 #include "StdInc.h"
 #include "ExecuteHeroChain.h"
 #include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/GatherTroops.cpp

@@ -11,8 +11,6 @@
 #include "Goals.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
@@ -20,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 1
AI/Nullkiller/Goals/GetArtOfType.cpp

@@ -15,7 +15,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 3
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -11,8 +11,6 @@
 #include "Goals.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
@@ -20,7 +18,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 1
AI/Nullkiller/Goals/UnlockCluster.cpp

@@ -15,7 +15,6 @@
 
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
 
 using namespace Goals;
 

+ 0 - 140
AI/Nullkiller/MapObjectsEvaluator.cpp

@@ -1,140 +0,0 @@
-#include "StdInc.h"
-#include "MapObjectsEvaluator.h"
-#include "../../lib/GameConstants.h"
-#include "../../lib/VCMI_Lib.h"
-#include "../../lib/CCreatureHandler.h"
-#include "../../lib/CHeroHandler.h"
-#include "../../lib/mapObjects/CGHeroInstance.h"
-#include "../../lib/mapObjects/CGTownInstance.h"
-#include "../../lib/mapObjects/MiscObjects.h"
-#include "../../lib/CRandomGenerator.h"
-#include "../../lib/spells/CSpellHandler.h"
-
-MapObjectsEvaluator & MapObjectsEvaluator::getInstance()
-{
-	static std::unique_ptr<MapObjectsEvaluator> singletonInstance;
-	if(singletonInstance == nullptr)
-		singletonInstance.reset(new MapObjectsEvaluator());
-
-	return *(singletonInstance.get());
-}
-
-MapObjectsEvaluator::MapObjectsEvaluator()
-{
-	for(auto primaryID : VLC->objtypeh->knownObjects())
-	{
-		for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
-		{
-			auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
-			if(!handler->isStaticObject())
-			{
-				if(handler->getAiValue() != boost::none)
-				{
-					objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = handler->getAiValue().get();
-				}
-				else if(VLC->objtypeh->getObjGroupAiValue(primaryID) != boost::none) //if value is not initialized - fallback to default value for this object family if it exists
-				{
-					objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = VLC->objtypeh->getObjGroupAiValue(primaryID).get();
-				}
-				else //some default handling when aiValue not found, objects that require advanced properties (unavailable from handler) get their value calculated in getObjectValue
-				{
-					objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = 0;
-				}
-			}
-		}	
-	}
-}
-
-boost::optional<int> MapObjectsEvaluator::getObjectValue(int primaryID, int secondaryID) const
-{
-	CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
-	auto object = objectDatabase.find(internalIdentifier);
-	if(object != objectDatabase.end())
-		return object->second;
-
-	logGlobal->trace("Unknown object for AI, ID: " + std::to_string(primaryID) + ", SubID: " + std::to_string(secondaryID));
-	return boost::optional<int>();
-}
-
-boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance * obj) const
-{
-	if(obj->ID == Obj::HERO)
-	{
-		//special case handling: in-game heroes have hero ID as object subID, but when reading configs available hero object subID's are hero classes
-		auto hero = dynamic_cast<const CGHeroInstance*>(obj);
-		return getObjectValue(obj->ID, hero->type->heroClass->id);
-	}
-	else if(obj->ID == Obj::PRISON)
-	{
-		//special case: in-game prison subID is captured hero ID, but config has one subID with index 0 for normal prison - use that one
-		return getObjectValue(obj->ID, 0);
-	}
-	else if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)
-	{
-		auto dwelling = dynamic_cast<const CGDwelling *>(obj);
-		int aiValue = 0;
-		for(auto & creLevel : dwelling->creatures)
-		{
-			for(auto & creatureID : creLevel.second)
-			{
-				auto creature = VLC->creh->creatures[creatureID];
-				aiValue += (creature->AIValue * creature->growth);
-			}
-		}
-		return aiValue;
-	}
-	else if(obj->ID == Obj::ARTIFACT)
-	{
-		auto artifactObject = dynamic_cast<const CGArtifact *>(obj);
-		switch(artifactObject->storedArtifact->artType->aClass)
-		{
-		case CArtifact::EartClass::ART_TREASURE:
-			return 2000;
-		case CArtifact::EartClass::ART_MINOR:
-			return 5000;
-		case CArtifact::EartClass::ART_MAJOR:
-			return 10000;
-		case CArtifact::EartClass::ART_RELIC:
-			return 20000;
-		case CArtifact::EartClass::ART_SPECIAL:
-			return 20000;
-		default:
-			return 0; //invalid artifact class
-		}
-	}
-	else if(obj->ID == Obj::SPELL_SCROLL)
-	{
-		auto scrollObject = dynamic_cast<const CGArtifact *>(obj);
-		auto spell = scrollObject->storedArtifact->getGivenSpellID().toSpell();
-		if(spell)
-		{
-			switch(spell->getLevel())
-			{
-			case 0: return 0; //scroll with creature ability? Let's assume it is useless
-			case 1: return 1000;
-			case 2: return 2000;
-			case 3: return 5000;
-			case 4: return 10000;
-			case 5: return 20000;
-			default: logAi->warn("AI detected spell scroll with spell level %s", spell->getLevel());
-			}
-		}
-		else
-			logAi->warn("AI found spell scroll with invalid spell ID: %s", scrollObject->storedArtifact->getGivenSpellID());
-	}
-
-	return getObjectValue(obj->ID, obj->subID);
-}
-
-void MapObjectsEvaluator::addObjectData(int primaryID, int secondaryID, int value) //by current design it updates value if already in AI database
-{
-	CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
-	objectDatabase[internalIdentifier] = value;
-}
-
-void MapObjectsEvaluator::removeObjectData(int primaryID, int secondaryID)
-{
-	CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
-	vstd::erase_if_present(objectDatabase, internalIdentifier);
-}
-

+ 0 - 26
AI/Nullkiller/MapObjectsEvaluator.h

@@ -1,26 +0,0 @@
-/*
-* MapObjectsEvaluator.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-#pragma once
-#include "../../lib/mapObjects/CObjectClassesHandler.h"
-
-class MapObjectsEvaluator
-{
-private:
-	std::map<CompoundMapObjectID, int> objectDatabase; //value for each object type
-
-public:
-	MapObjectsEvaluator();
-	static MapObjectsEvaluator & getInstance();
-	boost::optional<int> getObjectValue(int primaryID, int secondaryID) const;
-	boost::optional<int> getObjectValue(const CGObjectInstance * obj) const;
-	void addObjectData(int primaryID, int secondaryID, int value);
-	void removeObjectData(int primaryID, int secondaryID);
-};
-

+ 5 - 8
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -22,11 +22,11 @@
 /// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
 boost::multi_array<AIPathNode, 5> nodes;
 
-AINodeStorage::AINodeStorage(const int3 & Sizes)
-	: sizes(Sizes)
+AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
+	: sizes(Sizes), ai(ai), cb(ai->cb.get())
 {
 	nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]);
-	dangerEvaluator.reset(new FuzzyHelper());
+	dangerEvaluator.reset(new FuzzyHelper(ai));
 }
 
 AINodeStorage::~AINodeStorage() = default;
@@ -614,17 +614,14 @@ const std::set<const CGHeroInstance *> AINodeStorage::getAllHeroes() const
 	return heroes;
 }
 
-void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
+void AINodeStorage::setHeroes(std::vector<const CGHeroInstance *> heroes)
 {
-	cb = _ai->myCb.get();
-	ai = _ai;
-
 	playerID = ai->playerID;
 
 	for(auto & hero : heroes)
 	{
 		uint64_t mask = 1 << actors.size();
-		auto actor = std::make_shared<HeroActor>(hero.get(), mask, ai);
+		auto actor = std::make_shared<HeroActor>(hero, mask, ai);
 
 		if(hero->tempOwner != ai->playerID)
 		{

+ 5 - 5
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -16,7 +16,7 @@
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../AIUtility.h"
-#include "../FuzzyHelper.h"
+#include "../Engine/FuzzyHelper.h"
 #include "../Goals/AbstractGoal.h"
 #include "Actions/SpecialAction.h"
 #include "Actors.h"
@@ -107,7 +107,7 @@ private:
 	int3 sizes;
 
 	const CPlayerSpecificInfoCallback * cb;
-	const VCAI * ai;
+	const Nullkiller * ai;
 	std::unique_ptr<FuzzyHelper> dangerEvaluator;
 	std::vector<std::shared_ptr<ChainActor>> actors;
 	std::vector<CGPathNode *> heroChain;
@@ -121,7 +121,7 @@ public:
 	/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
 	static const int NUM_CHAINS = 10 * GameConstants::MAX_HEROES_PER_PLAYER;
 	
-	AINodeStorage(const int3 & sizes);
+	AINodeStorage(const Nullkiller * ai, const int3 & sizes);
 	~AINodeStorage();
 
 	void initialize(const PathfinderOptions & options, const CGameState * gs) override;
@@ -176,7 +176,7 @@ public:
 	boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
 	std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
 	bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
-	void setHeroes(std::vector<HeroPtr> heroes, const VCAI * ai);
+	void setHeroes(std::vector<const CGHeroInstance *> heroes);
 	void setTownsAndDwellings(
 		const std::vector<const CGTownInstance *> & towns,
 		const std::set<const CGObjectInstance *> & visitableObjs);
@@ -188,7 +188,7 @@ public:
 
 	uint64_t evaluateDanger(const int3 &  tile, const CGHeroInstance * hero, bool checkGuards) const
 	{
-		return dangerEvaluator->evaluateDanger(tile, hero, ai, checkGuards);
+		return dangerEvaluator->evaluateDanger(tile, hero, checkGuards);
 	}
 
 	uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const

+ 6 - 5
AI/Nullkiller/Pathfinding/AIPathfinder.cpp

@@ -12,10 +12,11 @@
 #include "AIPathfinderConfig.h"
 #include "../../../CCallback.h"
 #include "../../../lib/mapping/CMap.h"
+#include "../Engine/Nullkiller.h"
 
 std::shared_ptr<AINodeStorage> AIPathfinder::storage;
 
-AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai)
+AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai)
 	:cb(cb), ai(ai)
 {
 }
@@ -43,22 +44,22 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile) const
 	return storage->getChainInfo(tile, !tileInfo->isWater());
 }
 
-void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
+void AIPathfinder::updatePaths(std::vector<const CGHeroInstance *> heroes, bool useHeroChain)
 {
 	if(!storage)
 	{
-		storage = std::make_shared<AINodeStorage>(cb->getMapSize());
+		storage.reset(new AINodeStorage(ai, cb->getMapSize()));
 	}
 
 	logAi->debug("Recalculate all paths");
 	int pass = 0;
 
 	storage->clear();
-	storage->setHeroes(heroes, ai);
+	storage->setHeroes(heroes);
 
 	if(useHeroChain)
 	{
-		storage->setTownsAndDwellings(cb->getTownsInfo(), ai->visitableObjs);
+		storage->setTownsAndDwellings(cb->getTownsInfo(), ai->memory->visitableObjs);
 	}
 
 	auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);

+ 5 - 4
AI/Nullkiller/Pathfinding/AIPathfinder.h

@@ -12,19 +12,20 @@
 
 #include "AINodeStorage.h"
 #include "../AIUtility.h"
-#include "../VCAI.h"
+
+class Nullkiller;
 
 class AIPathfinder
 {
 private:
 	static std::shared_ptr<AINodeStorage> storage;
 	CPlayerSpecificInfoCallback * cb;
-	VCAI * ai;
+	Nullkiller * ai;
 
 public:
-	AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
+	AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
 	std::vector<AIPath> getPathInfo(const int3 & tile) const;
 	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
-	void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false);
+	void updatePaths(std::vector<const CGHeroInstance *> heroes, bool useHeroChain = false);
 	void init();
 };

+ 3 - 2
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp

@@ -13,12 +13,13 @@
 #include "Rules/AIMovementAfterDestinationRule.h"
 #include "Rules/AIMovementToDestinationRule.h"
 #include "Rules/AIPreviousNodeRule.h"
+#include "../Engine//Nullkiller.h"
 
 namespace AIPathfinding
 {
 	std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset(
 		CPlayerSpecificInfoCallback * cb,
-		VCAI * ai,
+		Nullkiller * ai,
 		std::shared_ptr<AINodeStorage> nodeStorage)
 	{
 		std::vector<std::shared_ptr<IPathfindingRule>> rules = {
@@ -35,7 +36,7 @@ namespace AIPathfinding
 
 	AIPathfinderConfig::AIPathfinderConfig(
 		CPlayerSpecificInfoCallback * cb,
-		VCAI * ai,
+		Nullkiller * ai,
 		std::shared_ptr<AINodeStorage> nodeStorage)
 		:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)), aiNodeStorage(nodeStorage)
 	{

+ 3 - 2
AI/Nullkiller/Pathfinding/AIPathfinderConfig.h

@@ -11,7 +11,8 @@
 #pragma once
 
 #include "AINodeStorage.h"
-#include "../VCAI.h"
+
+class Nullkiller;
 
 namespace AIPathfinding
 {
@@ -24,7 +25,7 @@ namespace AIPathfinding
 	public:
 		AIPathfinderConfig(
 			CPlayerSpecificInfoCallback * cb,
-			VCAI * ai,
+			Nullkiller * ai,
 			std::shared_ptr<AINodeStorage> nodeStorage);
 
 		virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;

+ 9 - 9
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -10,7 +10,7 @@
 #include "StdInc.h"
 #include "Actors.h"
 #include "../VCAI.h"
-#include "../AIhelper.h"
+#include "../Engine/Nullkiller.h"
 #include "../../../CCallback.h"
 #include "../../../lib/mapping/CMap.h"
 #include "../../../lib/mapObjects/MapObjects.h"
@@ -72,7 +72,7 @@ std::string ObjectActor::toString() const
 	return object->getObjectName() + " at " + object->visitablePos().toString();
 }
 
-HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai)
+HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const Nullkiller * ai)
 	:ChainActor(hero, chainMask)
 {
 	exchangeMap = new HeroExchangeMap(this, ai);
@@ -83,7 +83,7 @@ HeroActor::HeroActor(
 	const ChainActor * carrier, 
 	const ChainActor * other, 
 	const CCreatureSet * army, 
-	const VCAI * ai)
+	const Nullkiller * ai)
 	:ChainActor(carrier, other,	army)
 {
 	exchangeMap = new HeroExchangeMap(this, ai);
@@ -167,7 +167,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
 
 		if(result)
 		{
-			TResources resources = ai->myCb->getResourceAmount();
+			TResources resources = ai->cb->getResourceAmount();
 
 			if(!resources.canAfford(actor->armyCost + other->armyCost))
 			{
@@ -181,7 +181,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
 				return;
 			}
 
-			auto upgradeInfo = ai->ah->calculateCreateresUpgrade(
+			auto upgradeInfo = ai->armyManager->calculateCreateresUpgrade(
 				actor->creatureSet, 
 				other->getActorObject(),
 				resources - actor->armyCost - other->armyCost);
@@ -189,7 +189,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
 			uint64_t reinforcment = upgradeInfo.upgradeValue;
 			
 			if(other->creatureSet->Slots().size())
-				reinforcment += ai->ah->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
+				reinforcment += ai->armyManager->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
 
 #if PATHFINDER_TRACE_LEVEL >= 2
 			logAi->trace(
@@ -240,7 +240,7 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
 		result = exchangeMap.at(other);
 	else 
 	{
-		TResources availableResources = ai->myCb->getResourceAmount() - actor->armyCost - other->armyCost;
+		TResources availableResources = ai->cb->getResourceAmount() - actor->armyCost - other->armyCost;
 		CCreatureSet * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);
 		CCreatureSet * newArmy;
 		
@@ -270,7 +270,7 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
 
 CCreatureSet * HeroExchangeMap::tryUpgrade(const CCreatureSet * army, const CGObjectInstance * upgrader, TResources resources) const
 {
-	auto upgradeInfo = ai->ah->calculateCreateresUpgrade(army, upgrader, resources);
+	auto upgradeInfo = ai->armyManager->calculateCreateresUpgrade(army, upgrader, resources);
 
 	if(!upgradeInfo.upgradeValue)
 		return nullptr;
@@ -290,7 +290,7 @@ CCreatureSet * HeroExchangeMap::tryUpgrade(const CCreatureSet * army, const CGOb
 CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
 {
 	CCreatureSet * target = new HeroExchangeArmy();
-	auto bestArmy = ai->ah->getBestArmy(army1, army2);
+	auto bestArmy = ai->armyManager->getBestArmy(army1, army2);
 
 	for(auto & slotInfo : bestArmy)
 	{

+ 5 - 5
AI/Nullkiller/Pathfinding/Actors.h

@@ -16,7 +16,7 @@
 #include "Actions/SpecialAction.h"
 
 class HeroActor;
-class VCAI;
+class Nullkiller;
 
 class HeroExchangeArmy : public CCreatureSet
 {
@@ -72,10 +72,10 @@ private:
 	const HeroActor * actor;
 	std::map<const ChainActor *, HeroActor *> exchangeMap;
 	std::map<const ChainActor *, bool> canExchangeCache;
-	const VCAI * ai;
+	const Nullkiller * ai;
 
 public:
-	HeroExchangeMap(const HeroActor * actor, const VCAI * ai)
+	HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai)
 		:actor(actor), ai(ai)
 	{
 	}
@@ -105,8 +105,8 @@ public:
 	std::shared_ptr<SpecialAction> exchangeAction;
 	// chain flags, can be combined meaning hero exchange and so on
 
-	HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai);
-	HeroActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * army, const VCAI * ai);
+	HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const Nullkiller * ai);
+	HeroActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * army, const Nullkiller * ai);
 
 	virtual bool canExchange(const ChainActor * other) const override;
 

+ 0 - 62
AI/Nullkiller/Pathfinding/PathfindingManager.h

@@ -1,62 +0,0 @@
-/*
-* PathfindingManager.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#pragma once
-
-#include "../VCAI.h"
-#include "AINodeStorage.h"
-
-class DLL_EXPORT IPathfindingManager
-{
-public:
-	virtual ~IPathfindingManager() = default;
-	virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
-	virtual void setAI(VCAI * AI) = 0;
-
-	virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0;
-	virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0;
-	virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0;
-};
-
-class DLL_EXPORT PathfindingManager : public IPathfindingManager
-{
-	friend class AIhelper;
-
-private:
-	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
-	VCAI * ai;
-	std::unique_ptr<AIPathfinder> pathfinder;
-
-public:
-	PathfindingManager() = default;
-	PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
-
-	std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
-	std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
-	void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
-
-	STRONG_INLINE
-	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
-	{
-		return pathfinder->isTileAccessible(hero, tile);
-	}
-
-private:
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
-
-	Goals::TGoalVec findPaths(
-		crint3 dest,
-		bool allowGatherArmy,
-		HeroPtr hero,
-		const std::function<Goals::TSubgoal(int3)> goalFactory) const;
-
-	Goals::TSubgoal clearWayTo(HeroPtr hero, int3 firstTileToGet) const;
-};

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

@@ -9,10 +9,11 @@
 */
 #include "StdInc.h"
 #include "AILayerTransitionRule.h"
+#include "../../Engine/Nullkiller.h"
 
 namespace AIPathfinding
 {
-	AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
+	AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage)
 		:cb(cb), ai(ai), nodeStorage(nodeStorage)
 	{
 		setup();
@@ -54,7 +55,7 @@ namespace AIPathfinding
 				shipyards.push_back(t);
 		}
 
-		for(const CGObjectInstance * obj : ai->visitableObjs)
+		for(const CGObjectInstance * obj : ai->memory->visitableObjs)
 		{
 			if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
 			{

+ 2 - 2
AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.h

@@ -23,13 +23,13 @@ namespace AIPathfinding
 	{
 	private:
 		CPlayerSpecificInfoCallback * cb;
-		VCAI * ai;
+		Nullkiller * ai;
 		std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
 		std::shared_ptr<AINodeStorage> nodeStorage;
 		std::map<const CGHeroInstance *, std::shared_ptr<const SummonBoatAction>> summonableVirtualBoats;
 
 	public:
-		AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);
+		AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage);
 
 		virtual void process(
 			const PathNodeInfo & source,

+ 0 - 431
AI/Nullkiller/SectorMap.cpp

@@ -1,431 +0,0 @@
-/*
-* SectorMap.cpp, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#include "StdInc.h"
-#include "SectorMap.h"
-#include "VCAI.h"
-
-#include "../../CCallback.h"
-#include "../../lib/mapping/CMap.h"
-#include "../../lib/mapObjects/MapObjects.h"
-#include "../../lib/CPathfinder.h"
-#include "../../lib/CGameState.h"
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-
-SectorMap::SectorMap()
-{
-	update();
-}
-
-SectorMap::SectorMap(HeroPtr h)
-{
-	update();
-	makeParentBFS(h->visitablePos());
-}
-
-bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t)
-{
-	if (t->blocked && !t->visitable)
-	{
-		sec = NOT_AVAILABLE;
-		return true;
-	}
-
-	return false;
-}
-
-bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos)
-{
-	return markIfBlocked(sec, pos, getTile(pos));
-}
-
-void SectorMap::update()
-{
-	visibleTiles = cb->getAllVisibleTiles();
-	auto shape = visibleTiles->shape();
-	sector.resize(boost::extents[shape[0]][shape[1]][shape[2]]);
-
-	clear();
-	int curSector = 3; //0 is invisible, 1 is not explored
-
-	CCallback * cbp = cb.get(); //optimization
-	foreach_tile_pos([&](crint3 pos)
-	{
-		if (retrieveTile(pos) == NOT_CHECKED)
-		{
-			if (!markIfBlocked(retrieveTile(pos), pos))
-				exploreNewSector(pos, curSector++, cbp);
-		}
-	});
-	valid = true;
-}
-
-SectorMap::TSectorID & SectorMap::retrieveTileN(SectorMap::TSectorArray & a, const int3 & pos)
-{
-	return a[pos.x][pos.y][pos.z];
-}
-
-const SectorMap::TSectorID & SectorMap::retrieveTileN(const SectorMap::TSectorArray & a, const int3 & pos)
-{
-	return a[pos.x][pos.y][pos.z];
-}
-
-void SectorMap::clear()
-{
-	//TODO: rotate to [z][x][y]
-	auto fow = cb->getVisibilityMap();
-	//TODO: any magic to automate this? will need array->array conversion
-	//std::transform(fow.begin(), fow.end(), sector.begin(), [](const ui8 &f) -> unsigned short
-	//{
-	//	return f; //type conversion
-	//});
-	auto width = fow.size();
-	auto height = fow.front().size();
-	auto depth = fow.front().front().size();
-	for (size_t x = 0; x < width; x++)
-	{
-		for (size_t y = 0; y < height; y++)
-		{
-			for (size_t z = 0; z < depth; z++)
-				sector[x][y][z] = fow[x][y][z];
-		}
-	}
-	valid = false;
-}
-
-void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
-{
-	Sector & s = infoOnSectors[num];
-	s.id = num;
-	s.water = getTile(pos)->isWater();
-
-	std::queue<int3> toVisit;
-	toVisit.push(pos);
-	while (!toVisit.empty())
-	{
-		int3 curPos = toVisit.front();
-		toVisit.pop();
-		TSectorID & sec = retrieveTile(curPos);
-		if (sec == NOT_CHECKED)
-		{
-			const TerrainTile * t = getTile(curPos);
-			if (!markIfBlocked(sec, curPos, t))
-			{
-				if (t->isWater() == s.water) //sector is only-water or only-land
-				{
-					sec = num;
-					s.tiles.push_back(curPos);
-					foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos)
-					{
-						if (retrieveTile(neighPos) == NOT_CHECKED)
-						{
-							toVisit.push(neighPos);
-							//parent[neighPos] = curPos;
-						}
-						const TerrainTile * nt = getTile(neighPos);
-						if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
-						{
-							s.embarkmentPoints.push_back(neighPos);
-						}
-					});
-
-					if (t->visitable)
-					{
-						auto obj = t->visitableObjects.front();
-						if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all
-							s.visitableObjs.push_back(obj);
-					}
-				}
-			}
-		}
-	}
-
-	vstd::removeDuplicates(s.embarkmentPoints);
-}
-
-void SectorMap::write(crstring fname)
-{
-	std::ofstream out(fname);
-	for (int k = 0; k < cb->getMapSize().z; k++)
-	{
-		for (int j = 0; j < cb->getMapSize().y; j++)
-		{
-			for (int i = 0; i < cb->getMapSize().x; i++)
-			{
-				out << (int)sector[i][j][k] << '\t';
-			}
-			out << std::endl;
-		}
-		out << std::endl;
-	}
-}
-
-/*
-this functions returns one target tile or invalid tile. We will use it to poll possible destinations
-For ship construction etc, another function (goal?) is needed
-*/
-int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
-{
-	int3 ret(-1, -1, -1);
-
-	int sourceSector = retrieveTile(h->visitablePos());
-	int destinationSector = retrieveTile(dst);
-
-	const Sector * src = &infoOnSectors[sourceSector];
-	const Sector * dest = &infoOnSectors[destinationSector];
-
-	if (sourceSector != destinationSector) //use ships, shipyards etc..
-	{
-		if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects
-			return dst;
-
-		std::map<const Sector *, const Sector *> preds;
-		std::queue<const Sector *> sectorQueue;
-		sectorQueue.push(src);
-		while (!sectorQueue.empty())
-		{
-			const Sector * s = sectorQueue.front();
-			sectorQueue.pop();
-
-			for (int3 ep : s->embarkmentPoints)
-			{
-				Sector * neigh = &infoOnSectors[retrieveTile(ep)];
-				//preds[s].push_back(neigh);
-				if (!preds[neigh])
-				{
-					preds[neigh] = s;
-					sectorQueue.push(neigh);
-				}
-			}
-		}
-
-		if (!preds[dest])
-		{
-			//write("test.txt");
-
-			return ret;
-			//throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
-		}
-
-		std::vector<const Sector *> toTraverse;
-		toTraverse.push_back(dest);
-		while (toTraverse.back() != src)
-		{
-			toTraverse.push_back(preds[toTraverse.back()]);
-		}
-
-		if (preds[dest])
-		{
-			//TODO: would be nice to find sectors in loop
-			const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2);
-
-			if (!src->water && sectorToReach->water) //embark
-			{
-				//embark on ship -> look for an EP with a boat
-				auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
-				{
-					const TerrainTile * t = getTile(pos);
-					if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
-					{
-						if (retrieveTile(pos) == sectorToReach->id)
-							return true;
-					}
-					return false;
-				});
-
-				if (firstEP != src->embarkmentPoints.end())
-				{
-					return *firstEP;
-				}
-				else
-				{
-					//we need to find a shipyard with an access to the desired sector's EP
-					//TODO what about Summon Boat spell?
-					std::vector<const IShipyard *> shipyards;
-					for (const CGTownInstance * t : cb->getTownsInfo())
-					{
-						if (t->hasBuilt(BuildingID::SHIPYARD))
-							shipyards.push_back(t);
-					}
-
-					for (const CGObjectInstance * obj : ai->getFlaggedObjects())
-					{
-						if (obj->ID != Obj::TOWN) //towns were handled in the previous loop
-						{
-							if (const IShipyard * shipyard = IShipyard::castFrom(obj))
-								shipyards.push_back(shipyard);
-						}
-					}
-
-					shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool
-					{
-						return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id;
-					}), shipyards.end());
-
-					if (!shipyards.size())
-					{
-						//TODO consider possibility of building shipyard in a town
-						return ret;
-
-						//throw cannotFulfillGoalException("There is no known shipyard!");
-					}
-
-					//we have only shipyards that possibly can build ships onto the appropriate EP
-					auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool
-					{
-						return s->o->tempOwner == ai->playerID;
-					});
-
-					if (ownedGoodShipyard != shipyards.end())
-					{
-						const IShipyard * s = *ownedGoodShipyard;
-						TResources shipCost;
-						s->getBoatCost(shipCost);
-						if (cb->getResourceAmount().canAfford(shipCost))
-						{
-							int3 ret = s->bestLocation();
-							cb->buildBoat(s); //TODO: move actions elsewhere
-							return ret;
-						}
-						else
-						{
-							//TODO gather res
-							return ret;
-
-							//throw cannotFulfillGoalException("Not enough resources to build a boat");
-						}
-					}
-					else
-					{
-						//TODO pick best shipyard to take over
-						return shipyards.front()->o->visitablePos();
-					}
-				}
-			}
-			else if (src->water && !sectorToReach->water)
-			{
-				//TODO
-				//disembark
-				return ret;
-			}
-			else //use subterranean gates - not needed since gates are now handled via Pathfinder
-			{
-				return ret;
-				//throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
-			}
-		}
-		else
-		{
-			return ret;
-			//throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
-		}
-	}
-	else //tiles are in same sector
-	{
-		return findFirstVisitableTile(h, dst);
-	}
-}
-
-int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst)
-{
-	int3 ret(-1, -1, -1);
-	int3 curtile = dst;
-
-	while (curtile != h->visitablePos())
-	{
-		auto topObj = cb->getTopObj(curtile);
-		if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
-		{
-			if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
-			{
-				logAi->warn("Another allied hero stands in our way");
-				return ret;
-			}
-		}
-		if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable())
-		{
-			return curtile;
-		}
-		else
-		{
-			auto i = parent.find(curtile);
-			if (i != parent.end())
-			{
-				assert(curtile != i->second);
-				curtile = i->second;
-			}
-			else
-			{
-				return ret;
-				//throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
-			}
-		}
-	}
-	return ret;
-}
-
-void SectorMap::makeParentBFS(crint3 source)
-{
-	parent.clear();
-
-	int mySector = retrieveTile(source);
-	std::queue<int3> toVisit;
-	toVisit.push(source);
-	while (!toVisit.empty())
-	{
-		int3 curPos = toVisit.front();
-		toVisit.pop();
-		TSectorID & sec = retrieveTile(curPos);
-		assert(sec == mySector); //consider only tiles from the same sector
-		UNUSED(sec);
-
-		foreach_neighbour(curPos, [&](crint3 neighPos)
-		{
-			if (retrieveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
-			{
-				if (cb->canMoveBetween(curPos, neighPos))
-				{
-					toVisit.push(neighPos);
-					parent[neighPos] = curPos;
-				}
-			}
-		});
-	}
-}
-
-SectorMap::TSectorID & SectorMap::retrieveTile(crint3 pos)
-{
-	return retrieveTileN(sector, pos);
-}
-
-TerrainTile * SectorMap::getTile(crint3 pos) const
-{
-	//out of bounds access should be handled by boost::multi_array
-	//still we cached this array to avoid any checks
-	return visibleTiles->operator[](pos.x)[pos.y][pos.z];
-}
-
-std::vector<const CGObjectInstance *> SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround)
-{
-	const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())];
-	if (sectorsAround)
-	{
-		std::vector<const CGObjectInstance *> ret;
-		for (auto embarkPoint : heroSector->embarkmentPoints)
-		{
-			const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)];
-			range::copy(embarkSector->visitableObjs, std::back_inserter(ret));
-		}
-		return ret;
-	}
-	return heroSector->visitableObjs;
-}

+ 0 - 70
AI/Nullkiller/SectorMap.h

@@ -1,70 +0,0 @@
-/*
-* SectorMap.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-
-#pragma once
-
-#include "AIUtility.h"
-
-enum
-{
-	NOT_VISIBLE = 0,
-	NOT_CHECKED = 1,
-	NOT_AVAILABLE
-};
-
-struct SectorMap
-{
-	//a sector is set of tiles that would be mutually reachable if all visitable objs would be passable (incl monsters)
-	struct Sector
-	{
-		int id;
-		std::vector<int3> tiles;
-		std::vector<int3> embarkmentPoints; //tiles of other sectors onto which we can (dis)embark
-		std::vector<const CGObjectInstance *> visitableObjs;
-		bool water; //all tiles of sector are land or water
-		Sector()
-		{
-			id = -1;
-			water = false;
-		}
-	};
-
-	typedef unsigned short TSectorID; //smaller than int to allow -1 value. Max number of sectors 65K should be enough for any proper map.
-	typedef boost::multi_array<TSectorID, 3> TSectorArray;
-
-	bool valid; //some kind of lazy eval
-	std::map<int3, int3> parent;
-	TSectorArray sector;
-	//std::vector<std::vector<std::vector<unsigned char>>> pathfinderSector;
-
-	std::map<int, Sector> infoOnSectors;
-	std::shared_ptr<boost::multi_array<TerrainTile *, 3>> visibleTiles;
-
-	SectorMap();
-	SectorMap(HeroPtr h);
-	void update();
-	void clear();
-	void exploreNewSector(crint3 pos, int num, CCallback * cbp);
-	void write(crstring fname);
-
-	bool markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t);
-	bool markIfBlocked(TSectorID & sec, crint3 pos);
-	TSectorID & retrieveTile(crint3 pos);
-	TSectorID & retrieveTileN(TSectorArray & vectors, const int3 & pos);
-	const TSectorID & retrieveTileN(const TSectorArray & vectors, const int3 & pos);
-	TerrainTile * getTile(crint3 pos) const;
-	std::vector<const CGObjectInstance *> getNearbyObjs(HeroPtr h, bool sectorsAround);
-
-	void makeParentBFS(crint3 source);
-
-	int3 firstTileToGet(HeroPtr h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way?
-	int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
-};

+ 31 - 429
AI/Nullkiller/VCAI.cpp

@@ -9,7 +9,6 @@
  */
 #include "StdInc.h"
 #include "VCAI.h"
-#include "FuzzyHelper.h"
 #include "Goals/Goals.h"
 
 #include "../../lib/UnlockGuard.h"
@@ -23,10 +22,8 @@
 #include "../../lib/serializer/BinarySerializer.h"
 #include "../../lib/serializer/BinaryDeserializer.h"
 
-#include "AIhelper.h"
 #include "Engine/Nullkiller.h"
 
-extern FuzzyHelper * fh;
 
 class CGVisitableOPW;
 
@@ -68,14 +65,11 @@ VCAI::VCAI()
 	makingTurn = nullptr;
 	destinationTeleport = ObjectInstanceID();
 	destinationTeleportPos = int3(-1);
-
-	ah = new AIhelper();
-	ah->setAI(this);
+	nullkiller.reset(new Nullkiller());
 }
 
 VCAI::~VCAI()
 {
-	delete ah;
 	LOG_TRACE(logAi);
 	finish();
 }
@@ -109,9 +103,7 @@ void VCAI::heroMoved(const TryMoveHero & details)
 			{
 				if(o1->ID == Obj::SUBTERRANEAN_GATE && o1->ID == o2->ID) // We need to only add subterranean gates in knownSubterraneanGates. Used for features not yet ported to use teleport channels
 				{
-					knownSubterraneanGates[o1] = o2;
-					knownSubterraneanGates[o2] = o1;
-					logAi->debug("Found a pair of subterranean gates between %s and %s!", from.toString(), to.toString());
+					nullkiller->memory->addSubterraneanGate(o1, o2);
 				}
 			}
 		}
@@ -238,12 +230,7 @@ void VCAI::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * vi
 
 	if(start && visitedObj) //we can end visit with null object, anyway
 	{
-		markObjectVisited(visitedObj);
-		//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..)
-		if (visitedObj->ID == Obj::HERO)
-		{
-			visitedHeroes[visitor].insert(HeroPtr(dynamic_cast<const CGHeroInstance *>(visitedObj)));
-		}
+		nullkiller->memory->markObjectVisited(visitedObj);
 	}
 
 	status.heroVisit(visitedObj, start);
@@ -259,8 +246,6 @@ void VCAI::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * to
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
-	//buildArmyIn(town);
-	//moveCreaturesToHero(town);
 }
 
 void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
@@ -268,7 +253,7 @@ void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 
-	validateVisitableObjs();
+	nullkiller->memory->removeInvisibleObjects(myCb.get());
 	clearPathsInfo();
 }
 
@@ -360,22 +345,7 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
 
-	vstd::erase_if_present(visitableObjs, obj);
-	vstd::erase_if_present(alreadyVisited, obj);
-
-	//TODO: Find better way to handle hero boat removal
-	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
-	{
-		if(hero->boat)
-		{
-			vstd::erase_if_present(visitableObjs, hero->boat);
-			vstd::erase_if_present(alreadyVisited, hero->boat);
-		}
-	}
-
-	//TODO
-	//there are other places where CGObjectinstance ptrs are stored...
-	//
+	nullkiller->memory->removeFromMemory(obj);
 
 	if(obj->ID == Obj::HERO && obj->tempOwner == playerID)
 	{
@@ -398,8 +368,6 @@ void VCAI::playerBonusChanged(const Bonus & bonus, bool gain)
 void VCAI::heroCreated(const CGHeroInstance * h)
 {
 	LOG_TRACE(logAi);
-	if(h->visitedTown)
-		townVisitsThisWeek[HeroPtr(h)].insert(h->visitedTown);
 	NET_EVENT_HANDLER;
 }
 
@@ -480,10 +448,10 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
 			if(relations == PlayerRelations::ENEMIES)
 			{
 				//we want to visit objects owned by oppponents
-				addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
-				vstd::erase_if_present(alreadyVisited, obj);
+				//addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
+				nullkiller->memory->markObjectUnvisited(obj);
 			}
-			else if(relations == PlayerRelations::SAME_PLAYER && obj->ID == Obj::TOWN && nullkiller)
+			else if(relations == PlayerRelations::SAME_PLAYER && obj->ID == Obj::TOWN)
 			{
 				// reevaluate defence for a new town
 				nullkiller->dangerHitMap->reset();
@@ -523,20 +491,12 @@ void VCAI::init(std::shared_ptr<CCallback> CB)
 	myCb = CB;
 	cbc = CB;
 
-	ah->init(CB.get());
-
-	NET_EVENT_HANDLER; //sets ah->rm->cb
+	NET_EVENT_HANDLER;
 	playerID = *myCb->getMyColor();
 	myCb->waitTillRealize = true;
 	myCb->unlockGsWhenWaiting = true;
 
-	if(!fh)
-		fh = new FuzzyHelper();
-
-	//if(playerID.getStr(false) == "blue")
-	{
-		nullkiller.reset(new Nullkiller());
-	}
+	nullkiller->init(CB, playerID);
 
 	retrieveVisitableObjs();
 }
@@ -554,7 +514,7 @@ void VCAI::heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill
 	LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
 	NET_EVENT_HANDLER;
 	status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->name % hero->level));
-	requestActionASAP([=](){ answerQuery(queryID, ah->selectBestSkill(hero, skills)); });
+	requestActionASAP([=](){ answerQuery(queryID, nullkiller->heroManager->selectBestSkill(hero, skills)); });
 }
 
 void VCAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
@@ -589,7 +549,7 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector<Compon
 
 				if(objType == Obj::ARTIFACT || objType == Obj::RESOURCE)
 				{
-					auto ratio = (float)fh->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength();
+					auto ratio = (float)nullkiller->dangerEvaluator->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength();
 					bool dangerUnknown = ratio == 0;
 					bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT);
 
@@ -615,7 +575,7 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector<Compon
 		// TODO: Find better way to understand it is Chest of Treasures
 		if(components.size() == 2
 			&& components.front().id == Component::RESOURCE
-			&& ah->getHeroRole(hero) != HeroRole::MAIN)
+			&& nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN)
 		{
 			sel = 1; // for now lets pick gold from a chest.
 		}
@@ -626,15 +586,13 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector<Compon
 
 void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
 {
-//	LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
 	NET_EVENT_HANDLER;
-	status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits")
-																			% exits.size()));
+	status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits") % exits.size()));
 
 	int choosenExit = -1;
 	if(impassable)
 	{
-		knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
+		nullkiller->memory->knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
 	}
 	else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid())
 	{
@@ -700,12 +658,8 @@ void VCAI::saveGame(BinarySerializer & h, const int version)
 {
 	LOG_TRACE_PARAMS(logAi, "version '%i'", version);
 	NET_EVENT_HANDLER;
-	validateVisitableObjs();
+	nullkiller->memory->removeInvisibleObjects(myCb.get());
 
-	#if 0
-	//disabled due to issue 2890
-	registerGoals(h);
-	#endif // 0
 	CAdventureAI::saveGame(h, version);
 	serializeInternal(h, version);
 }
@@ -760,7 +714,6 @@ void VCAI::makeTurn()
 
 	if(cb->getDate(Date::DAY_OF_WEEK) == 1)
 	{
-		townVisitsThisWeek.clear();
 		std::vector<const CGObjectInstance *> objs;
 		retrieveVisitableObjs(objs, true);
 
@@ -769,14 +722,11 @@ void VCAI::makeTurn()
 			if(isWeeklyRevisitable(obj))
 			{
 				addVisitableObj(obj);
-				vstd::erase_if_present(alreadyVisited, obj);
+				nullkiller->memory->markObjectUnvisited(obj);
 			}
 		}
 	}
 
-	markHeroAbleToExplore(primaryHero());
-	visitedHeroes.clear();
-
 	if(cb->getDate(Date::DAY) == 1)
 	{
 		retrieveVisitableObjs();
@@ -807,18 +757,6 @@ void VCAI::makeTurn()
 	endTurn();
 }
 
-std::vector<HeroPtr> VCAI::getMyHeroes() const
-{
-	std::vector<HeroPtr> ret;
-
-	for(auto h : cb->getHeroesInfo())
-	{
-		ret.push_back(h);
-	}
-
-	return ret;
-}
-
 void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 {
 	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString());
@@ -832,17 +770,8 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 		{
 			makePossibleUpgrades(h.get());
 
-			if(!nullkiller || !h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
-				moveCreaturesToHero(h->visitedTown);
-
-			townVisitsThisWeek[h].insert(h->visitedTown);
-
-			if(!ai->nullkiller)
-			{
-				ah->update();
-			}
-
-			if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST)
+			if(ai->nullkiller->heroManager->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook()
+				&& cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST)
 			{
 				if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
 					cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
@@ -867,7 +796,7 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme
 {
 	const CArmedInstance * armies[] = {destinationArmy, source};
 
-	auto bestArmy = ah->getSortedSlots(destinationArmy, source);
+	auto bestArmy = nullkiller->armyManager->getSortedSlots(destinationArmy, source);
 
 	//foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs
 	for(SlotID i = SlotID(0); i.getNum() < bestArmy.size() && i.validSlot(); i.advance(1)) //i-th strongest creature type will go to i-th slot
@@ -886,7 +815,7 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme
 						&& source->stacksCount() == 1
 						&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
 					{
-						auto weakest = ah->getWeakestCreature(bestArmy);
+						auto weakest = nullkiller->armyManager->getWeakestCreature(bestArmy);
 						
 						if(weakest->creature == targetCreature)
 						{
@@ -1040,54 +969,6 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
 	}
 }
 
-bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit)
-{
-	int3 op = obj->visitablePos();
-	auto paths = ah->getPathsToTile(h, op);
-
-	for(const auto & path : paths)
-	{
-		if(movementCostLimit && movementCostLimit.get() < path.movementCost())
-			return false;
-
-		if(isGoodForVisit(obj, h, path))
-			return true;
-	}
-
-	return false;
-}
-
-bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const
-{
-	const int3 pos = obj->visitablePos();
-	const int3 targetPos = path.firstTileToGet();
-	if (!targetPos.valid())
-		return false;
-	if (!isTileNotReserved(h.get(), targetPos))
-		return false;
-	if (obj->wasVisited(playerID))
-		return false;
-	if (cb->getPlayerRelations(playerID, obj->tempOwner) != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
-		return false; // Otherwise we flag or get weekly resources / creatures
-	if (!isSafeToVisit(h, pos))
-		return false;
-	if (!shouldVisit(h, obj))
-		return false;
-	if (vstd::contains(alreadyVisited, obj))
-		return false;
-	if (vstd::contains(reservedObjs, obj))
-		return false;
-
-	// TODO: looks extra if we already have AIPath
-	//if (!isAccessibleForHero(targetPos, h))
-	//	return false;
-
-	const CGObjectInstance * topObj = cb->getVisitableObjs(obj->visitablePos()).back(); //it may be hero visiting this obj
-																						//we don't try visiting object on which allied or owned hero stands
-																						// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
-	return !(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES); //all of the following is met
-}
-
 bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t) const
 {
 	if(t.valid())
@@ -1150,19 +1031,6 @@ void VCAI::waitTillFree()
 	status.waitTillFree();
 }
 
-void VCAI::markObjectVisited(const CGObjectInstance * obj)
-{
-	if(!obj)
-		return;
-	if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
-		return;
-	if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
-		return;
-	if(obj->ID == Obj::MONSTER)
-		return;
-	alreadyVisited.insert(obj);
-}
-
 void VCAI::markHeroUnableToExplore(HeroPtr h)
 {
 	heroesUnableToExplore.insert(h);
@@ -1180,40 +1048,6 @@ void VCAI::clearPathsInfo()
 	heroesUnableToExplore.clear();
 }
 
-void VCAI::validateVisitableObjs()
-{
-	std::string errorMsg;
-	auto shouldBeErased = [&](const CGObjectInstance * obj) -> bool
-	{
-		if(obj)
-			return !cb->getObj(obj->id, false); // no verbose output needed as we check object visibility
-		else
-			return true;
-	};
-
-	//errorMsg is captured by ref so lambda will take the new text
-	errorMsg = " shouldn't be on the visitable objects list!";
-	vstd::erase_if(visitableObjs, shouldBeErased);
-
-	//FIXME: how comes our own heroes become inaccessible?
-	vstd::erase_if(reservedHeroesMap, [](std::pair<HeroPtr, std::set<const CGObjectInstance *>> hp) -> bool
-	{
-		return !hp.first.get(true);
-	});
-	for(auto & p : reservedHeroesMap)
-	{
-		errorMsg = " shouldn't be on list for hero " + p.first->name + "!";
-		vstd::erase_if(p.second, shouldBeErased);
-	}
-
-	errorMsg = " shouldn't be on the reserved objs list!";
-	vstd::erase_if(reservedObjs, shouldBeErased);
-
-	//TODO overkill, hidden object should not be removed. However, we can't know if hidden object is erased from game.
-	errorMsg = " shouldn't be on the already visited objs list!";
-	vstd::erase_if(alreadyVisited, shouldBeErased);
-}
-
 void VCAI::retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned) const
 {
 	foreach_tile_pos([&](const int3 & pos)
@@ -1241,7 +1075,7 @@ void VCAI::retrieveVisitableObjs()
 std::vector<const CGObjectInstance *> VCAI::getFlaggedObjects() const
 {
 	std::vector<const CGObjectInstance *> ret;
-	for(const CGObjectInstance * obj : visitableObjs)
+	for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs)
 	{
 		if(obj->tempOwner == playerID)
 			ret.push_back(obj);
@@ -1254,43 +1088,12 @@ void VCAI::addVisitableObj(const CGObjectInstance * obj)
 	if(obj->ID == Obj::EVENT)
 		return;
 
-	visitableObjs.insert(obj);
-
-	// All teleport objects seen automatically assigned to appropriate channels
-	auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
-	if(teleportObj)
-		CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
+	nullkiller->memory->addVisitableObject(obj);
 
 	if(obj->ID == Obj::HERO && cb->getPlayerRelations(obj->tempOwner, playerID) == PlayerRelations::ENEMIES)
 	{
-		if(nullkiller) nullkiller->dangerHitMap->reset();
-	}
-}
-
-const CGObjectInstance * VCAI::lookForArt(int aid) const
-{
-	for(const CGObjectInstance * obj : ai->visitableObjs)
-	{
-		if(obj->ID == Obj::ARTIFACT && obj->subID == aid)
-			return obj;
-	}
-
-	return nullptr;
-
-	//TODO what if more than one artifact is available? return them all or some slection criteria
-}
-
-bool VCAI::isAccessible(const int3 & pos) const
-{
-	//TODO precalculate for speed
-
-	for(const CGHeroInstance * h : cb->getHeroesInfo())
-	{
-		if(isAccessibleForHero(pos, h))
-			return true;
+		nullkiller->dangerHitMap->reset();
 	}
-
-	return false;
 }
 
 HeroPtr VCAI::getHeroWithGrail() const
@@ -1303,34 +1106,6 @@ HeroPtr VCAI::getHeroWithGrail() const
 	return nullptr;
 }
 
-const CGObjectInstance * VCAI::getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate)
-{
-	//TODO smarter definition of unvisited
-	for(const CGObjectInstance * obj : visitableObjs)
-	{
-		if(predicate(obj) && !vstd::contains(alreadyVisited, obj))
-			return obj;
-	}
-	return nullptr;
-}
-
-bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies) const
-{
-	// Don't visit tile occupied by allied hero
-	if(!includeAllies)
-	{
-		for(auto obj : cb->getVisitableObjs(pos))
-		{
-			if(obj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES)
-			{
-				if(obj != h.get())
-					return false;
-			}
-		}
-	}
-	return cb->getPathsInfo(h.get())->getPathInfo(pos)->reachable();
-}
-
 bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 {
 	if(h->inTownGarrison && h->visitedTown)
@@ -1363,6 +1138,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
 		cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
 		afterMovementCheck(); // TODO: is it feasible to hero get killed there if game work properly?
+		// If revisiting, teleport probing is never done, and so the entries into the list would remain unused and uncleared
+		teleportChannelProbingList.clear();
 		// not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
 		ret = true;
 	}
@@ -1456,7 +1233,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 				doTeleportMovement(destTeleportObj->id, nextCoord);
 				if(teleportChannelProbingList.size())
 					doChannelProbing();
-				markObjectVisited(destTeleportObj); //FIXME: Monoliths are not correctly visited
+				nullkiller->memory->markObjectVisited(destTeleportObj); //FIXME: Monoliths are not correctly visited
 
 				continue;
 			}
@@ -1596,17 +1373,6 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
 	}
 }
 
-void VCAI::tryRealize(Goals::Invalid & g)
-{
-	throw cannotFulfillGoalException("I don't know how to fulfill this!");
-}
-
-void VCAI::tryRealize(Goals::AbstractGoal & g)
-{
-	logAi->debug("Attempting realizing goal with code %s", g.toString());
-	throw cannotFulfillGoalException("Unknown type of goal !");
-}
-
 const CGTownInstance * VCAI::findTownWithTavern() const
 {
 	for(const CGTownInstance * t : cb->getTownsInfo())
@@ -1616,15 +1382,6 @@ const CGTownInstance * VCAI::findTownWithTavern() const
 	return nullptr;
 }
 
-HeroPtr VCAI::primaryHero() const
-{
-	auto hs = cb->getHeroesInfo();
-	if (hs.empty())
-		return nullptr;
-	else
-		return *boost::max_element(hs, compareHeroStrength);
-}
-
 void VCAI::endTurn()
 {
 	logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr());
@@ -1663,9 +1420,9 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
 			if(heroes[1]->getTotalStrength() > hero->getTotalStrength())
 				hero = heroes[1];
 		}
-		cb->recruitHero(t, hero);
 
-		ai->ah->update();
+		cb->recruitHero(t, hero);
+		nullkiller->heroManager->update();
 
 		if(t->visitingHero)
 			moveHeroToTile(t->visitablePos(), t->visitingHero.get());
@@ -1750,18 +1507,9 @@ void VCAI::validateObject(const CGObjectInstance * obj)
 
 void VCAI::validateObject(ObjectIdRef obj)
 {
-	auto matchesId = [&](const CGObjectInstance * hlpObj) -> bool
-	{
-		return hlpObj->id == obj.id;
-	};
 	if(!obj)
 	{
-		vstd::erase_if(visitableObjs, matchesId);
-
-		for(auto & p : reservedHeroesMap)
-			vstd::erase_if(p.second, matchesId);
-
-		vstd::erase_if(reservedObjs, matchesId);
+		nullkiller->memory->removeFromMemory(obj);
 	}
 }
 
@@ -1918,150 +1666,4 @@ void AIStatus::setChannelProbing(bool ongoing)
 bool AIStatus::channelProbing()
 {
 	return ongoingChannelProbing;
-}
-
-
-
-bool isWeeklyRevisitable(const CGObjectInstance * obj)
-{
-	//TODO: allow polling of remaining creatures in dwelling
-	if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
-		return true;
-	if(dynamic_cast<const CGDwelling *>(obj))
-		return true;
-	if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
-		return true;
-
-	switch(obj->ID)
-	{
-	case Obj::STABLES:
-	case Obj::MAGIC_WELL:
-	case Obj::HILL_FORT:
-		return true;
-	case Obj::BORDER_GATE:
-	case Obj::BORDERGUARD:
-		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
-	}
-	return false;
-}
-
-bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
-{
-	switch(obj->ID)
-	{
-	case Obj::TOWN:
-	case Obj::HERO: //never visit our heroes at random
-		return obj->tempOwner != h->tempOwner; //do not visit our towns at random
-	case Obj::BORDER_GATE:
-	{
-		for(auto q : ai->myCb->getMyQuests())
-		{
-			if(q.obj == obj)
-			{
-				return false; // do not visit guards or gates when wandering
-			}
-		}
-		return true; //we don't have this quest yet
-	}
-	case Obj::BORDERGUARD: //open borderguard if possible
-		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
-	case Obj::SEER_HUT:
-	case Obj::QUEST_GUARD:
-	{
-		for(auto q : ai->myCb->getMyQuests())
-		{
-			if(q.obj == obj)
-			{
-				if(q.quest->checkQuest(h.h))
-					return true; //we completed the quest
-				else
-					return false; //we can't complete this quest
-			}
-		}
-		return true; //we don't have this quest yet
-	}
-	case Obj::CREATURE_GENERATOR1:
-	{
-		if(obj->tempOwner != h->tempOwner)
-			return true; //flag just in case
-
-		const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
-
-		for(auto level : d->creatures)
-		{
-			for(auto c : level.second)
-			{
-				if(level.first
-					&& h->getSlotFor(CreatureID(c)) != SlotID()
-					&& cb->getResourceAmount().canAfford(c.toCreature()->cost))
-				{
-					return true;
-				}
-			}
-		}
-
-		return false;
-	}
-	case Obj::HILL_FORT:
-	{
-		for(auto slot : h->Slots())
-		{
-			if(slot.second->type->upgrades.size())
-				return true; //TODO: check price?
-		}
-		return false;
-	}
-	case Obj::MONOLITH_ONE_WAY_ENTRANCE:
-	case Obj::MONOLITH_ONE_WAY_EXIT:
-	case Obj::MONOLITH_TWO_WAY:
-	case Obj::WHIRLPOOL:
-		return false;
-	case Obj::SCHOOL_OF_MAGIC:
-	case Obj::SCHOOL_OF_WAR:
-	{
-		if (cb->getResourceAmount(Res::GOLD) < 1000)
-			return false;
-		break;
-	}
-	case Obj::LIBRARY_OF_ENLIGHTENMENT:
-		if(h->level < 12)
-			return false;
-		break;
-	case Obj::TREE_OF_KNOWLEDGE:
-	{
-		if(ai->ah->getHeroRole(h) == HeroRole::SCOUT)
-			return false;
-
-		TResources myRes = cb->getResourceAmount();
-		if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
-			return false;
-		break;
-	}
-	case Obj::MAGIC_WELL:
-		return h->mana < h->manaLimit();
-	case Obj::PRISON:
-		return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
-	case Obj::TAVERN:
-	{
-		//TODO: make AI actually recruit heroes
-		//TODO: only on request
-		if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
-			return false;
-		else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
-			return false;
-		break;
-	}
-	case Obj::BOAT:
-		return false;
-	//Boats are handled by pathfinder
-	case Obj::EYE_OF_MAGI:
-		return false; //this object is useless to visit, but could be visited indefinitely
-	}
-
-	if(obj->wasVisited(*h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
-		return false;
-
-	return true;
-}
-
-
+}

+ 7 - 32
AI/Nullkiller/VCAI.h

@@ -26,7 +26,6 @@
 
 struct QuestInfo;
 
-class AIhelper;
 class Nullkiller;
 
 class AIStatus
@@ -75,18 +74,10 @@ public:
 class DLL_EXPORT VCAI : public CAdventureAI
 {
 public:
-
-	friend class FuzzyHelper;
-	friend class ResourceManager;
-	friend class BuildingManager;
-
-	std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
-	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
 	ObjectInstanceID destinationTeleport;
 	int3 destinationTeleportPos;
 	std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
-	std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
 
 	std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
 	std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
@@ -94,8 +85,6 @@ public:
 	std::set<HeroPtr> heroesUnableToExplore; //these heroes will not be polled for exploration in current state of game
 
 	//sets are faster to search, also do not contain duplicates
-	std::set<const CGObjectInstance *> visitableObjs;
-	std::set<const CGObjectInstance *> alreadyVisited;
 	std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
 	std::map<HeroPtr, std::set<HeroPtr>> visitedHeroes; //visited this turn //FIXME: this is just bug workaround
 
@@ -110,7 +99,6 @@ private:
 public:
 	ObjectInstanceID selectedObject;
 
-	AIhelper * ah;
 	std::unique_ptr<Nullkiller> nullkiller;
 
 	VCAI();
@@ -119,8 +107,6 @@ public:
 	//TODO: use only smart pointers?
 	void tryRealize(Goals::DigAtTile & g);
 	void tryRealize(Goals::Trade & g);
-	void tryRealize(Goals::Invalid & g);
-	void tryRealize(Goals::AbstractGoal & g);
 
 	bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved
 
@@ -192,8 +178,6 @@ public:
 	void endTurn();
 
 	void recruitHero(const CGTownInstance * t, bool throwing = false);
-	bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
-	bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
 	//void recruitCreatures(const CGTownInstance * t);
 	void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
 	void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
@@ -209,7 +193,6 @@ public:
 	void waitTillFree();
 
 	void addVisitableObj(const CGObjectInstance * obj);
-	void markObjectVisited(const CGObjectInstance * obj);
 
 	void markHeroUnableToExplore(HeroPtr h);
 	void markHeroAbleToExplore(HeroPtr h);
@@ -218,25 +201,15 @@ public:
 
 	void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
 	void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
-	void validateVisitableObjs();
 	void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
 	void retrieveVisitableObjs();
 	virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
 
-	const CGObjectInstance * lookForArt(int aid) const;
-	bool isAccessible(const int3 & pos) const;
 	HeroPtr getHeroWithGrail() const;
 
-	const CGObjectInstance * getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate);
-	bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
-	//optimization - use one SM for every hero call
-
 	const CGTownInstance * findTownWithTavern() const;
 	bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
 
-	std::vector<HeroPtr> getMyHeroes() const;
-	HeroPtr primaryHero() const;
-
 	void requestSent(const CPackForServer * pack, int requestID) override;
 	void answerQuery(QueryID queryID, int selection);
 	//special function that can be called ONLY from game events handling thread and will send request ASAP
@@ -271,10 +244,12 @@ public:
 
 	template<typename Handler> void serializeInternal(Handler & h, const int version)
 	{
-		h & knownTeleportChannels;
-		h & knownSubterraneanGates;
+		std::map<HeroPtr, std::set<const CGTownInstance *>> ignore;
+
+		h & nullkiller->memory->knownTeleportChannels;
+		h & nullkiller->memory->knownSubterraneanGates;
 		h & destinationTeleport;
-		h & townVisitsThisWeek;
+		h & ignore;
 
 		#if 0
 		//disabled due to issue 2890
@@ -318,8 +293,8 @@ public:
 		#endif
 
 		h & reservedHeroesMap; //FIXME: cannot instantiate abstract class
-		h & visitableObjs;
-		h & alreadyVisited;
+		h & nullkiller->memory->visitableObjs;
+		h & nullkiller->memory->alreadyVisited;
 		h & reservedObjs;
 		if (version < 788 && !h.saving)
 		{