浏览代码

Merge remote-tracking branch 'upstream/develop' into mp-disconnection

# Conflicts:
#	server/CVCMIServer.cpp
nordsoft 3 年之前
父节点
当前提交
0ea5a8fbe7
共有 100 个文件被更改,包括 830 次插入566 次删除
  1. 6 3
      AI/Nullkiller/AIGateway.cpp
  2. 4 5
      AI/Nullkiller/AIGateway.h
  3. 8 0
      AI/Nullkiller/AIUtility.cpp
  4. 9 40
      AI/Nullkiller/AIUtility.h
  5. 4 0
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  6. 5 0
      AI/Nullkiller/Analyzers/ArmyManager.h
  7. 5 4
      AI/Nullkiller/Analyzers/BuildAnalyzer.cpp
  8. 5 0
      AI/Nullkiller/Analyzers/BuildAnalyzer.h
  9. 5 0
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp
  10. 5 0
      AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h
  11. 5 0
      AI/Nullkiller/Analyzers/HeroManager.cpp
  12. 5 0
      AI/Nullkiller/Analyzers/HeroManager.h
  13. 26 16
      AI/Nullkiller/Analyzers/ObjectClusterizer.cpp
  14. 5 0
      AI/Nullkiller/Analyzers/ObjectClusterizer.h
  15. 0 0
      AI/Nullkiller/Behaviors/Behavior.cpp
  16. 0 19
      AI/Nullkiller/Behaviors/Behavior.h
  17. 5 0
      AI/Nullkiller/Behaviors/BuildingBehavior.cpp
  18. 3 0
      AI/Nullkiller/Behaviors/BuildingBehavior.h
  19. 5 0
      AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp
  20. 4 0
      AI/Nullkiller/Behaviors/BuyArmyBehavior.h
  21. 13 6
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp
  22. 4 0
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h
  23. 9 4
      AI/Nullkiller/Behaviors/ClusterBehavior.cpp
  24. 6 1
      AI/Nullkiller/Behaviors/ClusterBehavior.h
  25. 31 14
      AI/Nullkiller/Behaviors/DefenceBehavior.cpp
  26. 4 0
      AI/Nullkiller/Behaviors/DefenceBehavior.h
  27. 23 18
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  28. 4 0
      AI/Nullkiller/Behaviors/GatherArmyBehavior.h
  29. 5 0
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp
  30. 5 1
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.h
  31. 5 0
      AI/Nullkiller/Behaviors/StartupBehavior.cpp
  32. 4 0
      AI/Nullkiller/Behaviors/StartupBehavior.h
  33. 6 1
      AI/Nullkiller/Engine/AIMemory.cpp
  34. 5 0
      AI/Nullkiller/Engine/AIMemory.h
  35. 13 8
      AI/Nullkiller/Engine/DeepDecomposer.cpp
  36. 6 1
      AI/Nullkiller/Engine/DeepDecomposer.h
  37. 5 0
      AI/Nullkiller/Engine/FuzzyEngines.cpp
  38. 5 0
      AI/Nullkiller/Engine/FuzzyEngines.h
  39. 7 2
      AI/Nullkiller/Engine/FuzzyHelper.cpp
  40. 6 1
      AI/Nullkiller/Engine/FuzzyHelper.h
  41. 89 52
      AI/Nullkiller/Engine/Nullkiller.cpp
  42. 8 1
      AI/Nullkiller/Engine/Nullkiller.h
  43. 6 1
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  44. 5 0
      AI/Nullkiller/Engine/PriorityEvaluator.h
  45. 6 1
      AI/Nullkiller/Goals/AbstractGoal.cpp
  46. 8 2
      AI/Nullkiller/Goals/AbstractGoal.h
  47. 5 0
      AI/Nullkiller/Goals/AdventureSpellCast.cpp
  48. 5 0
      AI/Nullkiller/Goals/AdventureSpellCast.h
  49. 5 0
      AI/Nullkiller/Goals/Build.cpp
  50. 5 0
      AI/Nullkiller/Goals/Build.h
  51. 5 0
      AI/Nullkiller/Goals/BuildBoat.cpp
  52. 5 0
      AI/Nullkiller/Goals/BuildBoat.h
  53. 6 1
      AI/Nullkiller/Goals/BuildThis.cpp
  54. 5 0
      AI/Nullkiller/Goals/BuildThis.h
  55. 6 1
      AI/Nullkiller/Goals/BuyArmy.cpp
  56. 5 0
      AI/Nullkiller/Goals/BuyArmy.h
  57. 6 28
      AI/Nullkiller/Goals/CGoal.h
  58. 6 1
      AI/Nullkiller/Goals/CaptureObject.cpp
  59. 5 0
      AI/Nullkiller/Goals/CaptureObject.h
  60. 6 1
      AI/Nullkiller/Goals/CompleteQuest.cpp
  61. 4 0
      AI/Nullkiller/Goals/CompleteQuest.h
  62. 11 1
      AI/Nullkiller/Goals/Composition.cpp
  63. 5 0
      AI/Nullkiller/Goals/Composition.h
  64. 5 0
      AI/Nullkiller/Goals/DigAtTile.cpp
  65. 5 0
      AI/Nullkiller/Goals/DigAtTile.h
  66. 5 0
      AI/Nullkiller/Goals/DismissHero.cpp
  67. 4 0
      AI/Nullkiller/Goals/DismissHero.h
  68. 6 1
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp
  69. 4 0
      AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h
  70. 6 1
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  71. 6 0
      AI/Nullkiller/Goals/ExecuteHeroChain.h
  72. 5 0
      AI/Nullkiller/Goals/GatherArmy.cpp
  73. 5 0
      AI/Nullkiller/Goals/GatherArmy.h
  74. 1 1
      AI/Nullkiller/Goals/Goals.h
  75. 33 0
      AI/Nullkiller/Goals/Invalid.h
  76. 6 1
      AI/Nullkiller/Goals/RecruitHero.cpp
  77. 5 0
      AI/Nullkiller/Goals/RecruitHero.h
  78. 5 0
      AI/Nullkiller/Goals/SaveResources.cpp
  79. 4 0
      AI/Nullkiller/Goals/SaveResources.h
  80. 5 1
      AI/Nullkiller/Goals/Trade.cpp
  81. 5 0
      AI/Nullkiller/Goals/Trade.h
  82. 0 189
      AI/Nullkiller/Goals/Win.cpp
  83. 0 39
      AI/Nullkiller/Goals/Win.h
  84. 6 1
      AI/Nullkiller/Markers/ArmyUpgrade.cpp
  85. 5 1
      AI/Nullkiller/Markers/ArmyUpgrade.h
  86. 6 1
      AI/Nullkiller/Markers/DefendTown.cpp
  87. 5 1
      AI/Nullkiller/Markers/DefendTown.h
  88. 6 1
      AI/Nullkiller/Markers/HeroExchange.cpp
  89. 5 1
      AI/Nullkiller/Markers/HeroExchange.h
  90. 6 1
      AI/Nullkiller/Markers/UnlockCluster.cpp
  91. 6 1
      AI/Nullkiller/Markers/UnlockCluster.h
  92. 107 78
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  93. 25 7
      AI/Nullkiller/Pathfinding/AINodeStorage.h
  94. 5 0
      AI/Nullkiller/Pathfinding/AIPathfinder.cpp
  95. 5 0
      AI/Nullkiller/Pathfinding/AIPathfinder.h
  96. 4 0
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp
  97. 5 0
      AI/Nullkiller/Pathfinding/AIPathfinderConfig.h
  98. 6 1
      AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp
  99. 6 1
      AI/Nullkiller/Pathfinding/Actions/BattleAction.h
  100. 10 4
      AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp

+ 6 - 3
AI/Nullkiller/AIGateway.cpp

@@ -23,7 +23,8 @@
 #include "AIGateway.h"
 #include "Goals/Goals.h"
 
-class CGVisitableOPW;
+namespace NKAI
+{
 
 const float SAFE_ATTACK_CONSTANT = 1.2;
 
@@ -766,10 +767,10 @@ void AIGateway::makeTurn()
 		logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
 		return;
 	}
-	/*catch (std::exception & e)
+	catch (std::exception & e)
 	{
 		logAi->debug("Making turn thread has caught an exception: %s", e.what());
-	}*/
+	}
 
 	endTurn();
 }
@@ -1668,3 +1669,5 @@ bool AIStatus::channelProbing()
 {
 	return ongoingChannelProbing;
 }
+
+}

+ 4 - 5
AI/Nullkiller/AIGateway.h

@@ -25,11 +25,8 @@
 #include "Pathfinding/AIPathfinder.h"
 #include "Engine/Nullkiller.h"
 
-VCMI_LIB_NAMESPACE_BEGIN
-
-struct QuestInfo;
-
-VCMI_LIB_NAMESPACE_END
+namespace NKAI
+{
 
 class AIStatus
 {
@@ -222,3 +219,5 @@ public:
 		h & battlename;
 	}
 };
+
+}

+ 8 - 0
AI/Nullkiller/AIUtility.cpp

@@ -20,6 +20,9 @@
 
 #include "../../lib/CModHandler.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<AIGateway> ai;
 
 //extern static const int3 dirs[8];
@@ -307,6 +310,9 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
 
 bool isWeeklyRevisitable(const CGObjectInstance * obj)
 {
+	if(!obj)
+		return false;
+
 	//TODO: allow polling of remaining creatures in dwelling
 	if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
 		return true;
@@ -443,3 +449,5 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 
 	return true;
 }
+
+}

+ 9 - 40
AI/Nullkiller/AIUtility.h

@@ -48,19 +48,19 @@
 #include "../../lib/mapObjects/CObjectHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/CPathfinder.h"
+#include "../../CCallback.h"
 
 #include <chrono>
 
 using namespace tbb;
 
-class CCallback;
-class Nullkiller;
-struct creInfo;
-
-typedef const int3 & crint3;
-typedef const std::string & crstring;
 typedef std::pair<ui32, std::vector<CreatureID>> dwellingContent;
 
+namespace NKAI
+{
+struct creInfo;
+class Nullkiller;
+
 const int GOLD_MINE_PRODUCTION = 1000, WOOD_ORE_MINE_PRODUCTION = 2, RESOURCE_MINE_PRODUCTION = 1;
 const int ACTUAL_RESOURCE_COUNT = 7;
 const int ALLOWED_ROAMING_HEROES = 8;
@@ -151,39 +151,6 @@ struct ObjectIdRef
 	}
 };
 
-struct TimeCheck
-{
-	CStopWatch time;
-	std::string txt;
-	TimeCheck(crstring TXT)
-		: txt(TXT)
-	{
-	}
-
-	~TimeCheck()
-	{
-		logAi->trace("Time of %s was %d ms.", txt, time.getDiff());
-	}
-};
-
-//TODO: replace with vstd::
-struct AtScopeExit
-{
-	std::function<void()> foo;
-	AtScopeExit(const std::function<void()> & FOO)
-		: foo(FOO)
-	{}
-	~AtScopeExit()
-	{
-		foo();
-	}
-};
-
-
-class ObjsVector : public std::vector<ObjectIdRef>
-{
-};
-
 template<int id>
 bool objWithID(const CGObjectInstance * obj)
 {
@@ -278,7 +245,7 @@ uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock>
 bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObjectInstance * obj);
 
 template<typename TFunc>
-void pforeachTilePos(crint3 mapSize, TFunc fn)
+void pforeachTilePos(const int3 & mapSize, TFunc fn)
 {
 	for(int z = 0; z < mapSize.z; ++z)
 	{
@@ -380,3 +347,5 @@ private:
 	std::shared_ptr<SharedPool<T> *> instance_tracker;
 	boost::mutex sync;
 };
+
+}

