Browse Source

Nullkiller: fix file headers, move fuzzy config to ai folder

Andrii Danylchenko 4 years ago
parent
commit
ada76a5603

+ 10 - 1
AI/Nullkiller/Behaviors/Behavior.h

@@ -1,3 +1,12 @@
+/*
+* 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 "../VCAI.h"
@@ -7,4 +16,4 @@ class Behavior
 public:
 	virtual Goals::TGoalVec getTasks() = 0;
 	virtual std::string toString() const = 0;
-};
+};

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

@@ -1,5 +1,5 @@
 /*
-* Goals.cpp, part of VCMI engine
+* BuyArmyBehavior.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -73,4 +73,4 @@ Goals::TGoalVec BuyArmyBehavior::getTasks()
 	}
 
 	return tasks;
-}
+}

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

@@ -1,5 +1,5 @@
 /*
-* Goals.h, part of VCMI engine
+* BuyArmyBehavior.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *

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

@@ -1,12 +1,12 @@
 /*
- * Goals.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
- *
- */
+* CaptureObjectsBehavior.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 "../VCAI.h"
 #include "../Engine/Nullkiller.h"

+ 8 - 8
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h

@@ -1,12 +1,12 @@
 /*
- * Goals.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
- *
- */
+* CaptureObjectsBehavior.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
 #pragma once
 
 #include "lib/VCMI_Lib.h"

+ 1 - 154
AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp

@@ -1,5 +1,5 @@
 /*
-* Goals.cpp, part of VCMI engine
+* RecruitHeroBehavior.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -43,156 +43,3 @@ Goals::TGoalVec RecruitHeroBehavior::getTasks()
 
 	return tasks;
 }
-
-std::string StartupBehavior::toString() const
-{
-	return "Startup";
-}
-
-const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPath> & paths)
-{
-	auto shortestPath = *vstd::minElementByFun(paths, [town](const AIPath & path) -> float
-	{
-		if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
-			return 1;
-
-		return path.movementCost();
-	});
-
-	return shortestPath;
-}
-
-const CGHeroInstance * getNearestHero(const CGTownInstance * town)
-{
-	auto paths = ai->ah->getPathsToTile(town->visitablePos());
-
-	if(paths.empty())
-		return nullptr;
-
-	auto shortestPath = getShortestPath(town, paths);
-
-	if(shortestPath.nodes.size() > 1 
-		|| shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4
-		|| town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get())
-		return nullptr;
-
-	return shortestPath.targetHero;
-}
-
-bool needToRecruitHero(const CGTownInstance * startupTown)
-{
-	if(!ai->canRecruitAnyHero(startupTown))
-		return false;
-
-	if(!startupTown->garrisonHero && !startupTown->visitingHero)
-		return false;
-
-	auto heroToCheck = startupTown->garrisonHero ? startupTown->garrisonHero.get() : startupTown->visitingHero.get();
-	auto paths = cb->getPathsInfo(heroToCheck);
-
-	for(auto obj : ai->visitableObjs)
-	{
-		if(obj->ID == Obj::RESOURCE && obj->subID == Res::GOLD
-			|| obj->ID == Obj::TREASURE_CHEST
-			|| obj->ID == Obj::CAMPFIRE
-			|| obj->ID == Obj::WATER_WHEEL)
-		{
-			auto path = paths->getPathInfo(obj->visitablePos());
-			if(path->accessible == CGPathNode::BLOCKVIS && path->turns != 255)
-			{
-				return true;
-			}
-		}
-	}
-
-	return false;
-}
-
-Goals::TGoalVec StartupBehavior::getTasks()
-{
-	Goals::TGoalVec tasks;
-	auto towns = cb->getTownsInfo();
-
-	if(!towns.size())
-		return tasks;
-
-	const CGTownInstance * startupTown = towns.front();
-	bool canRecruitHero = needToRecruitHero(startupTown);
-
-	if(towns.size() > 1)
-	{
-		startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
-		{
-			auto closestHero = getNearestHero(town);
-
-			if(!closestHero)
-				return 0;
-
-			return ai->ah->evaluateHero(closestHero);
-		});
-	}
-
-	auto closestHero = getNearestHero(startupTown);
-
-	if(closestHero)
-	{
-		if(!startupTown->visitingHero)
-		{
-			if(ai->ah->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
-			{
-				auto paths = ai->ah->getPathsToTile(startupTown->visitablePos());
-
-				if(paths.size())
-				{
-					auto path = getShortestPath(startupTown, paths);
-
-					tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, startupTown).setpriority(100)));
-				}
-			}
-		}
-		else
-		{
-			auto visitingHero = startupTown->visitingHero.get();
-			auto visitingHeroScore = ai->ah->evaluateHero(visitingHero);
-				
-			if(startupTown->garrisonHero)
-			{
-				auto garrisonHero = startupTown->garrisonHero.get();
-				auto garrisonHeroScore = ai->ah->evaluateHero(garrisonHero);
-
-				if(visitingHeroScore > garrisonHeroScore
-					|| ai->ah->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->ah->getHeroRole(visitingHero) == HeroRole::MAIN)
-				{
-					if(canRecruitHero || ai->ah->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
-					{
-						tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
-					}
-				}
-				else if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
-				{
-					tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero).setpriority(100)));
-				}
-			}
-			else if(canRecruitHero)
-			{
-				tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
-			}
-		}
-	}
-
-	if(tasks.empty() && canRecruitHero && !startupTown->visitingHero)
-	{
-		tasks.push_back(Goals::sptr(Goals::RecruitHero()));
-	}
-
-	if(tasks.empty() && towns.size())
-	{
-		for(const CGTownInstance * town : towns)
-		{
-			if(town->garrisonHero && town->garrisonHero->movement)
-				tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(0.0001f)));
-		}
-	}
-
-	return tasks;
-}

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

@@ -1,5 +1,5 @@
 /*
-* Goals.h, part of VCMI engine
+* RecruitHeroBehavior.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -23,15 +23,3 @@ public:
 	virtual Goals::TGoalVec getTasks() override;
 	virtual std::string toString() const override;
 };
-
-class StartupBehavior : public Behavior
-{
-public:
-	StartupBehavior()
-	{
-	}
-
-	virtual Goals::TGoalVec getTasks() override;
-	virtual std::string toString() const override;
-};
-

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

@@ -0,0 +1,178 @@
+/*
+* StartupBehavior.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 "StartupBehavior.h"
+#include "../VCAI.h"
+#include "../AIhelper.h"
+#include "../AIUtility.h"
+#include "../Goals/RecruitHero.h"
+#include "../Goals/ExecuteHeroChain.h"
+#include "../Goals/ExchangeSwapTownHeroes.h"
+#include "lib/mapping/CMap.h" //for victory conditions
+#include "lib/CPathfinder.h"
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+std::string StartupBehavior::toString() const
+{
+	return "Startup";
+}
+
+const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPath> & paths)
+{
+	auto shortestPath = *vstd::minElementByFun(paths, [town](const AIPath & path) -> float
+	{
+		if(town->garrisonHero && path.targetHero == town->garrisonHero.get())
+			return 1;
+
+		return path.movementCost();
+	});
+
+	return shortestPath;
+}
+
+const CGHeroInstance * getNearestHero(const CGTownInstance * town)
+{
+	auto paths = ai->ah->getPathsToTile(town->visitablePos());
+
+	if(paths.empty())
+		return nullptr;
+
+	auto shortestPath = getShortestPath(town, paths);
+
+	if(shortestPath.nodes.size() > 1 
+		|| shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4
+		|| town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get())
+		return nullptr;
+
+	return shortestPath.targetHero;
+}
+
+bool needToRecruitHero(const CGTownInstance * startupTown)
+{
+	if(!ai->canRecruitAnyHero(startupTown))
+		return false;
+
+	if(!startupTown->garrisonHero && !startupTown->visitingHero)
+		return false;
+
+	auto heroToCheck = startupTown->garrisonHero ? startupTown->garrisonHero.get() : startupTown->visitingHero.get();
+	auto paths = cb->getPathsInfo(heroToCheck);
+
+	for(auto obj : ai->visitableObjs)
+	{
+		if(obj->ID == Obj::RESOURCE && obj->subID == Res::GOLD
+			|| obj->ID == Obj::TREASURE_CHEST
+			|| obj->ID == Obj::CAMPFIRE
+			|| obj->ID == Obj::WATER_WHEEL)
+		{
+			auto path = paths->getPathInfo(obj->visitablePos());
+			if(path->accessible == CGPathNode::BLOCKVIS && path->turns != 255)
+			{
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+Goals::TGoalVec StartupBehavior::getTasks()
+{
+	Goals::TGoalVec tasks;
+	auto towns = cb->getTownsInfo();
+
+	if(!towns.size())
+		return tasks;
+
+	const CGTownInstance * startupTown = towns.front();
+	bool canRecruitHero = needToRecruitHero(startupTown);
+
+	if(towns.size() > 1)
+	{
+		startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
+		{
+			auto closestHero = getNearestHero(town);
+
+			if(!closestHero)
+				return 0;
+
+			return ai->ah->evaluateHero(closestHero);
+		});
+	}
+
+	auto closestHero = getNearestHero(startupTown);
+
+	if(closestHero)
+	{
+		if(!startupTown->visitingHero)
+		{
+			if(ai->ah->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
+			{
+				auto paths = ai->ah->getPathsToTile(startupTown->visitablePos());
+
+				if(paths.size())
+				{
+					auto path = getShortestPath(startupTown, paths);
+
+					tasks.push_back(Goals::sptr(Goals::ExecuteHeroChain(path, startupTown).setpriority(100)));
+				}
+			}
+		}
+		else
+		{
+			auto visitingHero = startupTown->visitingHero.get();
+			auto visitingHeroScore = ai->ah->evaluateHero(visitingHero);
+				
+			if(startupTown->garrisonHero)
+			{
+				auto garrisonHero = startupTown->garrisonHero.get();
+				auto garrisonHeroScore = ai->ah->evaluateHero(garrisonHero);
+
+				if(visitingHeroScore > garrisonHeroScore
+					|| ai->ah->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->ah->getHeroRole(visitingHero) == HeroRole::MAIN)
+				{
+					if(canRecruitHero || ai->ah->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
+					{
+						tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
+					}
+				}
+				else if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
+				{
+					tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero).setpriority(100)));
+				}
+			}
+			else if(canRecruitHero)
+			{
+				tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero).setpriority(100)));
+			}
+		}
+	}
+
+	if(tasks.empty() && canRecruitHero && !startupTown->visitingHero)
+	{
+		tasks.push_back(Goals::sptr(Goals::RecruitHero()));
+	}
+
+	if(tasks.empty() && towns.size())
+	{
+		for(const CGTownInstance * town : towns)
+		{
+			if(town->garrisonHero && town->garrisonHero->movement)
+				tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(0.0001f)));
+		}
+	}
+
+	return tasks;
+}

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

@@ -0,0 +1,26 @@
+/*
+* StartupBehavior.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "lib/VCMI_Lib.h"
+#include "Behavior.h"
+#include "../AIUtility.h"
+
+class StartupBehavior : public Behavior
+{
+public:
+	StartupBehavior()
+	{
+	}
+
+	virtual Goals::TGoalVec getTasks() override;
+	virtual std::string toString() const override;
+};
+

+ 4 - 0
AI/Nullkiller/CMakeLists.txt

@@ -47,6 +47,7 @@ set(VCAI_SRCS
 		Goals/FindObj.cpp
 		Goals/CompleteQuest.cpp
 		Goals/ExecuteHeroChain.cpp
+		Goals/ExchangeSwapTownHeroes.cpp
 		Engine/Nullkiller.cpp
 		Engine/PriorityEvaluator.cpp
 		Engine/DangerHitMapAnalyzer.cpp
@@ -54,6 +55,7 @@ set(VCAI_SRCS
 		Behaviors/CaptureObjectsBehavior.cpp
 		Behaviors/RecruitHeroBehavior.cpp
 		Behaviors/BuyArmyBehavior.cpp
+		Behaviors/StartupBehavior.cpp
 		main.cpp
 		VCAI.cpp
 )
@@ -110,6 +112,7 @@ set(VCAI_HEADERS
 		Goals/FindObj.h
 		Goals/CompleteQuest.h
 		Goals/ExecuteHeroChain.h
+		Goals/ExchangeSwapTownHeroes.h
 		Goals/Goals.h
 		Engine/Nullkiller.h
 		Engine/PriorityEvaluator.h
@@ -118,6 +121,7 @@ set(VCAI_HEADERS
 		Behaviors/CaptureObjectsBehavior.h
 		Behaviors/RecruitHeroBehavior.h
 		Behaviors/BuyArmyBehavior.h
+		Behaviors/StartupBehavior.h
 		VCAI.h
 )
 

+ 10 - 1
AI/Nullkiller/Engine/DangerHitMapAnalyzer.cpp

@@ -1,3 +1,12 @@
+/*
+* DangerHitMapAnalyzer.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 "DangerHitMapAnalyzer.h"
 
@@ -62,4 +71,4 @@ uint64_t DangerHitMapAnalyzer::enemyCanKillOurHeroesAlongThePath(const AIPath &
 
 	return info.fastestDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.fastestDanger.danger)
 		|| info.maximumDanger.turn <= turn && !isSafeToVisit(path.targetHero, path.heroArmy, info.maximumDanger.danger);
-}
+}

+ 10 - 1
AI/Nullkiller/Engine/DangerHitMapAnalyzer.h

@@ -1,3 +1,12 @@
+/*
+* DangerHitMapAnalyzer.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
 #pragma once
 
 #include "../VCAI.h"
@@ -35,4 +44,4 @@ private:
 public:
 	void updateHitMap();
 	uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
-};
+};

+ 11 - 1
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -1,3 +1,12 @@
+/*
+* Nullkiller.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 "Nullkiller.h"
 #include "../VCAI.h"
@@ -5,6 +14,7 @@
 #include "../Behaviors/CaptureObjectsBehavior.h"
 #include "../Behaviors/RecruitHeroBehavior.h"
 #include "../Behaviors/BuyArmyBehavior.h"
+#include "../Behaviors/StartupBehavior.h"
 #include "../Goals/Invalid.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
@@ -122,4 +132,4 @@ void Nullkiller::makeTurn()
 			return;
 		}
 	}
-}
+}

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

@@ -1,3 +1,12 @@
+/*
+* Nullkiller.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
 #pragma once
 
 #include "PriorityEvaluator.h"
@@ -27,4 +36,4 @@ private:
 	void updateAiState();
 	Goals::TSubgoal choseBestTask(std::shared_ptr<Behavior> behavior) const;
 	Goals::TSubgoal choseBestTask(Goals::TGoalVec & tasks) const;
-};
+};

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

@@ -1,11 +1,11 @@
 /*
- * Fuzzy.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
- *
+* PriorityEvaluator.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 "PriorityEvaluator.h"
@@ -46,7 +46,7 @@ PriorityEvaluator::~PriorityEvaluator()
 
 void PriorityEvaluator::initVisitTile()
 {
-	auto file = CResourceHandler::get()->load(ResourceID("config/ai-priorities.txt"))->readAll();
+	auto file = CResourceHandler::get()->load(ResourceID("config/ai/object-priorities.txt"))->readAll();
 	std::string str = std::string((char *)file.first.get(), file.second);
 	engine = fl::FllImporter().fromString(str);
 	armyLossPersentageVariable = engine->getInputVariable("armyLoss");

+ 7 - 7
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -1,11 +1,11 @@
 /*
- * Fuzzy.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
- *
+* PriorityEvaluator.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 "fl/Headers.h"

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

@@ -0,0 +1,81 @@
+/*
+* ExchangeSwapTownHeroes.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 "ExchangeSwapTownHeroes.h"
+#include "ExecuteHeroChain.h"
+#include "../VCAI.h"
+#include "../FuzzyHelper.h"
+#include "../AIhelper.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../Engine/Nullkiller.h"
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero)
+	:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero)
+{
+}
+
+std::string ExchangeSwapTownHeroes::name() const
+{
+	return "Exchange and swap heroes of " + town->name;
+}
+
+std::string ExchangeSwapTownHeroes::completeMessage() const
+{
+	return "Exchange and swap heroes of " + town->name + " compelete";
+}
+
+bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
+{
+	return town == other.town;
+}
+
+TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve()
+{
+	return iAmElementar();
+}
+
+void ExchangeSwapTownHeroes::accept(VCAI * ai)
+{
+	if(!garrisonHero)
+	{
+		if(!town->garrisonHero)
+			throw cannotFulfillGoalException("Invalid configuration. There is no hero in town garrison.");
+		
+		cb->swapGarrisonHero(town);
+		ai->buildArmyIn(town);
+		ai->nullkiller->unlockHero(town->visitingHero.get());
+		logAi->debug("Extracted hero %s from garrison of %s", town->visitingHero->name, town->name);
+
+		return;
+	}
+
+	if(town->visitingHero && town->visitingHero.get() != garrisonHero)
+		cb->swapGarrisonHero(town);
+
+	ai->moveHeroToTile(town->visitablePos(), garrisonHero);
+
+	cb->swapGarrisonHero(town); // selected hero left in garrison with strongest army
+	ai->nullkiller->lockHero(town->garrisonHero.get());
+
+	if(town->visitingHero)
+	{
+		ai->nullkiller->unlockHero(town->visitingHero.get());
+		makePossibleUpgrades(town->visitingHero);
+	}
+
+	logAi->debug("Put hero %s to garrison of %s", town->garrisonHero->name, town->name);
+}

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

@@ -0,0 +1,36 @@
+/*
+* ExchangeSwapTownHeroes.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"
+
+namespace Goals
+{
+	class DLL_EXPORT ExchangeSwapTownHeroes : public CGoal<ExchangeSwapTownHeroes>
+	{
+	private:
+		const CGTownInstance * town;
+		const CGHeroInstance * garrisonHero;
+
+	public:
+		ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero = nullptr);
+
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+
+		TSubgoal whatToDoToAchieve() override;
+		void accept(VCAI * ai) override;
+		std::string name() const override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
+	};
+}

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

@@ -117,61 +117,4 @@ std::string ExecuteHeroChain::name() const
 std::string ExecuteHeroChain::completeMessage() const
 {
 	return "Hero chain completed";
-}
-
-ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero)
-	:CGoal(Goals::EXCHANGE_SWAP_TOWN_HEROES), town(town), garrisonHero(garrisonHero)
-{
-}
-
-std::string ExchangeSwapTownHeroes::name() const
-{
-	return "Exchange and swap heroes of " + town->name;
-}
-
-std::string ExchangeSwapTownHeroes::completeMessage() const
-{
-	return "Exchange and swap heroes of " + town->name + " compelete";
-}
-
-bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
-{
-	return town == other.town;
-}
-
-TSubgoal ExchangeSwapTownHeroes::whatToDoToAchieve()
-{
-	return iAmElementar();
-}
-
-void ExchangeSwapTownHeroes::accept(VCAI * ai)
-{
-	if(!garrisonHero)
-	{
-		if(!town->garrisonHero)
-			throw cannotFulfillGoalException("Invalid configuration. There is no hero in town garrison.");
-		
-		cb->swapGarrisonHero(town);
-		ai->buildArmyIn(town);
-		ai->nullkiller->unlockHero(town->visitingHero.get());
-		logAi->debug("Extracted hero %s from garrison of %s", town->visitingHero->name, town->name);
-
-		return;
-	}
-
-	if(town->visitingHero && town->visitingHero.get() != garrisonHero)
-		cb->swapGarrisonHero(town);
-
-	ai->moveHeroToTile(town->visitablePos(), garrisonHero);
-
-	cb->swapGarrisonHero(town); // selected hero left in garrison with strongest army
-	ai->nullkiller->lockHero(town->garrisonHero.get());
-
-	if(town->visitingHero)
-	{
-		ai->nullkiller->unlockHero(town->visitingHero.get());
-		makePossibleUpgrades(town->visitingHero);
-	}
-
-	logAi->debug("Put hero %s to garrison of %s", town->garrisonHero->name, town->name);
 }

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

@@ -33,25 +33,4 @@ namespace Goals
 		std::string completeMessage() const override;
 		virtual bool operator==(const ExecuteHeroChain & other) const override;
 	};
-
-	class DLL_EXPORT ExchangeSwapTownHeroes : public CGoal<ExchangeSwapTownHeroes>
-	{
-	private:
-		const CGTownInstance * town;
-		const CGHeroInstance * garrisonHero;
-
-	public:
-		ExchangeSwapTownHeroes(const CGTownInstance * town, const CGHeroInstance * garrisonHero = nullptr);
-
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-
-		TSubgoal whatToDoToAchieve() override;
-		void accept(VCAI * ai) override;
-		std::string name() const override;
-		std::string completeMessage() const override;
-		virtual bool operator==(const ExchangeSwapTownHeroes & other) const override;
-	};
 }