+ 4 - 0
AI/Nullkiller/Analyzers/ArmyManager.cpp

@@ -14,6 +14,8 @@
 #include "../../../CCallback.h"
 #include "../../../lib/mapObjects/MapObjects.h"
 
+namespace NKAI
+{
 class StackUpgradeInfo
 {
 public:
@@ -497,3 +499,5 @@ ArmyUpgradeInfo ArmyManager::calculateCreaturesUpgrade(
 
 	return result;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/ArmyManager.h

@@ -17,6 +17,9 @@
 #include "../../../lib/CTownHandler.h"
 #include "../../../lib/CBuildingHandler.h"
 
+namespace NKAI
+{
+
 class Nullkiller;
 
 struct SlotInfo
@@ -102,3 +105,5 @@ private:
 	std::vector<StackUpgradeInfo> getHillFortUpgrades(const CCreatureSet * army) const;
 	std::vector<StackUpgradeInfo> getDwellingUpgrades(const CCreatureSet * army, const CGDwelling * dwelling) const;
 };
+
+}

+ 5 - 4
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -12,6 +12,9 @@
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
 {
 	auto townInfo = developmentInfo.town->town;
@@ -205,8 +208,6 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
 	logAi->trace("checking %s", info.name);
 	logAi->trace("buildInfo %s", info.toString());
 
-	buildPtr = nullptr;
-
 	if(!town->hasBuilt(building))
 	{
 		auto canBuild = ai->cb->canBuildStructure(town, building);
@@ -239,8 +240,6 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
 			}
 			else
 			{
-				buildPtr = townInfo->buildings.at(building);
-
 				logAi->trace("cant build. Need %d", missingBuildings[0].num);
 
 				BuildingInfo prerequisite = getBuildingOrPrerequisite(town, missingBuildings[0], excludeDwellingDependencies);
@@ -398,3 +397,5 @@ std::string BuildingInfo::toString() const
 		+ " x " + creatureCost.toString()
 		+ ", daily: " + dailyIncome.toString();
 }
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/BuildAnalyzer.h

@@ -12,6 +12,9 @@
 #include "../AIUtility.h"
 #include "../../../lib/ResourceSet.h"
 
+namespace NKAI
+{
+
 class Nullkiller;
 
 class DLL_EXPORT BuildingInfo
@@ -104,3 +107,5 @@ private:
 	void updateDailyIncome();
 	void reset();
 };
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.cpp

@@ -11,6 +11,9 @@
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 void DangerHitMapAnalyzer::updateHitMap()
 {
 	if(upToDate)
@@ -137,3 +140,5 @@ void DangerHitMapAnalyzer::reset()
 {
 	upToDate = false;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/DangerHitMapAnalyzer.h

@@ -11,6 +11,9 @@
 
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 struct HitMapInfo
 {
 	uint64_t danger;
@@ -55,3 +58,5 @@ public:
 	const std::set<const CGObjectInstance *> & getOneTurnAccessibleObjects(const CGHeroInstance * enemy) const;
 	void reset();
 };
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/HeroManager.cpp

@@ -13,6 +13,9 @@
 #include "../../../lib/mapObjects/MapObjects.h"
 #include "../../../lib/CHeroHandler.h"
 
+namespace NKAI
+{
+
 SecondarySkillEvaluator HeroManager::wariorSkillsScores = SecondarySkillEvaluator(
 	{
 		std::make_shared<SecondarySkillScoreMap>(
@@ -305,3 +308,5 @@ float SecondarySkillEvaluator::evaluateSecSkill(const CGHeroInstance * hero, Sec
 
 	return score;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/HeroManager.h

@@ -17,6 +17,9 @@
 #include "../../../lib/CTownHandler.h"
 #include "../../../lib/CBuildingHandler.h"
 
+namespace NKAI
+{
+
 class DLL_EXPORT IHeroManager //: public: IAbstractManager
 {
 public:
@@ -104,3 +107,5 @@ private:
 public:
 	void evaluateScore(const CGHeroInstance * hero, SecondarySkill skill, float & score) const override;
 };
+
+}

+ 26 - 16
AI/Nullkiller/Analyzers/ObjectClusterizer.cpp

@@ -14,6 +14,9 @@
 #include "../Engine/Nullkiller.h"
 #include "lib/mapping/CMap.h" //for victory conditions
 
+namespace NKAI
+{
+
 void ObjectCluster::addObject(const CGObjectInstance * obj, const AIPath & path, float priority)
 {
 	ClusterObjects::accessor info;
@@ -120,18 +123,23 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
 
 		auto blocker = blockers.front();
 
+		if(isObjectPassable(ai, blocker))
+			continue;
+
 		if(blocker->ID == Obj::GARRISON
-			|| blocker->ID == Obj::MONSTER
-			|| blocker->ID == Obj::GARRISON2
-			|| blocker->ID == Obj::BORDERGUARD
-			|| blocker->ID == Obj::BORDER_GATE
-			|| blocker->ID == Obj::SHIPYARD)
+			|| blocker->ID == Obj::GARRISON2)
 		{
-			if(!isObjectPassable(ai, blocker))
+			if(dynamic_cast<const CArmedInstance *>(blocker)->getArmyStrength() == 0)
+				continue;
+			else
 				return blocker;
 		}
 
-		if(blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked)
+		if(blocker->ID == Obj::MONSTER
+			|| blocker->ID == Obj::BORDERGUARD
+			|| blocker->ID == Obj::BORDER_GATE
+			|| blocker->ID == Obj::SHIPYARD
+			|| (blocker->ID == Obj::QUEST_GUARD && node->actionIsBlocked))
 		{
 			return blocker;
 		}
@@ -223,7 +231,7 @@ void ObjectClusterizer::clusterize()
 			if(!shouldVisitObject(obj))
 				return;
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Check object %s%s.", obj->getObjectName(), obj->visitablePos().toString());
 #endif
 
@@ -231,7 +239,7 @@ void ObjectClusterizer::clusterize()
 
 			if(paths.empty())
 			{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("No paths found.");
 #endif
 				continue;
@@ -246,7 +254,7 @@ void ObjectClusterizer::clusterize()
 			{
 				farObjects.addObject(obj, paths.front(), 0);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Object ignored. Moved to far objects with path %s", paths.front().toString());
 #endif
 
@@ -257,13 +265,13 @@ void ObjectClusterizer::clusterize()
 
 			for(auto & path : paths)
 			{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Checking path %s", path.toString());
 #endif
 
 				if(!shouldVisit(ai, path.targetHero, obj))
 				{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 					logAi->trace("Hero %s does not need to visit %s", path.targetHero->name, obj->getObjectName());
 #endif
 					continue;
@@ -277,7 +285,7 @@ void ObjectClusterizer::clusterize()
 					{
 						if(vstd::contains(heroesProcessed, path.targetHero))
 						{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 							logAi->trace("Hero %s is already processed.", path.targetHero->name);
 #endif
 							continue;
@@ -297,7 +305,7 @@ void ObjectClusterizer::clusterize()
 
 						cluster->second->addObject(obj, path, priority);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 						logAi->trace("Path added to cluster %s%s", blocker->getObjectName(), blocker->visitablePos().toString());
 #endif
 						continue;
@@ -322,7 +330,7 @@ void ObjectClusterizer::clusterize()
 					farObjects.addObject(obj, path, priority);
 				}
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Path %s added to %s objects. Turn: %d, priority: %f",
 					path.toString(),
 					interestingObject ? "near" : "far",
@@ -340,7 +348,7 @@ void ObjectClusterizer::clusterize()
 	{
 		logAi->trace("Cluster %s %s count: %i", pair.first->getObjectName(), pair.first->visitablePos().toString(), pair.second->objects.size());
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 		for(auto obj : pair.second->getObjects())
 		{
 			logAi->trace("Object %s %s", obj->getObjectName(), obj->visitablePos().toString());
@@ -350,3 +358,5 @@ void ObjectClusterizer::clusterize()
 
 	logAi->trace("Clusterization complete in %ld", timeElapsed(start));
 }
+
+}

+ 5 - 0
AI/Nullkiller/Analyzers/ObjectClusterizer.h

@@ -11,6 +11,9 @@
 
 #include "../Pathfinding/AINodeStorage.h"
 
+namespace NKAI
+{
+
 struct ClusterObjectInfo
 {
 	float priority;
@@ -72,3 +75,5 @@ public:
 private:
 	bool shouldVisitObject(const CGObjectInstance * obj) const;
 };
+
+}

+ 0 - 0
AI/Nullkiller/Behaviors/Behavior.cpp


+ 0 - 19
AI/Nullkiller/Behaviors/Behavior.h

@@ -1,19 +0,0 @@
-/*
-* Behavior.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 "../AIGateway.h"
-#error REMOVE THIS FILE
-
-class Behavior : public Goals::AbstractGoal
-{
-public:
-	virtual std::string toString() const = 0;
-};

+ 5 - 0
AI/Nullkiller/Behaviors/BuildingBehavior.cpp

@@ -19,6 +19,9 @@
 #include "lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -77,3 +80,5 @@ Goals::TGoalVec BuildingBehavior::decompose() const
 
 	return tasks;
 }
+
+}

+ 3 - 0
AI/Nullkiller/Behaviors/BuildingBehavior.h

@@ -13,6 +13,8 @@
 #include "../AIUtility.h"
 #include "../Goals/CGoal.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class BuildingBehavior : public CGoal<BuildingBehavior>
@@ -32,3 +34,4 @@ namespace Goals
 	};
 }
 
+}

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

@@ -16,6 +16,9 @@
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -75,3 +78,5 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
 
 	return tasks;
 }
+
+}

+ 4 - 0
AI/Nullkiller/Behaviors/BuyArmyBehavior.h

@@ -13,6 +13,8 @@
 #include "../AIUtility.h"
 #include "../Goals/CGoal.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class BuyArmyBehavior : public CGoal<BuyArmyBehavior>
@@ -30,3 +32,5 @@ namespace Goals
 		}
 	};
 }
+
+}

+ 13 - 6
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -12,9 +12,13 @@
 #include "../Engine/Nullkiller.h"
 #include "../Goals/Composition.h"
 #include "../Goals/ExecuteHeroChain.h"
+#include "../Goals/Invalid.h"
 #include "CaptureObjectsBehavior.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -59,13 +63,13 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
 	{
 		tasks.push_back(sptr(Goals::Invalid()));
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace("Path found %s", path.toString());
 #endif
 
 		if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
 #endif
 			continue;
@@ -85,7 +89,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
 		{
 			auto subGoal = firstBlockedAction->decompose(path.targetHero);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
 #endif
 
@@ -104,7 +108,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
 
 		auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace(
 			"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
 			isSafe ? "safe" : "not safe",
@@ -133,6 +137,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
 		}
 	}
 
+	assert(closestWay || waysToVisitObj.empty());
 	for(auto way : waysToVisitObj)
 	{
 		way->closestWayRatio
@@ -160,7 +165,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 			if(!objectMatchesFilter(objToVisit))
 				continue;
 	
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 			logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());
 #endif
 
@@ -170,7 +175,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 			std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
 			std::shared_ptr<ExecuteHeroChain> closestWay;
 					
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 			logAi->trace("Found %d paths", paths.size());
 #endif
 			vstd::concatenate(tasks, getVisitGoals(paths, objToVisit));
@@ -218,3 +223,5 @@ bool CaptureObjectsBehavior::objectMatchesFilter(const CGObjectInstance * obj) c
 
 	return true;
 }
+
+}

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

@@ -14,6 +14,8 @@
 #include "../Goals/CGoal.h"
 #include "../Pathfinding/AINodeStorage.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class CaptureObjectsBehavior : public CGoal<CaptureObjectsBehavior>
@@ -72,3 +74,5 @@ namespace Goals
 	};
 }
 
+
+}

+ 9 - 4
AI/Nullkiller/Behaviors/ClusterBehavior.cpp

@@ -16,6 +16,9 @@
 #include "../Goals/Composition.h"
 #include "../Behaviors/CaptureObjectsBehavior.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -50,7 +53,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 
 	TGoalVec goals;
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 	logAi->trace(
 		"Checking cluster %s %s, found %d paths",
 		cluster->blocker->getObjectName(),
@@ -60,7 +63,7 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 
 	for(auto path = paths.begin(); path != paths.end();)
 	{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace("Checking path %s", path->toString());
 #endif
 
@@ -89,13 +92,13 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 		for(auto & node : clonedPath.nodes)
 			node.parentIndex -= path->nodes.size() - clonedPath.nodes.size();
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace("Unlock path found %s", blockerPaths.back().toString());
 #endif
 		path++;
 	}
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 	logAi->trace("Decompose unlock paths");
 #endif
 
@@ -114,3 +117,5 @@ Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster>
 
 	return goals;
 }
+
+}

+ 6 - 1
AI/Nullkiller/Behaviors/ClusterBehavior.h

@@ -13,6 +13,9 @@
 #include "../Goals/CGoal.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 struct ObjectCluster;
 
 namespace Goals
@@ -36,4 +39,6 @@ namespace Goals
 	private:
 		Goals::TGoalVec decomposeCluster(std::shared_ptr<ObjectCluster> cluster) const;
 	};
-}
+}
+
+}

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

@@ -23,6 +23,9 @@
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -47,14 +50,14 @@ Goals::TGoalVec DefenceBehavior::decompose() const
 
 void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
 {
-	logAi->debug("Evaluating defence for %s", town->name);
+	logAi->trace("Evaluating defence for %s", town->name);
 
 	auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
 	auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
 
 	if(!treatNode.fastestDanger.hero)
 	{
-		logAi->debug("No treat found for town %s", town->name);
+		logAi->trace("No treat found for town %s", town->name);
 
 		return;
 	}
@@ -73,7 +76,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			return;
 		}
 
-		logAi->debug(
+		logAi->trace(
 			"Hero %s in garrison of town %s is suposed to defend the town",
 			town->garrisonHero->name,
 			town->name);
@@ -85,7 +88,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 	if(reinforcement)
 	{
-		logAi->debug("Town %s can buy defence army %lld", town->name, reinforcement);
+		logAi->trace("Town %s can buy defence army %lld", town->name, reinforcement);
 		tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
 	}
 
@@ -93,7 +96,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 	for(auto & treat : treats)
 	{
-		logAi->debug(
+		logAi->trace(
 			"Town %s has treat %lld in %s turns, hero: %s",
 			town->name,
 			treat.danger,
@@ -104,6 +107,12 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 		for(AIPath & path : paths)
 		{
+			if(town->visitingHero && path.targetHero != town->visitingHero.get())
+				continue;
+
+			if(town->visitingHero && path.getHeroStrength() < town->visitingHero->getHeroStrength())
+				continue;
+
 			if(path.getHeroStrength() > treat.danger)
 			{
 				if((path.turn() <= treat.turn && dayOfWeek + treat.turn < 6 && isSafeToVisit(path.targetHero, path.heroArmy, treat.danger))
@@ -111,11 +120,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 					|| path.turn() < treat.turn - 1
 					|| (path.turn() < treat.turn && treat.turn >= 2))
 				{
-					logAi->debug(
+#if NKAI_TRACE_LEVEL >= 1
+					logAi->trace(
 						"Hero %s can eliminate danger for town %s using path %s.",
 						path.targetHero->name,
 						town->name,
 						path.toString());
+#endif
 
 					treatIsUnderControl = true;
 					break;
@@ -140,7 +151,9 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 					if(cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES)
 					{
-						logAi->debug("Hero %s can be recruited to defend %s", hero->name, town->name);
+#if NKAI_TRACE_LEVEL >= 1
+						logAi->trace("Hero %s can be recruited to defend %s", hero->name, town->name);
+#endif
 						tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(1)));
 						continue;
 					}
@@ -174,7 +187,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 		if(paths.empty())
 		{
-			logAi->debug("No ways to defend town %s", town->name);
+			logAi->trace("No ways to defend town %s", town->name);
 
 			continue;
 		}
@@ -186,7 +199,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 		{
 			auto & path = paths[i];
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 			logAi->trace(
 				"Hero %s can defend town with force %lld in %s turns, cost: %f, path: %s",
 				path.targetHero->name,
@@ -197,9 +210,11 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 #endif
 			if(path.turn() <= treat.turn - 2)
 			{
+#if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Deffer defence of %s by %s because he has enough time to rich the town next trun",
 					town->name,
 					path.targetHero->name);
+#endif
 
 				defferedPaths[path.targetHero].push_back(i);
 
@@ -208,7 +223,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 			if(path.targetHero == town->visitingHero && path.exchangeCount == 1)
 			{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Put %s to garrison of town %s",
 					path.targetHero->name,
 					town->name);
@@ -232,7 +247,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			{
 				if(ai->nullkiller->arePathHeroesLocked(path))
 				{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 					logAi->trace("Can not move %s to defend town %s. Path is locked.",
 						path.targetHero->name,
 						town->name);
@@ -260,7 +275,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 				}
 			}
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 			logAi->trace("Move %s to defend town %s",
 				path.targetHero->name,
 				town->name);
@@ -274,13 +289,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 			{
 				auto subGoal = firstBlockedAction->decompose(path.targetHero);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
 #endif
 
 				if(subGoal->invalid())
 				{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 					logAi->trace("Path is invalid. Skipping");
 #endif
 					continue;
@@ -295,3 +310,5 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 	logAi->debug("Found %d tasks", tasks.size());
 }
+
+}

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

@@ -13,6 +13,8 @@
 #include "../Goals/CGoal.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DefenceBehavior : public CGoal<DefenceBehavior>
@@ -36,3 +38,5 @@ namespace Goals
 	};
 }
 
+
+}

+ 23 - 18
AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp

@@ -19,6 +19,9 @@
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -64,12 +67,12 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 	Goals::TGoalVec tasks;
 	const int3 pos = hero->visitablePos();
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Checking ways to gaher army for hero %s, %s", hero->getObjectName(), pos.toString());
 #endif
 	if(ai->nullkiller->isHeroLocked(hero))
 	{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 		logAi->trace("Skipping locked hero %s, %s", hero->getObjectName(), pos.toString());
 #endif
 		return tasks;
@@ -77,13 +80,13 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 	auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Gather army found %d paths", paths.size());
 #endif
 
 	for(const AIPath & path : paths)
 	{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace("Path found %s", path.toString());
 #endif
 		
@@ -91,7 +94,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 		if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
 #endif
 			continue;
@@ -99,7 +102,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 		if(ai->nullkiller->arePathHeroesLocked(path))
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path because of locked hero");
 #endif
 			continue;
@@ -112,7 +115,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		// avoid transferring very small amount of army
 		if(armyValue < 0.1f)
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Army value is too small.");
 #endif
 			continue;
@@ -121,7 +124,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		// avoid trying to move bigger army to the weaker one.
 		if(armyValue > 1)
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Army value is too large.");
 #endif
 			continue;
@@ -131,7 +134,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 		auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace(
 			"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
 			isSafe ? "safe" : "not safe",
@@ -156,7 +159,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 
 			if(blockedAction)
 			{
-	#if AI_TRACE_LEVEL >= 2
+	#if NKAI_TRACE_LEVEL >= 2
 				logAi->trace("Action is blocked. Considering decomposition.");
 	#endif
 				composition.addNext(blockedAction->decompose(path.targetHero));
@@ -175,25 +178,25 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 	const int3 pos = upgrader->visitablePos();
 	TResources availableResources = ai->nullkiller->getFreeResources();
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
 #endif
 	
 	auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
 	std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 	logAi->trace("Found %d paths", paths.size());
 #endif
 
 	for(const AIPath & path : paths)
 	{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace("Path found %s", path.toString());
 #endif
 		if(upgrader->visitingHero != path.targetHero)
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path. Town has visiting hero.");
 #endif
 			continue;
@@ -201,7 +204,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 		if(ai->nullkiller->arePathHeroesLocked(path))
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path because of locked hero");
 #endif
 			continue;
@@ -209,7 +212,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 		if(path.getFirstBlockedAction())
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			// TODO: decomposition?
 			logAi->trace("Ignore path. Action is blocked.");
 #endif
@@ -218,7 +221,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 		if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 		{
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.heroArmy->getArmyStrength());
 #endif
 			continue;
@@ -234,7 +237,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 		auto isSafe = isSafeToVisit(path.targetHero, path.heroArmy, danger);
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 		logAi->trace(
 			"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",
 			isSafe ? "safe" : "not safe",
@@ -257,3 +260,5 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 
 	return tasks;
 }
+
+}

+ 4 - 0
AI/Nullkiller/Behaviors/GatherArmyBehavior.h

@@ -13,6 +13,8 @@
 #include "../Goals/CGoal.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class GatherArmyBehavior : public CGoal<GatherArmyBehavior>
@@ -37,3 +39,5 @@ namespace Goals
 	};
 }
 
+
+}

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

@@ -16,6 +16,9 @@
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -46,3 +49,5 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 
 	return tasks;
 }
+
+}

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

@@ -13,6 +13,8 @@
 #include "../Goals/CGoal.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class RecruitHeroBehavior : public CGoal<RecruitHeroBehavior>
@@ -31,4 +33,6 @@ namespace Goals
 			return true;
 		}
 	};
-}
+}
+
+}

+ 5 - 0
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -20,6 +20,9 @@
 #include "lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -221,3 +224,5 @@ Goals::TGoalVec StartupBehavior::decompose() const
 
 	return tasks;
 }
+
+}

+ 4 - 0
AI/Nullkiller/Behaviors/StartupBehavior.h

@@ -13,6 +13,8 @@
 #include "../Goals/CGoal.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class StartupBehavior : public CGoal<StartupBehavior>
@@ -33,3 +35,5 @@ namespace Goals
 	};
 }
 
+
+}

+ 6 - 1
AI/Nullkiller/Engine/AIMemory.cpp

@@ -11,6 +11,9 @@
 #include "AIMemory.h"
 #include "../../../CCallback.h"
 
+namespace NKAI
+{
+
 void AIMemory::removeFromMemory(const CGObjectInstance * obj)
 {
 	vstd::erase_if_present(visitableObjs, obj);
@@ -101,4 +104,6 @@ void AIMemory::removeInvisibleObjects(CCallback * cb)
 
 	vstd::erase_if(visitableObjs, shouldBeErased);
 	vstd::erase_if(alreadyVisited, shouldBeErased);
-}
+}
+
+}

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

@@ -12,6 +12,9 @@
 #include "../AIUtility.h"
 #include "../../../lib/mapObjects/MapObjects.h"
 
+namespace NKAI
+{
+
 class AIMemory
 {
 public:
@@ -29,3 +32,5 @@ public:
 	bool wasVisited(const CGObjectInstance * obj) const;
 	void removeInvisibleObjects(CCallback * cb);
 };
+
+}

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

@@ -21,6 +21,9 @@
 #include "../Goals/Invalid.h"
 #include "../Goals/Composition.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -49,7 +52,7 @@ Goals::TGoalVec DeepDecomposer::decompose(TSubgoal behavior, int depthLimit)
 		TSubgoal current = goals[depth].back();
 		TGoalVec subgoals = decomposeCached(unwrapComposition(current), fromCache);
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 		logAi->trace("Decomposition level %d returned %d goals", depth, subgoals.size());
 #endif
 
@@ -69,7 +72,7 @@ Goals::TGoalVec DeepDecomposer::decompose(TSubgoal behavior, int depthLimit)
 				// 0 - goals directly from behavior
 				Goals::TSubgoal task = depth >= 1 ? aggregateGoals(0, subgoal) : subgoal;
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Found task %s", task->toString());
 #endif
 				if(!isCompositionLoop(subgoal))
@@ -84,7 +87,7 @@ Goals::TGoalVec DeepDecomposer::decompose(TSubgoal behavior, int depthLimit)
 			}
 			else if(depth < depthLimit - 1)
 			{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Found abstract goal %s", subgoal->toString());
 #endif
 				if(!isCompositionLoop(subgoal))
@@ -176,7 +179,7 @@ bool DeepDecomposer::isCompositionLoop(TSubgoal goal)
 
 TGoalVec DeepDecomposer::decomposeCached(TSubgoal goal, bool & fromCache)
 {
-#if AI_TRACE_LEVEL >= 1	
+#if NKAI_TRACE_LEVEL >= 1	
 	logAi->trace("Decomposing %s, level %s", goal->toString(), depth);
 #endif
 
@@ -188,7 +191,7 @@ TGoalVec DeepDecomposer::decomposeCached(TSubgoal goal, bool & fromCache)
 
 			if(cached != decompositionCache[i].end())
 			{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 				logAi->trace("Use decomposition cache for %s, level: %d", goal->toString(), depth);
 #endif
 				fromCache = true;
@@ -200,7 +203,7 @@ TGoalVec DeepDecomposer::decomposeCached(TSubgoal goal, bool & fromCache)
 		decompositionCache[depth][goal] = {}; // if goal decomposition yields no goals we still need it in cache to not decompose again
 	}
 
-#if AI_TRACE_LEVEL >= 2	
+#if NKAI_TRACE_LEVEL >= 2	
 	logAi->trace("Calling decompose on %s, level %s", goal->toString(), depth);
 #endif
 
@@ -221,7 +224,7 @@ void DeepDecomposer::addToCache(TSubgoal goal)
 		{
 			auto solution = parentDepth < depth ? aggregateGoals(parentDepth + 1, goal) : goal;
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 			logAi->trace("Adding %s to decomosition cache of %s at level %d", solution->toString(), parent->toString(), parentDepth);
 #endif
 
@@ -234,4 +237,6 @@ void DeepDecomposer::addToCache(TSubgoal goal)
 			}
 		}
 	}
-}
+}
+
+}

+ 6 - 1
AI/Nullkiller/Engine/DeepDecomposer.h

@@ -11,6 +11,9 @@
 
 #include "../Goals/AbstractGoal.h"
 
+namespace NKAI
+{
+
 struct GoalHash
 {
 	uint64_t operator()(const Goals::TSubgoal & goal) const
@@ -38,4 +41,6 @@ private:
 	bool isCompositionLoop(Goals::TSubgoal goal);
 	Goals::TGoalVec decomposeCached(Goals::TSubgoal goal, bool & fromCache);
 	void addToCache(Goals::TSubgoal goal);
-};
+};
+
+}

+ 5 - 0
AI/Nullkiller/Engine/FuzzyEngines.cpp

@@ -14,6 +14,9 @@
 #include "../../../lib/mapObjects/MapObjects.h"
 #include "../AIGateway.h"
 
+namespace NKAI
+{
+
 #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
 
@@ -239,3 +242,5 @@ float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, c
 
 	return output;
 }
+
+}

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

@@ -17,6 +17,9 @@ class CArmedInstance;
 
 VCMI_LIB_NAMESPACE_END
 
+namespace NKAI
+{
+
 class engineBase //subclasses create fuzzylite variables with "new" that are not freed - this is desired as fl::Engine wants to destroy these...
 {
 protected:
@@ -40,3 +43,5 @@ private:
 	fl::InputVariable * castleWalls;
 	fl::OutputVariable * threat;
 };
+
+}

+ 7 - 2
AI/Nullkiller/Engine/FuzzyHelper.cpp

@@ -14,6 +14,9 @@
 #include "../Goals/Goals.h"
 #include "Nullkiller.h"
 
+namespace NKAI
+{
+
 ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
 {
 	//this one is not fuzzy anymore, just calculate weighted average
@@ -33,7 +36,7 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
 
 }
 
-ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, bool checkGuards)
+ui64 FuzzyHelper::evaluateDanger(const int3 & tile, const CGHeroInstance * visitor, bool checkGuards)
 {
 	auto cb = ai->cb.get();
 	const TerrainTile * t = cb->getTile(tile, false);
@@ -153,4 +156,6 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
 	default:
 		return 0;
 	}
-}
+}
+
+}

+ 6 - 1
AI/Nullkiller/Engine/FuzzyHelper.h

@@ -16,6 +16,9 @@ class CBank;
 
 VCMI_LIB_NAMESPACE_END
 
+namespace NKAI
+{
+
 class Nullkiller;
 
 class DLL_EXPORT FuzzyHelper
@@ -30,5 +33,7 @@ public:
 	ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
 
 	ui64 evaluateDanger(const CGObjectInstance * obj);
-	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, bool checkGuards = true);
+	ui64 evaluateDanger(const int3 & tile, const CGHeroInstance * visitor, bool checkGuards = true);
 };
+
+}

+ 89 - 52
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -21,12 +21,15 @@
 #include "../Goals/Invalid.h"
 #include "../Goals/Composition.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
 using namespace Goals;
 
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 #define MAXPASS 1000000
 #else
 #define MAXPASS 30
@@ -116,9 +119,10 @@ void Nullkiller::resetAiState()
 	playerID = ai->playerID;
 	lockedHeroes.clear();
 	dangerHitMap->reset();
+	useHeroChain = true;
 }
 
-void Nullkiller::updateAiState(int pass)
+void Nullkiller::updateAiState(int pass, bool fast)
 {
 	boost::this_thread::interruption_point();
 
@@ -126,39 +130,42 @@ void Nullkiller::updateAiState(int pass)
 
 	activeHero = nullptr;
 
-	memory->removeInvisibleObjects(cb.get());
+	if(!fast)
+	{
+		memory->removeInvisibleObjects(cb.get());
 
-	dangerHitMap->updateHitMap();
+		dangerHitMap->updateHitMap();
 
-	boost::this_thread::interruption_point();
+		boost::this_thread::interruption_point();
 
-	heroManager->update();
-	logAi->trace("Updating paths");
+		heroManager->update();
+		logAi->trace("Updating paths");
 
-	std::map<const CGHeroInstance *, HeroRole> activeHeroes;
+		std::map<const CGHeroInstance *, HeroRole> activeHeroes;
 
-	for(auto hero : cb->getHeroesInfo())
-	{
-		if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
-			continue;
+		for(auto hero : cb->getHeroesInfo())
+		{
+			if(getHeroLockedReason(hero) == HeroLockedReason::DEFENCE)
+				continue;
 
-		activeHeroes[hero] = heroManager->getHeroRole(hero);
-	}
+			activeHeroes[hero] = heroManager->getHeroRole(hero);
+		}
 
-	PathfinderSettings cfg;
-	cfg.useHeroChain = true;
-	cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
+		PathfinderSettings cfg;
+		cfg.useHeroChain = useHeroChain;
+		cfg.scoutTurnDistanceLimit = SCOUT_TURN_DISTANCE_LIMIT;
 
-	if(scanDepth != ScanDepth::FULL)
-	{
-		cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
-	}
+		if(scanDepth != ScanDepth::FULL)
+		{
+			cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
+		}
 
-	pathfinder->updatePaths(activeHeroes, cfg);
+		pathfinder->updatePaths(activeHeroes, cfg);
 
-	armyManager->update();
+		objectClusterizer->clusterize();
+	}
 
-	objectClusterizer->clusterize();
+	armyManager->update();
 	buildAnalyzer->update();
 	decomposer->reset();
 
@@ -174,7 +181,7 @@ bool Nullkiller::arePathHeroesLocked(const AIPath & path) const
 {
 	if(getHeroLockedReason(path.targetHero) == HeroLockedReason::STARTUP)
 	{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 		logAi->trace("Hero %s is locked by STARTUP. Discarding %s", path.targetHero->name, path.toString());
 #endif
 		return true;
@@ -186,7 +193,7 @@ bool Nullkiller::arePathHeroesLocked(const AIPath & path) const
 
 		if(lockReason != HeroLockedReason::NOT_LOCKED)
 		{
-#if AI_TRACE_LEVEL >= 1
+#if NKAI_TRACE_LEVEL >= 1
 			logAi->trace("Hero %s is locked by STARTUP. Discarding %s", path.targetHero->name, path.toString());
 #endif
 			return true;
@@ -213,13 +220,30 @@ void Nullkiller::makeTurn()
 	{
 		updateAiState(i);
 
+		Goals::TTask bestTask = taskptr(Goals::Invalid());
+		
+		do
+		{
+			Goals::TTaskVec fastTasks = {
+				choseBestTask(sptr(BuyArmyBehavior()), 1),
+				choseBestTask(sptr(RecruitHeroBehavior()), 1),
+				choseBestTask(sptr(BuildingBehavior()), 1)
+			};
+
+			bestTask = choseBestTask(fastTasks);
+
+			if(bestTask->priority >= 1)
+			{
+				executeTask(bestTask);
+				updateAiState(i, true);
+			}
+		} while(bestTask->priority >= 1);
+
 		Goals::TTaskVec bestTasks = {
-			choseBestTask(sptr(BuyArmyBehavior()), 1),
+			bestTask,
 			choseBestTask(sptr(CaptureObjectsBehavior()), 1),
 			choseBestTask(sptr(ClusterBehavior()), MAX_DEPTH),
-			choseBestTask(sptr(RecruitHeroBehavior()), 1),
 			choseBestTask(sptr(DefenceBehavior()), MAX_DEPTH),
-			choseBestTask(sptr(BuildingBehavior()), 1),
 			choseBestTask(sptr(GatherArmyBehavior()), MAX_DEPTH)
 		};
 
@@ -228,19 +252,25 @@ void Nullkiller::makeTurn()
 			bestTasks.push_back(choseBestTask(sptr(StartupBehavior()), 1));
 		}
 
-		Goals::TTask bestTask = choseBestTask(bestTasks);
+		bestTask = choseBestTask(bestTasks);
+
 		HeroPtr hero = bestTask->getHero();
 
+		HeroRole heroRole = HeroRole::MAIN;
+
+		if(hero.validAndSet())
+			heroRole = heroManager->getHeroRole(hero);
+
+		if(heroRole != HeroRole::MAIN || bestTask->getHeroExchangeCount() <= 1)
+			useHeroChain = false;
+
 		if(bestTask->priority < NEXT_SCAN_MIN_PRIORITY
 			&& scanDepth != ScanDepth::FULL)
 		{
-			HeroRole heroRole = HeroRole::MAIN;
-
-			if(hero.validAndSet())
-				heroRole = heroManager->getHeroRole(hero);
-
 			if(heroRole == HeroRole::MAIN || bestTask->priority < MIN_PRIORITY)
 			{
+				useHeroChain = false;
+
 				logAi->trace(
 					"Goal %s has too low priority %f so increasing scan depth",
 					bestTask->toString(),
@@ -258,26 +288,31 @@ void Nullkiller::makeTurn()
 			return;
 		}
 
-		std::string taskDescr = bestTask->toString();
+		executeTask(bestTask);
+	}
+}
 
-		boost::this_thread::interruption_point();
-		logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, bestTask->priority);
+void Nullkiller::executeTask(Goals::TTask task)
+{
+	std::string taskDescr = task->toString();
 
-		try
-		{
-			bestTask->accept(ai.get());
-		}
-		catch(goalFulfilledException &)
-		{
-			logAi->trace("Task %s completed", bestTask->toString());
-		}
-		catch(std::exception & e)
-		{
-			logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr);
-			logAi->debug("The error message was: %s", e.what());
+	boost::this_thread::interruption_point();
+	logAi->debug("Trying to realize %s (value %2.3f)", taskDescr, task->priority);
 
-			return;
-		}
+	try
+	{
+		task->accept(ai.get());
+	}
+	catch(goalFulfilledException &)
+	{
+		logAi->trace("Task %s completed", task->toString());
+	}
+	catch(std::exception & e)
+	{
+		logAi->debug("Failed to realize subgoal of type %s, I will stop.", taskDescr);
+		logAi->debug("The error message was: %s", e.what());
+
+		throw;
 	}
 }
 
@@ -294,3 +329,5 @@ void Nullkiller::lockResources(const TResources & res)
 {
 	lockedResources += res;
 }
+
+}

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

@@ -19,6 +19,9 @@
 #include "../Analyzers/HeroManager.h"
 #include "../Analyzers/ObjectClusterizer.h"
 
+namespace NKAI
+{
+
 const float MAX_GOLD_PEASURE = 0.3f;
 const float MIN_PRIORITY = 0.01f;
 const float NEXT_SCAN_MIN_PRIORITY = 0.4f;
@@ -51,6 +54,7 @@ private:
 	std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes;
 	ScanDepth scanDepth;
 	TResources lockedResources;
+	bool useHeroChain;
 
 public:
 	std::unique_ptr<DangerHitMapAnalyzer> dangerHitMap;
@@ -86,7 +90,10 @@ public:
 
 private:
 	void resetAiState();
-	void updateAiState(int pass);
+	void updateAiState(int pass, bool fast = false);
 	Goals::TTask choseBestTask(Goals::TSubgoal behavior, int decompositionMaxDepth) const;
 	Goals::TTask choseBestTask(Goals::TTaskVec & tasks) const;
+	void executeTask(Goals::TTask task);
 };
+
+}

+ 6 - 1
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -26,6 +26,9 @@
 #include "../Markers/ArmyUpgrade.h"
 #include "../Markers/DefendTown.h"
 
+namespace NKAI
+{
+
 #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
 
@@ -847,7 +850,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
 		logAi->error("evaluate VisitTile: %s", fe.getWhat());
 	}
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_TRACE_LEVEL >= 2
 	logAi->trace("Evaluated %s, loss: %f, turn: %d, turns main: %f, scout: %f, gold: %d, cost: %d, army gain: %d, danger: %d, role: %s, strategical value: %f, cwr: %f, fear: %f, result %f",
 		task->toString(),
 		evaluationContext.armyLossPersentage,
@@ -867,3 +870,5 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
 
 	return result;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -18,6 +18,9 @@ class CGWitchHut;
 
 VCMI_LIB_NAMESPACE_END
 
+namespace NKAI
+{
+
 class BuildingInfo;
 class Nullkiller;
 
@@ -105,3 +108,5 @@ private:
 
 	EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const;
 };
+
+}

+ 6 - 1
AI/Nullkiller/Goals/AbstractGoal.cpp

@@ -14,6 +14,9 @@
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/StringConstants.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -84,4 +87,6 @@ bool TSubgoal::operator==(const TSubgoal & rhs) const
 bool AbstractGoal::invalid() const
 {
 	return goalType == EGoals::INVALID;
-}
+}
+
+}

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

@@ -15,6 +15,9 @@
 #include "../../../lib/CTownHandler.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -162,6 +165,7 @@ namespace Goals
 		virtual std::string toString() const = 0;
 		virtual HeroPtr getHero() const = 0;
 		virtual ~ITask() {}
+		virtual int getHeroExchangeCount() const = 0;
 	};
 }
 
@@ -170,8 +174,8 @@ class cannotFulfillGoalException : public std::exception
 	std::string msg;
 
 public:
-	explicit cannotFulfillGoalException(crstring _Message)
-		: msg(_Message)
+	explicit cannotFulfillGoalException(const std::string  & message)
+		: msg(message)
 	{
 	}
 
@@ -207,3 +211,5 @@ public:
 		return msg.c_str();
 	}
 };
+
+}

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

@@ -13,6 +13,9 @@
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -69,3 +72,5 @@ std::string AdventureSpellCast::toString() const
 {
 	return "AdventureSpellCast " + spellID.toSpell()->name;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/AdventureSpellCast.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 namespace Goals
 {
 	class DLL_EXPORT AdventureSpellCast : public ElementarGoal<AdventureSpellCast>
@@ -35,3 +38,5 @@ namespace Goals
 		virtual bool operator==(const AdventureSpellCast & other) const override;
 	};
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/Build.cpp

@@ -1,3 +1,6 @@
+namespace Nullkiller
+{
+
 /*
 * Build.cpp, part of VCMI engine
 *
@@ -91,3 +94,5 @@ bool Build::fulfillsMe(TSubgoal goal)
 	else
 		return false;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/Build.h

@@ -1,3 +1,6 @@
+namespace Nullkiller
+{
+
 /*
 * Build.h, part of VCMI engine
 *
@@ -35,3 +38,5 @@ namespace Goals
 		}
 	};
 }
+
+}

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

@@ -14,6 +14,9 @@
 #include "../../../lib/CPathfinder.h"
 #include "../Behaviors/CaptureObjectsBehavior.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -77,3 +80,5 @@ std::string BuildBoat::toString() const
 {
 	return "BuildBoat";
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/BuildBoat.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 namespace Goals
 {
 	class DLL_EXPORT BuildBoat : public ElementarGoal<BuildBoat>
@@ -29,3 +32,5 @@ namespace Goals
 		virtual bool operator==(const BuildBoat & other) const override;
 	};
 }
+
+}

+ 6 - 1
AI/Nullkiller/Goals/BuildThis.cpp

@@ -16,6 +16,9 @@
 #include "../../../lib/StringConstants.h"
 
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -63,4 +66,6 @@ void BuildThis::accept(AIGateway * ai)
 	}
 
 	throw cannotFulfillGoalException("Cannot build a given structure!");
-}
+}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/BuildThis.h

@@ -12,6 +12,9 @@
 #include "CGoal.h"
 #include "../Analyzers/BuildAnalyzer.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -41,3 +44,5 @@ namespace Goals
 		void accept(AIGateway * ai) override;
 	};
 }
+
+}

+ 6 - 1
AI/Nullkiller/Goals/BuyArmy.cpp

@@ -14,6 +14,9 @@
 #include "../Engine/Nullkiller.h"
 
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -72,4 +75,6 @@ void BuyArmy::accept(AIGateway * ai)
 	{
 		ai->moveHeroToTile(town->visitablePos(), town->visitingHero.get());
 	}
-}
+}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/BuyArmy.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -40,3 +43,5 @@ namespace Goals
 		virtual void accept(AIGateway * ai) override;
 	};
 }
+
+}

+ 6 - 28
AI/Nullkiller/Goals/CGoal.h

@@ -11,6 +11,9 @@
 
 #include "AbstractGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 
@@ -90,34 +93,9 @@ namespace Goals
 		virtual bool isElementar() const override { return true; }
 
 		virtual HeroPtr getHero() const override { return AbstractGoal::hero; }
-	};
-
-	class DLL_EXPORT Invalid : public ElementarGoal<Invalid>
-	{
-	public:
-		Invalid()
-			: ElementarGoal(Goals::INVALID)
-		{
-			priority = -1;
-		}
-		TGoalVec decompose() const override
-		{
-			return TGoalVec();
-		}
 
-		virtual bool operator==(const Invalid & other) const override
-		{
-			return true;
-		}
-
-		virtual std::string toString() const override
-		{
-			return "Invalid";
-		}
-
-		virtual void accept(AIGateway * ai) override
-		{
-			throw cannotFulfillGoalException("Can not fulfill Invalid goal!");
-		}
+		virtual int getHeroExchangeCount() const override { return 0; }
 	};
 }
+
+}

+ 6 - 1
AI/Nullkiller/Goals/CaptureObject.cpp

@@ -15,6 +15,9 @@
 #include "../Behaviors/CaptureObjectsBehavior.h"
 
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 
 using namespace Goals;
@@ -37,4 +40,6 @@ std::string CaptureObject::toString() const
 TGoalVec CaptureObject::decompose() const
 {
 	return CaptureObjectsBehavior(cb->getObj(ObjectInstanceID(objid))).decompose();
-}
+}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/CaptureObject.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -38,3 +41,5 @@ namespace Goals
 		virtual uint64_t getHash() const override;
 	};
 }
+
+}

+ 6 - 1
AI/Nullkiller/Goals/CompleteQuest.cpp

@@ -16,6 +16,9 @@
 #include "../../../lib/VCMI_Lib.h"
 #include "../../../lib/CGeneralTextHandler.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -245,4 +248,6 @@ TGoalVec CompleteQuest::missionDestroyObj() const
 	}
 
 	return TGoalVec();
-}
+}
+
+}

+ 4 - 0
AI/Nullkiller/Goals/CompleteQuest.h

@@ -14,6 +14,8 @@
 #include "../Goals/CGoal.h"
 #include "../../../lib/CGameState.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class CompleteQuest : public CGoal<CompleteQuest>
@@ -47,3 +49,5 @@ namespace Goals
 		std::string questToString() const;
 	};
 }
+
+}

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

@@ -16,6 +16,9 @@
 #include "../../../lib/StringConstants.h"
 
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -72,4 +75,11 @@ Composition & Composition::addNext(TSubgoal goal)
 bool Composition::isElementar() const
 {
 	return subtasks.back()->isElementar();
-}
+}
+
+int Composition::getHeroExchangeCount() const
+{
+	return isElementar() ? taskptr(*subtasks.back())->getHeroExchangeCount() : 0;
+}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/Composition.h

@@ -11,6 +11,8 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT Composition : public ElementarGoal<Composition>
@@ -36,5 +38,8 @@ namespace Goals
 		Composition & addNext(TSubgoal goal);
 		virtual TGoalVec decompose() const override;
 		virtual bool isElementar() const override;
+		virtual int getHeroExchangeCount() const override;
 	};
 }
+
+}

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

@@ -13,6 +13,9 @@
 #include "../AIUtility.h"
 
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -35,3 +38,5 @@ bool DigAtTile::operator==(const DigAtTile & other) const
 //
 //	return sptr(VisitTile(tile));
 //}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/DigAtTile.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -36,3 +39,5 @@ namespace Goals
 		//TSubgoal decomposeSingle() const override;
 	};
 }
+
+}

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

@@ -13,6 +13,9 @@
 #include "../../../lib/mapping/CMap.h" //for victory conditions
 #include "../../../lib/CPathfinder.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -37,3 +40,5 @@ std::string DismissHero::toString() const
 {
 	return "DismissHero " + hero.name;
 }
+
+}

+ 4 - 0
AI/Nullkiller/Goals/DismissHero.h

@@ -11,6 +11,8 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT DismissHero : public ElementarGoal<DismissHero>
@@ -27,3 +29,5 @@ namespace Goals
 		virtual bool operator==(const DismissHero & other) const override;
 	};
 }
+
+}

+ 6 - 1
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp

@@ -15,6 +15,9 @@
 #include "../../../lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -89,4 +92,6 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
 	}
 
 	logAi->debug("Put hero %s to garrison of %s", garrisonHero->name, town->name);
-}
+}
+
+}

+ 4 - 0
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.h

@@ -12,6 +12,8 @@
 #include "CGoal.h"
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT ExchangeSwapTownHeroes : public ElementarGoal<ExchangeSwapTownHeroes>
@@ -32,3 +34,5 @@ namespace Goals
 		virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
 	};
 }
+
+}

+ 6 - 1
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -14,6 +14,9 @@
 #include "../../../lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -197,4 +200,6 @@ bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 &
 	}
 
 	return ai->moveHeroToTile(tile, hero);
-}
+}
+
+}

+ 6 - 0
AI/Nullkiller/Goals/ExecuteHeroChain.h

@@ -12,6 +12,8 @@
 #include "CGoal.h"
 #include "../Pathfinding/AIPathfinder.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT ExecuteHeroChain : public ElementarGoal<ExecuteHeroChain>
@@ -31,7 +33,11 @@ namespace Goals
 		virtual bool operator==(const ExecuteHeroChain & other) const override;
 		const AIPath & getPath() const { return chainPath; }
 
+		virtual int getHeroExchangeCount() const override { return chainPath.exchangeCount; }
+
 	private:
 		bool moveHeroToTile(const CGHeroInstance * hero, const int3 & tile);
 	};
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/GatherArmy.cpp

@@ -1,3 +1,6 @@
+namespace Nullkiller
+{
+
 /*
 * GatherArmy.cpp, part of VCMI engine
 *
@@ -208,3 +211,5 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
 
 	return ret;
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/GatherArmy.h

@@ -1,3 +1,6 @@
+namespace Nullkiller
+{
+
 /*
 * GatherArmy.h, part of VCMI engine
 *
@@ -36,3 +39,5 @@ namespace Goals
 		virtual bool operator==(const GatherArmy & other) const override;
 	};
 }
+
+}

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

@@ -17,4 +17,4 @@
 #include "Trade.h"
 #include "RecruitHero.h"
 #include "DigAtTile.h"
-#include "AdventureSpellCast.h"
+#include "AdventureSpellCast.h"

+ 33 - 0
AI/Nullkiller/Goals/Invalid.h

@@ -11,9 +11,42 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 
 namespace Goals
 {
+	class DLL_EXPORT Invalid : public ElementarGoal<Invalid>
+	{
+	public:
+		Invalid()
+			: ElementarGoal(Goals::INVALID)
+		{
+			priority = -1;
+		}
+		TGoalVec decompose() const override
+		{
+			return TGoalVec();
+		}
+
+		virtual bool operator==(const Invalid & other) const override
+		{
+			return true;
+		}
+
+		virtual std::string toString() const override
+		{
+			return "Invalid";
+		}
+
+		virtual void accept(AIGateway * ai) override
+		{
+			throw cannotFulfillGoalException("Can not fulfill Invalid goal!");
+		}
+	};
+}
+
 }

+ 6 - 1
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -16,6 +16,9 @@
 #include "../../../lib/StringConstants.h"
 
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -42,4 +45,6 @@ void RecruitHero::accept(AIGateway * ai)
 	{
 		throw cannotFulfillGoalException("No town to recruit hero!");
 	}
-}
+}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/RecruitHero.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -42,3 +45,5 @@ namespace Goals
 		void accept(AIGateway * ai) override;
 	};
 }
+
+}

+ 5 - 0
AI/Nullkiller/Goals/SaveResources.cpp

@@ -14,6 +14,9 @@
 #include "../../../lib/CPathfinder.h"
 #include "../Behaviors/CaptureObjectsBehavior.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -37,3 +40,5 @@ std::string SaveResources::toString() const
 {
 	return "SaveResources " + resources.toString();
 }
+
+}

+ 4 - 0
AI/Nullkiller/Goals/SaveResources.h

@@ -11,6 +11,8 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT SaveResources : public ElementarGoal<SaveResources>
@@ -29,3 +31,5 @@ namespace Goals
 		virtual bool operator==(const SaveResources & other) const override;
 	};
 }
+
+}

+ 5 - 1
AI/Nullkiller/Goals/Trade.cpp

@@ -10,9 +10,13 @@
 #include "StdInc.h"
 #include "Trade.h"
 
+namespace NKAI
+{
 using namespace Goals;
 
 bool Trade::operator==(const Trade & other) const
 {
 	return resID == other.resID;
-}
+}
+
+}

+ 5 - 0
AI/Nullkiller/Goals/Trade.h

@@ -11,6 +11,9 @@
 
 #include "CGoal.h"
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -34,3 +37,5 @@ namespace Goals
 		virtual bool operator==(const Trade & other) const override;
 	};
 }
+
+}

+ 0 - 189
AI/Nullkiller/Goals/Win.cpp

@@ -1,189 +0,0 @@
-/*
-* Win.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 "Goals.h"
-#include "../AIGateway.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"
-
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<AIGateway> ai;
-extern FuzzyHelper * fh;
-
-using namespace Goals;
-
-TSubgoal Win::whatToDoToAchieve()
-{
-	auto toBool = [=](const EventCondition &)
-	{
-		// TODO: proper implementation
-		// Right now even already fulfilled goals will be included into generated list
-		// Proper check should test if event condition is already fulfilled
-		// Easiest way to do this is to call CGameState::checkForVictory but this function should not be
-		// used on client side or in AI code
-		return false;
-	};
-
-	std::vector<EventCondition> goals;
-
-	for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
-	{
-		//TODO: try to eliminate human player(s) using loss conditions that have isHuman element
-
-		if(event.effect.type == EventEffect::VICTORY)
-		{
-			boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
-		}
-	}
-
-	//TODO: instead of returning first encountered goal AI should generate list of possible subgoals
-	for(const EventCondition & goal : goals)
-	{
-		switch(goal.condition)
-		{
-		case EventCondition::HAVE_ARTIFACT:
-			return sptr(GetArtOfType(goal.objectType));
-		case EventCondition::DESTROY:
-		{
-			if(goal.object)
-			{
-				auto obj = cb->getObj(goal.object->id);
-				/*if(obj)
-					if(obj->getOwner() == ai->playerID) //we can't capture our own object
-						return sptr(Conquer());*/
-
-
-				return sptr(VisitObj(goal.object->id.getNum()));
-			}
-			else
-			{
-				// TODO: destroy all objects of type goal.objectType
-				// This situation represents "kill all creatures" condition from H3
-				break;
-			}
-		}
-		case EventCondition::HAVE_BUILDING:
-		{
-			// TODO build other buildings apart from Grail
-			// goal.objectType = buidingID to build
-			// goal.object = optional, town in which building should be built
-			// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
-
-			if(goal.objectType == BuildingID::GRAIL)
-			{
-				if(auto h = ai->getHeroWithGrail())
-				{
-					//hero is in a town that can host Grail
-					if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
-					{
-						const CGTownInstance * t = h->visitedTown;
-						return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
-					}
-					else
-					{
-						auto towns = cb->getTownsInfo();
-						towns.erase(boost::remove_if(towns,
-										[](const CGTownInstance * t) -> bool
-							{
-								return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
-							}),
-								towns.end());
-						boost::sort(towns, CDistanceSorter(h.get()));
-						if(towns.size())
-						{
-							return sptr(VisitTile(towns.front()->visitablePos()).sethero(h));
-						}
-					}
-				}
-				double ratio = 0;
-				// maybe make this check a bit more complex? For example:
-				// 0.75 -> dig randomly within 3 tiles radius
-				// 0.85 -> radius now 2 tiles
-				// 0.95 -> 1 tile radius, position is fully known
-				// AFAIK H3 AI does something like this
-				int3 grailPos = cb->getGrailPos(&ratio);
-				if(ratio > 0.99)
-				{
-					return sptr(DigAtTile(grailPos));
-				} //TODO: use FIND_OBJ
-				else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
-					return sptr(VisitObj(obj->id.getNum()));
-				/*else
-					return sptr(Explore());*/
-			}
-			break;
-		}
-		case EventCondition::CONTROL:
-		{
-			if(goal.object)
-			{
-				auto objRelations = cb->getPlayerRelations(ai->playerID, goal.object->tempOwner);
-				
-				if(objRelations == PlayerRelations::ENEMIES)
-				{
-					return sptr(VisitObj(goal.object->id.getNum()));
-				}
-				else
-				{
-					// TODO: Defance
-					break;
-				}
-			}
-			else
-			{
-				//TODO: control all objects of type "goal.objectType"
-				// Represents H3 condition "Flag all mines"
-				break;
-			}
-		}
-
-		case EventCondition::HAVE_RESOURCES:
-			//TODO mines? piles? marketplace?
-			//save?
-			return sptr(CollectRes(static_cast<Res::ERes>(goal.objectType), goal.value));
-		case EventCondition::HAVE_CREATURES:
-			return sptr(GatherTroops(goal.objectType, goal.value));
-		case EventCondition::TRANSPORT:
-		{
-			//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
-			// Represents "transport artifact" condition:
-			// goal.objectType = type of artifact
-			// goal.object = destination-town where artifact should be transported
-			break;
-		}
-		case EventCondition::STANDARD_WIN:
-			return sptr(Invalid());
-
-		// Conditions that likely don't need any implementation
-		case EventCondition::DAYS_PASSED:
-			break; // goal.value = number of days for condition to trigger
-		case EventCondition::DAYS_WITHOUT_TOWN:
-			break; // goal.value = number of days to trigger this
-		case EventCondition::IS_HUMAN:
-			break; // Should be only used in calculation of candidates (see toBool lambda)
-		case EventCondition::CONST_VALUE:
-			break;
-
-		case EventCondition::HAVE_0:
-		case EventCondition::HAVE_BUILDING_0:
-		case EventCondition::DESTROY_0:
-			//TODO: support new condition format
-			return sptr(Invalid());
-		default:
-			assert(0);
-		}
-	}
-	return sptr(Invalid());
-}

+ 0 - 39
AI/Nullkiller/Goals/Win.h

@@ -1,39 +0,0 @@
-/*
-* Win.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 "CGoal.h"
-
-struct HeroPtr;
-class AIGateway;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT Win : public CGoal<Win>
-	{
-	public:
-		Win()
-			: CGoal(Goals::WIN)
-		{
-			priority = 100;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-
-		virtual bool operator==(const Win & other) const override
-		{
-			return true;
-		}
-	};
-}

+ 6 - 1
AI/Nullkiller/Markers/ArmyUpgrade.cpp

@@ -13,6 +13,9 @@
 #include "../Engine/Nullkiller.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -33,4 +36,6 @@ bool ArmyUpgrade::operator==(const ArmyUpgrade & other) const
 std::string ArmyUpgrade::toString() const
 {
 	return "Army upgrade at " + upgrader->getObjectName() + upgrader->visitablePos().toString();
-}
+}
+
+}

+ 5 - 1
AI/Nullkiller/Markers/ArmyUpgrade.h

@@ -13,6 +13,8 @@
 #include "../Pathfinding/AINodeStorage.h"
 #include "../Analyzers/ArmyManager.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT ArmyUpgrade : public CGoal<ArmyUpgrade>
@@ -32,4 +34,6 @@ namespace Goals
 		uint64_t getUpgradeValue() const { return upgradeValue; }
 		uint64_t getInitialArmyValue() const { return initialValue; }
 	};
-}
+}
+
+}

+ 6 - 1
AI/Nullkiller/Markers/DefendTown.cpp

@@ -13,6 +13,9 @@
 #include "../Engine/Nullkiller.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 using namespace Goals;
 
 DefendTown::DefendTown(const CGTownInstance * town, const HitMapInfo & treat, const AIPath & defencePath)
@@ -37,4 +40,6 @@ bool DefendTown::operator==(const DefendTown & other) const
 std::string DefendTown::toString() const
 {
 	return "Defend town " + town->getObjectName();
-}
+}
+
+}

+ 5 - 1
AI/Nullkiller/Markers/DefendTown.h

@@ -14,6 +14,8 @@
 #include "../Analyzers/ArmyManager.h"
 #include "../Analyzers/DangerHitMapAnalyzer.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT DefendTown : public CGoal<DefendTown>
@@ -36,4 +38,6 @@ namespace Goals
 
 		uint8_t getTurn() const { return turn; }
 	};
-}
+}
+
+}

+ 6 - 1
AI/Nullkiller/Markers/HeroExchange.cpp

@@ -14,6 +14,9 @@
 #include "../AIUtility.h"
 #include "../Analyzers/ArmyManager.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -34,4 +37,6 @@ uint64_t HeroExchange::getReinforcementArmyStrength() const
 	uint64_t armyValue = ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero.get(), exchangePath.heroArmy);
 
 	return armyValue;
-}
+}
+
+}

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

@@ -12,6 +12,8 @@
 #include "../Goals/CGoal.h"
 #include "../Pathfinding/AINodeStorage.h"
 
+namespace NKAI
+{
 namespace Goals
 {
 	class DLL_EXPORT HeroExchange : public CGoal<HeroExchange>
@@ -31,4 +33,6 @@ namespace Goals
 
 		uint64_t getReinforcementArmyStrength() const;
 	};
-}
+}
+
+}

+ 6 - 1
AI/Nullkiller/Markers/UnlockCluster.cpp

@@ -13,6 +13,9 @@
 #include "../Engine/Nullkiller.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -26,4 +29,6 @@ bool UnlockCluster::operator==(const UnlockCluster & other) const
 std::string UnlockCluster::toString() const
 {
 	return "Unlock Cluster " + cluster->blocker->getObjectName() + tile.toString();
-}
+}
+
+}

+ 6 - 1
AI/Nullkiller/Markers/UnlockCluster.h

@@ -13,6 +13,9 @@
 #include "../Analyzers/ObjectClusterizer.h"
 
 
+namespace NKAI
+{
+
 struct HeroPtr;
 class AIGateway;
 class FuzzyHelper;
@@ -38,4 +41,6 @@ namespace Goals
 		std::shared_ptr<ObjectCluster> getCluster() const { return cluster; }
 		const AIPath & getPathToCenter() { return pathToCenter; }
 	};
-}
+}
+
+}

+ 107 - 78
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -19,27 +19,24 @@
 #include "../../../lib/PathfinderUtil.h"
 #include "../../../lib/CPlayerState.h"
 
+namespace NKAI
+{
+
 std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
 std::set<int3> commitedTiles;
 std::set<int3> commitedTilesInitial;
 
-#ifdef ENVIRONMENT64
-const int BUCKET_COUNT = 11;
-#else
-const int BUCKET_COUNT = 7;
-#endif // ENVIRONMENT64
 
 const uint64_t FirstActorMask = 1;
-const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
-const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
 const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN = 5000;
 const uint64_t MIN_ARMY_STRENGTH_FOR_NEXT_ACTOR = 1000;
+const uint64_t CHAIN_MAX_DEPTH = 4;
 
 AISharedStorage::AISharedStorage(int3 sizes)
 {
 	if(!shared){
 		shared.reset(new boost::multi_array<AIPathNode, 5>(
-			boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]));
+			boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][AIPathfinding::NUM_CHAINS]));
 	}
 
 	nodes = shared;
@@ -88,11 +85,11 @@ void AINodeStorage::initialize(const PathfinderOptions & options, const CGameSta
 			{
 				for(pos.y = 0; pos.y < sizes.y; ++pos.y)
 				{
-					const TerrainTile* tile = &gs->map->getTile(pos);
-					if (!tile->terType.isPassable())
+					const TerrainTile & tile = gs->map->getTile(pos);
+					if (!tile.terType->isPassable())
 						continue;
 
-					if (tile->terType.isWater())
+					if (tile.terType->isWater())
 					{
 						resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
 						if (useFlying)
@@ -139,16 +136,16 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
 	const EPathfindingLayer layer, 
 	const ChainActor * actor)
 {
-	int bucketIndex = ((uintptr_t)actor) % BUCKET_COUNT;
-	int bucketOffset = bucketIndex * BUCKET_SIZE;
-	auto chains = nodes.get(pos, layer); //FIXME: chain was the innermost layer
+	int bucketIndex = ((uintptr_t)actor) % AIPathfinding::BUCKET_COUNT;
+	int bucketOffset = bucketIndex * AIPathfinding::BUCKET_SIZE;
+	auto chains = nodes.get(pos, layer);
 
 	if(chains[0].blocked())
 	{
 		return boost::none;
 	}
 
-	for(auto i = BUCKET_SIZE - 1; i >= 0; i--)
+	for(auto i = AIPathfinding::BUCKET_SIZE - 1; i >= 0; i--)
 	{
 		AIPathNode & node = chains[i + bucketOffset];
 
@@ -171,8 +168,9 @@ boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
 std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
 {
 	if(heroChainPass)
-{
-		calculateTownPortalTeleportations(heroChain);
+	{
+		if(heroChainTurn == 0)
+			calculateTownPortalTeleportations(heroChain);
 
 		return heroChain;
 	}
@@ -207,7 +205,8 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
 		}
 	}
 
-	calculateTownPortalTeleportations(initialNodes);
+	if(heroChainTurn == 0)
+		calculateTownPortalTeleportations(initialNodes);
 
 	return initialNodes;
 }
@@ -265,7 +264,7 @@ void AINodeStorage::commit(
 	destination->theNodeBefore = source->theNodeBefore;
 	destination->chainOther = nullptr;
 
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 	logAi->trace(
 		"Commited %s -> %s, cost: %f, turn: %s, mp: %d, hero: %s, mask: %x, army: %lld",
 		source->coord.toString(),
@@ -406,8 +405,8 @@ public:
 		AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
 		:existingChains(), newChains(), delayedWork(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
 	{
-		existingChains.reserve(NUM_CHAINS);
-		newChains.reserve(NUM_CHAINS);
+		existingChains.reserve(AIPathfinding::NUM_CHAINS);
+		newChains.reserve(AIPathfinding::NUM_CHAINS);
 	}
 
 	void execute(const blocked_range<size_t>& r)
@@ -593,7 +592,7 @@ void HeroChainCalculationTask::cleanupInefectiveChains(std::vector<ExchangeCandi
 		auto isNotEffective = storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, chains)
 			|| storage.hasBetterChain(chainInfo.carrierParent, &chainInfo, result);
 
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 		if(isNotEffective)
 		{
 			logAi->trace(
@@ -623,6 +622,9 @@ void HeroChainCalculationTask::calculateHeroChain(
 		if((node->actor->chainMask & chainMask) == 0 && (srcNode->actor->chainMask & chainMask) == 0)
 			continue;
 
+		if(node->actor->actorExchangeCount + srcNode->actor->actorExchangeCount > CHAIN_MAX_DEPTH)
+			continue;
+
 		if(node->action == CGPathNode::ENodeAction::BATTLE
 			|| node->action == CGPathNode::ENodeAction::TELEPORT_BATTLE
 			|| node->action == CGPathNode::ENodeAction::TELEPORT_NORMAL
@@ -635,7 +637,7 @@ void HeroChainCalculationTask::calculateHeroChain(
 			|| (node->action == CGPathNode::ENodeAction::UNKNOWN && node->actor->hero)
 			|| (node->actor->chainMask & srcNode->actor->chainMask) != 0)
 		{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 			logAi->trace(
 				"Skip exchange %s[%x] -> %s[%x] at %s because of %s",
 				node->actor->toString(),
@@ -652,7 +654,7 @@ void HeroChainCalculationTask::calculateHeroChain(
 			continue;
 		}
 
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 		logAi->trace(
 			"Thy exchange %s[%x] -> %s[%x] at %s",
 			node->actor->toString(),
@@ -676,7 +678,7 @@ void HeroChainCalculationTask::calculateHeroChain(
 		&& carrier->action != CGPathNode::BLOCKING_VISIT
 		&& (other->armyLoss == 0 || other->armyLoss < other->actor->armyValue))
 	{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 		logAi->trace(
 			"Exchange allowed %s[%x] -> %s[%x] at %s",
 			other->actor->toString(),
@@ -693,7 +695,7 @@ void HeroChainCalculationTask::calculateHeroChain(
 
 			if(hasLessMp && hasLessExperience)
 			{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 				logAi->trace("Exchange at %s is ineficient. Blocked.", carrier->coord.toString());
 #endif
 				return;
@@ -718,7 +720,7 @@ void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate>
 
 		if(!chainNodeOptional)
 		{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 			logAi->trace("Exchange at %s can not allocate node. Blocked.", carrier->coord.toString());
 #endif
 			continue;
@@ -728,7 +730,7 @@ void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate>
 
 		if(exchangeNode->action != CGPathNode::ENodeAction::UNKNOWN)
 		{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 			logAi->trace(
 				"Skip exchange %s[%x] -> %s[%x] at %s because node is in use",
 				other->actor->toString(),
@@ -742,7 +744,7 @@ void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate>
 		
 		if(exchangeNode->turns != 0xFF && exchangeNode->getCost() < chainInfo.getCost())
 		{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 			logAi->trace(
 				"Skip exchange %s[%x] -> %s[%x] at %s because not effective enough. %f < %f",
 				other->actor->toString(),
@@ -773,7 +775,7 @@ void HeroChainCalculationTask::addHeroChain(const std::vector<ExchangeCandidate>
 		exchangeNode->chainOther = other;
 		exchangeNode->armyLoss = chainInfo.armyLoss;
 
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 		logAi->trace(
 			"Chain accepted at %s %s -> %s, mask %x, cost %f, turn: %s, mp: %d, army %i", 
 			exchangeNode->coord.toString(), 
@@ -994,8 +996,6 @@ struct TowmPortalFinder
 
 	CGPathNode * getBestInitialNodeForTownPortal(const CGTownInstance * targetTown)
 	{
-		CGPathNode * bestNode = nullptr;
-
 		for(CGPathNode * node : initialNodes)
 		{
 			auto aiNode = nodeStorage->getAINode(node);
@@ -1018,11 +1018,10 @@ struct TowmPortalFinder
 					continue;
 			}
 
-			if(!bestNode || bestNode->getCost() > node->getCost())
-				bestNode = node;
+			return node;
 		}
 
-		return bestNode;
+		return nullptr;
 	}
 
 	boost::optional<AIPathNode *> createTownPortalNode(const CGTownInstance * targetTown)
@@ -1060,6 +1059,55 @@ struct TowmPortalFinder
 	}
 };
 
+template<class TVector>
+void AINodeStorage::calculateTownPortal(
+	const ChainActor * actor,
+	const std::map<const CGHeroInstance *, int> & maskMap,
+	const std::vector<CGPathNode *> & initialNodes,
+	TVector & output)
+{
+	auto towns = cb->getTownsInfo(false);
+
+	vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
+		{
+			return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
+		});
+
+	if(!towns.size())
+	{
+		return; // no towns no need to run loop further
+	}
+
+	TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
+
+	if(townPortalFinder.actorCanCastTownPortal())
+	{
+		for(const CGTownInstance * targetTown : towns)
+		{
+			// TODO: allow to hide visiting hero in garrison
+			if(targetTown->visitingHero)
+			{
+				auto basicMask = maskMap.at(targetTown->visitingHero.get());
+				bool heroIsInChain = (actor->chainMask & basicMask) != 0;
+				bool sameActorInTown = actor->chainMask == basicMask;
+
+				if(sameActorInTown || !heroIsInChain)
+					continue;
+			}
+
+			auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
+
+			if(nodeOptional)
+			{
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 1
+				logAi->trace("Adding town portal node at %s", targetTown->name);
+#endif
+				output.push_back(nodeOptional.get());
+			}
+		}
+	}
+}
+
 void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *> & initialNodes)
 {
 	std::set<const ChainActor *> actorsOfInitial;
@@ -1068,7 +1116,8 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
 	{
 		auto aiNode = getAINode(node);
 
-		actorsOfInitial.insert(aiNode->actor->baseActor);
+		if(aiNode->actor->hero)
+			actorsOfInitial.insert(aiNode->actor->baseActor);
 	}
 
 	std::map<const CGHeroInstance *, int> maskMap;
@@ -1079,50 +1128,28 @@ void AINodeStorage::calculateTownPortalTeleportations(std::vector<CGPathNode *>
 			maskMap[basicActor->hero] = basicActor->chainMask;
 	}
 
-	for(const ChainActor * actor : actorsOfInitial)
-	{
-		if(!actor->hero)
-			continue;
+	boost::sort(initialNodes, NodeComparer<CGPathNode>());
 
-		auto towns = cb->getTownsInfo(false);
+	std::vector<const ChainActor *> actorsVector(actorsOfInitial.begin(), actorsOfInitial.end());
+	tbb::concurrent_vector<CGPathNode *> output;
 
-		vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
-		{
-			return cb->getPlayerRelations(actor->hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
-		});
-
-		if(!towns.size())
-		{
-			return; // no towns no need to run loop further
-		}
-
-		TowmPortalFinder townPortalFinder(actor, initialNodes, towns, this);
-
-		if(townPortalFinder.actorCanCastTownPortal())
-		{
-			for(const CGTownInstance * targetTown : towns)
+	if(actorsVector.size() * initialNodes.size() > 1000)
+	{
+		parallel_for(blocked_range<size_t>(0, actorsVector.size()), [&](const blocked_range<size_t> & r)
 			{
-				// TODO: allow to hide visiting hero in garrison
-				if(targetTown->visitingHero)
+				for(int i = r.begin(); i != r.end(); i++)
 				{
-					auto basicMask = maskMap[targetTown->visitingHero.get()];
-					bool heroIsInChain = (actor->chainMask & basicMask) != 0;
-					bool sameActorInTown = actor->chainMask == basicMask;
-
-					if(sameActorInTown || !heroIsInChain)
-						continue;
+					calculateTownPortal(actorsVector[i], maskMap, initialNodes, output);
 				}
+			});
 
-				auto nodeOptional = townPortalFinder.createTownPortalNode(targetTown);
-
-				if(nodeOptional)
-				{
-#if PATHFINDER_TRACE_LEVEL >= 1
-					logAi->trace("Adding town portal node at %s", targetTown->name);
-#endif
-					initialNodes.push_back(nodeOptional.get());
-				}
-			}
+		std::copy(output.begin(), output.end(), std::back_inserter(initialNodes));
+	}
+	else
+	{
+		for(auto actor : actorsVector)
+		{
+			calculateTownPortal(actor, maskMap, initialNodes, initialNodes);
 		}
 	}
 }
@@ -1156,7 +1183,7 @@ bool AINodeStorage::hasBetterChain(
 		{
 			if(node.getCost() < candidateNode->getCost())
 			{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 				logAi->trace(
 					"Block ineficient battle move %s->%s, hero: %s[%X], army %lld, mp diff: %i",
 					source->coord.toString(),
@@ -1180,7 +1207,7 @@ bool AINodeStorage::hasBetterChain(
 		if(nodeArmyValue > candidateArmyValue
 			&& node.getCost() <= candidateNode->getCost())
 		{
-#if PATHFINDER_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 			logAi->trace(
 				"Block ineficient move because of stronger army %s->%s, hero: %s[%X], army %lld, mp diff: %i",
 				source->coord.toString(),
@@ -1206,7 +1233,7 @@ bool AINodeStorage::hasBetterChain(
 					continue;
 				}
 
-#if AI_TRACE_LEVEL >= 2
+#if NKAI_PATHFINDER_TRACE_LEVEL >= 2
 				logAi->trace(
 					"Block ineficient move because of stronger hero %s->%s, hero: %s[%X], army %lld, mp diff: %i",
 					source->coord.toString(),
@@ -1244,7 +1271,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
 {
 	std::vector<AIPath> paths;
 
-	paths.reserve(NUM_CHAINS / 4);
+	paths.reserve(AIPathfinding::NUM_CHAINS / 4);
 
 	auto chains = nodes.get(pos, isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL);
 
@@ -1428,3 +1455,5 @@ std::string AIPath::toString() const
 
 	return str.str();
 }
+
+}

+ 25 - 7
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -10,10 +10,8 @@
 
 #pragma once
 
-#define PATHFINDER_TRACE_LEVEL 0
-#define AI_TRACE_LEVEL 0
-#define SCOUT_TURN_DISTANCE_LIMIT 3
-#define MAIN_TURN_DISTANCE_LIMIT 5
+#define NKAI_PATHFINDER_TRACE_LEVEL 0
+#define NKAI_TRACE_LEVEL 0
 
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
@@ -23,12 +21,23 @@
 #include "Actions/SpecialAction.h"
 #include "Actors.h"
 
+namespace NKAI
+{
+	const int SCOUT_TURN_DISTANCE_LIMIT = 3;
+	const int MAIN_TURN_DISTANCE_LIMIT = 5;
+
 namespace AIPathfinding
 {
-	const int BUCKET_COUNT = 11;
-	const int BUCKET_SIZE = GameConstants::MAX_HEROES_PER_PLAYER;
+#ifdef ENVIRONMENT64
+	const int BUCKET_COUNT = 7;
+#else
+	const int BUCKET_COUNT = 5;
+#endif // ENVIRONMENT64
+
+	const int BUCKET_SIZE = 5;
 	const int NUM_CHAINS = BUCKET_COUNT * BUCKET_SIZE;
 	const int THREAD_COUNT = 8;
+	const int CHAIN_MAX_DEPTH = 4;
 }
 
 struct AIPathNode : public CGPathNode
@@ -239,7 +248,16 @@ public:
 		return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;
 	}
 
-
 	void calculateTownPortalTeleportations(std::vector<CGPathNode *> & neighbours);
 	void fillChainInfo(const AIPathNode * node, AIPath & path, int parentIndex) const;
+
+private:
+	template<class TVector>
+	void calculateTownPortal(
+		const ChainActor * actor,
+		const std::map<const CGHeroInstance *, int> & maskMap,
+		const std::vector<CGPathNode *> & initialNodes,
+		TVector & output);
 };
+
+}

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

@@ -14,6 +14,9 @@
 #include "../../../lib/mapping/CMap.h"
 #include "../Engine/Nullkiller.h"
 
+namespace NKAI
+{
+
 AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai)
 	:cb(cb), ai(ai)
 {
@@ -99,3 +102,5 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
 
 	logAi->trace("Recalculated paths in %ld", timeElapsed(start));
 }
+
+}

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

@@ -13,6 +13,9 @@
 #include "AINodeStorage.h"
 #include "../AIUtility.h"
 
+namespace NKAI
+{
+
 class Nullkiller;
 
 struct PathfinderSettings
@@ -42,3 +45,5 @@ public:
 	void updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes, PathfinderSettings pathfinderSettings);
 	void init();
 };
+
+}

+ 4 - 0
AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp

@@ -15,6 +15,8 @@
 #include "Rules/AIPreviousNodeRule.h"
 #include "../Engine//Nullkiller.h"
 
+namespace NKAI
+{
 namespace AIPathfinding
 {
 	std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset(
@@ -55,3 +57,5 @@ namespace AIPathfinding
 		return helper.get();
 	}
 }
+
+}

+ 5 - 0
AI/Nullkiller/Pathfinding/AIPathfinderConfig.h

@@ -12,6 +12,9 @@
 
 #include "AINodeStorage.h"
 
+namespace NKAI
+{
+
 class Nullkiller;
 
 namespace AIPathfinding
@@ -31,3 +34,5 @@ namespace AIPathfinding
 		virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;
 	};
 }
+
+}

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

@@ -14,6 +14,9 @@
 #include "../../Goals/CompleteQuest.h"
 #include "../../../../lib/mapping/CMap.h" //for victory conditions
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -28,4 +31,6 @@ namespace AIPathfinding
 	{
 		return "Battle at " + targetTile.toString();
 	}
-}
+}
+
+}

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

@@ -13,6 +13,9 @@
 #include "SpecialAction.h"
 #include "../../../../lib/CGameState.h"
 
+namespace NKAI
+{
+
 namespace AIPathfinding
 {
 	class BattleAction : public SpecialAction
@@ -30,4 +33,6 @@ namespace AIPathfinding
 
 		virtual std::string toString() const override;
 	};
-}
+}
+
+}

+ 10 - 4
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp

@@ -12,11 +12,15 @@
 #include "../../AIGateway.h"
 #include "../../Goals/AdventureSpellCast.h"
 #include "../../Goals/CaptureObject.h"
+#include "../../Goals/Invalid.h"
 #include "../../Goals/BuildBoat.h"
 #include "../../../../lib/mapping/CMap.h"
 #include "../../../../lib/mapObjects/MapObjects.h"
 #include "BoatActions.h"
 
+namespace NKAI
+{
+
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<AIGateway> ai;
 
@@ -34,7 +38,7 @@ namespace AIPathfinding
 			return Goals::sptr(Goals::CaptureObject(shipyard->o));
 		}
 		
-		return sptr(Goals::Invalid());
+		return Goals::sptr(Goals::Invalid());
 	}
 
 	bool BuildBoatAction::canAct(const AIPathNode * source) const
@@ -43,7 +47,7 @@ namespace AIPathfinding
 
 		if(cb->getPlayerRelations(hero->tempOwner, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
 		{
-#if AI_TRACE_LEVEL > 1
+#if NKAI_TRACE_LEVEL > 1
 			logAi->trace("Can not build a boat. Shipyard is enemy.");
 #endif
 			return false;
@@ -55,7 +59,7 @@ namespace AIPathfinding
 
 		if(!cb->getResourceAmount().canAfford(source->actor->armyCost + boatCost))
 		{
-#if AI_TRACE_LEVEL > 1
+#if NKAI_TRACE_LEVEL > 1
 			logAi->trace("Can not build a boat. Not enough resources.");
 #endif
 
@@ -128,4 +132,6 @@ namespace AIPathfinding
 
 		return hero->getSpellCost(summonBoat.toSpell());
 	}
-}
+}
+
+}

部分文件因为文件数量过多而无法显示