Jelajahi Sumber

Merge pull request #6343 from IvanSavenko/remove_vcai

Remove VCAI & use NKAI2 as default
Ivan Savenko 3 minggu lalu
induk
melakukan
ab9496c511
100 mengubah file dengan 165 tambahan dan 12054 penghapusan
  1. 14 3
      AI/CMakeLists.txt
  2. 0 263
      AI/VCAI/AIUtility.cpp
  3. 0 222
      AI/VCAI/AIUtility.h
  4. 0 181
      AI/VCAI/AIhelper.cpp
  5. 0 85
      AI/VCAI/AIhelper.h
  6. 0 158
      AI/VCAI/ArmyManager.cpp
  7. 0 56
      AI/VCAI/ArmyManager.h
  8. 0 256
      AI/VCAI/BuildingManager.cpp
  9. 0 73
      AI/VCAI/BuildingManager.h
  10. 0 113
      AI/VCAI/CMakeLists.txt
  11. 0 456
      AI/VCAI/FuzzyEngines.cpp
  12. 0 86
      AI/VCAI/FuzzyEngines.h
  13. 0 293
      AI/VCAI/FuzzyHelper.cpp
  14. 0 47
      AI/VCAI/FuzzyHelper.h
  15. 0 185
      AI/VCAI/Goals/AbstractGoal.cpp
  16. 0 175
      AI/VCAI/Goals/AbstractGoal.h
  17. 0 85
      AI/VCAI/Goals/AdventureSpellCast.cpp
  18. 0 44
      AI/VCAI/Goals/AdventureSpellCast.h
  19. 0 87
      AI/VCAI/Goals/Build.cpp
  20. 0 37
      AI/VCAI/Goals/Build.h
  21. 0 79
      AI/VCAI/Goals/BuildBoat.cpp
  22. 0 37
      AI/VCAI/Goals/BuildBoat.h
  23. 0 66
      AI/VCAI/Goals/BuildThis.cpp
  24. 0 48
      AI/VCAI/Goals/BuildThis.h
  25. 0 40
      AI/VCAI/Goals/BuyArmy.cpp
  26. 0 41
      AI/VCAI/Goals/BuyArmy.h
  27. 0 84
      AI/VCAI/Goals/CGoal.h
  28. 0 80
      AI/VCAI/Goals/ClearWayTo.cpp
  29. 0 45
      AI/VCAI/Goals/ClearWayTo.h
  30. 0 212
      AI/VCAI/Goals/CollectRes.cpp
  31. 0 40
      AI/VCAI/Goals/CollectRes.h
  32. 0 278
      AI/VCAI/Goals/CompleteQuest.cpp
  33. 0 46
      AI/VCAI/Goals/CompleteQuest.h
  34. 0 83
      AI/VCAI/Goals/Conquer.cpp
  35. 0 32
      AI/VCAI/Goals/Conquer.h
  36. 0 34
      AI/VCAI/Goals/DigAtTile.cpp
  37. 0 41
      AI/VCAI/Goals/DigAtTile.h
  38. 0 448
      AI/VCAI/Goals/Explore.cpp
  39. 0 66
      AI/VCAI/Goals/Explore.h
  40. 0 66
      AI/VCAI/Goals/FindObj.cpp
  41. 0 47
      AI/VCAI/Goals/FindObj.h
  42. 0 203
      AI/VCAI/Goals/GatherArmy.cpp
  43. 0 38
      AI/VCAI/Goals/GatherArmy.h
  44. 0 163
      AI/VCAI/Goals/GatherTroops.cpp
  45. 0 43
      AI/VCAI/Goals/GatherTroops.h
  46. 0 26
      AI/VCAI/Goals/GetArtOfType.cpp
  47. 0 40
      AI/VCAI/Goals/GetArtOfType.h
  48. 0 33
      AI/VCAI/Goals/Goals.h
  49. 0 41
      AI/VCAI/Goals/Invalid.h
  50. 0 31
      AI/VCAI/Goals/RecruitHero.cpp
  51. 0 41
      AI/VCAI/Goals/RecruitHero.h
  52. 0 23
      AI/VCAI/Goals/Trade.cpp
  53. 0 38
      AI/VCAI/Goals/Trade.h
  54. 0 68
      AI/VCAI/Goals/VisitHero.cpp
  55. 0 42
      AI/VCAI/Goals/VisitHero.h
  56. 0 111
      AI/VCAI/Goals/VisitObj.cpp
  57. 0 32
      AI/VCAI/Goals/VisitObj.h
  58. 0 91
      AI/VCAI/Goals/VisitTile.cpp
  59. 0 37
      AI/VCAI/Goals/VisitTile.h
  60. 0 181
      AI/VCAI/Goals/Win.cpp
  61. 0 39
      AI/VCAI/Goals/Win.h
  62. 0 147
      AI/VCAI/MapObjectsEvaluator.cpp
  63. 0 27
      AI/VCAI/MapObjectsEvaluator.h
  64. 0 414
      AI/VCAI/Pathfinding/AINodeStorage.cpp
  65. 0 120
      AI/VCAI/Pathfinding/AINodeStorage.h
  66. 0 96
      AI/VCAI/Pathfinding/AIPathfinder.cpp
  67. 0 32
      AI/VCAI/Pathfinding/AIPathfinder.h
  68. 0 63
      AI/VCAI/Pathfinding/AIPathfinderConfig.cpp
  69. 0 34
      AI/VCAI/Pathfinding/AIPathfinderConfig.h
  70. 0 21
      AI/VCAI/Pathfinding/Actions/BattleAction.cpp
  71. 0 30
      AI/VCAI/Pathfinding/Actions/BattleAction.h
  72. 0 58
      AI/VCAI/Pathfinding/Actions/BoatActions.cpp
  73. 0 73
      AI/VCAI/Pathfinding/Actions/BoatActions.h
  74. 0 37
      AI/VCAI/Pathfinding/Actions/ISpecialAction.h
  75. 0 23
      AI/VCAI/Pathfinding/Actions/TownPortalAction.cpp
  76. 0 34
      AI/VCAI/Pathfinding/Actions/TownPortalAction.h
  77. 0 243
      AI/VCAI/Pathfinding/PathfindingManager.cpp
  78. 0 68
      AI/VCAI/Pathfinding/PathfindingManager.h
  79. 0 166
      AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.cpp
  80. 0 51
      AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.h
  81. 0 147
      AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
  82. 0 35
      AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.h
  83. 0 51
      AI/VCAI/Pathfinding/Rules/AIMovementToDestinationRule.cpp
  84. 0 34
      AI/VCAI/Pathfinding/Rules/AIMovementToDestinationRule.h
  85. 0 47
      AI/VCAI/Pathfinding/Rules/AIPreviousNodeRule.cpp
  86. 0 34
      AI/VCAI/Pathfinding/Rules/AIPreviousNodeRule.h
  87. 0 344
      AI/VCAI/ResourceManager.cpp
  88. 0 99
      AI/VCAI/ResourceManager.h
  89. 0 10
      AI/VCAI/StdInc.cpp
  90. 0 13
      AI/VCAI/StdInc.h
  91. 0 2914
      AI/VCAI/VCAI.cpp
  92. 0 304
      AI/VCAI/VCAI.h
  93. 0 32
      AI/VCAI/main.cpp
  94. 20 1
      CMakeLists.txt
  95. 14 6
      client/CMakeLists.txt
  96. 7 2
      config/schemas/settings.json
  97. 78 25
      launcher/settingsView/csettingsview_moc.cpp
  98. 10 5
      launcher/settingsView/csettingsview_moc.h
  99. 9 69
      launcher/settingsView/csettingsview_moc.ui
  100. 13 6
      lib/CMakeLists.txt

+ 14 - 3
AI/CMakeLists.txt

@@ -35,11 +35,22 @@ endif()
 #        Add subdirectories           #
 #######################################
 
-add_subdirectory(BattleAI)
-add_subdirectory(VCAI)
-add_subdirectory(StupidAI)
+# Combat AI's
+if(ENABLE_STUPID_AI)
+	add_subdirectory(StupidAI)
+endif()
+
+if(ENABLE_BATTLE_AI)
+	add_subdirectory(BattleAI)
+endif()
+
+# Adventure AI's
 add_subdirectory(EmptyAI)
+
 if(ENABLE_NULLKILLER_AI)
 	add_subdirectory(Nullkiller)
+endif()
+
+if(ENABLE_NULLKILLER2_AI)
 	add_subdirectory(Nullkiller2)
 endif()

+ 0 - 263
AI/VCAI/AIUtility.cpp

@@ -1,263 +0,0 @@
-/*
- * AIUtility.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 "AIUtility.h"
-#include "VCAI.h"
-#include "FuzzyHelper.h"
-#include "Goals/Goals.h"
-
-#include "../../lib/UnlockGuard.h"
-#include "../../lib/CConfigHandler.h"
-#include "../../lib/entities/artifact/CArtifact.h"
-#include "../../lib/mapObjects/CGTownInstance.h"
-#include "../../lib/mapObjects/CQuest.h"
-#include "../../lib/mapping/TerrainTile.h"
-
-extern FuzzyHelper * fh;
-
-const CGObjectInstance * ObjectIdRef::operator->() const
-{
-	return cb->getObj(id, false);
-}
-
-ObjectIdRef::operator const CGObjectInstance *() const
-{
-	return cb->getObj(id, false);
-}
-
-ObjectIdRef::operator bool() const
-{
-	return cb->getObj(id, false);
-}
-
-ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
-	: id(_id)
-{
-
-}
-
-ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
-	: id(obj->id)
-{
-
-}
-
-bool ObjectIdRef::operator<(const ObjectIdRef & rhs) const
-{
-	return id < rhs.id;
-}
-
-HeroPtr::HeroPtr(const CGHeroInstance * H)
-{
-	if(!H)
-	{
-		//init from nullptr should equal to default init
-		*this = HeroPtr();
-		return;
-	}
-
-	h = H;
-	name = h->getNameTranslated();
-	hid = H->id;
-//	infosCount[ai->playerID][hid]++;
-}
-
-HeroPtr::HeroPtr()
-{
-	h = nullptr;
-	hid = ObjectInstanceID();
-}
-
-HeroPtr::~HeroPtr()
-{
-//	if(hid >= 0)
-//		infosCount[ai->playerID][hid]--;
-}
-
-bool HeroPtr::operator<(const HeroPtr & rhs) const
-{
-	return hid < rhs.hid;
-}
-
-const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
-{
-	//TODO? check if these all assertions every time we get info about hero affect efficiency
-	//
-	//behave terribly when attempting unauthorized access to hero that is not ours (or was lost)
-	assert(doWeExpectNull || h);
-
-	if(h)
-	{
-		auto obj = cb->getObj(hid);
-		const bool owned = obj && obj->tempOwner == ai->playerID;
-
-		if(doWeExpectNull && !owned)
-		{
-			return nullptr;
-		}
-		else
-		{
-			assert(obj);
-			assert(owned);
-		}
-	}
-
-	return h;
-}
-
-const CGHeroInstance * HeroPtr::operator->() const
-{
-	return get();
-}
-
-bool HeroPtr::validAndSet() const
-{
-	return get(true);
-}
-
-const CGHeroInstance * HeroPtr::operator*() const
-{
-	return get();
-}
-
-bool HeroPtr::operator==(const HeroPtr & rhs) const
-{
-	return h == rhs.get(true);
-}
-
-bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const
-{
-	const CGPathNode * ln = ai->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
-	const CGPathNode * rn = ai->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
-
-	return ln->getCost() < rn->getCost();
-}
-
-bool isSafeToVisit(HeroPtr h, crint3 tile)
-{
-	return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
-}
-
-bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength)
-{
-	const ui64 heroStrength = h->getTotalStrength();
-
-	if(dangerStrength)
-	{
-		return heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength;
-	}
-
-	return true; //there's no danger
-}
-
-bool isObjectRemovable(const CGObjectInstance * obj)
-{
-	//FIXME: move logic to object property!
-	switch (obj->ID)
-	{
-	case Obj::MONSTER:
-	case Obj::RESOURCE:
-	case Obj::CAMPFIRE:
-	case Obj::TREASURE_CHEST:
-	case Obj::ARTIFACT:
-	case Obj::BORDERGUARD:
-	case Obj::FLOTSAM:
-	case Obj::PANDORAS_BOX:
-	case Obj::OCEAN_BOTTLE:
-	case Obj::SEA_CHEST:
-	case Obj::SHIPWRECK_SURVIVOR:
-	case Obj::SPELL_SCROLL:
-		return true;
-		break;
-	default:
-		return false;
-		break;
-	}
-
-}
-
-bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
-{
-	// TODO: Such information should be provided by pathfinder
-	// Tile must be free or with unoccupied boat
-	if(!t->blocked())
-	{
-		return true;
-	}
-	else if(!fromWater) // do not try to board when in water sector
-	{
-		if(t->visitableObjects.size() == 1 && cb->getObjInstance(t->topVisitableObj())->ID == Obj::BOAT)
-			return true;
-	}
-	return false;
-}
-
-bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
-{
-	const auto * object = cb->getTopObj(tileToHit);
-	if(!object)
-		return false;
-
-	if(object->ID != Obj::BORDER_GATE)
-		return false;
-
-	auto gate = dynamic_cast<const CGKeys *>(object);
-	return !gate->passableFor(ai->playerID);
-}
-
-bool isBlockVisitObj(const int3 & pos)
-{
-	if(auto obj = cb->getTopObj(pos))
-	{
-		if(obj->isBlockedVisitable()) //we can't stand on that object
-			return true;
-	}
-
-	return false;
-}
-
-creInfo infoFromDC(const dwellingContent & dc)
-{
-	creInfo ci;
-	ci.count = dc.first;
-	ci.creID = dc.second.size() ? dc.second.back() : CreatureID(-1); //should never be accessed
-	if (ci.creID != CreatureID::NONE)
-	{
-		ci.cre = LIBRARY->creatures()->getById(ci.creID);
-		ci.level = ci.cre->getLevel(); //this is creature tier, while tryRealize expects dwelling level. Ignore.
-	}
-	else
-	{
-		ci.cre = nullptr;
-		ci.level = 0;
-	}
-	return ci;
-}
-
-bool compareHeroStrength(HeroPtr h1, HeroPtr h2)
-{
-	return h1->getTotalStrength() < h2->getTotalStrength();
-}
-
-bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2)
-{
-	return a1->getArmyStrength() < a2->getArmyStrength();
-}
-
-bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2)
-{
-	auto art1 = a1->getType();
-	auto art2 = a2->getType();
-
-	if(art1->getPrice() == art2->getPrice())
-		return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
-	else
-		return art1->getPrice() > art2->getPrice();
-}

+ 0 - 222
AI/VCAI/AIUtility.h

@@ -1,222 +0,0 @@
-/*
- * AIUtility.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/GameLibrary.h"
-#include "../../lib/CCreatureHandler.h"
-#include "../../lib/spells/CSpellHandler.h"
-#include "../../lib/CStopWatch.h"
-#include "../../lib/mapObjects/CGHeroInstance.h"
-#include "../../lib/callback/CCallback.h"
-
-class VCAI;
-struct creInfo;
-
-using crint3 = const int3 &;
-using crstring = const std::string &;
-using dwellingContent = std::pair<ui32, std::vector<CreatureID>>;
-
-const int ACTUAL_RESOURCE_COUNT = 7;
-
-//implementation-dependent
-extern const double SAFE_ATTACK_CONSTANT;
-
-extern thread_local CCallback * cb;
-extern thread_local VCAI * ai;
-
-//provisional class for AI to store a reference to an owned hero object
-//checks if it's valid on access, should be used in place of const CGHeroInstance*
-
-struct DLL_EXPORT HeroPtr
-{
-	const CGHeroInstance * h;
-	ObjectInstanceID hid;
-
-public:
-	std::string name;
-
-
-	HeroPtr();
-	HeroPtr(const CGHeroInstance * H);
-	~HeroPtr();
-
-	operator bool() const
-	{
-		return validAndSet();
-	}
-
-	bool operator<(const HeroPtr & rhs) const;
-	const CGHeroInstance * operator->() const;
-	const CGHeroInstance * operator*() const; //not that consistent with -> but all interfaces use CGHeroInstance*, so it's convenient
-	bool operator==(const HeroPtr & rhs) const;
-	bool operator!=(const HeroPtr & rhs) const
-	{
-		return !(*this == rhs);
-	}
-
-	const CGHeroInstance * get(bool doWeExpectNull = false) const;
-	bool validAndSet() const;
-};
-
-enum BattleState
-{
-	NO_BATTLE,
-	UPCOMING_BATTLE,
-	ONGOING_BATTLE,
-	ENDING_BATTLE
-};
-
-// AI lives in a dangerous world. CGObjectInstances under pointer may got deleted/hidden.
-// This class stores object id, so we can detect when we lose access to the underlying object.
-struct ObjectIdRef
-{
-	ObjectInstanceID id;
-
-	const CGObjectInstance * operator->() const;
-	operator const CGObjectInstance *() const;
-	operator bool() const;
-
-	ObjectIdRef(ObjectInstanceID _id);
-	ObjectIdRef(const CGObjectInstance * obj);
-
-	bool operator<(const ObjectIdRef & rhs) const;
-};
-
-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<Obj::Type id>
-bool objWithID(const CGObjectInstance * obj)
-{
-	return obj->ID == id;
-}
-
-struct creInfo
-{
-	int count;
-	CreatureID creID;
-	const Creature * cre;
-	int level;
-};
-creInfo infoFromDC(const dwellingContent & dc);
-
-template<class Func>
-void foreach_tile_pos(const Func & foo)
-{
-	// some micro-optimizations since this function gets called a LOT
-	// callback pointer is thread-specific and slow to retrieve -> read map size only once
-	int3 mapSize = cb->getMapSize();
-	for(int z = 0; z < mapSize.z; z++)
-	{
-		for(int x = 0; x < mapSize.x; x++)
-		{
-			for(int y = 0; y < mapSize.y; y++)
-			{
-				foo(int3(x, y, z));
-			}
-		}
-	}
-}
-
-template<class Func>
-void foreach_tile_pos(CCallback * cbp, const Func & foo) // avoid costly retrieval of thread-specific pointer
-{
-	int3 mapSize = cbp->getMapSize();
-	for(int z = 0; z < mapSize.z; z++)
-	{
-		for(int x = 0; x < mapSize.x; x++)
-		{
-			for(int y = 0; y < mapSize.y; y++)
-			{
-				foo(cbp, int3(x, y, z));
-			}
-		}
-	}
-}
-
-template<class Func>
-void foreach_neighbour(const int3 & pos, const Func & foo)
-{
-	CCallback * cbp = cb; // avoid costly retrieval of thread-specific pointer
-	for(const int3 & dir : int3::getDirs())
-	{
-		const int3 n = pos + dir;
-		if(cbp->isInTheMap(n))
-			foo(pos + dir);
-	}
-}
-
-template<class Func>
-void foreach_neighbour(CCallback * cbp, const int3 & pos, const Func & foo) // avoid costly retrieval of thread-specific pointer
-{
-	for(const int3 & dir : int3::getDirs())
-	{
-		const int3 n = pos + dir;
-		if(cbp->isInTheMap(n))
-			foo(cbp, pos + dir);
-	}
-}
-
-bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater);
-bool isBlockedBorderGate(int3 tileToHit);
-bool isBlockVisitObj(const int3 & pos);
-
-bool isWeeklyRevisitable(const CGObjectInstance * obj);
-bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
-
-bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
-bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
-bool isSafeToVisit(HeroPtr h, crint3 tile);
-
-bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
-bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
-bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
-
-class CDistanceSorter
-{
-	const CGHeroInstance * hero;
-
-public:
-	CDistanceSorter(const CGHeroInstance * hero)
-		: hero(hero)
-	{
-	}
-	bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const;
-};

+ 0 - 181
AI/VCAI/AIhelper.cpp

@@ -1,181 +0,0 @@
-/*
-* AIhelper.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 "AIhelper.h"
-
-AIhelper::AIhelper()
-{
-	resourceManager.reset(new ResourceManager());
-	buildingManager.reset(new BuildingManager());
-	pathfindingManager.reset(new PathfindingManager());
-	armyManager.reset(new ArmyManager());
-}
-
-bool AIhelper::notifyGoalCompleted(Goals::TSubgoal goal)
-{
-	return resourceManager->notifyGoalCompleted(goal);
-}
-
-void AIhelper::init(CPlayerSpecificInfoCallback * CB)
-{
-	resourceManager->init(CB);
-	buildingManager->init(CB);
-	pathfindingManager->init(CB);
-	armyManager->init(CB);
-}
-
-void AIhelper::setAI(VCAI * AI)
-{
-	resourceManager->setAI(AI);
-	buildingManager->setAI(AI);
-	pathfindingManager->setAI(AI);
-	armyManager->setAI(AI);
-}
-
-bool AIhelper::getBuildingOptions(const CGTownInstance * t)
-{
-	return buildingManager->getBuildingOptions(t);
-}
-
-BuildingID AIhelper::getMaxPossibleGoldBuilding(const CGTownInstance * t)
-{
-	return buildingManager->getMaxPossibleGoldBuilding(t);
-}
-
-std::optional<PotentialBuilding> AIhelper::immediateBuilding() const
-{
-	return buildingManager->immediateBuilding();
-}
-
-std::optional<PotentialBuilding> AIhelper::expensiveBuilding() const
-{
-	return buildingManager->expensiveBuilding();
-}
-
-std::optional<BuildingID> AIhelper::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
-{
-	return buildingManager->canBuildAnyStructure(t, buildList, maxDays);
-}
-
-Goals::TSubgoal AIhelper::whatToDo(TResources & res, Goals::TSubgoal goal)
-{
-	return resourceManager->whatToDo(res, goal);
-}
-
-Goals::TSubgoal AIhelper::whatToDo() const
-{
-	return resourceManager->whatToDo();
-}
-
-bool AIhelper::containsObjective(Goals::TSubgoal goal) const
-{
-	return resourceManager->containsObjective(goal);
-}
-
-bool AIhelper::hasTasksLeft() const
-{
-	return resourceManager->hasTasksLeft();
-}
-
-bool AIhelper::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal&)> predicate)
-{
-	return resourceManager->removeOutdatedObjectives(predicate);
-}
-
-bool AIhelper::canAfford(const TResources & cost) const
-{
-	return resourceManager->canAfford(cost);
-}
-
-TResources AIhelper::reservedResources() const
-{
-	return resourceManager->reservedResources();
-}
-
-TResources AIhelper::freeResources() const
-{
-	return resourceManager->freeResources();
-}
-
-TResource AIhelper::freeGold() const
-{
-	return resourceManager->freeGold();
-}
-
-TResources AIhelper::allResources() const
-{
-	return resourceManager->allResources();
-}
-
-TResource AIhelper::allGold() const
-{
-	return resourceManager->allGold();
-}
-
-Goals::TGoalVec AIhelper::howToVisitTile(const int3 & tile) const
-{
-	return pathfindingManager->howToVisitTile(tile);
-}
-
-Goals::TGoalVec AIhelper::howToVisitObj(ObjectIdRef obj) const
-{
-	return pathfindingManager->howToVisitObj(obj);
-}
-
-Goals::TGoalVec AIhelper::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
-{
-	return pathfindingManager->howToVisitTile(hero, tile, allowGatherArmy);
-}
-
-Goals::TGoalVec AIhelper::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
-{
-	return pathfindingManager->howToVisitObj(hero, obj, allowGatherArmy);
-}
-
-std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
-{
-	return pathfindingManager->getPathsToTile(hero, tile);
-}
-
-void AIhelper::updatePaths(std::vector<HeroPtr> heroes)
-{
-	pathfindingManager->updatePaths(heroes);
-}
-
-bool AIhelper::canGetArmy(const CArmedInstance * army, const CArmedInstance * source) const
-{
-	return armyManager->canGetArmy(army, source);
-}
-
-ui64 AIhelper::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
-{
-	return armyManager->howManyReinforcementsCanBuy(h, t);
-}
-
-ui64 AIhelper::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	return armyManager->howManyReinforcementsCanGet(target, source);
-}
-
-std::vector<SlotInfo> AIhelper::getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	return armyManager->getBestArmy(target, source);
-}
-
-std::vector<SlotInfo>::iterator AIhelper::getWeakestCreature(std::vector<SlotInfo> & army) const
-{
-	return armyManager->getWeakestCreature(army);
-}
-
-std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	return armyManager->getSortedSlots(target, source);
-}

+ 0 - 85
AI/VCAI/AIhelper.h

@@ -1,85 +0,0 @@
-/*
-* AIhelper.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#pragma once
-
-/*
-	!!!	Note: Include THIS file at the end of include list to avoid "undefined base class" error
-*/
-
-#include "ResourceManager.h"
-#include "BuildingManager.h"
-#include "ArmyManager.h"
-#include "Pathfinding/PathfindingManager.h"
-
-class ResourceManager;
-class BuildingManager;
-
-
-//indirection interface for various modules
-class DLL_EXPORT AIhelper : public IResourceManager, public IBuildingManager, public IPathfindingManager, public IArmyManager
-{
-	friend class VCAI;
-	friend struct SetGlobalState; //mess?
-
-	std::shared_ptr<ResourceManager> resourceManager;
-	std::shared_ptr<BuildingManager> buildingManager;
-	std::shared_ptr<PathfindingManager> pathfindingManager;
-	std::shared_ptr<ArmyManager> armyManager;
-	//TODO: vector<IAbstractManager>
-public:
-	AIhelper();
-
-	bool canAfford(const TResources & cost) const;
-	TResources reservedResources() const override;
-	TResources freeResources() const override;
-	TResource freeGold() const override;
-	TResources allResources() const override;
-	TResource allGold() const override;
-
-	Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) override;
-	Goals::TSubgoal whatToDo() const override;
-	bool containsObjective(Goals::TSubgoal goal) const override;
-	bool hasTasksLeft() const override;
-	bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
-
-	bool getBuildingOptions(const CGTownInstance * t) override;
-	BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
-	std::optional<PotentialBuilding> immediateBuilding() const override;
-	std::optional<PotentialBuilding> expensiveBuilding() const override;
-	std::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
-
-	Goals::TGoalVec howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy = true) const override;
-	Goals::TGoalVec howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy = true) const override;
-	Goals::TGoalVec howToVisitTile(const int3 & tile) const override;
-	Goals::TGoalVec howToVisitObj(ObjectIdRef obj) const override;
-	std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
-	void updatePaths(std::vector<HeroPtr> heroes) override;
-
-	STRONG_INLINE
-	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
-	{
-		return pathfindingManager->isTileAccessible(hero, tile);
-	}
-
-	bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override;
-	ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override;
-	ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
-	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
-
-private:
-	bool notifyGoalCompleted(Goals::TSubgoal goal) override;
-
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
-};
-

+ 0 - 158
AI/VCAI/ArmyManager.cpp

@@ -1,158 +0,0 @@
-/*
-* BuildingManager.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 "ArmyManager.h"
-
-#include "../../lib/mapObjects/MapObjects.h"
-
-void ArmyManager::init(CPlayerSpecificInfoCallback * CB)
-{
-	cb = CB;
-}
-
-void ArmyManager::setAI(VCAI * AI)
-{
-	ai = AI;
-}
-
-std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	const CCreatureSet * armies[] = { target, source };
-
-	//we calculate total strength for each creature type available in armies
-	std::map<const CCreature *, SlotInfo> creToPower;
-	std::vector<SlotInfo> resultingArmy;
-
-	for(auto armyPtr : armies)
-	{
-		for(auto & i : armyPtr->Slots())
-		{
-			auto cre = dynamic_cast<const CCreature*>(i.second->getType());
-			auto & slotInfp = creToPower[cre];
-
-			slotInfp.creature = cre;
-			slotInfp.power += i.second->getPower();
-			slotInfp.count += i.second->getCount();
-		}
-	}
-
-	for(auto pair : creToPower)
-		resultingArmy.push_back(pair.second);
-
-	boost::sort(resultingArmy, [](const SlotInfo & left, const SlotInfo & right) -> bool
-	{
-		return left.power > right.power;
-	});
-
-	return resultingArmy;
-}
-
-std::vector<SlotInfo>::iterator ArmyManager::getWeakestCreature(std::vector<SlotInfo> & army) const
-{
-	auto weakest = boost::min_element(army, [](const SlotInfo & left, const SlotInfo & right) -> bool
-	{
-		if(left.creature->getLevel() != right.creature->getLevel())
-			return left.creature->getLevel() < right.creature->getLevel();
-		
-		return left.creature->getMovementRange() > right.creature->getMovementRange();
-	});
-
-	return weakest;
-}
-
-std::vector<SlotInfo> ArmyManager::getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	auto resultingArmy = getSortedSlots(target, source);
-
-	if(resultingArmy.size() > GameConstants::ARMY_SIZE)
-	{
-		resultingArmy.resize(GameConstants::ARMY_SIZE);
-	}
-	else if(source->needsLastStack())
-	{
-		auto weakest = getWeakestCreature(resultingArmy);
-
-		if(weakest->count == 1)
-		{
-			resultingArmy.erase(weakest);
-		}
-		else
-		{
-			weakest->power -= weakest->power / weakest->count;
-			weakest->count--;
-		}
-	}
-
-	return resultingArmy;
-}
-
-bool ArmyManager::canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const
-{
-	//TODO: merge with pickBestCreatures
-	//if (ai->primaryHero().h == source)
-	if(target->tempOwner != source->tempOwner)
-	{
-		logAi->error("Why are we even considering exchange between heroes from different players?");
-		return false;
-	}
-
-	return 0 < howManyReinforcementsCanGet(target, source);
-}
-
-ui64 ArmyManager::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
-{
-	ui64 aivalue = 0;
-	TResources availableRes = cb->getResourceAmount();
-	int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount();
-
-	for(auto const & dc : t->creatures)
-	{
-		creInfo ci = infoFromDC(dc);
-
-		if(!ci.count || ci.creID == CreatureID::NONE)
-			continue;
-
-		vstd::amin(ci.count, availableRes / ci.cre->getFullRecruitCost()); //max count we can afford
-
-		if(ci.count && ci.creID != CreatureID::NONE) //valid creature at this level
-		{
-			//can be merged with another stack?
-			SlotID dst = h->getSlotFor(ci.creID);
-			if(!h->hasStackAtSlot(dst)) //need another new slot for this stack
-			{
-				if(!freeHeroSlots) //no more place for stacks
-					continue;
-				else
-					freeHeroSlots--; //new slot will be occupied
-			}
-
-			//we found matching occupied or free slot
-			aivalue += ci.count * ci.cre->getAIValue();
-			availableRes -= ci.cre->getFullRecruitCost() * ci.count;
-		}
-	}
-
-	return aivalue;
-}
-
-ui64 ArmyManager::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const
-{
-	auto bestArmy = getBestArmy(target, source);
-	uint64_t newArmy = 0;
-	uint64_t oldArmy = target->getArmyStrength();
-
-	for(auto & slot : bestArmy)
-	{
-		newArmy += slot.power;
-	}
-
-	return newArmy > oldArmy ? newArmy - oldArmy : 0;
-}

+ 0 - 56
AI/VCAI/ArmyManager.h

@@ -1,56 +0,0 @@
-/*
-* ArmyManager.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#pragma once
-
-#include "AIUtility.h"
-
-#include "../../lib/GameConstants.h"
-#include "../../lib/GameLibrary.h"
-#include "VCAI.h"
-
-struct SlotInfo
-{
-	const CCreature * creature;
-	int count;
-	uint64_t power;
-};
-
-class DLL_EXPORT IArmyManager //: public: IAbstractManager
-{
-public:
-	virtual ~IArmyManager() = default;
-	virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
-	virtual void setAI(VCAI * AI) = 0;
-	virtual bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const = 0;
-	virtual ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const = 0;
-	virtual ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const = 0;
-	virtual std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const = 0;
-	virtual std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const = 0;
-	virtual std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const = 0;
-};
-
-class DLL_EXPORT ArmyManager : public IArmyManager
-{
-private:
-	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
-	VCAI * ai;
-
-public:
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
-
-	bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override;
-	ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override;
-	ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
-	std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
-	std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
-};

+ 0 - 256
AI/VCAI/BuildingManager.cpp

@@ -1,256 +0,0 @@
-/*
-* BuildingManager.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 "BuildingManager.h"
-
-#include "../../lib/mapObjects/MapObjects.h"
-#include "../../lib/entities/building/CBuilding.h"
-
-bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays)
-{
-	if (maxDays == 0)
-	{
-		logAi->warn("Request to build building %d in 0 days!", building.toEnum());
-		return false;
-	}
-
-	if (!vstd::contains(t->getTown()->buildings, building))
-		return false; // no such building in town
-
-	if (t->hasBuilt(building)) //Already built? Shouldn't happen in general
-		return true;
-
-	const auto & buildPtr = t->getTown()->buildings.at(building);
-
-	auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID)
-	{
-		return t->hasBuilt(buildID);
-	});
-	toBuild.push_back(building);
-
-	for (BuildingID buildID : toBuild)
-	{
-		EBuildingState canBuild = cb->canBuildStructure(t, buildID);
-		if (canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER)
-			return false; //we won't be able to build this
-	}
-
-	if (maxDays && toBuild.size() > maxDays)
-		return false;
-
-	//TODO: calculate if we have enough resources to build it in maxDays?
-
-	for (const auto & buildID : toBuild)
-	{
-		const auto & b = t->getTown()->buildings.at(buildID);
-
-		EBuildingState canBuild = cb->canBuildStructure(t, buildID);
-		if (canBuild == EBuildingState::ALLOWED)
-		{
-			PotentialBuilding pb;
-			pb.bid = buildID;
-			pb.price = t->getBuildingCost(buildID);
-			immediateBuildings.push_back(pb); //these are checked again in try
-			return true;
-		}
-		else if (canBuild == EBuildingState::PREREQUIRES)
-		{
-			// can happen when dependencies have their own missing dependencies
-			if (tryBuildThisStructure(t, buildID, maxDays - 1))
-				return true;
-		}
-		else if (canBuild == EBuildingState::MISSING_BASE)
-		{
-			if (tryBuildThisStructure(t, b->upgrade, maxDays - 1))
-				return true;
-		}
-		else if (canBuild == EBuildingState::NO_RESOURCES)
-		{
-			//we may need to gather resources for those
-			PotentialBuilding pb;
-			pb.bid = buildID;
-			pb.price = t->getBuildingCost(buildID);
-			expensiveBuildings.push_back(pb); //these are checked again in try
-			return false;
-		}
-		else
-			return false;
-	}
-	return false;
-}
-
-bool BuildingManager::tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
-{
-	for (const auto & building : buildList)
-	{
-		if (t->hasBuilt(building))
-			continue;
-		return tryBuildThisStructure(t, building, maxDays);
-
-	}
-	return false; //Can't build anything
-}
-
-std::optional<BuildingID> BuildingManager::canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const
-{
-	for (const auto & building : buildList)
-	{
-		if (t->hasBuilt(building))
-			continue;
-		switch (cb->canBuildStructure(t, building))
-		{
-			case EBuildingState::ALLOWED:
-			case EBuildingState::NO_RESOURCES: //TODO: allow this via optional parameter?
-				return std::optional<BuildingID>(building);
-				break;
-		}
-	}
-	return std::optional<BuildingID>(); //Can't build anything
-}
-
-bool BuildingManager::tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays)
-{
-	for (const auto & building : buildList)
-	{
-		if (t->hasBuilt(building))
-			continue;
-		return tryBuildThisStructure(t, building, maxDays);
-	}
-	return false; //Nothing to build
-}
-
-void BuildingManager::init(CPlayerSpecificInfoCallback * CB)
-{
-	cb = CB;
-}
-
-void BuildingManager::setAI(VCAI * AI)
-{
-	ai = AI;
-}
-//Set of buildings for different goals. Does not include any prerequisites.
-static const std::vector<BuildingID> essential = { BuildingID::TAVERN, BuildingID::TOWN_HALL };
-static const std::vector<BuildingID> basicGoldSource = { BuildingID::TOWN_HALL, BuildingID::CITY_HALL };
-static const std::vector<BuildingID> defence = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE };
-static const std::vector<BuildingID> capitolAndRequirements = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::CAPITOL };
-static const std::vector<BuildingID> unitsSource = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
-BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7, BuildingID::DWELL_LVL_8 };
-static const std::vector<BuildingID> unitsUpgrade = { BuildingID::DWELL_LVL_1_UP, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP,
-BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP, BuildingID::DWELL_LVL_8_UP };
-static const std::vector<BuildingID> unitGrowth = { BuildingID::HORDE_1, BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR };
-static const std::vector<BuildingID> _spells = { BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
-BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5 };
-static const std::vector<BuildingID> extra = { BuildingID::MARKETPLACE, BuildingID::BLACKSMITH, BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2,
-BuildingID::SPECIAL_3, BuildingID::SPECIAL_4, BuildingID::SHIPYARD }; // all remaining buildings
-
-bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
-{
-	//TODO make *real* town development system
-	//TODO: faction-specific development: use special buildings, build dwellings in better order, etc
-	//TODO: build resource silo, defences when needed
-	//Possible - allow "locking" on specific building (build prerequisites and then building itself)
-	
-	//TODO: There is some disabled building code in GatherTroops and GatherArmy - take it into account when enhancing building. For now AI works best with building only via Build goal.
-
-	immediateBuildings.clear();
-	expensiveBuildings.clear();
-
-	//below algorithm focuses on economy growth at start of the game, saving money instead of build rushing is handled by Build goal
-	//changing code blocks order will alter behavior by changing order of adding elements to immediateBuildings / expensiveBuildings
-
-	// TResources currentRes = cb->getResourceAmount();
-	// TResources currentIncome = t->dailyIncome();
-
-	if(tryBuildAnyStructure(t, essential))
-		return true;
-
-	if (cb->getDate(Date::DAY_OF_WEEK) < 5) // first 4 days of week - try to focus on dwellings
-	{
-		if (tryBuildNextStructure(t, unitsSource, 4))
-			return true;
-	}
-
-	if (cb->getDate(Date::DAY_OF_WEEK) > 4) // last 3 days of week - try to focus on growth by building Fort/Citadel/Castle
-	{
-		if (tryBuildNextStructure(t, defence, 3))
-			return true;
-	}
-
-	if (t->hasBuilt(BuildingID::CASTLE))
-	{
-		if (tryBuildAnyStructure(t, unitGrowth))
-			return true;
-	}
-
-	//try to make City Hall
-	if (tryBuildNextStructure(t, basicGoldSource))
-		return true;
-
-	//workaround for mantis #2696 - build capitol with separate algorithm if it is available
-	if(t->hasBuilt(BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
-	{
-		if(tryBuildNextStructure(t, capitolAndRequirements))
-			return true;
-	}
-
-	//try to upgrade dwelling
-	for (int i = 0; i < unitsUpgrade.size(); i++)
-	{
-		if (t->hasBuilt(unitsSource[i]) && !t->hasBuilt(unitsUpgrade[i]) && t->hasBuilt(BuildingID::FORT))
-		{
-			if (tryBuildThisStructure(t, unitsUpgrade[i]))
-				return true;
-		}
-	}
-
-	//remaining tasks
-	if (tryBuildNextStructure(t, _spells))
-		return true;
-	if (tryBuildAnyStructure(t, extra))
-		return true;
-
-	//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
-	std::vector<BuildingID> extraBuildings;
-	for (const auto & buildingInfo : t->getTown()->buildings)
-	{
-		if (buildingInfo.first.isDwelling() && BuildingID::getUpgradeNoFromDwelling(buildingInfo.first) > 1)
-			extraBuildings.push_back(buildingInfo.first);
-	}
-	return tryBuildAnyStructure(t, extraBuildings);
-}
-
-BuildingID BuildingManager::getMaxPossibleGoldBuilding(const CGTownInstance * t)
-{
-	if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
-		return BuildingID::CAPITOL;
-	else if(cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
-		return BuildingID::CITY_HALL;
-	else if(cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
-		return BuildingID::TOWN_HALL;
-	else
-		return BuildingID::VILLAGE_HALL;
-}
-
-std::optional<PotentialBuilding> BuildingManager::immediateBuilding() const
-{
-	if (immediateBuildings.size())
-		return std::optional<PotentialBuilding>(immediateBuildings.front()); //back? whatever
-	else
-		return std::optional<PotentialBuilding>();
-}
-
-std::optional<PotentialBuilding> BuildingManager::expensiveBuilding() const
-{
-	if (expensiveBuildings.size())
-		return std::optional<PotentialBuilding>(expensiveBuildings.front());
-	else
-		return std::optional<PotentialBuilding>();
-}

+ 0 - 73
AI/VCAI/BuildingManager.h

@@ -1,73 +0,0 @@
-/*
-* BuildingManager.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#pragma once
-
-#include "AIUtility.h"
-
-#include "../../lib/GameConstants.h"
-#include "../../lib/GameLibrary.h"
-#include "VCAI.h"
-
-struct DLL_EXPORT PotentialBuilding
-{
-	BuildingID bid;
-	TResources price;
-	//days to build?
-};
-
-class DLL_EXPORT IBuildingManager //: public: IAbstractManager
-{ //info about town development
-public:
-	virtual ~IBuildingManager() = default;
-	virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
-	virtual void setAI(VCAI * AI) = 0;
-
-	virtual bool getBuildingOptions(const CGTownInstance * t) = 0;
-	virtual std::optional<PotentialBuilding> immediateBuilding() const = 0;
-	virtual std::optional<PotentialBuilding> expensiveBuilding() const = 0;
-	virtual std::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays) const = 0;
-};
-
-class DLL_EXPORT BuildingManager : public IBuildingManager
-{
-	friend class VCAI;
-	friend class AIhelper;
-	friend struct SetGlobalState;
-
-	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
-	VCAI * ai;
-
-public:
-
-	//try build anything in given town, and execute resulting Goal if any
-	bool getBuildingOptions(const CGTownInstance * t) override;
-	BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t);
-	std::optional<PotentialBuilding> immediateBuilding() const override;
-	std::optional<PotentialBuilding> expensiveBuilding() const override;
-	std::optional<BuildingID> canBuildAnyStructure(const CGTownInstance * t, const std::vector<BuildingID> & buildList, unsigned int maxDays = 7) const override;
-
-protected:
-
-	bool tryBuildAnyStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
-	//try build first unbuilt structure
-	bool tryBuildThisStructure(const CGTownInstance * t, BuildingID building, unsigned int maxDays = 7);
-	//try build ANY unbuilt structure
-	
-	bool tryBuildNextStructure(const CGTownInstance * t, std::vector<BuildingID> buildList, unsigned int maxDays = 7);
-
-private:
-	//TODO: remember current town?
-	std::vector<PotentialBuilding> immediateBuildings; //what we can build right now in current town
-	std::vector<PotentialBuilding> expensiveBuildings; //what we coudl build but can't afford
-
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
-};

+ 0 - 113
AI/VCAI/CMakeLists.txt

@@ -1,113 +0,0 @@
-set(VCAI_SRCS
-		Pathfinding/AIPathfinderConfig.cpp
-		Pathfinding/AIPathfinder.cpp
-		Pathfinding/AINodeStorage.cpp
-		Pathfinding/PathfindingManager.cpp
-		Pathfinding/Actions/BattleAction.cpp
-		Pathfinding/Actions/BoatActions.cpp
-		Pathfinding/Actions/TownPortalAction.cpp
-		Pathfinding/Rules/AILayerTransitionRule.cpp
-		Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
-		Pathfinding/Rules/AIMovementToDestinationRule.cpp
-		Pathfinding/Rules/AIPreviousNodeRule.cpp
-		AIUtility.cpp
-		AIhelper.cpp
-		ArmyManager.cpp
-		ResourceManager.cpp
-		BuildingManager.cpp
-		MapObjectsEvaluator.cpp
-		FuzzyEngines.cpp
-		FuzzyHelper.cpp
-		Goals/AbstractGoal.cpp
-		Goals/BuildBoat.cpp
-		Goals/Build.cpp
-		Goals/BuildThis.cpp
-		Goals/Explore.cpp
-		Goals/GatherArmy.cpp
-		Goals/GatherTroops.cpp
-		Goals/BuyArmy.cpp
-		Goals/AdventureSpellCast.cpp
-		Goals/Win.cpp
-		Goals/VisitTile.cpp
-		Goals/VisitObj.cpp
-		Goals/VisitHero.cpp
-		Goals/CollectRes.cpp
-		Goals/Trade.cpp
-		Goals/RecruitHero.cpp
-		Goals/Conquer.cpp
-		Goals/ClearWayTo.cpp
-		Goals/DigAtTile.cpp
-		Goals/GetArtOfType.cpp
-		Goals/FindObj.cpp
-		Goals/CompleteQuest.cpp
-		VCAI.cpp
-)
-
-set(VCAI_HEADERS
-		StdInc.h
-		
-		Pathfinding/AIPathfinderConfig.h
-		Pathfinding/AIPathfinder.h
-		Pathfinding/AINodeStorage.h
-		Pathfinding/PathfindingManager.h
-		Pathfinding/Actions/ISpecialAction.h
-		Pathfinding/Actions/BattleAction.h
-		Pathfinding/Actions/BoatActions.h
-		Pathfinding/Actions/TownPortalAction.h
-		Pathfinding/Rules/AILayerTransitionRule.h
-		Pathfinding/Rules/AIMovementAfterDestinationRule.h
-		Pathfinding/Rules/AIMovementToDestinationRule.h
-		Pathfinding/Rules/AIPreviousNodeRule.h
-		AIUtility.h
-		AIhelper.h
-		ArmyManager.h
-		ResourceManager.h
-		BuildingManager.h
-		MapObjectsEvaluator.h
-		FuzzyEngines.h
-		FuzzyHelper.h
-		Goals/AbstractGoal.h
-		Goals/CGoal.h
-		Goals/Invalid.h
-		Goals/BuildBoat.h
-		Goals/Build.h
-		Goals/BuildThis.h
-		Goals/Explore.h
-		Goals/GatherArmy.h
-		Goals/GatherTroops.h
-		Goals/BuyArmy.h
-		Goals/AdventureSpellCast.h
-		Goals/Win.h
-		Goals/VisitTile.h
-		Goals/VisitObj.h
-		Goals/VisitHero.h
-		Goals/CollectRes.h
-		Goals/Trade.h
-		Goals/RecruitHero.h
-		Goals/Conquer.h
-		Goals/ClearWayTo.h
-		Goals/DigAtTile.h
-		Goals/GetArtOfType.h
-		Goals/FindObj.h
-		Goals/CompleteQuest.h
-		Goals/Goals.h
-		VCAI.h
-)
-
-if(NOT ENABLE_STATIC_LIBS)
-	list(APPEND VCAI_SRCS main.cpp StdInc.cpp)
-endif()
-assign_source_group(${VCAI_SRCS} ${VCAI_HEADERS})
-
-if(ENABLE_STATIC_LIBS)
-	add_library(VCAI STATIC ${VCAI_SRCS} ${VCAI_HEADERS})
-else()
-	add_library(VCAI SHARED ${VCAI_SRCS} ${VCAI_HEADERS})
-	install(TARGETS VCAI RUNTIME DESTINATION ${AI_LIB_DIR} LIBRARY DESTINATION ${AI_LIB_DIR})
-endif()
-
-target_include_directories(VCAI PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_link_libraries(VCAI PUBLIC vcmi fuzzylite::fuzzylite)
-
-vcmi_set_output_dir(VCAI "AI")
-enable_pch(VCAI)

+ 0 - 456
AI/VCAI/FuzzyEngines.cpp

@@ -1,456 +0,0 @@
-/*
-* FuzzyEngines.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 "FuzzyEngines.h"
-#include "Goals/Goals.h"
-
-#include "../../lib/mapObjects/MapObjects.h"
-#include "VCAI.h"
-#include "MapObjectsEvaluator.h"
-
-#define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
-#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
-
-engineBase::engineBase()
-{
-	rules = new fl::RuleBlock();
-	engine.addRuleBlock(rules);
-}
-
-void engineBase::configure()
-{
-	engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid", "Proportional");
-	logAi->trace(engine.toString());
-}
-
-void engineBase::addRule(const std::string & txt)
-{
-	rules->addRule(fl::Rule::parse(txt, &engine));
-}
-
-struct armyStructure
-{
-	float walkers;
-	float shooters;
-	float flyers;
-	ui32 maxSpeed;
-};
-
-armyStructure evaluateArmyStructure(const CArmedInstance * army)
-{
-	ui64 totalStrength = army->getArmyStrength();
-	double walkersStrength = 0;
-	double flyersStrength = 0;
-	double shootersStrength = 0;
-	ui32 maxSpeed = 0;
-
-	static const CSelector selectorSHOOTER = Selector::type()(BonusType::SHOOTER);
-	static const std::string keySHOOTER = "type_"+std::to_string((int32_t)BonusType::SHOOTER);
-
-	static const CSelector selectorFLYING = Selector::type()(BonusType::FLYING);
-	static const std::string keyFLYING = "type_"+std::to_string((int32_t)BonusType::FLYING);
-
-	static const CSelector selectorSTACKS_SPEED = Selector::type()(BonusType::STACKS_SPEED);
-	static const std::string keySTACKS_SPEED = "type_"+std::to_string((int32_t)BonusType::STACKS_SPEED);
-
-	for(const auto & s : army->Slots())
-	{
-		bool walker = true;
-		auto bearer = s.second->getType()->getBonusBearer();
-		if(bearer->hasBonus(selectorSHOOTER, keySHOOTER))
-		{
-			shootersStrength += s.second->getPower();
-			walker = false;
-		}
-		if(bearer->hasBonus(selectorFLYING, keyFLYING))
-		{
-			flyersStrength += s.second->getPower();
-			walker = false;
-		}
-		if(walker)
-			walkersStrength += s.second->getPower();
-
-		vstd::amax(maxSpeed, bearer->valOfBonuses(selectorSTACKS_SPEED, keySTACKS_SPEED));
-	}
-	armyStructure as;
-	as.walkers = static_cast<float>(walkersStrength / totalStrength);
-	as.shooters = static_cast<float>(shootersStrength / totalStrength);
-	as.flyers = static_cast<float>(flyersStrength / totalStrength);
-	as.maxSpeed = maxSpeed;
-	assert(as.walkers || as.flyers || as.shooters);
-	return as;
-}
-
-float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const
-{
-	if(goal.evaluationContext.movementCost > 0)
-	{
-		return goal.evaluationContext.movementCost;
-	}
-	else
-	{
-		auto pathInfo = ai->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile);
-		return pathInfo->getCost();
-	}
-}
-
-TacticalAdvantageEngine::TacticalAdvantageEngine()
-{
-	try
-	{
-		ourShooters = new fl::InputVariable("OurShooters");
-		ourWalkers = new fl::InputVariable("OurWalkers");
-		ourFlyers = new fl::InputVariable("OurFlyers");
-		enemyShooters = new fl::InputVariable("EnemyShooters");
-		enemyWalkers = new fl::InputVariable("EnemyWalkers");
-		enemyFlyers = new fl::InputVariable("EnemyFlyers");
-
-		//Tactical advantage calculation
-		std::vector<fl::InputVariable *> helper =
-		{
-			ourShooters, ourWalkers, ourFlyers, enemyShooters, enemyWalkers, enemyFlyers
-		};
-
-		for(auto val : helper)
-		{
-			engine.addInputVariable(val);
-			val->addTerm(new fl::Ramp("FEW", 0.6, 0.0));
-			val->addTerm(new fl::Ramp("MANY", 0.4, 1));
-			val->setRange(0.0, 1.0);
-		}
-
-		ourSpeed = new fl::InputVariable("OurSpeed");
-		enemySpeed = new fl::InputVariable("EnemySpeed");
-
-		helper = { ourSpeed, enemySpeed };
-
-		for(auto val : helper)
-		{
-			engine.addInputVariable(val);
-			val->addTerm(new fl::Ramp("LOW", 6.5, 3));
-			val->addTerm(new fl::Triangle("MEDIUM", 5.5, 10.5));
-			val->addTerm(new fl::Ramp("HIGH", 8.5, 16));
-			val->setRange(0, 25);
-		}
-
-		castleWalls = new fl::InputVariable("CastleWalls");
-		engine.addInputVariable(castleWalls);
-		{
-			int wallsNone = CGTownInstance::NONE;
-			int wallsFort = CGTownInstance::FORT;
-			int wallsCitadel = CGTownInstance::CITADEL;
-			int wallsCastle = CGTownInstance::CASTLE;
-
-			fl::Rectangle * none = new fl::Rectangle("NONE", wallsNone, wallsNone + (wallsFort - wallsNone) * 0.5f);
-			castleWalls->addTerm(none);
-
-			fl::Trapezoid * medium = new fl::Trapezoid("MEDIUM", (wallsFort - wallsNone) * 0.5f, wallsFort, wallsCitadel, wallsCitadel + (wallsCastle - wallsCitadel) * 0.5f);
-			castleWalls->addTerm(medium);
-
-			fl::Ramp * high = new fl::Ramp("HIGH", wallsCitadel - 0.1, wallsCastle);
-			castleWalls->addTerm(high);
-
-			castleWalls->setRange(wallsNone, wallsCastle);
-		}
-
-
-		bankPresent = new fl::InputVariable("Bank");
-		engine.addInputVariable(bankPresent);
-		{
-			fl::Rectangle * termFalse = new fl::Rectangle("FALSE", 0.0, 0.5f);
-			bankPresent->addTerm(termFalse);
-			fl::Rectangle * termTrue = new fl::Rectangle("TRUE", 0.5f, 1);
-			bankPresent->addTerm(termTrue);
-			bankPresent->setRange(0, 1);
-		}
-
-		threat = new fl::OutputVariable("Threat");
-		engine.addOutputVariable(threat);
-		threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGTH));
-		threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2));
-		threat->addTerm(new fl::Ramp("HIGH", 1, 1.5));
-		threat->setRange(MIN_AI_STRENGTH, 1.5);
-
-		addRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW");
-		addRule("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW");
-		addRule("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH");
-		addRule("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW");
-
-		addRule("if OurWalkers is FEW and EnemyShooters is MANY then Threat is LOW");
-		addRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is HIGH");
-		//just to cover all cases
-		addRule("if OurShooters is FEW and EnemySpeed is HIGH then Threat is MEDIUM");
-		addRule("if EnemySpeed is MEDIUM then Threat is MEDIUM");
-		addRule("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM");
-
-		addRule("if Bank is TRUE and OurShooters is MANY then Threat is HIGH");
-		addRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW");
-
-		addRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is HIGH");
-		addRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM");
-		addRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW");
-
-	}
-	catch(fl::Exception & pe)
-	{
-		logAi->error("initTacticalAdvantage: %s", pe.getWhat());
-	}
-	configure();
-}
-
-float TacticalAdvantageEngine::getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy)
-{
-	float output = 1;
-	/*try //TODO: rework this engine, it tends to produce nonsense output
-	{
-		armyStructure ourStructure = evaluateArmyStructure(we);
-		armyStructure enemyStructure = evaluateArmyStructure(enemy);
-
-		ourWalkers->setValue(ourStructure.walkers);
-		ourShooters->setValue(ourStructure.shooters);
-		ourFlyers->setValue(ourStructure.flyers);
-		ourSpeed->setValue(ourStructure.maxSpeed);
-
-		enemyWalkers->setValue(enemyStructure.walkers);
-		enemyShooters->setValue(enemyStructure.shooters);
-		enemyFlyers->setValue(enemyStructure.flyers);
-		enemySpeed->setValue(enemyStructure.maxSpeed);
-
-		const CGTownInstance * fort = dynamic_cast<const CGTownInstance *>(enemy);
-		if(fort)
-			castleWalls->setValue(fort->fortLevel());
-		else
-			castleWalls->setValue(0);
-
-		engine.process();
-		output = threat->getValue();
-	}
-	catch(fl::Exception & fe)
-	{
-		logAi->error("getTacticalAdvantage: %s ", fe.getWhat());
-	}
-
-	if(output < 0 || (output != output))
-	{
-		fl::InputVariable * tab[] = { bankPresent, castleWalls, ourWalkers, ourShooters, ourFlyers, ourSpeed, enemyWalkers, enemyShooters, enemyFlyers, enemySpeed };
-		std::string names[] = { "bankPresent", "castleWalls", "ourWalkers", "ourShooters", "ourFlyers", "ourSpeed", "enemyWalkers", "enemyShooters", "enemyFlyers", "enemySpeed" };
-		std::stringstream log("Warning! Fuzzy engine doesn't cover this set of parameters: ");
-
-		for(int i = 0; i < boost::size(tab); i++)
-			log << names[i] << ": " << tab[i]->getValue() << " ";
-		logAi->error(log.str());
-		assert(false);
-	}*/
-
-	return output;
-}
-
-//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec)
-
-HeroMovementGoalEngineBase::HeroMovementGoalEngineBase()
-{
-	try
-	{
-		strengthRatio = new fl::InputVariable("strengthRatio"); //hero must be strong enough to defeat guards
-		heroStrength = new fl::InputVariable("heroStrength"); //we want to use weakest possible hero
-		turnDistance = new fl::InputVariable("turnDistance"); //we want to use hero who is near
-		missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission
-		value = new fl::OutputVariable("Value");
-		value->setMinimum(0);
-		value->setMaximum(5);
-
-		std::vector<fl::InputVariable *> helper = { strengthRatio, heroStrength, turnDistance, missionImportance };
-		for(auto val : helper)
-		{
-			engine.addInputVariable(val);
-		}
-		engine.addOutputVariable(value);
-
-		strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0));
-		strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3));
-		strengthRatio->setRange(0, SAFE_ATTACK_CONSTANT * 3);
-
-		//strength compared to our main hero
-		heroStrength->addTerm(new fl::Ramp("LOW", 0.5, 0));
-		heroStrength->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8));
-		heroStrength->addTerm(new fl::Ramp("HIGH", 0.5, 1));
-		heroStrength->setRange(0.0, 1.0);
-
-		turnDistance->addTerm(new fl::Ramp("SHORT", 0.5, 0));
-		turnDistance->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8));
-		turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 10));
-		turnDistance->setRange(0.0, 10.0);
-
-		missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0));
-		missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3));
-		missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5));
-		missionImportance->setRange(0.0, 5.0);
-
-		//an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/
-		//should be same as "mission Importance" to keep consistency
-		value->addTerm(new fl::Ramp("LOW", 2.5, 0));
-		value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/
-		value->addTerm(new fl::Ramp("HIGH", 2.5, 5));
-		value->setRange(0.0, 5.0);
-
-		//use unarmed scouts if possible
-		addRule("if strengthRatio is HIGH and heroStrength is LOW then Value is HIGH");
-		//we may want to use secondary hero(es) rather than main hero
-		addRule("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is MEDIUM");
-		addRule("if strengthRatio is HIGH and heroStrength is HIGH then Value is LOW");
-		//don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army)
-		addRule("if strengthRatio is LOW and heroStrength is LOW then Value is LOW");
-		//attempt to arm secondary heroes is not stupid
-		addRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is HIGH");
-		addRule("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW");
-
-		//do not cancel important goals
-		addRule("if lockedMissionImportance is HIGH then Value is LOW");
-		addRule("if lockedMissionImportance is MEDIUM then Value is MEDIUM");
-		addRule("if lockedMissionImportance is LOW then Value is HIGH");
-		//pick nearby objects if it's easy, avoid long walks
-		addRule("if turnDistance is SHORT then Value is HIGH");
-		addRule("if turnDistance is MEDIUM then Value is MEDIUM");
-		addRule("if turnDistance is LONG then Value is LOW");
-	}
-	catch(fl::Exception & fe)
-	{
-		logAi->error("HeroMovementGoalEngineBase: %s", fe.getWhat());
-	}
-}
-
-void HeroMovementGoalEngineBase::setSharedFuzzyVariables(Goals::AbstractGoal & goal)
-{
-	float turns = calculateTurnDistanceInputValue(goal);
-	float missionImportanceData = 0;
-
-	if(vstd::contains(ai->lockedHeroes, goal.hero))
-	{
-		missionImportanceData = ai->lockedHeroes[goal.hero]->priority;
-	}
-	else if(goal.parent)
-	{
-		missionImportanceData = goal.parent->priority;
-	}
-
-	float strengthRatioData = 10.0f; //we are much stronger than enemy
-	ui64 danger = fh->evaluateDanger(goal.tile, goal.hero.h);
-	if(danger)
-		strengthRatioData = static_cast<float>((fl::scalar)goal.hero.h->getTotalStrength() / danger);
-
-	try
-	{
-		strengthRatio->setValue(strengthRatioData);
-		heroStrength->setValue((fl::scalar)goal.hero->getTotalStrength() / ai->primaryHero()->getTotalStrength());
-		turnDistance->setValue(turns);
-		missionImportance->setValue(missionImportanceData);
-	}
-	catch(fl::Exception & fe)
-	{
-		logAi->error("HeroMovementGoalEngineBase::setSharedFuzzyVariables: %s", fe.getWhat());
-	}
-}
-
-VisitObjEngine::VisitObjEngine()
-{
-	try
-	{
-		objectValue = new fl::InputVariable("objectValue"); //value of that object type known by AI
-
-		engine.addInputVariable(objectValue);
-
-		//objectValue ranges are based on checking RMG priorities of some objects and checking LOW/MID/HIGH proportions for various values in QtFuzzyLite
-		objectValue->addTerm(new fl::Ramp("LOW", 3500.0, 0.0));
-		objectValue->addTerm(new fl::Triangle("MEDIUM", 0.0, 8500.0));
-		std::vector<fl::Discrete::Pair> multiRamp = { fl::Discrete::Pair(5000.0, 0.0), fl::Discrete::Pair(10000.0, 0.75), fl::Discrete::Pair(20000.0, 1.0) };
-		objectValue->addTerm(new fl::Discrete("HIGH", multiRamp));
-		objectValue->setRange(0.0, 20000.0); //relic artifact value is border value by design, even better things are scaled down.
-
-		addRule("if objectValue is HIGH then Value is HIGH");
-		addRule("if objectValue is MEDIUM then Value is MEDIUM");
-		addRule("if objectValue is LOW then Value is LOW");
-	}
-	catch(fl::Exception & fe)
-	{
-		logAi->error("FindWanderTarget: %s", fe.getWhat());
-	}
-	configure();
-}
-
-float VisitObjEngine::evaluate(Goals::VisitObj & goal)
-{
-	if(!goal.hero)
-		return 0;
-
-	auto obj = ai->myCb->getObj(ObjectInstanceID(goal.objid));
-	if(!obj)
-	{
-		logAi->error("Goals::VisitObj objid " + std::to_string(goal.objid) + " no longer visible, probably goal used for something it's not intended");
-		return -100; // FIXME: Added check when goal was used for hero instead of VisitHero, but crashes are bad anyway
-	}
-
-	std::optional<int> objValueKnownByAI = MapObjectsEvaluator::getInstance().getObjectValue(obj);
-	int objValue = 0;
-
-	if(objValueKnownByAI != std::nullopt) //consider adding value manipulation based on object instances on map
-	{
-		objValue = std::min(std::max(objValueKnownByAI.value(), 0), 20000);
-	}
-	else
-	{
-		MapObjectsEvaluator::getInstance().addObjectData(obj->ID, obj->subID, 0);
-		logGlobal->error("AI met object type it doesn't know - ID: %d, subID: %d - adding to database with value %d ", obj->ID, obj->subID, objValue);
-	}
-
-	setSharedFuzzyVariables(goal);
-
-	float output = -1.0f;
-	try
-	{
-		objectValue->setValue(objValue);
-		engine.process();
-		output = static_cast<float>(value->getValue());
-	}
-	catch(fl::Exception & fe)
-	{
-		logAi->error("evaluate getWanderTargetObjectValue: %s", fe.getWhat());
-	}
-	assert(output >= 0.0f);
-	return output;
-}
-
-VisitTileEngine::VisitTileEngine() //so far no VisitTile-specific variables that are not shared with HeroMovementGoalEngineBase
-{
-	configure();
-}
-
-float VisitTileEngine::evaluate(Goals::VisitTile & goal)
-{
-	//we assume that hero is already set and we want to choose most suitable one for the mission
-	if(!goal.hero)
-		return 0;
-
-	//assert(cb->isInTheMap(g.tile));
-
-	setSharedFuzzyVariables(goal);
-
-	try
-	{
-		engine.process();
-
-		goal.priority = static_cast<float>(value->getValue());
-	}
-	catch(fl::Exception & fe)
-	{
-		logAi->error("evaluate VisitTile: %s", fe.getWhat());
-	}
-	assert(goal.priority >= 0);
-	return goal.priority;
-}

+ 0 - 86
AI/VCAI/FuzzyEngines.h

@@ -1,86 +0,0 @@
-/*
-* FuzzyEngines.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
-#if __has_include(<fuzzylite/Headers.h>)
-#  include <fuzzylite/Headers.h>
-#else
-#  include <fl/Headers.h>
-#endif
-#include "Goals/AbstractGoal.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-class CArmedInstance;
-
-VCMI_LIB_NAMESPACE_END
-
-class engineBase //subclasses create fuzzylite variables with "new" that are not freed - this is desired as fl::Engine wants to destroy these...
-{
-protected:
-	fl::Engine engine;
-	fl::RuleBlock * rules;
-	virtual void configure();
-	void addRule(const std::string & txt);
-public:
-	engineBase();
-};
-
-class TacticalAdvantageEngine : public engineBase //TODO: rework this engine, it does not work well (example: AI hero with 140 beholders attacked 150 beholders - engine lowered danger 50000 -> 35000)
-{
-public:
-	TacticalAdvantageEngine();
-	float getTacticalAdvantage(const CArmedInstance * we, const CArmedInstance * enemy); //returns factor how many times enemy is stronger than us
-private:
-	fl::InputVariable * ourWalkers;
-	fl::InputVariable * ourShooters;
-	fl::InputVariable * ourFlyers;
-	fl::InputVariable * enemyWalkers;
-	fl::InputVariable * enemyShooters;
-	fl::InputVariable * enemyFlyers;
-	fl::InputVariable * ourSpeed;
-	fl::InputVariable * enemySpeed;
-	fl::InputVariable * bankPresent;
-	fl::InputVariable * castleWalls;
-	fl::OutputVariable * threat;
-};
-
-class HeroMovementGoalEngineBase : public engineBase //in future - maybe derive from some (GoalEngineBase : public engineBase) class for handling non-movement goals with common utility for goal engines
-{
-public:
-	HeroMovementGoalEngineBase();
-
-protected:
-	void setSharedFuzzyVariables(Goals::AbstractGoal & goal);
-
-	fl::InputVariable * strengthRatio;
-	fl::InputVariable * heroStrength;
-	fl::InputVariable * turnDistance;
-	fl::InputVariable * missionImportance;
-	fl::OutputVariable * value;
-
-private:
-	float calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const;
-};
-
-class VisitTileEngine : public HeroMovementGoalEngineBase
-{
-public:
-	VisitTileEngine();
-	float evaluate(Goals::VisitTile & goal);
-};
-
-class VisitObjEngine : public HeroMovementGoalEngineBase
-{
-public:
-	VisitObjEngine();
-	float evaluate(Goals::VisitObj & goal);
-protected:
-	fl::InputVariable * objectValue;
-};

+ 0 - 293
AI/VCAI/FuzzyHelper.cpp

@@ -1,293 +0,0 @@
-/*
- * FuzzyHelper.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 "FuzzyHelper.h"
-
-#include "Goals/Goals.h"
-#include "Goals/CompleteQuest.h"
-#include "VCAI.h"
-
-#include "../../lib/mapObjectConstructors/AObjectTypeHandler.h"
-#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
-#include "../../lib/mapObjects/CGCreature.h"
-#include "../../lib/mapObjects/CGDwelling.h"
-#include "../../lib/gameState/InfoAboutArmy.h"
-
-FuzzyHelper * fh;
-
-Goals::TSubgoal FuzzyHelper::chooseSolution(Goals::TGoalVec vec)
-{
-	if(vec.empty())
-	{
-		logAi->debug("FuzzyHelper found no goals. Returning Goals::Invalid.");
-
-		//no possibilities found
-		return sptr(Goals::Invalid());
-	}
-
-	//a trick to switch between heroes less often - calculatePaths is costly
-	auto sortByHeroes = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
-	{
-		return lhs->hero.h < rhs->hero.h;
-	};
-	boost::sort(vec, sortByHeroes);
-
-	for(auto g : vec)
-	{
-		setPriority(g);
-	}
-
-	auto compareGoals = [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
-	{
-		return lhs->priority < rhs->priority;
-	};
-
-	for(auto goal : vec)
-	{
-		logAi->trace("FuzzyHelper evaluated goal %s, priority=%.4f", goal->name(), goal->priority);
-	}
-
-	Goals::TSubgoal result = *boost::max_element(vec, compareGoals);
-
-	logAi->debug("FuzzyHelper returned goal %s, priority=%.4f", result->name(), result->priority);
-
-	return result;
-}
-
-float FuzzyHelper::evaluate(Goals::VisitTile & g)
-{
-	if(g.parent)
-	{
-		g.parent->accept(this);
-	}
-
-	return visitTileEngine.evaluate(g);
-}
-
-float FuzzyHelper::evaluate(Goals::BuildBoat & g)
-{
-	const float buildBoatPenalty = 0.25;
-
-	if(!g.parent)
-	{
-		return 0;
-	}
-
-	return g.parent->accept(this) - buildBoatPenalty;
-}
-
-float FuzzyHelper::evaluate(Goals::AdventureSpellCast & g)
-{
-	if(!g.parent)
-	{
-		return 0;
-	}
-
-	const CSpell * spell = g.getSpell();
-	const float spellCastPenalty = (float)g.hero->getSpellCost(spell) / g.hero->mana;
-
-	return g.parent->accept(this) - spellCastPenalty;
-}
-
-float FuzzyHelper::evaluate(Goals::CompleteQuest & g)
-{
-	// TODO: How to evaluate quest complexity?
-	const float questPenalty = 0.2f;
-
-	if(!g.parent)
-	{
-		return 0;
-	}
-
-	return g.parent->accept(this) * questPenalty;
-}
-
-float FuzzyHelper::evaluate(Goals::VisitObj & g)
-{
-	if(g.parent)
-	{
-		g.parent->accept(this);
-	}
-
-	return visitObjEngine.evaluate(g);
-}
-
-float FuzzyHelper::evaluate(Goals::VisitHero & g)
-{
-	auto obj = ai->myCb->getObj(ObjectInstanceID(g.objid)); //we assume for now that these goals are similar
-	if(!obj)
-	{
-		return -100; //hero died in the meantime
-	}
-	else
-	{
-		g.setpriority(Goals::VisitTile(obj->visitablePos()).sethero(g.hero).accept(this));
-	}
-	return g.priority;
-}
-float FuzzyHelper::evaluate(Goals::GatherArmy & g)
-{
-	//the more army we need, the more important goal
-	//the more army we lack, the less important goal
-	float army = static_cast<float>(g.hero->getArmyStrength());
-	float ratio = g.value / std::max(g.value - army, 2000.0f); //2000 is about the value of hero recruited from tavern
-	return 5 * (ratio / (ratio + 2)); //so 50% army gives 2.5, asymptotic 5
-}
-
-float FuzzyHelper::evaluate(Goals::ClearWayTo & g)
-{
-	if (!g.hero.h)
-		return 0; //lowest priority
-
-	return g.whatToDoToAchieve()->accept(this);
-}
-
-float FuzzyHelper::evaluate(Goals::BuildThis & g)
-{
-	return g.priority; //TODO
-}
-float FuzzyHelper::evaluate(Goals::DigAtTile & g)
-{
-	return 0;
-}
-float FuzzyHelper::evaluate(Goals::CollectRes & g)
-{
-	return g.priority; //handled by ResourceManager
-}
-float FuzzyHelper::evaluate(Goals::Build & g)
-{
-	return 0;
-}
-float FuzzyHelper::evaluate(Goals::BuyArmy & g)
-{
-	return g.priority;
-}
-float FuzzyHelper::evaluate(Goals::Explore & g)
-{
-	return 1;
-}
-float FuzzyHelper::evaluate(Goals::RecruitHero & g)
-{
-	return 1;
-}
-float FuzzyHelper::evaluate(Goals::Invalid & g)
-{
-	return -1e10;
-}
-float FuzzyHelper::evaluate(Goals::AbstractGoal & g)
-{
-	logAi->warn("Cannot evaluate goal %s", g.name());
-	return g.priority;
-}
-void FuzzyHelper::setPriority(Goals::TSubgoal & g) //calls evaluate - Visitor pattern
-{
-	g->setpriority(g->accept(this)); //this enforces returned value is set
-}
-
-ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
-{
-	return evaluateDanger(tile, visitor, ai);
-}
-
-ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai)
-{
-	auto cb = ai->myCb;
-	const TerrainTile * t = cb->getTile(tile, false);
-	if(!t) //we can know about guard but can't check its tile (the edge of fow)
-		return 190000000; //MUCH
-
-	ui64 objectDanger = 0;
-	ui64 guardDanger = 0;
-
-	auto visitableObjects = cb->getVisitableObjs(tile);
-	// in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero.
-	if(vstd::contains_if(visitableObjects, objWithID<Obj::HERO>))
-	{
-		vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj)
-		{
-			return !objWithID<Obj::HERO>(obj);
-		});
-	}
-
-	if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
-	{
-		objectDanger = evaluateDanger(dangerousObject, ai); //unguarded objects can also be dangerous or unhandled
-		if(objectDanger)
-		{
-			//TODO: don't downcast objects AI shouldn't know about!
-			auto armedObj = dynamic_cast<const CArmedInstance *>(dangerousObject);
-			if(armedObj)
-			{
-				float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, armedObj);
-				objectDanger = static_cast<ui64>(objectDanger * tacticalAdvantage); //this line tends to go infinite for allied towns (?)
-			}
-		}
-		if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
-		{
-			//check guard on the other side of the gate
-			auto it = ai->knownSubterraneanGates.find(dangerousObject);
-			if(it != ai->knownSubterraneanGates.end())
-			{
-				auto guards = cb->getGuardingCreatures(it->second->visitablePos());
-				for(auto cre : guards)
-				{
-					float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
-
-					vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage);
-				}
-			}
-		}
-	}
-
-	auto guards = cb->getGuardingCreatures(tile);
-	for(auto cre : guards)
-	{
-		float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
-
-		vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage); //we are interested in strongest monster around
-	}
-
-	//TODO mozna odwiedzic blockvis nie ruszajac straznika
-	return std::max(objectDanger, guardDanger);
-}
-
-ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
-{
-	auto cb = ai->myCb;
-
-	if(obj->tempOwner.isValidPlayer() && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat
-		return 0;
-
-	switch(obj->ID)
-	{
-	case Obj::HERO:
-	{
-		InfoAboutHero iah;
-		cb->getHeroInfo(obj, iah);
-		return iah.army.getStrength();
-	}
-	case Obj::TOWN:
-	case Obj::GARRISON:
-	case Obj::GARRISON2:
-	{
-		InfoAboutTown iat;
-		cb->getTownInfo(obj, iat);
-		return iat.army.getStrength();
-	}
-	default:
-	{
-		const CArmedInstance * a = dynamic_cast<const CArmedInstance *>(obj);
-		if (a)
-			return a->getArmyStrength();
-		else
-			return 0;
-	}
-	}
-}

+ 0 - 47
AI/VCAI/FuzzyHelper.h

@@ -1,47 +0,0 @@
-/*
- * FuzzyHelper.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 "FuzzyEngines.h"
-
-class DLL_EXPORT FuzzyHelper
-{
-public:
-	TacticalAdvantageEngine tacticalAdvantageEngine;
-	VisitTileEngine visitTileEngine;
-	VisitObjEngine visitObjEngine;
-
-	float evaluate(Goals::Explore & g);
-	float evaluate(Goals::RecruitHero & g);
-	float evaluate(Goals::VisitTile & g);
-	float evaluate(Goals::VisitObj & g);
-	float evaluate(Goals::VisitHero & g);
-	float evaluate(Goals::BuildThis & g);
-	float evaluate(Goals::DigAtTile & g);
-	float evaluate(Goals::CollectRes & g);
-	float evaluate(Goals::Build & g);
-	float evaluate(Goals::BuyArmy & g);
-	float evaluate(Goals::BuildBoat & g);
-	float evaluate(Goals::GatherArmy & g);
-	float evaluate(Goals::ClearWayTo & g);
-	float evaluate(Goals::CompleteQuest & g);
-	float evaluate(Goals::AdventureSpellCast & g);
-	float evaluate(Goals::Invalid & g);
-	float evaluate(Goals::AbstractGoal & g);
-	void setPriority(Goals::TSubgoal & g);
-
-	Goals::TSubgoal chooseSolution(Goals::TGoalVec vec);
-	//std::shared_ptr<AbstractGoal> chooseSolution (std::vector<std::shared_ptr<AbstractGoal>> & vec);
-
-	ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
-	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai);
-	ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
-};
-
-extern FuzzyHelper * fh;

+ 0 - 185
AI/VCAI/Goals/AbstractGoal.cpp

@@ -1,185 +0,0 @@
-/*
-* AbstractGoal.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 "AbstractGoal.h"
-#include "../VCAI.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/constants/StringConstants.h"
-#include "../../../lib/entities/artifact/CArtifact.h"
-#include "../../../lib/entities/ResourceTypeHandler.h"
-
-using namespace Goals;
-
-TSubgoal Goals::sptr(const AbstractGoal & tmp)
-{
-	TSubgoal ptr;
-	ptr.reset(tmp.clone());
-	return ptr;
-}
-
-std::string AbstractGoal::name() const //TODO: virtualize
-{
-	std::string desc;
-	switch(goalType)
-	{
-	case INVALID:
-		return "INVALID";
-	case WIN:
-		return "WIN";
-	case CONQUER:
-		return "CONQUER";
-	case BUILD:
-		return "BUILD";
-	case EXPLORE:
-		desc = "EXPLORE";
-		break;
-	case GATHER_ARMY:
-		desc = "GATHER ARMY";
-		break;
-	case BUY_ARMY:
-		return "BUY ARMY";
-		break;
-	case BOOST_HERO:
-		desc = "BOOST_HERO (unsupported)";
-		break;
-	case RECRUIT_HERO:
-		return "RECRUIT HERO";
-	case BUILD_STRUCTURE:
-		return "BUILD STRUCTURE";
-	case COLLECT_RES:
-		desc = "COLLECT RESOURCE " + GameResID(resID).toResource()->getJsonKey() + " (" + std::to_string(value) + ")";
-		break;
-	case TRADE:
-	{
-		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-		if (obj)
-			desc = (boost::format("TRADE %d of %s at %s") % value % GameResID(resID).toResource()->getJsonKey() % obj->getObjectName()).str();
-	}
-	break;
-	case GATHER_TROOPS:
-		desc = "GATHER TROOPS";
-		break;
-	case VISIT_OBJ:
-	{
-		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-		if(obj)
-			desc = "VISIT OBJ " + obj->getObjectName();
-	}
-	break;
-	case FIND_OBJ:
-		desc = "FIND OBJ " + std::to_string(objid);
-		break;
-	case VISIT_HERO:
-	{
-		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-		if(obj)
-			desc = "VISIT HERO " + obj->getObjectName();
-	}
-	break;
-	case GET_ART_TYPE:
-		desc = "GET ARTIFACT OF TYPE " + ArtifactID(aid).toEntity(LIBRARY)->getNameTranslated();
-		break;
-	case VISIT_TILE:
-		desc = "VISIT TILE " + tile.toString();
-		break;
-	case CLEAR_WAY_TO:
-		desc = "CLEAR WAY TO " + tile.toString();
-		break;
-	case DIG_AT_TILE:
-		desc = "DIG AT TILE " + tile.toString();
-		break;
-	default:
-		return std::to_string(goalType);
-	}
-	if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
-		desc += " (" + hero->getNameTranslated() + ")";
-	return desc;
-}
-
-bool AbstractGoal::operator==(const AbstractGoal & g) const
-{
-	return false;
-}
-
-// FIXME: unused code?
-//bool AbstractGoal::operator<(AbstractGoal & g) //for std::unique
-//{
-//	//TODO: make sure it gets goals consistent with == operator
-//	if (goalType < g.goalType)
-//		return true;
-//	if (goalType > g.goalType)
-//		return false;
-//	if (hero < g.hero)
-//		return true;
-//	if (hero > g.hero)
-//		return false;
-//	if (tile < g.tile)
-//		return true;
-//	if (g.tile < tile)
-//		return false;
-//	if (objid < g.objid)
-//		return true;
-//	if (objid > g.objid)
-//		return false;
-//	if (town < g.town)
-//		return true;
-//	if (town > g.town)
-//		return false;
-//	if (value < g.value)
-//		return true;
-//	if (value > g.value)
-//		return false;
-//	if (priority < g.priority)
-//		return true;
-//	if (priority > g.priority)
-//		return false;
-//	if (resID < g.resID)
-//		return true;
-//	if (resID > g.resID)
-//		return false;
-//	if (bid < g.bid)
-//		return true;
-//	if (bid > g.bid)
-//		return false;
-//	if (aid < g.aid)
-//		return true;
-//	if (aid > g.aid)
-//		return false;
-//	return false;
-//}
-
-//TODO: find out why the following are not generated automatically on MVS?
-bool TSubgoal::operator==(const TSubgoal & rhs) const
-{
-	return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
-}
-
-bool TSubgoal::operator<(const TSubgoal & rhs) const
-{
-	return get() < rhs.get(); //compae by value
-}
-
-bool AbstractGoal::invalid() const
-{
-	return goalType == EGoals::INVALID;
-}
-
-void AbstractGoal::accept(VCAI * ai)
-{
-	ai->tryRealize(*this);
-}
-
-float AbstractGoal::accept(FuzzyHelper * f)
-{
-	return f->evaluate(*this);
-}

+ 0 - 175
AI/VCAI/Goals/AbstractGoal.h

@@ -1,175 +0,0 @@
-/*
-* AbstractGoal.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/GameLibrary.h"
-#include "../../../lib/CCreatureHandler.h"
-#include "../AIUtility.h"
-
-struct HeroPtr;
-class VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class AbstractGoal;
-	class Explore;
-	class RecruitHero;
-	class VisitTile;
-	class VisitObj;
-	class VisitHero;
-	class BuildThis;
-	class DigAtTile;
-	class CollectRes;
-	class Build;
-	class BuyArmy;
-	class BuildBoat;
-	class GatherArmy;
-	class ClearWayTo;
-	class Invalid;
-	class Trade;
-	class CompleteQuest;
-	class AdventureSpellCast;
-
-	enum EGoals
-	{
-		INVALID = -1,
-		WIN, CONQUER, BUILD, //build needs to get a real reasoning
-		EXPLORE, GATHER_ARMY,
-		BOOST_HERO,
-		RECRUIT_HERO,
-		BUILD_STRUCTURE, //if hero set, then in visited town
-		COLLECT_RES,
-		GATHER_TROOPS, // val of creatures with objid
-
-		VISIT_OBJ, //visit or defeat or collect the object
-		FIND_OBJ, //find and visit any obj with objid + resid //TODO: consider universal subid for various types (aid, bid)
-		VISIT_HERO, //heroes can move around - set goal abstract and track hero every turn
-
-		GET_ART_TYPE,
-
-		VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
-		CLEAR_WAY_TO,
-		DIG_AT_TILE,//elementar with hero on tile
-		BUY_ARMY, //at specific town
-		TRADE, //val resID at object objid
-		BUILD_BOAT,
-		COMPLETE_QUEST,
-		ADVENTURE_SPELL_CAST
-	};
-
-	class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
-	{
-	public:
-		bool operator==(const TSubgoal & rhs) const;
-		bool operator<(const TSubgoal & rhs) const;
-	};
-
-	using TGoalVec = std::vector<TSubgoal>;
-
-	//method chaining + clone pattern
-#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
-#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) override { field = rhs; return *this; };
-
-#if 0
-#define SETTER
-#endif // _DEBUG
-
-	enum { LOW_PR = -1 };
-
-	DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
-
-	struct DLL_EXPORT EvaluationContext
-	{
-		float movementCost;
-		int manaCost;
-		uint64_t danger;
-
-		EvaluationContext()
-			: movementCost(0.0),
-			manaCost(0),
-			danger(0)
-		{
-		}
-	};
-
-	class DLL_EXPORT AbstractGoal
-	{
-	public:
-		bool isElementar; VSETTER(bool, isElementar)
-			bool isAbstract; VSETTER(bool, isAbstract)
-			float priority; VSETTER(float, priority)
-			int value; VSETTER(int, value)
-			int resID; VSETTER(int, resID)
-			int objid; VSETTER(int, objid)
-			int aid; VSETTER(int, aid)
-			int3 tile; VSETTER(int3, tile)
-			HeroPtr hero; VSETTER(HeroPtr, hero)
-			const CGTownInstance *town; VSETTER(CGTownInstance *, town)
-			int bid; VSETTER(int, bid)
-			TSubgoal parent; VSETTER(TSubgoal, parent)
-			EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
-
-		AbstractGoal(EGoals goal = EGoals::INVALID): goalType(goal)
-		{
-			priority = 0;
-			isElementar = false;
-			isAbstract = false;
-			value = 0;
-			aid = -1;
-			resID = -1;
-			objid = -1;
-			tile = int3(-1, -1, -1);
-			town = nullptr;
-			bid = -1;
-		}
-		virtual ~AbstractGoal() {}
-		//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
-		virtual AbstractGoal * clone() const
-		{
-			return const_cast<AbstractGoal *>(this);
-		}
-		virtual TGoalVec getAllPossibleSubgoals()
-		{
-			return TGoalVec();
-		}
-		virtual TSubgoal whatToDoToAchieve()
-		{
-			return sptr(AbstractGoal());
-		}
-
-		EGoals goalType;
-
-		virtual std::string name() const;
-		virtual std::string completeMessage() const
-		{
-			return "This goal is unspecified!";
-		}
-
-		bool invalid() const;
-
-		///Visitor pattern
-		//TODO: make accept work for std::shared_ptr... somehow
-		virtual void accept(VCAI * ai); //unhandled goal will report standard error
-		virtual float accept(FuzzyHelper * f);
-
-		virtual bool operator==(const AbstractGoal & g) const;
-//		bool operator<(AbstractGoal & g); //final
-		virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
-		{
-			return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
-		}
-
-//		bool operator!=(const AbstractGoal & g) const
-//		{
-//			return !(*this == g);
-//		}
-	};
-}

+ 0 - 85
AI/VCAI/Goals/AdventureSpellCast.cpp

@@ -1,85 +0,0 @@
-/*
-* AdventureSpellCast.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 "AdventureSpellCast.h"
-#include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
-#include "../../../lib/mapObjects/CGTownInstance.h"
-#include "../../../lib/spells/ISpellMechanics.h"
-#include "../../../lib/spells/adventure/TownPortalEffect.h"
-
-using namespace Goals;
-
-bool AdventureSpellCast::operator==(const AdventureSpellCast & other) const
-{
-	return hero.h == other.hero.h;
-}
-
-TSubgoal AdventureSpellCast::whatToDoToAchieve()
-{
-	if(!hero.validAndSet())
-		throw cannotFulfillGoalException("Invalid hero!");
-
-	auto spell = getSpell();
-
-	logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->getNameTranslated(), hero->getNameTranslated());
-
-	if(!spell->isAdventure())
-		throw cannotFulfillGoalException(spell->getNameTranslated() + " is not an adventure spell.");
-
-	if(!hero->canCastThisSpell(spell))
-		throw cannotFulfillGoalException("Hero can not cast " + spell->getNameTranslated());
-
-	if(hero->mana < hero->getSpellCost(spell))
-		throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
-
-	auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero.h);
-
-	if(townPortalEffect && town && town->getVisitingHero())
-		throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
-
-	return iAmElementar();
-}
-
-void AdventureSpellCast::accept(VCAI * ai)
-{
-	auto townPortalEffect = spellID.toSpell()->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero.h);
-
-	if(town && townPortalEffect)
-	{
-		ai->selectedObject = town->id;
-	}
-
-	auto wait = cb->waitTillRealize;
-
-	cb->waitTillRealize = true;
-	cb->castSpell(hero.h, spellID, tile);
-
-	if(town && townPortalEffect)
-	{
-		// visit town
-		ai->moveHeroToTile(town->visitablePos(), hero);
-	}
-
-	cb->waitTillRealize = wait;
-
-	throw goalFulfilledException(sptr(*this));
-}
-
-std::string AdventureSpellCast::name() const
-{
-	return "AdventureSpellCast " + getSpell()->getNameTranslated();
-}
-
-std::string AdventureSpellCast::completeMessage() const
-{
-	return "Spell cast successfully " + getSpell()->getNameTranslated();
-}

+ 0 - 44
AI/VCAI/Goals/AdventureSpellCast.h

@@ -1,44 +0,0 @@
-/*
-* AdventureSpellCast.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 AdventureSpellCast : public CGoal<AdventureSpellCast>
-	{
-	private:
-		SpellID spellID;
-
-	public:
-		AdventureSpellCast(HeroPtr hero, SpellID spellID)
-			: CGoal(Goals::ADVENTURE_SPELL_CAST), spellID(spellID)
-		{
-			sethero(hero);
-		}
-
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-
-		const CSpell * getSpell() const
-		{ 
-			return spellID.toSpell();
-		}
-
-		TSubgoal whatToDoToAchieve() override;
-		void accept(VCAI * ai) override;
-		std::string name() const override;
-		std::string completeMessage() const override;
-		bool operator==(const AdventureSpellCast & other) const override;
-	};
-}

+ 0 - 87
AI/VCAI/Goals/Build.cpp

@@ -1,87 +0,0 @@
-/*
-* Build.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 "Build.h"
-#include "BuildThis.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/mapObjects/CGTownInstance.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-TGoalVec Build::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	for(const CGTownInstance * t : cb->getTownsInfo())
-	{
-		//start fresh with every town
-		ai->ah->getBuildingOptions(t);
-		auto immediateBuilding = ai->ah->immediateBuilding();
-		auto expensiveBuilding = ai->ah->expensiveBuilding();
-
-		//handling for early town development to save money and focus on income
-		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.has_value())
-		{
-			auto potentialBuilding = expensiveBuilding.value();
-			switch(expensiveBuilding.value().bid.toEnum())
-			{
-			case BuildingID::TOWN_HALL:
-			case BuildingID::CITY_HALL:
-			case BuildingID::CAPITOL:
-			case BuildingID::FORT:
-			case BuildingID::CITADEL:
-			case BuildingID::CASTLE:
-				//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
-				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(2.25)));
-				ret.push_back(goal);
-				return ret;
-				break;
-			}
-		}
-
-		if(immediateBuilding.has_value())
-		{
-			ret.push_back(sptr(BuildThis(immediateBuilding.value().bid, t).setpriority(2))); //prioritize buildings we can build quick
-		}
-		else //try build later
-		{
-			if(expensiveBuilding.has_value())
-			{
-				auto potentialBuilding = expensiveBuilding.value(); //gather resources for any we can't afford
-				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(0.5)));
-				ret.push_back(goal);
-			}
-		}
-	}
-
-	if(ret.empty())
-		throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
-	else
-		return ret;
-}
-
-TSubgoal Build::whatToDoToAchieve()
-{
-	return fh->chooseSolution(getAllPossibleSubgoals());
-}
-
-bool Build::fulfillsMe(TSubgoal goal)
-{
-	if(goal->goalType == BUILD || goal->goalType == BUILD_STRUCTURE)
-		return (!town || town == goal->town); //building anything will do, in this town if set
-	else
-		return false;
-}

+ 0 - 37
AI/VCAI/Goals/Build.h

@@ -1,37 +0,0 @@
-/*
-* Build.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT Build : public CGoal<Build>
-	{
-	public:
-		Build()
-			: CGoal(Goals::BUILD)
-		{
-			priority = 1;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		bool fulfillsMe(TSubgoal goal) override;
-
-		bool operator==(const Build & other) const override
-		{
-			return true;
-		}
-	};
-}

+ 0 - 79
AI/VCAI/Goals/BuildBoat.cpp

@@ -1,79 +0,0 @@
-/*
-* BuildBoat.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 "BuildBoat.h"
-#include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
-
-using namespace Goals;
-
-bool BuildBoat::operator==(const BuildBoat & other) const
-{
-	return shipyard == other.shipyard;
-}
-
-TSubgoal BuildBoat::whatToDoToAchieve()
-{
-	if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
-	{
-		return fh->chooseSolution(ai->ah->howToVisitObj(dynamic_cast<const CGObjectInstance*>(shipyard)));
-	}
-
-	if(shipyard->shipyardStatus() != IShipyard::GOOD)
-	{
-		throw cannotFulfillGoalException("Shipyard is busy.");
-	}
-
-	TResources boatCost;
-	shipyard->getBoatCost(boatCost);
-
-	return ai->ah->whatToDo(boatCost, this->iAmElementar());
-}
-
-void BuildBoat::accept(VCAI * ai)
-{
-	TResources boatCost;
-	shipyard->getBoatCost(boatCost);
-
-	if(!cb->getResourceAmount().canAfford(boatCost))
-	{
-		throw cannotFulfillGoalException("Can not afford boat");
-	}
-
-	if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
-	{
-		throw cannotFulfillGoalException("Can not build boat in enemy shipyard");
-	}
-
-	if(shipyard->shipyardStatus() != IShipyard::GOOD)
-	{
-		throw cannotFulfillGoalException("Shipyard is busy.");
-	}
-
-	logAi->trace(
-		"Building boat at shipyard located at %s, estimated boat position %s",
-		shipyard->getObject()->visitablePos().toString(),
-		shipyard->bestLocation().toString());
-
-	cb->buildBoat(shipyard);
-
-	throw goalFulfilledException(sptr(*this));
-}
-
-std::string BuildBoat::name() const
-{
-	return "BuildBoat";
-}
-
-std::string BuildBoat::completeMessage() const
-{
-	return "Boat have been built at " + shipyard->getObject()->visitablePos().toString();
-}

+ 0 - 37
AI/VCAI/Goals/BuildBoat.h

@@ -1,37 +0,0 @@
-/*
-* BuildBoat.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 BuildBoat : public CGoal<BuildBoat>
-	{
-	private:
-		const IShipyard * shipyard;
-
-	public:
-		BuildBoat(const IShipyard * shipyard)
-			: CGoal(Goals::BUILD_BOAT), shipyard(shipyard)
-		{
-			priority = 0;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-		void accept(VCAI * ai) override;
-		std::string name() const override;
-		std::string completeMessage() const override;
-		bool operator==(const BuildBoat & other) const override;
-	};
-}

+ 0 - 66
AI/VCAI/Goals/BuildThis.cpp

@@ -1,66 +0,0 @@
-/*
-* BuildThis.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 "BuildThis.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/mapObjects/CGTownInstance.h"
-#include "../../../lib/constants/StringConstants.h"
-#include "../../../lib/entities/building/CBuilding.h"
-
-using namespace Goals;
-
-bool BuildThis::operator==(const BuildThis & other) const
-{
-	return town == other.town && bid == other.bid;
-}
-
-TSubgoal BuildThis::whatToDoToAchieve()
-{
-	auto b = BuildingID(bid);
-
-	// find town if not set
-	if(!town && hero)
-		town = hero->getVisitedTown();
-
-	if(!town)
-	{
-		for(const CGTownInstance * candidateTown : cb->getTownsInfo())
-		{
-			switch(cb->canBuildStructure(candidateTown, b))
-			{
-			case EBuildingState::ALLOWED:
-				town = candidateTown;
-				break; //TODO: look for prerequisites? this is not our responsibility
-			default:;
-			}
-		}
-	}
-
-	if(town) //we have specific town to build this
-	{
-		switch(cb->canBuildStructure(town, b))
-		{
-		case EBuildingState::ALLOWED:
-		case EBuildingState::NO_RESOURCES:
-		{
-			auto res = town->getTown()->buildings.at(BuildingID(bid))->resources;
-			return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
-		}
-		default:
-			throw cannotFulfillGoalException("Not possible to build");
-		}
-	}
-	throw cannotFulfillGoalException("Cannot find town to build this");
-}

+ 0 - 48
AI/VCAI/Goals/BuildThis.h

@@ -1,48 +0,0 @@
-/*
-* BuildThis.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT BuildThis : public CGoal<BuildThis>
-	{
-	public:
-		BuildThis() //should be private, but unit test uses it
-			: CGoal(Goals::BUILD_STRUCTURE)
-		{
-		}
-		BuildThis(BuildingID Bid, const CGTownInstance * tid)
-			: CGoal(Goals::BUILD_STRUCTURE)
-		{
-			bid = Bid.getNum();
-			town = tid;
-			priority = 1;
-		}
-		BuildThis(BuildingID Bid)
-			: CGoal(Goals::BUILD_STRUCTURE)
-		{
-			bid = Bid.getNum();
-			priority = 1;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-		//bool fulfillsMe(TSubgoal goal) override;
-		bool operator==(const BuildThis & other) const override;
-	};
-}

+ 0 - 40
AI/VCAI/Goals/BuyArmy.cpp

@@ -1,40 +0,0 @@
-/*
-* BuyArmy.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 "BuyArmy.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
-#include "../../../lib/mapObjects/CGTownInstance.h"
-
-using namespace Goals;
-
-bool BuyArmy::operator==(const BuyArmy & other) const
-{
-	return town == other.town && objid == other.objid;
-}
-
-bool BuyArmy::fulfillsMe(TSubgoal goal)
-{
-	//if (hero && hero != goal->hero)
-	//	return false;
-	return town == goal->town && goal->value >= value; //can always buy more army
-}
-TSubgoal BuyArmy::whatToDoToAchieve()
-{
-	//TODO: calculate the actual cost of units instead
-	TResources price;
-	price[EGameResID::GOLD] = static_cast<int>(value * 0.4f); //some approximate value
-	return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
-}
-
-std::string BuyArmy::completeMessage() const
-{
-	return boost::format("Bought army of value %d in town of %s") % value, town->getNameTranslated();
-}

+ 0 - 41
AI/VCAI/Goals/BuyArmy.h

@@ -1,41 +0,0 @@
-/*
-* BuyArmy.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT BuyArmy : public CGoal<BuyArmy>
-	{
-	private:
-		BuyArmy()
-			: CGoal(Goals::BUY_ARMY)
-		{
-		}
-	public:
-		BuyArmy(const CGTownInstance * Town, int val)
-			: CGoal(Goals::BUY_ARMY)
-		{
-			town = Town; //where to buy this army
-			value = val; //expressed in AI unit strength
-			priority = 3;//TODO: evaluate?
-		}
-		bool fulfillsMe(TSubgoal goal) override;
-
-		TSubgoal whatToDoToAchieve() override;
-		std::string completeMessage() const override;
-		bool operator==(const BuyArmy & other) const override;
-	};
-}

+ 0 - 84
AI/VCAI/Goals/CGoal.h

@@ -1,84 +0,0 @@
-/*
-* CGoal.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 "AbstractGoal.h"
-#include "../FuzzyHelper.h"
-#include "../VCAI.h"
-
-struct HeroPtr;
-class VCAI;
-
-namespace Goals
-{
-	template<typename T>
-	class DLL_EXPORT CGoal : public AbstractGoal
-	{
-	public:
-		CGoal(EGoals goal = INVALID) : AbstractGoal(goal)
-		{
-			priority = 0;
-			isElementar = false;
-			isAbstract = false;
-			value = 0;
-			aid = -1;
-			objid = -1;
-			resID = -1;
-			tile = int3(-1, -1, -1);
-			town = nullptr;
-		}
-
-		OSETTER(bool, isElementar)
-		OSETTER(bool, isAbstract)
-		OSETTER(float, priority)
-		OSETTER(int, value)
-		OSETTER(int, resID)
-		OSETTER(int, objid)
-		OSETTER(int, aid)
-		OSETTER(int3, tile)
-		OSETTER(HeroPtr, hero)
-		OSETTER(CGTownInstance *, town)
-		OSETTER(int, bid)
-
-		void accept(VCAI * ai) override
-		{
-			ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
-		}
-
-		float accept(FuzzyHelper * f) override
-		{
-			return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
-		}
-
-		CGoal * clone() const override
-		{
-			return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
-		}
-		TSubgoal iAmElementar() const
-		{
-			TSubgoal ptr;
-
-			ptr.reset(clone());
-			ptr->setisElementar(true);
-
-			return ptr;
-		}
-
-		bool operator==(const AbstractGoal & g) const override
-		{
-			if(goalType != g.goalType)
-				return false;
-
-			return (*this) == (static_cast<const T &>(g));
-		}
-
-		virtual bool operator==(const T & other) const = 0;
-	};
-}

+ 0 - 80
AI/VCAI/Goals/ClearWayTo.cpp

@@ -1,80 +0,0 @@
-/*
-* ClearWayTo.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 "ClearWayTo.h"
-#include "Explore.h"
-#include "RecruitHero.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
-
-using namespace Goals;
-
-bool ClearWayTo::operator==(const ClearWayTo & other) const
-{
-	return other.hero.h == hero.h && other.tile == tile;
-}
-
-TSubgoal ClearWayTo::whatToDoToAchieve()
-{
-	assert(cb->isInTheMap(tile)); //set tile
-	if(!cb->isVisible(tile))
-	{
-		logAi->error("Clear way should be used with visible tiles!");
-		return sptr(Explore());
-	}
-
-	return (fh->chooseSolution(getAllPossibleSubgoals()));
-}
-
-bool ClearWayTo::fulfillsMe(TSubgoal goal)
-{
-	if (goal->goalType == VISIT_TILE)
-	{
-		if (!hero || hero == goal->hero)
-			return tile == goal->tile;
-	}
-	return false;
-}
-
-TGoalVec ClearWayTo::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	std::vector<const CGHeroInstance *> heroes;
-	if(hero)
-		heroes.push_back(hero.h);
-	else
-		heroes = cb->getHeroesInfo();
-
-	for(auto h : heroes)
-	{
-		//TODO: handle clearing way to allied heroes that are blocked
-		//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
-		//	h->visitablePos() == tile) //we are already on that tile! what does it mean?
-		//	continue;
-
-		//if our hero is trapped, make sure we request clearing the way from OUR perspective
-
-		vstd::concatenate(ret, ai->ah->howToVisitTile(h, tile));
-	}
-
-	if(ret.empty() && ai->canRecruitAnyHero())
-		ret.push_back(sptr(RecruitHero()));
-
-	if(ret.empty())
-	{
-		logAi->warn("There is no known way to clear the way to tile %s", tile.toString());
-		throw goalFulfilledException(sptr(ClearWayTo(tile))); //make sure assigned hero gets unlocked
-	}
-
-	return ret;
-}

+ 0 - 45
AI/VCAI/Goals/ClearWayTo.h

@@ -1,45 +0,0 @@
-/*
-* ClearWayTo.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT ClearWayTo : public CGoal<ClearWayTo>
-	{
-	public:
-		ClearWayTo()
-			: CGoal(Goals::CLEAR_WAY_TO)
-		{
-		}
-		ClearWayTo(int3 Tile)
-			: CGoal(Goals::CLEAR_WAY_TO)
-		{
-			tile = Tile;
-			priority = 5;
-		}
-		ClearWayTo(int3 Tile, HeroPtr h)
-			: CGoal(Goals::CLEAR_WAY_TO)
-		{
-			tile = Tile;
-			hero = h;
-			priority = 5;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		bool fulfillsMe(TSubgoal goal) override;
-		bool operator==(const ClearWayTo & other) const override;
-	};
-}

+ 0 - 212
AI/VCAI/Goals/CollectRes.cpp

@@ -1,212 +0,0 @@
-/*
-* CollectRes.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/mapObjects/CGMarket.h"
-#include "../../../lib/mapObjects/CGResource.h"
-#include "../../../lib/constants/StringConstants.h"
-#include "../../../lib/entities/ResourceTypeHandler.h"
-
-using namespace Goals;
-
-bool CollectRes::operator==(const CollectRes & other) const
-{
-	return resID == other.resID;
-}
-
-TGoalVec CollectRes::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	auto givesResource = [this](const CGObjectInstance * obj) -> bool
-	{
-		//TODO: move this logic to object side
-		//TODO: remember mithril exists
-		//TODO: water objects
-		//TODO: Creature banks
-
-		//return false first from once-visitable, before checking if they were even visited
-		switch (obj->ID.num)
-		{
-		case Obj::TREASURE_CHEST:
-			return resID == GameResID(EGameResID::GOLD).getNum();
-			break;
-		case Obj::RESOURCE:
-			return dynamic_cast<const CGResource*>(obj)->resourceID() == GameResID(resID);
-			break;
-		case Obj::MINE:
-			return (dynamic_cast<const CGMine*>(obj)->producedResource == GameResID(resID) &&
-				(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
-			break;
-		case Obj::CAMPFIRE:
-			return true; //contains all resources
-			break;
-		case Obj::WINDMILL:
-			switch (GameResID(resID).toEnum())
-			{
-			case EGameResID::GOLD:
-			case EGameResID::WOOD:
-				return false;
-			}
-			break;
-		case Obj::MYSTICAL_GARDEN:
-			if (resID != GameResID(EGameResID::GOLD).getNum() && resID != GameResID(EGameResID::GEMS).getNum())
-				return false;
-			break;
-		case Obj::WATER_WHEEL:
-		case Obj::LEAN_TO:
-		case Obj::WAGON:
-			if (resID != GameResID(EGameResID::GOLD).getNum())
-				return false;
-			break;
-		default:
-			return false;
-			break;
-		}
-		return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
-	};
-
-	std::vector<const CGObjectInstance *> objs;
-	for (auto obj : ai->visitableObjs)
-	{
-		if (givesResource(obj))
-			objs.push_back(obj);
-	}
-	for (auto h : cb->getHeroesInfo())
-	{
-		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
-
-		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
-		{
-			if (givesResource(obj))
-				ourObjs.push_back(obj);
-		}
-
-		for (auto obj : ourObjs)
-		{
-			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
-
-			vstd::concatenate(ret, waysToGo);
-		}
-	}
-	return ret;
-}
-
-TSubgoal CollectRes::whatToDoToAchieve()
-{
-	auto goals = getAllPossibleSubgoals();
-	auto trade = whatToDoToTrade();
-	if (!trade->invalid())
-		goals.push_back(trade);
-
-	if (goals.empty())
-		return sptr(Explore()); //we can always do that
-	else
-		return fh->chooseSolution(goals); //TODO: evaluate trading
-}
-
-TSubgoal CollectRes::whatToDoToTrade()
-{
-	std::vector<const IMarket *> markets;
-
-	std::vector<const CGObjectInstance *> visObjs;
-	ai->retrieveVisitableObjs(visObjs, true);
-	for(const CGObjectInstance * obj : visObjs)
-	{
-		const auto * m = dynamic_cast<const IMarket*>(obj);
-
-		if(m && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
-		{
-			if(obj->ID == Obj::TOWN)
-			{
-				if(obj->tempOwner == ai->playerID)
-					markets.push_back(m);
-			}
-			else
-				markets.push_back(m);
-		}
-	}
-
-	boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
-	{
-		return m1->getMarketEfficiency() < m2->getMarketEfficiency();
-	});
-
-	markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
-	{
-		auto * o = dynamic_cast<const CGObjectInstance *>(market);
-		// FIXME: disabled broken visitation of external markets
-		//if(o && !(o->ID == Obj::TOWN && o->tempOwner == ai->playerID))
-
-		if(o && o->ID == Obj::TOWN)
-		{
-			if(!ai->isAccessible(o->visitablePos()))
-				return true;
-		}
-		return false;
-	}), markets.end());
-
-	if (!markets.size())
-	{
-		for (const CGTownInstance * t : cb->getTownsInfo())
-		{
-			if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
-				return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
-		}
-	}
-	else
-	{
-		const IMarket * m = markets.back();
-		//attempt trade at back (best prices)
-		int howManyCanWeBuy = 0;
-		for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
-		{
-			if (i.getNum() == resID)
-				continue;
-			int toGive = -1;
-			int toReceive = -1;
-			m->getOffer(i.getNum(), resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
-			assert(toGive > 0 && toReceive > 0);
-			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
-		}
-
-		if (howManyCanWeBuy >= value)
-		{
-			auto * o = dynamic_cast<const CGObjectInstance *>(m);
-			auto backObj = cb->getTopObj(o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
-			assert(backObj);
-			auto objid = o->id.getNum();
-			if (backObj->tempOwner != ai->playerID) //top object not owned
-			{
-				return sptr(VisitObj(objid)); //just go there
-			}
-			else //either it's our town, or we have hero there
-			{
-				return sptr(Trade(static_cast<EGameResID>(resID), value, objid).setisElementar(true)); //we can do this immediately
-			}
-		}
-	}
-	return sptr(Invalid()); //cannot trade
-}
-
-bool CollectRes::fulfillsMe(TSubgoal goal)
-{
-	if (goal->resID == resID)
-		if (goal->value >= value)
-			return true;
-
-	return false;
-}

+ 0 - 40
AI/VCAI/Goals/CollectRes.h

@@ -1,40 +0,0 @@
-/*
-* CollectRes.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT CollectRes : public CGoal<CollectRes>
-	{
-	public:
-		CollectRes()
-			: CGoal(Goals::COLLECT_RES)
-		{
-		}
-		CollectRes(GameResID rid, int val)
-			: CGoal(Goals::COLLECT_RES)
-		{
-			resID = rid.getNum();
-			value = val;
-			priority = 2;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		TSubgoal whatToDoToTrade();
-		bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
-		bool operator==(const CollectRes & other) const override;
-	};
-}

+ 0 - 278
AI/VCAI/Goals/CompleteQuest.cpp

@@ -1,278 +0,0 @@
-/*
-* CompleteQuest.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 "CompleteQuest.h"
-
-#include "CollectRes.h"
-#include "FindObj.h"
-#include "GatherArmy.h"
-#include "GatherTroops.h"
-#include "GetArtOfType.h"
-#include "RecruitHero.h"
-
-#include "../VCAI.h"
-#include "../FuzzyHelper.h"
-#include "../AIhelper.h"
-#include "../../../lib/mapObjects/CQuest.h"
-
-using namespace Goals;
-
-bool CompleteQuest::operator==(const CompleteQuest & other) const
-{
-	return q.getQuest(cb) == other.q.getQuest(cb);
-}
-
-bool isKeyMaster(const QuestInfo & q)
-{
-	auto object = q.getObject(cb);
-	return object && (object->ID == Obj::BORDER_GATE || object->ID == Obj::BORDERGUARD);
-}
-
-TGoalVec CompleteQuest::getAllPossibleSubgoals()
-{
-	TGoalVec solutions;
-	auto quest = q.getQuest(cb);
-
-	if(!quest->isCompleted)
-	{
-		logAi->debug("Trying to realize quest: %s", questToString());
-		
-		if(isKeyMaster(q))
-			return missionKeymaster();
-
-		if(!quest->mission.artifacts.empty())
-			return missionArt();
-
-		if(!quest->mission.heroes.empty())
-			return missionHero();
-
-		if(!quest->mission.creatures.empty())
-			return missionArmy();
-
-		if(quest->mission.resources.nonZero())
-			return missionResources();
-
-		if(quest->killTarget != ObjectInstanceID::NONE)
-			return missionDestroyObj();
-
-		for(auto & s : quest->mission.primary)
-			if(s)
-				return missionIncreasePrimaryStat();
-
-		if(quest->mission.heroLevel > 0)
-			return missionLevel();
-	}
-
-	return TGoalVec();
-}
-
-TSubgoal CompleteQuest::whatToDoToAchieve()
-{
-	if(q.getQuest(cb)->mission == Rewardable::Limiter{})
-	{
-		throw cannotFulfillGoalException("Can not complete inactive quest");
-	}
-
-	TGoalVec solutions = getAllPossibleSubgoals();
-
-	if(solutions.empty())
-		throw cannotFulfillGoalException("Can not complete quest " + questToString());
-
-	TSubgoal result = fh->chooseSolution(solutions);
-
-	logAi->trace(
-		"Returning %s, tile: %s, objid: %d, hero: %s",
-		result->name(),
-		result->tile.toString(),
-		result->objid,
-		result->hero.validAndSet() ? result->hero->getNameTranslated() : "not specified");
-
-	return result;
-}
-
-std::string CompleteQuest::name() const
-{
-	return "CompleteQuest";
-}
-
-std::string CompleteQuest::completeMessage() const
-{
-	return "Completed quest " + questToString();
-}
-
-std::string CompleteQuest::questToString() const
-{
-	auto quest = q.getQuest(cb);
-
-	if(quest->questName == CQuest::missionName(EQuestMission::NONE))
-		return "inactive quest";
-
-	MetaString ms;
-	quest->getRolloverText(cb, ms, false);
-
-	return ms.toString();
-}
-
-TGoalVec CompleteQuest::tryCompleteQuest() const
-{
-	TGoalVec solutions;
-
-	auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
-
-	for(auto hero : heroes)
-	{
-		if(q.getQuest(cb)->checkQuest(hero))
-		{
-			vstd::concatenate(solutions, ai->ah->howToVisitObj(hero, ObjectIdRef(q.getObject(cb)->id)));
-		}
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionArt() const
-{
-	TGoalVec solutions = tryCompleteQuest();
-
-	if(!solutions.empty())
-		return solutions;
-
-	for(auto art : q.getQuest(cb)->mission.artifacts)
-	{
-		solutions.push_back(sptr(GetArtOfType(art.getNum()))); //TODO: transport?
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionHero() const
-{
-	TGoalVec solutions = tryCompleteQuest();
-
-	if(solutions.empty())
-	{
-		//rule of a thumb - quest heroes usually are locked in prisons
-		solutions.push_back(sptr(FindObj(Obj::PRISON)));
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionArmy() const
-{
-	TGoalVec solutions = tryCompleteQuest();
-
-	if(!solutions.empty())
-		return solutions;
-
-	for(auto creature : q.getQuest(cb)->mission.creatures)
-	{
-		solutions.push_back(sptr(GatherTroops(creature.getId().getNum(), creature.getCount())));
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionIncreasePrimaryStat() const
-{
-	TGoalVec solutions = tryCompleteQuest();
-
-	if(solutions.empty())
-	{
-		for(int i = 0; i < q.getQuest(cb)->mission.primary.size(); ++i)
-		{
-			// TODO: library, school and other boost objects
-			logAi->debug("Don't know how to increase primary stat %d", i);
-		}
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionLevel() const
-{
-	TGoalVec solutions = tryCompleteQuest();
-
-	if(solutions.empty())
-	{
-		logAi->debug("Don't know how to reach hero level %d", q.getQuest(cb)->mission.heroLevel);
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionKeymaster() const
-{
-	TGoalVec solutions = tryCompleteQuest();
-
-	if(solutions.empty())
-	{
-		solutions.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, q.getObject(cb)->subID)));
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionResources() const
-{
-	TGoalVec solutions;
-
-	auto heroes = cb->getHeroesInfo(); //TODO: choose best / free hero from among many possibilities?
-
-	if(heroes.size())
-	{
-		if(q.getQuest(cb)->checkQuest(heroes.front())) //it doesn't matter which hero it is
-		{
-			return ai->ah->howToVisitObj(q.getObject(cb));
-		}
-		else
-		{
-			for(int i = 0; i < q.getQuest(cb)->mission.resources.size(); ++i)
-			{
-				if(q.getQuest(cb)->mission.resources[i])
-					solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.getQuest(cb)->mission.resources[i])));
-			}
-		}
-	}
-	else
-	{
-		solutions.push_back(sptr(Goals::RecruitHero())); //FIXME: checkQuest requires any hero belonging to player :(
-	}
-
-	return solutions;
-}
-
-TGoalVec CompleteQuest::missionDestroyObj() const
-{
-	TGoalVec solutions;
-
-	auto obj = cb->getObj(q.getQuest(cb)->killTarget);
-
-	if(!obj)
-		return ai->ah->howToVisitObj(q.getObject(cb));
-
-	if(obj->ID == Obj::HERO)
-	{
-		auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
-
-		if(relations == PlayerRelations::SAME_PLAYER)
-		{
-			auto heroToProtect = cb->getHero(obj->id);
-
-			solutions.push_back(sptr(GatherArmy().sethero(heroToProtect)));
-		}
-		else if(relations == PlayerRelations::ENEMIES)
-		{
-			solutions = ai->ah->howToVisitObj(obj);
-		}
-	}
-
-	return solutions;
-}

+ 0 - 46
AI/VCAI/Goals/CompleteQuest.h

@@ -1,46 +0,0 @@
-/*
-* CompleteQuest.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"
-#include "../../../lib/gameState/QuestInfo.h"
-
-namespace Goals
-{
-	class DLL_EXPORT CompleteQuest : public CGoal<CompleteQuest>
-	{
-	private:
-		const QuestInfo q;
-
-	public:
-		CompleteQuest(const QuestInfo quest)
-			: CGoal(Goals::COMPLETE_QUEST), q(quest)
-		{
-		}
-
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		std::string name() const override;
-		std::string completeMessage() const override;
-		bool operator==(const CompleteQuest & other) const override;
-
-	private:
-		TGoalVec tryCompleteQuest() const;
-		TGoalVec missionArt() const;
-		TGoalVec missionHero() const;
-		TGoalVec missionArmy() const;
-		TGoalVec missionResources() const;
-		TGoalVec missionDestroyObj() const;
-		TGoalVec missionIncreasePrimaryStat() const;
-		TGoalVec missionLevel() const;
-		TGoalVec missionKeymaster() const;
-		std::string questToString() const;
-	};
-}

+ 0 - 83
AI/VCAI/Goals/Conquer.cpp

@@ -1,83 +0,0 @@
-/*
-* Conquer.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-bool Conquer::operator==(const Conquer & other) const
-{
-	return other.hero.h == hero.h;
-}
-
-TSubgoal Conquer::whatToDoToAchieve()
-{
-	logAi->trace("Entering goal CONQUER");
-
-	return fh->chooseSolution(getAllPossibleSubgoals());
-}
-
-TGoalVec Conquer::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	auto conquerable = [](const CGObjectInstance * obj) -> bool
-	{
-		if(cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
-		{
-			switch(obj->ID.num)
-			{
-			case Obj::TOWN:
-			case Obj::HERO:
-			case Obj::CREATURE_GENERATOR1:
-			case Obj::MINE: //TODO: check ai->knownSubterraneanGates
-				return true;
-			}
-		}
-		return false;
-	};
-
-	std::vector<const CGObjectInstance *> objs;
-	for(auto obj : ai->visitableObjs)
-	{
-		if(conquerable(obj))
-			objs.push_back(obj);
-	}
-
-	for(auto h : cb->getHeroesInfo())
-	{
-		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
-
-		for(auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
-		{
-			if(conquerable(obj))
-				ourObjs.push_back(obj);
-		}
-		for(auto obj : ourObjs)
-		{
-			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
-
-			vstd::concatenate(ret, waysToGo);
-		}
-	}
-	if(!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
-		ret.push_back(sptr(RecruitHero()));
-
-	if(ret.empty())
-		ret.push_back(sptr(Explore())); //we need to find an enemy
-	return ret;
-}

+ 0 - 32
AI/VCAI/Goals/Conquer.h

@@ -1,32 +0,0 @@
-/*
-* Conquer.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT Conquer : public CGoal<Conquer>
-	{
-	public:
-		Conquer()
-			: CGoal(Goals::CONQUER)
-		{
-			priority = 10;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		bool operator==(const Conquer & other) const override;
-	};
-}

+ 0 - 34
AI/VCAI/Goals/DigAtTile.cpp

@@ -1,34 +0,0 @@
-/*
-* DigAtTile.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 "DigAtTile.h"
-#include "VisitTile.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-
-using namespace Goals;
-
-bool DigAtTile::operator==(const DigAtTile & other) const
-{
-	return other.hero.h == hero.h && other.tile == tile;
-}
-
-TSubgoal DigAtTile::whatToDoToAchieve()
-{
-	const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
-	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
-	{
-		const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
-		sethero(h).setisElementar(true);
-		return sptr(*this);
-	}
-
-	return sptr(VisitTile(tile));
-}

+ 0 - 41
AI/VCAI/Goals/DigAtTile.h

@@ -1,41 +0,0 @@
-/*
-* DigAtTile.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT DigAtTile : public CGoal<DigAtTile>
-		//elementar with hero on tile
-	{
-	public:
-		DigAtTile()
-			: CGoal(Goals::DIG_AT_TILE)
-		{
-		}
-		DigAtTile(int3 Tile)
-			: CGoal(Goals::DIG_AT_TILE)
-		{
-			tile = Tile;
-			priority = 20;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-		bool operator==(const DigAtTile & other) const override;
-	};
-}

+ 0 - 448
AI/VCAI/Goals/Explore.cpp

@@ -1,448 +0,0 @@
-/*
-* Explore.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/constants/StringConstants.h"
-#include "../../../lib/CPlayerState.h"
-
-using namespace Goals;
-
-namespace Goals
-{
-	struct ExplorationHelper
-	{
-		HeroPtr hero;
-		int sightRadius;
-		float bestValue;
-		TSubgoal bestGoal;
-		VCAI * aip;
-		CCallback * cbp;
-		const TeamState * ts;
-		int3 ourPos;
-		bool allowDeadEndCancellation;
-		bool allowGatherArmy;
-
-		ExplorationHelper(HeroPtr h, bool gatherArmy)
-		{
-			cbp = cb;
-			aip = ai;
-			hero = h;
-			ts = cbp->getPlayerTeam(ai->playerID);
-			sightRadius = hero->getSightRadius();
-			bestGoal = sptr(Goals::Invalid());
-			bestValue = 0;
-			ourPos = h->visitablePos();
-			allowDeadEndCancellation = true;
-			allowGatherArmy = gatherArmy;
-		}
-
-		void scanSector(int scanRadius)
-		{
-			int3 tile = int3(0, 0, ourPos.z);
-
-			const auto & slice = ts->fogOfWarMap[ourPos.z];
-
-			for(tile.x = ourPos.x - scanRadius; tile.x <= ourPos.x + scanRadius; tile.x++)
-			{
-				for(tile.y = ourPos.y - scanRadius; tile.y <= ourPos.y + scanRadius; tile.y++)
-				{
-
-					if(cbp->isInTheMap(tile) && slice[tile.x][tile.y])
-					{
-						scanTile(tile);
-					}
-				}
-			}
-		}
-
-		void scanMap()
-		{
-			int3 mapSize = cbp->getMapSize();
-			int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
-
-			std::vector<int3> from;
-			std::vector<int3> to;
-
-			from.reserve(perimeter);
-			to.reserve(perimeter);
-
-			foreach_tile_pos([&](const int3 & pos)
-			{
-				if(ts->fogOfWarMap[pos.z][pos.x][pos.y])
-				{
-					bool hasInvisibleNeighbor = false;
-
-					foreach_neighbour(cbp, pos, [&](CCallback * cbp, int3 neighbour)
-					{
-						if(!ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y])
-						{
-							hasInvisibleNeighbor = true;
-						}
-					});
-
-					if(hasInvisibleNeighbor)
-						from.push_back(pos);
-				}
-			});
-
-			logAi->debug("Exploration scan visible area perimeter for hero %s", hero.name);
-
-			for(const int3 & tile : from)
-			{
-				scanTile(tile);
-			}
-
-			if(!bestGoal->invalid())
-			{
-				return;
-			}
-
-			allowDeadEndCancellation = false;
-
-			for(int i = 0; i < sightRadius; i++)
-			{
-				getVisibleNeighbours(from, to);
-				vstd::concatenate(from, to);
-				vstd::removeDuplicates(from);
-			}
-
-			logAi->debug("Exploration scan all possible tiles for hero %s", hero.name);
-
-			for(const int3 & tile : from)
-			{
-				scanTile(tile);
-			}
-		}
-
-		void scanTile(const int3 & tile)
-		{
-			if(tile == ourPos
-				|| !aip->ah->isTileAccessible(hero, tile)) //shouldn't happen, but it does
-				return;
-
-			int tilesDiscovered = howManyTilesWillBeDiscovered(tile);
-			if(!tilesDiscovered)
-				return;
-
-			auto waysToVisit = aip->ah->howToVisitTile(hero, tile, allowGatherArmy);
-			for(auto goal : waysToVisit)
-			{
-				if(goal->evaluationContext.movementCost <= 0.0) // should not happen
-					continue;
-
-				float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost;
-
-				if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much
-				{
-					auto obj = cb->getTopObj(tile);
-
-					// picking up resources does not yield any exploration at all.
-					// if it blocks the way to some explorable tile AIPathfinder will take care of it
-					if(obj && obj->isBlockedVisitable())
-					{
-						continue;
-					}
-
-					if(isSafeToVisit(hero, tile))
-					{
-						bestGoal = goal;
-						bestValue = ourValue;
-					}
-				}
-			}
-		}
-
-		void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
-		{
-			for(const int3 & tile : tiles)
-			{
-				foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
-				{
-					if(ts->fogOfWarMap[neighbour.z][neighbour.x][neighbour.y])
-					{
-						out.push_back(neighbour);
-					}
-				});
-			}
-		}
-
-		int howManyTilesWillBeDiscovered(const int3 & pos) const
-		{
-			int ret = 0;
-			int3 npos = int3(0, 0, pos.z);
-
-			const auto & slice = ts->fogOfWarMap[pos.z];
-
-			for(npos.x = pos.x - sightRadius; npos.x <= pos.x + sightRadius; npos.x++)
-			{
-				for(npos.y = pos.y - sightRadius; npos.y <= pos.y + sightRadius; npos.y++)
-				{
-					if(cbp->isInTheMap(npos)
-						&& pos.dist2d(npos) - 0.5 < sightRadius
-						&& !slice[npos.x][npos.y])
-					{
-						if(allowDeadEndCancellation
-							&& !hasReachableNeighbor(npos))
-						{
-							continue;
-						}
-
-						ret++;
-					}
-				}
-			}
-
-			return ret;
-		}
-
-		bool hasReachableNeighbor(const int3 &pos) const
-		{
-			for(crint3 dir : int3::getDirs())
-			{
-				int3 tile = pos + dir;
-				if(cbp->isInTheMap(tile))
-				{
-					auto isAccessible = aip->ah->isTileAccessible(hero, tile);
-
-					if(isAccessible)
-						return true;
-				}
-			}
-
-			return false;
-		}
-	};
-}
-
-bool Explore::operator==(const Explore & other) const
-{
-	return other.hero.h == hero.h && other.allowGatherArmy == allowGatherArmy;
-}
-
-std::string Explore::completeMessage() const
-{
-	return "Hero " + hero.get()->getNameTranslated() + " completed exploration";
-}
-
-TSubgoal Explore::whatToDoToAchieve()
-{
-	return fh->chooseSolution(getAllPossibleSubgoals());
-}
-
-TGoalVec Explore::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-	std::vector<const CGHeroInstance *> heroes;
-
-	if(hero)
-	{
-		heroes.push_back(hero.h);
-	}
-	else
-	{
-		//heroes = ai->getUnblockedHeroes();
-		heroes = cb->getHeroesInfo();
-		vstd::erase_if(heroes, [](const HeroPtr h)
-		{
-			if(ai->getGoal(h)->goalType == EXPLORE) //do not reassign hero who is already explorer
-				return true;
-
-			if(!ai->isAbleToExplore(h))
-				return true;
-
-			return !h->movementPointsRemaining(); //saves time, immobile heroes are useless anyway
-		});
-	}
-
-	//try to use buildings that uncover map
-	std::vector<const CGObjectInstance *> objs;
-	for(auto obj : ai->visitableObjs)
-	{
-		if(!vstd::contains(ai->alreadyVisited, obj))
-		{
-			switch(obj->ID.num)
-			{
-			case Obj::REDWOOD_OBSERVATORY:
-			case Obj::PILLAR_OF_FIRE:
-			case Obj::CARTOGRAPHER:
-				objs.push_back(obj);
-				break;
-			case Obj::MONOLITH_ONE_WAY_ENTRANCE:
-			case Obj::MONOLITH_TWO_WAY:
-			case Obj::SUBTERRANEAN_GATE:
-				auto tObj = dynamic_cast<const CGTeleport *>(obj);
-				assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end());
-				if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability)
-					objs.push_back(obj);
-				break;
-			}
-		}
-		else
-		{
-			switch(obj->ID.num)
-			{
-			case Obj::MONOLITH_TWO_WAY:
-			case Obj::SUBTERRANEAN_GATE:
-				auto tObj = dynamic_cast<const CGTeleport *>(obj);
-				if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability)
-					break;
-				for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits)
-				{
-					if(!cb->getObj(exit))
-					{ // Always attempt to visit two-way teleports if one of channel exits is not visible
-						objs.push_back(obj);
-						break;
-					}
-				}
-				break;
-			}
-		}
-	}
-
-	for(auto h : heroes)
-	{
-		for(auto obj : objs) //double loop, performance risk?
-		{
-			auto waysToVisitObj = ai->ah->howToVisitObj(h, obj, allowGatherArmy);
-
-			vstd::concatenate(ret, waysToVisitObj);
-		}
-
-		TSubgoal goal = exploreNearestNeighbour(h);
-
-		if(!goal->invalid())
-		{
-			ret.push_back(goal);
-		}
-	}
-
-	if(ret.empty())
-	{
-		for(auto h : heroes)
-		{
-			logAi->trace("Exploration searching for a new point for hero %s", h->getNameTranslated());
-
-			TSubgoal goal = explorationNewPoint(h);
-
-			if(goal->invalid())
-			{
-				ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
-			}
-			else
-			{
-				ret.push_back(goal);
-			}
-		}
-	}
-
-	//we either don't have hero yet or none of heroes can explore
-	if((!hero || ret.empty()) && ai->canRecruitAnyHero())
-		ret.push_back(sptr(RecruitHero()));
-
-	if(ret.empty())
-	{
-		throw goalFulfilledException(sptr(Explore().sethero(hero)));
-	}
-
-	return ret;
-}
-
-bool Explore::fulfillsMe(TSubgoal goal)
-{
-	if(goal->goalType == EXPLORE)
-	{
-		if(goal->hero)
-			return hero == goal->hero;
-		else
-			return true; //cancel ALL exploration
-	}
-	return false;
-}
-
-TSubgoal Explore::explorationBestNeighbour(int3 hpos, HeroPtr h) const
-{
-	ExplorationHelper scanResult(h, allowGatherArmy);
-
-	for(crint3 dir : int3::getDirs())
-	{
-		int3 tile = hpos + dir;
-		if(cb->isInTheMap(tile))
-		{
-			scanResult.scanTile(tile);
-		}
-	}
-
-	return scanResult.bestGoal;
-}
-
-
-TSubgoal Explore::explorationNewPoint(HeroPtr h) const
-{
-	ExplorationHelper scanResult(h, allowGatherArmy);
-
-	scanResult.scanSector(10);
-
-	if(!scanResult.bestGoal->invalid())
-	{
-		return scanResult.bestGoal;
-	}
-
-	scanResult.scanMap();
-
-	return scanResult.bestGoal;
-}
-
-
-TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
-{
-	TimeCheck tc("where to explore");
-	int3 hpos = h->visitablePos();
-
-	//look for nearby objs -> visit them if they're close enough
-	const int DIST_LIMIT = 3;
-	const float COST_LIMIT = .2f; //todo: fine tune
-
-	std::vector<const CGObjectInstance *> nearbyVisitableObjs;
-	for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map
-	{
-		for(int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y)
-		{
-			for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
-			{
-				if(ai->isGoodForVisit(obj, h, COST_LIMIT))
-				{
-					nearbyVisitableObjs.push_back(obj);
-				}
-			}
-		}
-	}
-
-	if(nearbyVisitableObjs.size())
-	{
-		vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
-		boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
-
-		TSubgoal pickupNearestObj = fh->chooseSolution(ai->ah->howToVisitObj(h, nearbyVisitableObjs.back(), false));
-
-		if(!pickupNearestObj->invalid())
-		{
-			return pickupNearestObj;
-		}
-	}
-
-	//check if nearby tiles allow us to reveal anything - this is quick
-	return explorationBestNeighbour(hpos, h);
-}

+ 0 - 66
AI/VCAI/Goals/Explore.h

@@ -1,66 +0,0 @@
-/*
-* Explore.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	struct ExplorationHelper;
-
-	class DLL_EXPORT Explore : public CGoal<Explore>
-	{
-	private:
-		bool allowGatherArmy;
-
-	public:
-		Explore(bool allowGatherArmy)
-			: CGoal(Goals::EXPLORE), allowGatherArmy(allowGatherArmy)
-		{
-			priority = 1;
-		}
-
-		Explore()
-			: Explore(true)
-		{
-		}
-
-		Explore(HeroPtr h)
-			: CGoal(Goals::EXPLORE)
-		{
-			hero = h;
-			priority = 1;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		std::string completeMessage() const override;
-		bool fulfillsMe(TSubgoal goal) override;
-		bool operator==(const Explore & other) const override;
-
-	private:
-		TSubgoal exploreNearestNeighbour(HeroPtr h) const;
-		TSubgoal explorationNewPoint(HeroPtr h) const;
-		TSubgoal explorationBestNeighbour(int3 hpos, HeroPtr h) const;
-		void explorationScanTile(const int3 & tile, ExplorationHelper & scanResult) const;
-		bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp, VCAI * vcai) const;
-
-		void getVisibleNeighbours(
-			const std::vector<int3> & tiles,
-			std::vector<int3> & out,
-			CCallback * cbp,
-			const TeamState * ts) const;
-
-		int howManyTilesWillBeDiscovered(const int3 & pos, ExplorationHelper & scanResult) const;
-	};
-}

+ 0 - 66
AI/VCAI/Goals/FindObj.cpp

@@ -1,66 +0,0 @@
-/*
-* FindObj.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 "FindObj.h"
-#include "VisitObj.h"
-#include "Explore.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-
-using namespace Goals;
-
-bool FindObj::operator==(const FindObj & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-TSubgoal FindObj::whatToDoToAchieve()
-{
-	const CGObjectInstance * o = nullptr;
-	if(resID > -1) //specified
-	{
-		for(const CGObjectInstance * obj : ai->visitableObjs)
-		{
-			if(obj->ID.getNum() == objid && obj->subID == resID)
-			{
-				o = obj;
-				break; //TODO: consider multiple objects and choose best
-			}
-		}
-	}
-	else
-	{
-		for(const CGObjectInstance * obj : ai->visitableObjs)
-		{
-			if(obj->ID.getNum() == objid)
-			{
-				o = obj;
-				break; //TODO: consider multiple objects and choose best
-			}
-		}
-	}
-	if(o && ai->isAccessible(o->visitablePos())) //we don't use isAccessibleForHero as we don't know which hero it is
-		return sptr(VisitObj(o->id.getNum()));
-	else
-		return sptr(Explore());
-}
-
-bool FindObj::fulfillsMe(TSubgoal goal)
-{
-	if (goal->goalType == VISIT_TILE) //visiting tile visits object at same time
-	{
-		if (!hero || hero == goal->hero)
-			for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria
-				if (obj->visitablePos() == goal->tile) //object could be removed
-					if (obj->ID.getNum() == objid && obj->subID == resID) //same type and subtype
-						return true;
-	}
-	return false;
-}

+ 0 - 47
AI/VCAI/Goals/FindObj.h

@@ -1,47 +0,0 @@
-/*
-* FindObj.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT FindObj : public CGoal<FindObj>
-	{
-	public:
-		FindObj() {} // empty constructor not allowed
-
-		FindObj(int ID)
-			: CGoal(Goals::FIND_OBJ)
-		{
-			objid = ID;
-			resID = -1; //subid unspecified
-			priority = 1;
-		}
-		FindObj(int ID, int subID)
-			: CGoal(Goals::FIND_OBJ)
-		{
-			objid = ID;
-			resID = subID;
-			priority = 1;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-		bool fulfillsMe(TSubgoal goal) override;
-		bool operator==(const FindObj & other) const override;
-	};
-}

+ 0 - 203
AI/VCAI/Goals/GatherArmy.cpp

@@ -1,203 +0,0 @@
-/*
-* GatherArmy.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/mapObjects/CGTownInstance.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-bool GatherArmy::operator==(const GatherArmy & other) const
-{
-	return other.hero.h == hero.h || town == other.town;
-}
-
-std::string GatherArmy::completeMessage() const
-{
-	return "Hero " + hero.get()->getNameTranslated() + " gathered army of value " + std::to_string(value);
-}
-
-TSubgoal GatherArmy::whatToDoToAchieve()
-{
-	//TODO: find hero if none set
-	assert(hero.h);
-
-	return fh->chooseSolution(getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
-}
-
-TGoalVec GatherArmy::getAllPossibleSubgoals()
-{
-	//get all possible towns, heroes and dwellings we may use
-	TGoalVec ret;
-
-	if(!hero.validAndSet())
-	{
-		return ret;
-	}
-
-	//TODO: include evaluation of monsters gather in calculation
-	for(auto t : cb->getTownsInfo())
-	{
-		auto waysToVisit = ai->ah->howToVisitObj(hero, t);
-
-		if(waysToVisit.size())
-		{
-			//grab army from town
-			if(!t->getVisitingHero() && ai->ah->howManyReinforcementsCanGet(hero.get(), t))
-			{
-				if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
-					vstd::concatenate(ret, waysToVisit);
-			}
-
-			//buy army in town
-			if (!t->getVisitingHero() || t->getVisitingHero() == hero.get(true))
-			{
-				std::vector<int> values = {
-					value,
-					(int)ai->ah->howManyReinforcementsCanBuy(t->getUpperArmy(), t),
-					(int)ai->ah->howManyReinforcementsCanBuy(hero.get(), t) };
-
-				int val = *std::min_element(values.begin(), values.end());
-
-				if (val)
-				{
-					auto goal = sptr(BuyArmy(t, val).sethero(hero));
-
-					if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
-						ret.push_back(goal);
-					else
-						logAi->debug("Can not buy army, because of ai->ah->containsObjective");
-				}
-			}
-			//build dwelling
-			//TODO: plan building over multiple turns?
-			//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + std::size(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
-
-			//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
-			/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + std::size(unitsSource)), 1);
-			if (bid.has_value())
-			{
-				auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
-				if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
-					ret.push_back(goal);
-				else
-					logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
-			}*/
-		}
-	}
-
-	auto otherHeroes = cb->getHeroesInfo();
-	auto heroDummy = hero;
-	vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
-	{
-		if(h == heroDummy.h)
-			return true;
-		else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
-			return true;
-		else if(!ai->ah->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
-			return true;
-		else if(ai->getGoal(h)->goalType == GATHER_ARMY)
-			return true;
-		else
-		   return false;
-	});
-
-	for(auto h : otherHeroes)
-	{
-		// Go to the other hero if we are faster
-		if(!vstd::contains(ai->visitedHeroes[hero], h))
-		{
-			vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h, false));
-		}
-
-		// Go to the other hero if we are faster
-		if(!vstd::contains(ai->visitedHeroes[h], hero))
-		{
-			vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get(), false));
-		}
-	}
-
-	std::vector<const CGObjectInstance *> objs;
-	for(auto obj : ai->visitableObjs)
-	{
-		if(obj->ID == Obj::CREATURE_GENERATOR1)
-		{
-			auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
-
-			//Use flagged dwellings only when there are available creatures that we can afford
-			if(relationToOwner == PlayerRelations::SAME_PLAYER)
-			{
-				auto dwelling = dynamic_cast<const CGDwelling *>(obj);
-
-				ui32 val = std::min((ui32)value, (ui32)ai->ah->howManyReinforcementsCanBuy(hero.get(), dwelling));
-
-				if(val)
-				{
-					for(auto & creLevel : dwelling->creatures)
-					{
-						if(creLevel.first)
-						{
-							for(auto & creatureID : creLevel.second)
-							{
-								if(ai->ah->freeResources().canAfford(creatureID.toCreature()->getFullRecruitCost()))
-									objs.push_back(obj); //TODO: reserve resources?
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-
-	for(auto h : cb->getHeroesInfo())
-	{
-		for(auto obj : objs)
-		{
-			//find safe dwelling
-			if(ai->isGoodForVisit(obj, h))
-			{
-				vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
-			}
-		}
-	}
-
-	if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
-	{
-		if(auto t = ai->findTownWithTavern())
-		{
-			for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
-			{
-				if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
-				{
-					ret.push_back(sptr(RecruitHero()));
-					break;
-				}
-			}
-		}
-	}
-
-	if(ret.empty())
-	{
-		const bool allowGatherArmy = false;
-
-		if(hero == ai->primaryHero())
-			ret.push_back(sptr(Explore(allowGatherArmy)));
-		else
-			throw cannotFulfillGoalException("No ways to gather army");
-	}
-
-	return ret;
-}

+ 0 - 38
AI/VCAI/Goals/GatherArmy.h

@@ -1,38 +0,0 @@
-/*
-* GatherArmy.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT GatherArmy : public CGoal<GatherArmy>
-	{
-	public:
-		GatherArmy()
-			: CGoal(Goals::GATHER_ARMY)
-		{
-		}
-		GatherArmy(int val)
-			: CGoal(Goals::GATHER_ARMY)
-		{
-			value = val;
-			priority = 2.5;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		std::string completeMessage() const override;
-		bool operator==(const GatherArmy & other) const override;
-	};
-}

+ 0 - 163
AI/VCAI/Goals/GatherTroops.cpp

@@ -1,163 +0,0 @@
-/*
-* GatherTroops.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/mapObjects/CGTownInstance.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-bool GatherTroops::operator==(const GatherTroops & other) const
-{
-	return objid == other.objid;
-}
-
-int GatherTroops::getCreaturesCount(const CArmedInstance * army)
-{
-	int count = 0;
-
-	for(const auto & stack : army->Slots())
-	{
-		if(objid == stack.second->getCreatureID().num)
-		{
-			count += stack.second->getCount();
-		}
-	}
-
-	return count;
-}
-
-TSubgoal GatherTroops::whatToDoToAchieve()
-{
-	logAi->trace("Entering GatherTroops::whatToDoToAchieve");
-
-	auto heroes = cb->getHeroesInfo();
-
-	for(auto hero : heroes)
-	{
-		if(getCreaturesCount(hero) >= this->value)
-		{
-			logAi->trace("Completing GATHER_TROOPS by hero %s", hero->getNameTranslated());
-
-			throw goalFulfilledException(sptr(*this));
-		}
-	}
-
-	TGoalVec solutions = getAllPossibleSubgoals();
-
-	if(solutions.empty())
-		return sptr(Explore());
-
-	return fh->chooseSolution(solutions);
-}
-
-
-TGoalVec GatherTroops::getAllPossibleSubgoals()
-{
-	TGoalVec solutions;
-
-	for(const CGTownInstance * t : cb->getTownsInfo())
-	{
-		int count = getCreaturesCount(t->getUpperArmy());
-
-		if(count >= this->value)
-		{
-			if(t->getVisitingHero())
-			{
-				solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->getVisitingHero())));
-			}
-			else
-			{
-				vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
-			}
-
-			continue;
-		}
-
-		auto creature = LIBRARY->creatures()->getByIndex(objid);
-		if(t->getFactionID() == creature->getFactionID()) //TODO: how to force AI to build unupgraded creatures? :O
-		{
-			auto tryFindCreature = [&]() -> std::optional<std::vector<CreatureID>>
-			{
-				if(vstd::isValidIndex(t->getTown()->creatures, creature->getLevel() - 1))
-				{
-					auto itr = t->getTown()->creatures.begin();
-					std::advance(itr, creature->getLevel() - 1);
-					return make_optional(*itr);
-				}
-				return std::nullopt;
-			};
-
-			auto creatures = tryFindCreature();
-			if(!creatures)
-				continue;
-
-			int upgradeNumber = vstd::find_pos(*creatures, creature->getId());
-			if(upgradeNumber < 0)
-				continue;
-
-			BuildingID bid(BuildingID::getDwellingFromLevel(creature->getLevel(), upgradeNumber));
-
-			if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->getFullRecruitCost())) //this assumes only creatures with dwellings are assigned to faction
-			{
-				solutions.push_back(sptr(BuyArmy(t, creature->getAIValue() * this->value).setobjid(objid)));
-			}
-			/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
-			{
-				return sptr(BuildThis(bid, t).setpriority(priority));
-			}*/
-		}
-	}
-
-	for(auto obj : ai->visitableObjs)
-	{
-		auto d = dynamic_cast<const CGDwelling *>(obj);
-
-		if(!d || obj->ID == Obj::TOWN)
-			continue;
-
-		for(auto creature : d->creatures)
-		{
-			if(creature.first) //there are more than 0 creatures avaliabe
-			{
-				for(auto type : creature.second)
-				{
-					if(type.getNum() == objid && ai->ah->freeResources().canAfford(LIBRARY->creatures()->getById(type)->getFullRecruitCost()))
-						vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
-				}
-			}
-		}
-	}
-
-	CreatureID creID = CreatureID(objid);
-
-	vstd::erase_if(solutions, [&](TSubgoal goal)->bool
-	{
-		return goal->hero && !goal->hero->getSlotFor(creID).validSlot() && !goal->hero->getFreeSlot().validSlot();
-	});
-
-	return solutions;
-	//TODO: exchange troops between heroes
-}
-
-bool GatherTroops::fulfillsMe(TSubgoal goal)
-{
-	if (!hero || hero == goal->hero) //we got army for desired hero or any hero
-		if (goal->objid == objid) //same creature type //TODO: consider upgrades?
-			if (goal->value >= value) //notify every time we get resources?
-				return true;
-	return false;
-}

+ 0 - 43
AI/VCAI/Goals/GatherTroops.h

@@ -1,43 +0,0 @@
-/*
-* GatherTroops.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT GatherTroops : public CGoal<GatherTroops>
-	{
-	public:
-		GatherTroops()
-			: CGoal(Goals::GATHER_TROOPS)
-		{
-			priority = 2;
-		}
-		GatherTroops(int type, int val)
-			: CGoal(Goals::GATHER_TROOPS)
-		{
-			objid = type;
-			value = val;
-			priority = 2;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		bool fulfillsMe(TSubgoal goal) override;
-		bool operator==(const GatherTroops & other) const override;
-
-	private:
-		int getCreaturesCount(const CArmedInstance * army);
-	};
-}

+ 0 - 26
AI/VCAI/Goals/GetArtOfType.cpp

@@ -1,26 +0,0 @@
-/*
-* GetArtOfType.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 "GetArtOfType.h"
-#include "FindObj.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-
-using namespace Goals;
-
-bool GetArtOfType::operator==(const GetArtOfType & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-TSubgoal GetArtOfType::whatToDoToAchieve()
-{
-	return sptr(FindObj(Obj::ARTIFACT, aid));
-}

+ 0 - 40
AI/VCAI/Goals/GetArtOfType.h

@@ -1,40 +0,0 @@
-/*
-* GetArtOfType.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT GetArtOfType : public CGoal<GetArtOfType>
-	{
-	public:
-		GetArtOfType()
-			: CGoal(Goals::GET_ART_TYPE)
-		{
-		}
-		GetArtOfType(int type)
-			: CGoal(Goals::GET_ART_TYPE)
-		{
-			aid = type;
-			priority = 2;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-		bool operator==(const GetArtOfType & other) const override;
-	};
-}

+ 0 - 33
AI/VCAI/Goals/Goals.h

@@ -1,33 +0,0 @@
-/*
-* 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
-*
-*/
-#pragma once
-
-#include "CGoal.h"
-#include "Invalid.h"
-#include "BuildBoat.h"
-#include "Build.h"
-#include "BuildThis.h"
-#include "Conquer.h"
-#include "GatherArmy.h"
-#include "Win.h"
-#include "VisitObj.h"
-#include "VisitTile.h"
-#include "VisitHero.h"
-#include "Explore.h"
-#include "BuyArmy.h"
-#include "GatherTroops.h"
-#include "Trade.h"
-#include "CollectRes.h"
-#include "RecruitHero.h"
-#include "GetArtOfType.h"
-#include "ClearWayTo.h"
-#include "DigAtTile.h"
-#include "FindObj.h"
-#include "AdventureSpellCast.h"

+ 0 - 41
AI/VCAI/Goals/Invalid.h

@@ -1,41 +0,0 @@
-/*
-* Invalid.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 VCAI;
-
-namespace Goals
-{
-	class DLL_EXPORT Invalid : public CGoal<Invalid>
-	{
-	public:
-		Invalid()
-			: CGoal(Goals::INVALID)
-		{
-			priority = -1e10;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override
-		{
-			return iAmElementar();
-		}
-
-		bool operator==(const Invalid & other) const override
-		{
-			return true;
-		}
-	};
-}

+ 0 - 31
AI/VCAI/Goals/RecruitHero.cpp

@@ -1,31 +0,0 @@
-/*
-* RecruitHero.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-TSubgoal RecruitHero::whatToDoToAchieve()
-{
-	const CGTownInstance * t = ai->findTownWithTavern();
-	if(!t)
-		return sptr(BuildThis(BuildingID::TAVERN).setpriority(2));
-
-	TResources res;
-	res[EGameResID::GOLD] = GameConstants::HERO_GOLD_COST;
-	return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
-}

+ 0 - 41
AI/VCAI/Goals/RecruitHero.h

@@ -1,41 +0,0 @@
-/*
-* RecruitHero.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT RecruitHero : public CGoal<RecruitHero>
-	{
-	public:
-		RecruitHero()
-			: CGoal(Goals::RECRUIT_HERO)
-		{
-			priority = 1;
-		}
-
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-
-		TSubgoal whatToDoToAchieve() override;
-
-		bool operator==(const RecruitHero & other) const override
-		{
-			return true;
-		}
-	};
-}

+ 0 - 23
AI/VCAI/Goals/Trade.cpp

@@ -1,23 +0,0 @@
-/*
-* Trade.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 "Trade.h"
-
-using namespace Goals;
-
-bool Trade::operator==(const Trade & other) const
-{
-	return resID == other.resID;
-}
-
-TSubgoal Trade::whatToDoToAchieve()
-{
-	return iAmElementar();
-}

+ 0 - 38
AI/VCAI/Goals/Trade.h

@@ -1,38 +0,0 @@
-/*
-* Trade.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT Trade : public CGoal<Trade>
-	{
-	public:
-		Trade()
-			: CGoal(Goals::TRADE)
-		{
-		}
-		Trade(GameResID rid, int val, int Objid)
-			: CGoal(Goals::TRADE)
-		{
-			resID = rid.getNum();
-			value = val;
-			objid = Objid;
-			priority = 3; //trading is instant, but picking resources is free
-		}
-		TSubgoal whatToDoToAchieve() override;
-		bool operator==(const Trade & other) const override;
-	};
-}

+ 0 - 68
AI/VCAI/Goals/VisitHero.cpp

@@ -1,68 +0,0 @@
-/*
-* VisitHero.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 "VisitHero.h"
-#include "Explore.h"
-#include "Invalid.h"
-#include "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-
-using namespace Goals;
-
-bool VisitHero::operator==(const VisitHero & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-std::string VisitHero::completeMessage() const
-{
-	return "hero " + hero.get()->getNameTranslated() + " visited hero " + std::to_string(objid);
-}
-
-TSubgoal VisitHero::whatToDoToAchieve()
-{
-	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
-	if(!obj)
-		return sptr(Explore());
-	int3 pos = obj->visitablePos();
-
-	if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
-	{
-		if(hero->visitablePos() == pos)
-			logAi->error("Hero %s tries to visit himself.", hero.name);
-		else
-		{
-			//can't use VISIT_TILE here as tile appears blocked by target hero
-			//FIXME: elementar goal should not be abstract
-			return sptr(VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
-		}
-	}
-	return sptr(Invalid());
-}
-
-bool VisitHero::fulfillsMe(TSubgoal goal)
-{
-	//TODO: VisitObj should not be used for heroes, but...
-	if(goal->goalType == VISIT_TILE)
-	{
-		auto obj = cb->getObj(ObjectInstanceID(objid));
-		if (!obj)
-		{
-			logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
-			return false;
-		}
-		return obj->visitablePos() == goal->tile;
-	}
-	return false;
-}

+ 0 - 42
AI/VCAI/Goals/VisitHero.h

@@ -1,42 +0,0 @@
-/*
-* VisitHero.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT VisitHero : public CGoal<VisitHero>
-	{
-	public:
-		VisitHero()
-			: CGoal(Goals::VISIT_HERO)
-		{
-		}
-		VisitHero(int hid)
-			: CGoal(Goals::VISIT_HERO)
-		{
-			objid = hid;
-			priority = 4;
-		}
-		TGoalVec getAllPossibleSubgoals() override
-		{
-			return TGoalVec();
-		}
-		TSubgoal whatToDoToAchieve() override;
-		bool fulfillsMe(TSubgoal goal) override;
-		std::string completeMessage() const override;
-		bool operator==(const VisitHero & other) const override;
-	};
-}

+ 0 - 111
AI/VCAI/Goals/VisitObj.cpp

@@ -1,111 +0,0 @@
-/*
-* VisitObj.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-bool VisitObj::operator==(const VisitObj & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-std::string VisitObj::completeMessage() const
-{
-	return "hero " + hero.get()->getNameTranslated() + " captured Object ID = " + std::to_string(objid);
-}
-
-TGoalVec VisitObj::getAllPossibleSubgoals()
-{
-	TGoalVec goalList;
-	const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid));
-	if(!obj)
-	{
-		throw cannotFulfillGoalException("Object is missing - goal is invalid now!");
-	}
-
-	int3 pos = obj->visitablePos();
-	if(hero)
-	{
-		if(ai->isAccessibleForHero(pos, hero))
-		{
-			if(isSafeToVisit(hero, pos))
-				goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero)));
-			else
-				goalList.push_back(sptr(GatherArmy((int)(fh->evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT)).sethero(hero).setisAbstract(true)));
-
-			return goalList;
-		}
-	}
-	else
-	{
-		for(auto potentialVisitor : cb->getHeroesInfo())
-		{
-			if(ai->isAccessibleForHero(pos, potentialVisitor))
-			{
-				if(isSafeToVisit(potentialVisitor, pos))
-					goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor)));
-				else
-					goalList.push_back(sptr(GatherArmy((int)(fh->evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT)).sethero(potentialVisitor).setisAbstract(true)));
-			}
-		}
-		if(!goalList.empty())
-		{
-			return goalList;
-		}
-	}
-
-	goalList.push_back(sptr(ClearWayTo(pos)));
-	return goalList;
-}
-
-TSubgoal VisitObj::whatToDoToAchieve()
-{
-	auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals());
-
-	if(bestGoal->goalType == VISIT_OBJ && bestGoal->hero)
-		bestGoal->setisElementar(true);
-
-	return bestGoal;
-}
-
-VisitObj::VisitObj(int Objid)
-	: CGoal(VISIT_OBJ)
-{
-	objid = Objid;
-	auto obj = ai->myCb->getObjInstance(ObjectInstanceID(objid));
-	if(obj)
-		tile = obj->visitablePos();
-	else
-		logAi->error("VisitObj constructed with invalid object instance %d", Objid);
-
-	priority = 3;
-}
-
-bool VisitObj::fulfillsMe(TSubgoal goal)
-{
-	if(goal->goalType == VISIT_TILE)
-	{
-		if (!hero || hero == goal->hero)
-		{
-			auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-			if (obj && obj->visitablePos() == goal->tile) //object could be removed
-				return true;
-		}
-	}
-	return false;
-}

+ 0 - 32
AI/VCAI/Goals/VisitObj.h

@@ -1,32 +0,0 @@
-/*
-* VisitObj.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT VisitObj : public CGoal<VisitObj> //this goal was previously known as GetObj
-	{
-	public:
-		VisitObj() = delete; // empty constructor not allowed
-		VisitObj(int Objid);
-
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		bool fulfillsMe(TSubgoal goal) override;
-		std::string completeMessage() const override;
-		bool operator==(const VisitObj & other) const override;
-	};
-}

+ 0 - 91
AI/VCAI/Goals/VisitTile.cpp

@@ -1,91 +0,0 @@
-/*
-* VisitTile.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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/constants/StringConstants.h"
-
-using namespace Goals;
-
-bool VisitTile::operator==(const VisitTile & other) const
-{
-	return other.hero.h == hero.h && other.tile == tile;
-}
-
-std::string VisitTile::completeMessage() const
-{
-	return "Hero " + hero.get()->getNameTranslated() + " visited tile " + tile.toString();
-}
-
-TSubgoal VisitTile::whatToDoToAchieve()
-{
-	auto ret = fh->chooseSolution(getAllPossibleSubgoals());
-
-	if(ret->hero)
-	{
-		if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero))
-		{
-			ret->setisElementar(true);
-			return ret;
-		}
-		else
-		{
-			return sptr(GatherArmy((int)(fh->evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT))
-						.sethero(ret->hero).setisAbstract(true));
-		}
-	}
-	return ret;
-}
-
-TGoalVec VisitTile::getAllPossibleSubgoals()
-{
-	assert(cb->isInTheMap(tile));
-
-	TGoalVec ret;
-	if(!cb->isVisible(tile))
-		ret.push_back(sptr(Explore())); //what sense does it make?
-	else
-	{
-		std::vector<const CGHeroInstance *> heroes;
-		if(hero)
-			heroes.push_back(hero.h); //use assigned hero if any
-		else
-			heroes = cb->getHeroesInfo(); //use most convenient hero
-
-		for(auto h : heroes)
-		{
-			if(ai->isAccessibleForHero(tile, h))
-				ret.push_back(sptr(VisitTile(tile).sethero(h)));
-		}
-		if(ai->canRecruitAnyHero())
-			ret.push_back(sptr(RecruitHero()));
-	}
-	if(ret.empty())
-	{
-		auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
-		if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
-		{
-			if(hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
-				ret.push_back(sptr(VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
-			else
-				throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
-		}
-		else
-			ret.push_back(sptr(ClearWayTo(tile)));
-	}
-
-	//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
-	return ret;
-}

+ 0 - 37
AI/VCAI/Goals/VisitTile.h

@@ -1,37 +0,0 @@
-/*
-* VisitTile.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 VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-	class DLL_EXPORT VisitTile : public CGoal<VisitTile>
-		//tile, in conjunction with hero elementar; assumes tile is reachable
-	{
-	public:
-		VisitTile() {} // empty constructor not allowed
-
-		VisitTile(int3 Tile)
-			: CGoal(Goals::VISIT_TILE)
-		{
-			tile = Tile;
-			priority = 5;
-		}
-		TGoalVec getAllPossibleSubgoals() override;
-		TSubgoal whatToDoToAchieve() override;
-		std::string completeMessage() const override;
-		bool operator==(const VisitTile & other) const override;
-	};
-}

+ 0 - 181
AI/VCAI/Goals/Win.cpp

@@ -1,181 +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 "../VCAI.h"
-#include "../AIUtility.h"
-#include "../AIhelper.h"
-#include "../FuzzyHelper.h"
-#include "../ResourceManager.h"
-#include "../BuildingManager.h"
-#include "../../../lib/mapping/CMapHeader.h" //for victory conditions
-#include "../../../lib/mapObjects/CGTownInstance.h"
-#include "../../../lib/constants/StringConstants.h"
-
-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.as<ArtifactID>().getNum()));
-		case EventCondition::DESTROY:
-		{
-			if(goal.objectID != ObjectInstanceID::NONE)
-			{
-				auto obj = cb->getObj(goal.objectID);
-				if(obj)
-					if(obj->getOwner() == ai->playerID) //we can't capture our own object
-						return sptr(Conquer());
-
-
-				return sptr(VisitObj(goal.objectID.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 = buildingID 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.as<BuildingID>() == BuildingID::GRAIL)
-			{
-				if(auto h = ai->getHeroWithGrail())
-				{
-					//hero is in a town that can host Grail
-					if(h->getVisitedTown() && !vstd::contains(h->getVisitedTown()->forbiddenBuildings, BuildingID::GRAIL))
-					{
-						const CGTownInstance * t = h->getVisitedTown();
-						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.objectID != ObjectInstanceID::NONE)
-			{
-				auto obj = cb->getObj(goal.objectID);
-				
-				if(obj && cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
-				{
-					return sptr(VisitObj(goal.objectID.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(goal.objectType.as<GameResID>(), goal.value));
-		case EventCondition::HAVE_CREATURES:
-			return sptr(GatherTroops(goal.objectType.as<CreatureID>().getNum(), 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(Conquer());
-
-		// 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;
-
-		default:
-			assert(0);
-		}
-	}
-	return sptr(Invalid());
-}

+ 0 - 39
AI/VCAI/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 VCAI;
-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;
-
-		bool operator==(const Win & other) const override
-		{
-			return true;
-		}
-	};
-}

+ 0 - 147
AI/VCAI/MapObjectsEvaluator.cpp

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

+ 0 - 27
AI/VCAI/MapObjectsEvaluator.h

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

+ 0 - 414
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -1,414 +0,0 @@
-/*
-* AINodeStorage.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 "AINodeStorage.h"
-#include "Actions/TownPortalAction.h"
-#include "../../../lib/callback/IGameInfoCallback.h"
-#include "../../../lib/mapping/CMap.h"
-#include "../../../lib/pathfinder/CPathfinder.h"
-#include "../../../lib/pathfinder/PathfinderOptions.h"
-#include "../../../lib/pathfinder/PathfinderUtil.h"
-#include "../../../lib/spells/ISpellMechanics.h"
-#include "../../../lib/spells/adventure/TownPortalEffect.h"
-#include "../../../lib/IGameSettings.h"
-#include "../../../lib/CPlayerState.h"
-
-AINodeStorage::AINodeStorage(const int3 & Sizes)
-	: sizes(Sizes)
-{
-	nodes.resize(boost::extents[EPathfindingLayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y][NUM_CHAINS]);
-	dangerEvaluator.reset(new FuzzyHelper());
-}
-
-AINodeStorage::~AINodeStorage() = default;
-
-void AINodeStorage::initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo)
-{
-	int3 pos;
-	const int3 sizes = gameInfo.getMapSize();
-	const auto & fow = gameInfo.getPlayerTeam(hero->tempOwner)->fogOfWarMap;
-	const PlayerColor player = hero->tempOwner;
-
-	//make 200% sure that these are loop invariants (also a bit shorter code), let compiler do the rest(loop unswitching)
-	const bool useFlying = options.useFlying;
-	const bool useWaterWalking = options.useWaterWalking;
-
-	for(pos.z=0; pos.z < sizes.z; ++pos.z)
-	{
-		for(pos.x=0; pos.x < sizes.x; ++pos.x)
-		{
-			for(pos.y=0; pos.y < sizes.y; ++pos.y)
-			{
-				const TerrainTile * tile = gameInfo.getTile(pos);
-				if(!tile->getTerrain()->isPassable())
-					continue;
-				
-				if(tile->getTerrain()->isWater())
-				{
-					resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, *tile, fow, player, gameInfo));
-					if(useFlying)
-						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
-					if(useWaterWalking)
-						resetTile(pos, ELayer::WATER, PathfinderUtil::evaluateAccessibility<ELayer::WATER>(pos, *tile, fow, player, gameInfo));
-				}
-				else
-				{
-					resetTile(pos, ELayer::LAND, PathfinderUtil::evaluateAccessibility<ELayer::LAND>(pos, *tile, fow, player, gameInfo));
-					if(useFlying)
-						resetTile(pos, ELayer::AIR, PathfinderUtil::evaluateAccessibility<ELayer::AIR>(pos, *tile, fow, player, gameInfo));
-				}
-			}
-		}
-	}
-}
-
-const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
-{
-	return static_cast<const AIPathNode *>(node);
-}
-
-void AINodeStorage::updateAINode(CGPathNode * node, std::function<void(AIPathNode *)> updater)
-{
-	auto aiNode = static_cast<AIPathNode *>(node);
-
-	updater(aiNode);
-}
-
-bool AINodeStorage::isBattleNode(const CGPathNode * node) const
-{
-	return (getAINode(node)->chainMask & BATTLE_CHAIN) > 0;
-}
-
-std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
-{
-	auto chains = nodes[layer.getNum()][pos.z][pos.x][pos.y];
-
-	for(AIPathNode & node : chains)
-	{
-		if(node.chainMask == chainNumber)
-		{
-			return &node;
-		}
-
-		if(node.chainMask == 0)
-		{
-			node.chainMask = chainNumber;
-
-			return &node;
-		}
-	}
-
-	return std::nullopt;
-}
-
-std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
-{
-	auto hpos = hero->visitablePos();
-	auto initialNode = getOrCreateNode(hpos, hero->inBoat() ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value();
-
-	initialNode->turns = 0;
-	initialNode->moveRemains = hero->movementPointsRemaining();
-	initialNode->danger = 0;
-	initialNode->setCost(0.0);
-
-	return {initialNode};
-}
-
-void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, EPathAccessibility accessibility)
-{
-	for(int i = 0; i < NUM_CHAINS; i++)
-	{
-		AIPathNode & heroNode = nodes[layer.getNum()][coord.z][coord.x][coord.y][i];
-
-		heroNode.chainMask = 0;
-		heroNode.danger = 0;
-		heroNode.manaCost = 0;
-		heroNode.specialAction.reset();
-		heroNode.update(coord, layer, accessibility);
-	}
-}
-
-void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
-{
-	const AIPathNode * srcNode = getAINode(source.node);
-
-	updateAINode(destination.node, [&](AIPathNode * dstNode)
-	{
-		dstNode->moveRemains = destination.movementLeft;
-		dstNode->turns = destination.turn;
-		dstNode->setCost(destination.cost);
-		dstNode->danger = srcNode->danger;
-		dstNode->action = destination.action;
-		dstNode->theNodeBefore = srcNode->theNodeBefore;
-		dstNode->manaCost = srcNode->manaCost;
-
-		if(dstNode->specialAction)
-		{
-			dstNode->specialAction->applyOnDestination(getHero(), destination, source, dstNode, srcNode);
-		}
-	});
-}
-
-void AINodeStorage::calculateNeighbours(
-	std::vector<CGPathNode *> & result,
-	const PathNodeInfo & source,
-	EPathfindingLayer layer,
-	const PathfinderConfig * pathfinderConfig,
-	const CPathfinderHelper * pathfinderHelper)
-{
-	NeighbourTilesVector accessibleNeighbourTiles;
-
-	result.clear();
-
-	pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
-
-	const AIPathNode * srcNode = getAINode(source.node);
-
-	for(auto & neighbour : accessibleNeighbourTiles)
-	{
-		for(EPathfindingLayer i = EPathfindingLayer::LAND; i < EPathfindingLayer::NUM_LAYERS; i.advance(1))
-		{
-			auto nextNode = getOrCreateNode(neighbour, i, srcNode->chainMask);
-
-			if(!nextNode || nextNode.value()->accessible == EPathAccessibility::NOT_SET)
-				continue;
-
-			result.push_back(nextNode.value());
-		}
-	}
-}
-
-void AINodeStorage::setHero(HeroPtr heroPtr, const VCAI * _ai)
-{
-	hero = heroPtr.get();
-	cb = _ai->myCb.get();
-	ai = _ai;
-}
-
-std::vector<CGPathNode *> AINodeStorage::calculateTeleportations(
-	const PathNodeInfo & source,
-	const PathfinderConfig * pathfinderConfig,
-	const CPathfinderHelper * pathfinderHelper)
-{
-	std::vector<CGPathNode *> neighbours;
-
-	if(source.isNodeObjectVisitable())
-	{
-		auto accessibleExits = pathfinderHelper->getTeleportExits(source);
-		auto srcNode = getAINode(source.node);
-
-		for(auto & neighbour : accessibleExits)
-		{
-			auto node = getOrCreateNode(neighbour, source.node->layer, srcNode->chainMask);
-
-			if(!node)
-				continue;
-
-			neighbours.push_back(node.value());
-		}
-	}
-
-	if(hero->visitablePos() == source.coord)
-	{
-		calculateTownPortalTeleportations(source, neighbours);
-	}
-
-	return neighbours;
-}
-
-void AINodeStorage::calculateTownPortalTeleportations(
-	const PathNodeInfo & source,
-	std::vector<CGPathNode *> & neighbours)
-{
-	auto srcNode = getAINode(source.node);
-
-	for (const auto & spell : LIBRARY->spellh->objects)
-	{
-		if (!spell || !spell->isAdventure())
-			continue;
-
-		auto townPortalEffect = spell->getAdventureMechanics().getEffectAs<TownPortalEffect>(hero);
-
-		if (!townPortalEffect)
-			continue;
-
-		if(!hero->canCastThisSpell(spell.get()) || hero->mana < hero->getSpellCost(spell.get()))
-			continue;
-
-		auto towns = cb->getTownsInfo(false);
-
-		vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
-		{
-			return cb->getPlayerRelations(hero->tempOwner, t->tempOwner) == PlayerRelations::ENEMIES;
-		});
-
-		if(towns.empty())
-			return;
-
-		if(hero->movementPointsRemaining() < townPortalEffect->getMovementPointsRequired())
-		{
-			return;
-		}
-
-		if(!townPortalEffect->townSelectionAllowed())
-		{
-			const CGTownInstance * nearestTown = *vstd::minElementByFun(towns, [&](const CGTownInstance * t) -> int
-			{
-				return hero->visitablePos().dist2dSQ(t->visitablePos());
-			});
-
-			towns = std::vector<const CGTownInstance *>{ nearestTown };
-		}
-
-		for(const CGTownInstance * targetTown : towns)
-		{
-			if(targetTown->getVisitingHero())
-				continue;
-
-			auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN);
-
-			if(nodeOptional)
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace("Adding town portal node at %s", targetTown->name);
-#endif
-
-				AIPathNode * node = nodeOptional.value();
-
-				node->theNodeBefore = source.node;
-				node->specialAction.reset(new AIPathfinding::TownPortalAction(targetTown, spell->id));
-				node->moveRemains = source.node->moveRemains;
-				
-				neighbours.push_back(node);
-			}
-		}
-	}
-}
-
-bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const
-{
-	auto pos = destination.coord;
-	auto chains = nodes[EPathfindingLayer::LAND][pos.z][pos.x][pos.y];
-	auto destinationNode = getAINode(destination.node);
-
-	for(const AIPathNode & node : chains)
-	{
-		auto sameNode = node.chainMask == destinationNode->chainMask;
-		if(sameNode	|| node.action == EPathNodeAction::UNKNOWN)
-		{
-			continue;
-		}
-
-		if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0)
-		{
-			if(node.getCost() < destinationNode->getCost())
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Block inefficient move %s:->%s, mask=%i, mp diff: %i",
-					source.coord.toString(),
-					destination.coord.toString(),
-					destinationNode->chainMask,
-					node.moveRemains - destinationNode->moveRemains);
-#endif
-				return true;
-			}
-		}
-	}
-
-	return false;
-}
-
-bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
-{
-	return nodes[layer.getNum()][pos.z][pos.x][pos.y][0].action != EPathNodeAction::UNKNOWN;
-}
-
-std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const
-{
-	std::vector<AIPath> paths;
-	auto chains = nodes[isOnLand ? EPathfindingLayer::LAND : EPathfindingLayer::SAIL][pos.z][pos.x][pos.y];
-	auto initialPos = hero->visitablePos();
-
-	for(const AIPathNode & node : chains)
-	{
-		if(node.action == EPathNodeAction::UNKNOWN)
-		{
-			continue;
-		}
-
-		AIPath path;
-		const AIPathNode * current = &node;
-
-		while(current != nullptr && current->coord != initialPos)
-		{
-			AIPathNodeInfo pathNode;
-			pathNode.cost = current->getCost();
-			pathNode.turns = current->turns;
-			pathNode.danger = current->danger;
-			pathNode.coord = current->coord;
-
-			path.nodes.push_back(pathNode);
-			path.specialAction = current->specialAction;
-
-			current = getAINode(current->theNodeBefore);
-		}
-
-		path.targetObjectDanger = evaluateDanger(pos);
-
-		paths.push_back(path);
-	}
-
-	return paths;
-}
-
-AIPath::AIPath()
-	: nodes({})
-{
-}
-
-int3 AIPath::firstTileToGet() const
-{
-	if(nodes.size())
-	{
-		return nodes.back().coord;
-	}
-
-	return int3(-1, -1, -1);
-}
-
-uint64_t AIPath::getPathDanger() const
-{
-	if(nodes.size())
-	{
-		return nodes.front().danger;
-	}
-
-	return 0;
-}
-
-float AIPath::movementCost() const
-{
-	if(nodes.size())
-	{
-		return nodes.front().cost;
-	}
-
-	// TODO: boost:optional?
-	return 0.0;
-}
-
-uint64_t AIPath::getTotalDanger(HeroPtr hero) const
-{
-	uint64_t pathDanger = getPathDanger();
-	uint64_t danger = pathDanger > targetObjectDanger ? pathDanger : targetObjectDanger;
-
-	return danger;
-}

+ 0 - 120
AI/VCAI/Pathfinding/AINodeStorage.h

@@ -1,120 +0,0 @@
-/*
-* AINodeStorage.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/pathfinder/CGPathNode.h"
-#include "../../../lib/pathfinder/INodeStorage.h"
-#include "../FuzzyHelper.h"
-#include "Actions/ISpecialAction.h"
-
-struct AIPathNode : public CGPathNode
-{
-	uint32_t chainMask;
-	uint64_t danger;
-	uint32_t manaCost;
-	std::shared_ptr<const ISpecialAction> specialAction;
-};
-
-struct AIPathNodeInfo
-{
-	float cost;
-	int turns;
-	int3 coord;
-	uint64_t danger;
-};
-
-struct AIPath
-{
-	std::vector<AIPathNodeInfo> nodes;
-	std::shared_ptr<const ISpecialAction> specialAction;
-	uint64_t targetObjectDanger;
-
-	AIPath();
-
-	/// Gets danger of path excluding danger of visiting the target object like creature bank
-	uint64_t getPathDanger() const;
-
-	/// Gets danger of path including danger of visiting the target object like creature bank
-	uint64_t getTotalDanger(HeroPtr hero) const;
-
-	int3 firstTileToGet() const;
-
-	float movementCost() const;
-};
-
-class AINodeStorage : public INodeStorage
-{
-private:
-	int3 sizes;
-
-	// 1 - layer (air, water, land)
-	// 2-4 - position on map[z][x][y]
-	// 5 - chain (normal, battle, spellcast and combinations)
-	boost::multi_array<AIPathNode, 5> nodes;
-	const CPlayerSpecificInfoCallback * cb;
-	const VCAI * ai;
-	const CGHeroInstance * hero;
-	std::unique_ptr<FuzzyHelper> dangerEvaluator;
-
-	STRONG_INLINE
-	void resetTile(const int3 & tile, EPathfindingLayer layer, EPathAccessibility accessibility);
-
-public:
-	/// more than 1 chain layer allows us to have more than 1 path to each tile so we can chose more optimal one.
-	static const int NUM_CHAINS = 3;
-
-	// chain flags, can be combined
-	static const int NORMAL_CHAIN = 1;
-	static const int BATTLE_CHAIN = 2;
-	static const int CAST_CHAIN = 4;
-	static const int RESOURCE_CHAIN = 8;
-
-	AINodeStorage(const int3 & sizes);
-	~AINodeStorage();
-
-	void initialize(const PathfinderOptions & options, const IGameInfoCallback & gameInfo) override;
-
-	std::vector<CGPathNode *> getInitialNodes() override;
-
-	virtual void calculateNeighbours(
-		std::vector<CGPathNode *> & result,
-		const PathNodeInfo & source,
-		EPathfindingLayer layer,
-		const PathfinderConfig * pathfinderConfig,
-		const CPathfinderHelper * pathfinderHelper) override;
-
-	virtual std::vector<CGPathNode *> calculateTeleportations(
-		const PathNodeInfo & source,
-		const PathfinderConfig * pathfinderConfig,
-		const CPathfinderHelper * pathfinderHelper) override;
-
-	void commit(CDestinationNodeInfo & destination, const PathNodeInfo & source) override;
-
-	const AIPathNode * getAINode(const CGPathNode * node) const;
-	void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater);
-
-	bool isBattleNode(const CGPathNode * node) const;
-	bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
-	std::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, int chainNumber);
-	std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
-	bool isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const;
-
-	void setHero(HeroPtr heroPtr, const VCAI * ai);
-	const CGHeroInstance * getHero() const { return hero; }
-
-	uint64_t evaluateDanger(const int3 &  tile) const
-	{
-		return dangerEvaluator->evaluateDanger(tile, hero, ai);
-	}
-
-private:
-	void calculateTownPortalTeleportations(const PathNodeInfo & source, std::vector<CGPathNode *> & neighbours);
-};

+ 0 - 96
AI/VCAI/Pathfinding/AIPathfinder.cpp

@@ -1,96 +0,0 @@
-/*
-* AIPathfinder.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 "AIPathfinder.h"
-#include "AIPathfinderConfig.h"
-
-#include "../../../lib/mapping/TerrainTile.h"
-
-#include <tbb/task_group.h>
-
-std::vector<std::shared_ptr<AINodeStorage>> AIPathfinder::storagePool;
-std::map<HeroPtr, std::shared_ptr<AINodeStorage>> AIPathfinder::storageMap;
-
-AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai)
-	:cb(cb), ai(ai)
-{
-}
-
-void AIPathfinder::init()
-{
-	storagePool.clear();
-	storageMap.clear();
-}
-
-bool AIPathfinder::isTileAccessible(const HeroPtr & hero, const int3 & tile) const
-{
-	std::shared_ptr<const AINodeStorage> nodeStorage = getStorage(hero);
-
-	return nodeStorage->isTileAccessible(tile, EPathfindingLayer::LAND)
-		|| nodeStorage->isTileAccessible(tile, EPathfindingLayer::SAIL);
-}
-
-std::vector<AIPath> AIPathfinder::getPathInfo(const HeroPtr & hero, const int3 & tile) const
-{
-	std::shared_ptr<const AINodeStorage> nodeStorage = getStorage(hero);
-
-	const TerrainTile * tileInfo = cb->getTile(tile, false);
-
-	if(!tileInfo)
-	{
-		return std::vector<AIPath>();
-	}
-
-	return nodeStorage->getChainInfo(tile, !tileInfo->isWater());
-}
-
-void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
-{
-	storageMap.clear();
-
-	auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr<AIPathfinding::AIPathfinderConfig> config)
-	{
-		logAi->debug("Recalculate paths for %s", hero->getNameTranslated());
-		
-		cb->calculatePaths(config);
-	};
-
-	tbb::task_group calculationTasks;
-
-	for(HeroPtr hero : heroes)
-	{
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-		if(storageMap.size() < storagePool.size())
-		{
-			nodeStorage = storagePool.at(storageMap.size());
-		}
-		else
-		{
-			nodeStorage = std::make_shared<AINodeStorage>(cb->getMapSize());
-			storagePool.push_back(nodeStorage);
-		}
-
-		storageMap[hero] = nodeStorage;
-		nodeStorage->setHero(hero, ai);
-
-		auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, nodeStorage);
-
-		calculationTasks.run(std::bind(calculatePaths, hero.get(), config));
-	}
-
-	calculationTasks.wait();
-}
-
-std::shared_ptr<const AINodeStorage> AIPathfinder::getStorage(const HeroPtr & hero) const
-{
-	return storageMap.at(hero);
-}
-

+ 0 - 32
AI/VCAI/Pathfinding/AIPathfinder.h

@@ -1,32 +0,0 @@
-/*
-* AIPathfinder.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 "AINodeStorage.h"
-#include "../AIUtility.h"
-#include "../VCAI.h"
-
-class AIPathfinder
-{
-private:
-	static std::vector<std::shared_ptr<AINodeStorage>> storagePool;
-	static std::map<HeroPtr, std::shared_ptr<AINodeStorage>> storageMap;
-	CPlayerSpecificInfoCallback * cb;
-	VCAI * ai;
-
-	std::shared_ptr<const AINodeStorage> getStorage(const HeroPtr & hero) const;
-public:
-	AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
-	std::vector<AIPath> getPathInfo(const HeroPtr & hero, const int3 & tile) const;
-	bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
-	void updatePaths(std::vector<HeroPtr> heroes);
-	void init();
-};

+ 0 - 63
AI/VCAI/Pathfinding/AIPathfinderConfig.cpp

@@ -1,63 +0,0 @@
-/*
-* AIPathfinderConfig.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 "AIPathfinderConfig.h"
-#include "Rules/AILayerTransitionRule.h"
-#include "Rules/AIMovementAfterDestinationRule.h"
-#include "Rules/AIMovementToDestinationRule.h"
-#include "Rules/AIPreviousNodeRule.h"
-
-#include "../../../lib/pathfinder/CPathfinder.h"
-
-namespace AIPathfinding
-{
-	std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset(
-		CPlayerSpecificInfoCallback * cb,
-		VCAI * ai,
-		std::shared_ptr<AINodeStorage> nodeStorage)
-	{
-		std::vector<std::shared_ptr<IPathfindingRule>> rules = {
-			std::make_shared<AILayerTransitionRule>(cb, ai, nodeStorage),
-			std::make_shared<DestinationActionRule>(),
-			std::make_shared<AIMovementToDestinationRule>(nodeStorage),
-			std::make_shared<MovementCostRule>(),
-			std::make_shared<AIPreviousNodeRule>(nodeStorage),
-			std::make_shared<AIMovementAfterDestinationRule>(cb, nodeStorage)
-		};
-
-		return rules;
-	}
-
-	AIPathfinderConfig::AIPathfinderConfig(
-		CPlayerSpecificInfoCallback * cb,
-		VCAI * ai,
-		std::shared_ptr<AINodeStorage> nodeStorage)
-		:PathfinderConfig(nodeStorage, *cb, makeRuleset(cb, ai, nodeStorage)), hero(nodeStorage->getHero())
-	{
-		options.ignoreGuards = false;
-		options.useEmbarkAndDisembark = true;
-		options.useTeleportTwoWay = true;
-		options.useTeleportOneWay = true;
-		options.useTeleportOneWayRandom = true;
-		options.useTeleportWhirlpool = true;
-	}
-
-	AIPathfinderConfig::~AIPathfinderConfig() = default;
-
-	CPathfinderHelper * AIPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo)
-	{
-		if(!helper)
-		{
-			helper.reset(new CPathfinderHelper(gameInfo, hero, options));
-		}
-
-		return helper.get();
-	}
-}

+ 0 - 34
AI/VCAI/Pathfinding/AIPathfinderConfig.h

@@ -1,34 +0,0 @@
-/*
-* AIPathfinderConfig.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 "AINodeStorage.h"
-#include "../../../lib/pathfinder/PathfinderOptions.h"
-
-namespace AIPathfinding
-{
-	class AIPathfinderConfig : public PathfinderConfig
-	{
-	private:
-		const CGHeroInstance * hero;
-		std::unique_ptr<CPathfinderHelper> helper;
-
-	public:
-		AIPathfinderConfig(
-			CPlayerSpecificInfoCallback * cb,
-			VCAI * ai,
-			std::shared_ptr<AINodeStorage> nodeStorage);
-
-		~AIPathfinderConfig();
-
-		CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, const IGameInfoCallback & gameInfo) override;
-	};
-}

+ 0 - 21
AI/VCAI/Pathfinding/Actions/BattleAction.cpp

@@ -1,21 +0,0 @@
-/*
-* BattleAction.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/VisitTile.h"
-#include "BattleAction.h"
-
-namespace AIPathfinding
-{
-	Goals::TSubgoal BattleAction::whatToDo(const HeroPtr & hero) const
-	{
-		return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
-	}
-}

+ 0 - 30
AI/VCAI/Pathfinding/Actions/BattleAction.h

@@ -1,30 +0,0 @@
-/*
-* BattleAction.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 "ISpecialAction.h"
-
-namespace AIPathfinding
-{
-	class BattleAction : public ISpecialAction
-	{
-	private:
-		const int3 targetTile;
-
-	public:
-		BattleAction(const int3 targetTile)
-			:targetTile(targetTile)
-		{
-		}
-
-		Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
-	};
-}

+ 0 - 58
AI/VCAI/Pathfinding/Actions/BoatActions.cpp

@@ -1,58 +0,0 @@
-/*
-* BoatActions.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/AdventureSpellCast.h"
-#include "../../Goals/BuildBoat.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "BoatActions.h"
-
-namespace AIPathfinding
-{
-	Goals::TSubgoal BuildBoatAction::whatToDo(const HeroPtr & hero) const
-	{
-		return Goals::sptr(Goals::BuildBoat(shipyard));
-	}
-
-	Goals::TSubgoal SummonBoatAction::whatToDo(const HeroPtr & hero) const
-	{
-		return Goals::sptr(Goals::AdventureSpellCast(hero, usedSpell));
-	}
-
-	void SummonBoatAction::applyOnDestination(
-		const CGHeroInstance * hero,
-		CDestinationNodeInfo & destination,
-		const PathNodeInfo & source,
-		AIPathNode * dstMode,
-		const AIPathNode * srcNode) const
-	{
-		dstMode->manaCost = srcNode->manaCost + getManaCost(hero);
-		dstMode->theNodeBefore = source.node;
-	}
-
-	bool SummonBoatAction::isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const
-	{
-#ifdef VCMI_TRACE_PATHFINDER
-		logAi->trace(
-			"Hero %s has %d mana and needed %d and already spent %d",
-			hero->name,
-			hero->mana,
-			getManaCost(hero),
-			source->manaCost);
-#endif
-
-		return hero->mana >= (si32)(source->manaCost + getManaCost(hero));
-	}
-
-	uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
-	{
-		return hero->getSpellCost(usedSpell.toSpell());
-	}
-}

+ 0 - 73
AI/VCAI/Pathfinding/Actions/BoatActions.h

@@ -1,73 +0,0 @@
-/*
-* BoatActions.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 "ISpecialAction.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-
-namespace AIPathfinding
-{
-	class VirtualBoatAction : public ISpecialAction
-	{
-	private:
-		uint64_t specialChain;
-
-	public:
-		VirtualBoatAction(uint64_t specialChain)
-			:specialChain(specialChain)
-		{
-		}
-
-		uint64_t getSpecialChain() const
-		{
-			return specialChain;
-		}
-	};
-	
-	class SummonBoatAction : public VirtualBoatAction
-	{
-		SpellID usedSpell;
-	public:
-		SummonBoatAction(SpellID usedSpell)
-			:VirtualBoatAction(AINodeStorage::CAST_CHAIN)
-			,usedSpell(usedSpell)
-		{
-		}
-
-		Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
-
-		virtual void applyOnDestination(
-			const CGHeroInstance * hero,
-			CDestinationNodeInfo & destination,
-			const PathNodeInfo & source,
-			AIPathNode * dstMode,
-			const AIPathNode * srcNode) const override;
-
-		bool isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const;
-
-	private:
-		uint32_t getManaCost(const CGHeroInstance * hero) const;
-	};
-
-	class BuildBoatAction : public VirtualBoatAction
-	{
-	private:
-		const IShipyard * shipyard;
-
-	public:
-		BuildBoatAction(const IShipyard * shipyard)
-			:VirtualBoatAction(AINodeStorage::RESOURCE_CHAIN), shipyard(shipyard)
-		{
-		}
-
-		Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
-	};
-}

+ 0 - 37
AI/VCAI/Pathfinding/Actions/ISpecialAction.h

@@ -1,37 +0,0 @@
-/*
-* ISpecialAction.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-
-#pragma once
-
-#include "../../AIUtility.h"
-#include "../../Goals/AbstractGoal.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-struct PathNodeInfo;
-struct CDestinationNodeInfo;
-VCMI_LIB_NAMESPACE_END
-
-struct AIPathNode;
-
-class ISpecialAction
-{
-public:
-	virtual ~ISpecialAction() = default;
-	virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
-
-	virtual void applyOnDestination(
-		const CGHeroInstance * hero,
-		CDestinationNodeInfo & destination,
-		const PathNodeInfo & source,
-		AIPathNode * dstMode,
-		const AIPathNode * srcNode) const
-	{
-	}
-};

+ 0 - 23
AI/VCAI/Pathfinding/Actions/TownPortalAction.cpp

@@ -1,23 +0,0 @@
-/*
-* TownPortalAction.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/AdventureSpellCast.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "TownPortalAction.h"
-
-using namespace AIPathfinding;
-
-Goals::TSubgoal TownPortalAction::whatToDo(const HeroPtr & hero) const
-{
-	const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
-
-	return Goals::sptr(Goals::AdventureSpellCast(hero, spellToUse).settown(targetTown).settile(targetTown->visitablePos()));
-}

+ 0 - 34
AI/VCAI/Pathfinding/Actions/TownPortalAction.h

@@ -1,34 +0,0 @@
-/*
-* TownPortalAction.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 "ISpecialAction.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "../../Goals/AdventureSpellCast.h"
-
-namespace AIPathfinding
-{
-	class TownPortalAction : public ISpecialAction
-	{
-	private:
-		const CGTownInstance * target;
-		SpellID spellToUse;
-
-	public:
-		TownPortalAction(const CGTownInstance * target, SpellID spellToUse)
-			:target(target)
-			,spellToUse(spellToUse)
-		{
-		}
-
-		Goals::TSubgoal whatToDo(const HeroPtr & hero) const override;
-	};
-}

+ 0 - 243
AI/VCAI/Pathfinding/PathfindingManager.cpp

@@ -1,243 +0,0 @@
-/*
-* PathfindingManager.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 "PathfindingManager.h"
-#include "AIPathfinder.h"
-#include "AIPathfinderConfig.h"
-#include "../Goals/Goals.h"
-#include "../Goals/CompleteQuest.h"
-#include "../../../lib/gameState/QuestInfo.h"
-#include "../../../lib/mapObjects/CQuest.h"
-
-PathfindingManager::PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
-	: ai(AI), cb(CB)
-{
-}
-
-void PathfindingManager::init(CPlayerSpecificInfoCallback * CB)
-{
-	cb = CB;
-	pathfinder.reset(new AIPathfinder(cb, ai));
-	pathfinder->init();
-}
-
-void PathfindingManager::setAI(VCAI * AI)
-{
-	ai = AI;
-}
-
-Goals::TGoalVec PathfindingManager::howToVisitTile(const int3 & tile) const
-{
-	Goals::TGoalVec result;
-
-	auto heroes = cb->getHeroesInfo();
-	result.reserve(heroes.size());
-
-	for(auto hero : heroes)
-	{
-		vstd::concatenate(result, howToVisitTile(hero, tile));
-	}
-
-	return result;
-}
-
-Goals::TGoalVec PathfindingManager::howToVisitObj(ObjectIdRef obj) const
-{
-	Goals::TGoalVec result;
-
-	auto heroes = cb->getHeroesInfo();
-	result.reserve(heroes.size());
-
-	for(auto hero : heroes)
-	{
-		vstd::concatenate(result, howToVisitObj(hero, obj));
-	}
-
-	return result;
-}
-
-Goals::TGoalVec PathfindingManager::howToVisitTile(const HeroPtr & hero, const int3 & tile, bool allowGatherArmy) const
-{
-	auto result = findPath(hero, tile, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal
-	{
-		return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
-	});
-
-	for(Goals::TSubgoal solution : result)
-	{
-		solution->setparent(sptr(Goals::VisitTile(tile).sethero(hero).setevaluationContext(solution->evaluationContext)));
-	}
-
-	return result;
-}
-
-Goals::TGoalVec PathfindingManager::howToVisitObj(const HeroPtr & hero, ObjectIdRef obj, bool allowGatherArmy) const
-{
-	if(!obj)
-	{
-		return Goals::TGoalVec();
-	}
-
-	int3 dest = obj->visitablePos();
-
-	auto result = findPath(hero, dest, allowGatherArmy, [&](int3 firstTileToGet) -> Goals::TSubgoal
-	{
-		if(obj->ID.num == Obj::HERO && obj->getOwner() == hero->getOwner())
-			return sptr(Goals::VisitHero(obj->id.getNum()).sethero(hero).setisAbstract(true));
-		else
-			return sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setisAbstract(true));
-	});
-
-	for(Goals::TSubgoal solution : result)
-	{
-		solution->setparent(sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero).setevaluationContext(solution->evaluationContext)));
-	}
-
-	return result;
-}
-
-std::vector<AIPath> PathfindingManager::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
-{
-	return pathfinder->getPathInfo(hero, tile);
-}
-
-Goals::TGoalVec PathfindingManager::findPath(
-	HeroPtr hero,
-	crint3 dest,
-	bool allowGatherArmy,
-	const std::function<Goals::TSubgoal(int3)> doVisitTile) const
-{
-	Goals::TGoalVec result;
-	std::optional<uint64_t> armyValueRequired;
-	uint64_t danger;
-
-	std::vector<AIPath> chainInfo = pathfinder->getPathInfo(hero, dest);
-
-#ifdef VCMI_TRACE_PATHFINDER
-	logAi->trace("Trying to find a way for %s to visit tile %s", hero->name, dest.toString());
-#endif
-
-	for(auto path : chainInfo)
-	{
-		int3 firstTileToGet = path.firstTileToGet();
-#ifdef VCMI_TRACE_PATHFINDER
-		logAi->trace("Path found size=%i, first tile=%s", path.nodes.size(), firstTileToGet.toString());
-#endif
-		if(firstTileToGet.isValid() && ai->isTileNotReserved(hero.get(), firstTileToGet))
-		{
-			danger = path.getTotalDanger(hero);
-
-			if(isSafeToVisit(hero, danger))
-			{
-				Goals::TSubgoal solution;
-
-				if(path.specialAction)
-				{
-					solution = path.specialAction->whatToDo(hero);
-				}
-				else
-				{
-					solution = dest == firstTileToGet
-						? doVisitTile(firstTileToGet)
-						: clearWayTo(hero, firstTileToGet);
-				}
-
-				if(solution->invalid())
-					continue;
-
-				if(solution->evaluationContext.danger < danger)
-					solution->evaluationContext.danger = danger;
-
-				solution->evaluationContext.movementCost += path.movementCost();
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace("It's safe for %s to visit tile %s with danger %s, goal %s", hero->name, dest.toString(), std::to_string(danger), solution->name());
-#endif
-				result.push_back(solution);
-
-				continue;
-			}
-
-			if(!armyValueRequired || armyValueRequired > danger)
-			{
-				armyValueRequired = std::make_optional(danger);
-			}
-		}
-	}
-
-	danger = armyValueRequired.value_or(0);
-
-	if(allowGatherArmy && danger > 0)
-	{
-		//we need to get army in order to conquer that place
-#ifdef VCMI_TRACE_PATHFINDER
-		logAi->trace("Gather army for %s, value=%s", hero->name, std::to_string(danger));
-#endif
-		result.push_back(sptr(Goals::GatherArmy((int)(danger * SAFE_ATTACK_CONSTANT)).sethero(hero).setisAbstract(true)));
-	}
-
-	return result;
-}
-
-Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet) const
-{
-	if(isBlockedBorderGate(firstTileToGet))
-	{
-		//FIXME: this way we'll not visit gate and activate quest :?
-		return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTopObj(firstTileToGet)->getObjTypeIndex()));
-	}
-
-	auto topObj = cb->getTopObj(firstTileToGet);
-	if(topObj)
-	{
-
-		if(vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[hero], topObj))
-		{
-			return sptr(Goals::Invalid());
-		}
-
-		if(topObj->ID == Obj::HERO && cb->getPlayerRelations(hero->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
-		{
-			if(topObj != hero.get(true)) //the hero we want to free
-			{
-				logAi->error("%s stands in the way of %s", topObj->getObjectName(), hero->getObjectName());
-
-				return sptr(Goals::Invalid());
-			}
-		}
-
-		if(topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
-		{
-			if(shouldVisit(hero, topObj))
-			{
-				//do NOT use VISIT_TILE, as tile with quets guard can't be visited
-				return sptr(Goals::VisitObj(topObj->id.getNum()).sethero(hero));
-			}
-
-			auto questObj = dynamic_cast<const IQuestObject*>(topObj);
-
-			if(questObj)
-			{
-				auto questInfo = QuestInfo(topObj->id);
-
-				return sptr(Goals::CompleteQuest(questInfo));
-			}
-
-			return sptr(Goals::Invalid());
-		}
-	}
-
-	return sptr(Goals::VisitTile(firstTileToGet).sethero(hero).setisAbstract(true));
-}
-
-void PathfindingManager::updatePaths(std::vector<HeroPtr> heroes)
-{
-	logAi->debug("AIPathfinder has been reset.");
-	pathfinder->updatePaths(heroes);
-}

+ 0 - 68
AI/VCAI/Pathfinding/PathfindingManager.h

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

+ 0 - 166
AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.cpp

@@ -1,166 +0,0 @@
-/*
-* AILayerTransitionRule.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 "AILayerTransitionRule.h"
-
-#include "../../../../lib/spells/ISpellMechanics.h"
-#include "../../../../lib/spells/adventure/SummonBoatEffect.h"
-
-namespace AIPathfinding
-{
-	AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
-		:cb(cb), ai(ai), nodeStorage(nodeStorage)
-	{
-		setup();
-	}
-
-	void AILayerTransitionRule::process(
-		const PathNodeInfo & source,
-		CDestinationNodeInfo & destination,
-		const PathfinderConfig * pathfinderConfig,
-		CPathfinderHelper * pathfinderHelper) const
-	{
-		LayerTransitionRule::process(source, destination, pathfinderConfig, pathfinderHelper);
-
-		if(!destination.blocked)
-		{
-			return;
-		}
-
-		if(source.node->layer == EPathfindingLayer::LAND && destination.node->layer == EPathfindingLayer::SAIL)
-		{
-			std::shared_ptr<const VirtualBoatAction> virtualBoat = findVirtualBoat(destination, source);
-
-			if(virtualBoat && tryEmbarkVirtualBoat(destination, source, virtualBoat))
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace("Embarking to virtual boat while moving %s -> %s!", source.coord.toString(), destination.coord.toString());
-#endif
-			}
-		}
-	}
-
-	void AILayerTransitionRule::setup()
-	{
-		std::vector<const IShipyard *> shipyards;
-
-		for(const CGTownInstance * t : cb->getTownsInfo())
-		{
-			if(t->hasBuilt(BuildingID::SHIPYARD))
-				shipyards.push_back(t);
-		}
-
-		for(const CGObjectInstance * obj : ai->visitableObjs)
-		{
-			if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
-			{
-				if(const auto * shipyard = dynamic_cast<const IShipyard *>(obj))
-					shipyards.push_back(shipyard);
-			}
-		}
-
-		for(const IShipyard * shipyard : shipyards)
-		{
-			if(shipyard->shipyardStatus() == IShipyard::GOOD)
-			{
-				int3 boatLocation = shipyard->bestLocation();
-				virtualBoats[boatLocation] = std::make_shared<BuildBoatAction>(shipyard);
-				logAi->debug("Virtual boat added at %s", boatLocation.toString());
-			}
-		}
-
-		auto hero = nodeStorage->getHero();
-
-		for (const auto & spell : LIBRARY->spellh->objects)
-		{
-			if (!spell || !spell->isAdventure())
-				continue;
-
-			auto effect = spell->getAdventureMechanics().getEffectAs<SummonBoatEffect>(hero);
-
-			if (!effect || !hero->canCastThisSpell(spell.get()))
-				continue;
-
-			if (effect->canCreateNewBoat() && effect->getSuccessChance(hero) == 100)
-			{
-				// TODO: For lower school level we might need to check the existence of some boat
-				summonableVirtualBoat.reset(new SummonBoatAction(spell->id));
-			}
-		}
-	}
-
-	std::shared_ptr<const VirtualBoatAction> AILayerTransitionRule::findVirtualBoat(
-		CDestinationNodeInfo & destination,
-		const PathNodeInfo & source) const
-	{
-		std::shared_ptr<const VirtualBoatAction> virtualBoat;
-
-		if(vstd::contains(virtualBoats, destination.coord))
-		{
-			virtualBoat = virtualBoats.at(destination.coord);
-		}
-		else if(
-			summonableVirtualBoat
-			&& summonableVirtualBoat->isAffordableBy(nodeStorage->getHero(), nodeStorage->getAINode(source.node)))
-		{
-			virtualBoat = summonableVirtualBoat;
-		}
-
-		return virtualBoat;
-	}
-
-	bool AILayerTransitionRule::tryEmbarkVirtualBoat(
-		CDestinationNodeInfo & destination,
-		const PathNodeInfo & source,
-		std::shared_ptr<const VirtualBoatAction> virtualBoat) const
-	{
-		bool result = false;
-
-		nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
-		{
-			auto boatNodeOptional = nodeStorage->getOrCreateNode(
-				node->coord,
-				node->layer,
-				(int)(node->chainMask | virtualBoat->getSpecialChain()));
-
-			if(boatNodeOptional)
-			{
-				AIPathNode * boatNode = boatNodeOptional.value();
-
-				if(boatNode->action == EPathNodeAction::UNKNOWN)
-				{
-					boatNode->specialAction = virtualBoat;
-					destination.blocked = false;
-					destination.action = EPathNodeAction::EMBARK;
-					destination.node = boatNode;
-					result = true;
-				}
-				else
-				{
-#ifdef VCMI_TRACE_PATHFINDER
-					logAi->trace(
-						"Special transition node already allocated. Blocked moving %s -> %s",
-						source.coord.toString(),
-						destination.coord.toString());
-#endif
-				}
-			}
-			else
-			{
-				logAi->debug(
-					"Can not allocate special transition node while moving %s -> %s",
-					source.coord.toString(),
-					destination.coord.toString());
-			}
-		});
-
-		return result;
-	}
-}

+ 0 - 51
AI/VCAI/Pathfinding/Rules/AILayerTransitionRule.h

@@ -1,51 +0,0 @@
-/*
-* AILayerTransitionRule.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 "../AINodeStorage.h"
-#include "../../VCAI.h"
-#include "../Actions/BoatActions.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "../../../../lib/pathfinder/PathfindingRules.h"
-
-namespace AIPathfinding
-{
-	class AILayerTransitionRule : public LayerTransitionRule
-	{
-	private:
-		CPlayerSpecificInfoCallback * cb;
-		VCAI * ai;
-		std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
-		std::shared_ptr<AINodeStorage> nodeStorage;
-		std::shared_ptr<const SummonBoatAction> summonableVirtualBoat;
-
-	public:
-		AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);
-
-		virtual void process(
-			const PathNodeInfo & source,
-			CDestinationNodeInfo & destination,
-			const PathfinderConfig * pathfinderConfig,
-			CPathfinderHelper * pathfinderHelper) const override;
-
-	private:
-		void setup();
-
-		std::shared_ptr<const VirtualBoatAction> findVirtualBoat(
-			CDestinationNodeInfo & destination,
-			const PathNodeInfo & source) const;
-
-		bool tryEmbarkVirtualBoat(
-			CDestinationNodeInfo & destination,
-			const PathNodeInfo & source,
-			std::shared_ptr<const VirtualBoatAction> virtualBoat) const;
-	};
-}

+ 0 - 147
AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -1,147 +0,0 @@
-/*
-* AIMovementAfterDestinationRule.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 "AIMovementAfterDestinationRule.h"
-#include "../Actions/BattleAction.h"
-
-namespace AIPathfinding
-{
-	AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
-		CPlayerSpecificInfoCallback * cb, 
-		std::shared_ptr<AINodeStorage> nodeStorage)
-		:cb(cb), nodeStorage(nodeStorage)
-	{
-	}
-
-	void AIMovementAfterDestinationRule::process(
-		const PathNodeInfo & source,
-		CDestinationNodeInfo & destination,
-		const PathfinderConfig * pathfinderConfig,
-		CPathfinderHelper * pathfinderHelper) const
-	{
-		if(nodeStorage->hasBetterChain(source, destination))
-		{
-			destination.blocked = true;
-
-			return;
-		}
-
-		auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
-
-		if(blocker == BlockingReason::NONE)
-			return;
-
-		if(blocker == BlockingReason::DESTINATION_BLOCKVIS && destination.nodeObject)
-		{
-			auto objID = destination.nodeObject->ID;
-			auto enemyHero = objID == Obj::HERO && destination.objectRelations == PlayerRelations::ENEMIES;
-
-			if(!enemyHero && !isObjectRemovable(destination.nodeObject))
-			{
-				destination.blocked = true;
-			}
-
-			return;
-		}
-
-		if(blocker == BlockingReason::DESTINATION_VISIT)
-		{
-			return;
-		}
-
-		if(blocker == BlockingReason::DESTINATION_GUARDED)
-		{
-			auto srcGuardians = cb->getGuardingCreatures(source.coord);
-			auto destGuardians = cb->getGuardingCreatures(destination.coord);
-
-			if(destGuardians.empty())
-			{
-				destination.blocked = true;
-
-				return;
-			}
-
-			vstd::erase_if(destGuardians, [&](const CGObjectInstance * destGuard) -> bool
-			{
-				return vstd::contains(srcGuardians, destGuard);
-			});
-
-			auto guardsAlreadyBypassed = destGuardians.empty() && srcGuardians.size();
-			if(guardsAlreadyBypassed && nodeStorage->isBattleNode(source.node))
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Bypass guard at destination while moving %s -> %s",
-					source.coord.toString(),
-					destination.coord.toString());
-#endif
-
-				return;
-			}
-
-			const AIPathNode * destNode = nodeStorage->getAINode(destination.node);
-			auto battleNodeOptional = nodeStorage->getOrCreateNode(
-				destination.coord,
-				destination.node->layer,
-				destNode->chainMask | AINodeStorage::BATTLE_CHAIN);
-
-			if(!battleNodeOptional)
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Can not allocate battle node while moving %s -> %s",
-					source.coord.toString(),
-					destination.coord.toString());
-#endif
-
-				destination.blocked = true;
-
-				return;
-			}
-
-			auto * battleNode = battleNodeOptional.value();
-
-			if(battleNode->locked)
-			{
-#ifdef VCMI_TRACE_PATHFINDER
-				logAi->trace(
-					"Block bypass guard at destination while moving %s -> %s",
-					source.coord.toString(),
-					destination.coord.toString());
-#endif
-				destination.blocked = true;
-
-				return;
-			}
-
-			auto danger = nodeStorage->evaluateDanger(destination.coord);
-
-			destination.node = battleNode;
-			nodeStorage->commit(destination, source);
-
-			if(battleNode->danger < danger)
-			{
-				battleNode->danger = danger;
-			}
-
-			battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
-#ifdef VCMI_TRACE_PATHFINDER
-			logAi->trace(
-				"Begin bypass guard at destination with danger %s while moving %s -> %s",
-				std::to_string(danger),
-				source.coord.toString(),
-				destination.coord.toString());
-#endif
-			return;
-		}
-
-		destination.blocked = true;
-	}
-}

+ 0 - 35
AI/VCAI/Pathfinding/Rules/AIMovementAfterDestinationRule.h

@@ -1,35 +0,0 @@
-/*
-* AIMovementAfterDestinationRule.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 "../AINodeStorage.h"
-#include "../../VCAI.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "../../../../lib/pathfinder/PathfindingRules.h"
-
-namespace AIPathfinding
-{
-	class AIMovementAfterDestinationRule : public MovementAfterDestinationRule
-	{
-	private:
-		CPlayerSpecificInfoCallback * cb;
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-	public:
-		AIMovementAfterDestinationRule(CPlayerSpecificInfoCallback * cb, std::shared_ptr<AINodeStorage> nodeStorage);
-
-		virtual void process(
-			const PathNodeInfo & source,
-			CDestinationNodeInfo & destination,
-			const PathfinderConfig * pathfinderConfig,
-			CPathfinderHelper * pathfinderHelper) const override;
-	};
-}

+ 0 - 51
AI/VCAI/Pathfinding/Rules/AIMovementToDestinationRule.cpp

@@ -1,51 +0,0 @@
-/*
-* AIMovementToDestinationRule.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 "AIMovementToDestinationRule.h"
-
-namespace AIPathfinding
-{
-	AIMovementToDestinationRule::AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage)
-		: nodeStorage(nodeStorage)
-	{
-	}
-
-	void AIMovementToDestinationRule::process(
-		const PathNodeInfo & source,
-		CDestinationNodeInfo & destination,
-		const PathfinderConfig * pathfinderConfig,
-		CPathfinderHelper * pathfinderHelper) const
-	{
-		auto blocker = getBlockingReason(source, destination, pathfinderConfig, pathfinderHelper);
-
-		if(blocker == BlockingReason::NONE)
-			return;
-
-		if(blocker == BlockingReason::DESTINATION_BLOCKED
-			&& destination.action == EPathNodeAction::EMBARK
-			&& nodeStorage->getAINode(destination.node)->specialAction)
-		{
-			return;
-		}
-
-		if(blocker == BlockingReason::SOURCE_GUARDED && nodeStorage->isBattleNode(source.node))
-		{
-#ifdef VCMI_TRACE_PATHFINDER
-			logAi->trace(
-				"Bypass src guard while moving from %s to %s",
-				source.coord.toString(),
-				destination.coord.toString());
-#endif
-			return;
-		}
-
-		destination.blocked = true;
-	}
-}

+ 0 - 34
AI/VCAI/Pathfinding/Rules/AIMovementToDestinationRule.h

@@ -1,34 +0,0 @@
-/*
-* AIMovementToDestinationRule.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 "../AINodeStorage.h"
-#include "../../VCAI.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "../../../../lib/pathfinder/PathfindingRules.h"
-
-namespace AIPathfinding
-{
-	class AIMovementToDestinationRule : public MovementToDestinationRule
-	{
-	private:
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-	public:
-		AIMovementToDestinationRule(std::shared_ptr<AINodeStorage> nodeStorage);
-
-		virtual void process(
-			const PathNodeInfo & source,
-			CDestinationNodeInfo & destination,
-			const PathfinderConfig * pathfinderConfig,
-			CPathfinderHelper * pathfinderHelper) const override;
-	};
-}

+ 0 - 47
AI/VCAI/Pathfinding/Rules/AIPreviousNodeRule.cpp

@@ -1,47 +0,0 @@
-/*
-* AIPreviousNodeRule.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 "AIPreviousNodeRule.h"
-
-namespace AIPathfinding
-{
-	AIPreviousNodeRule::AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage)
-		: nodeStorage(nodeStorage)
-	{
-	}
-
-	void AIPreviousNodeRule::process(
-		const PathNodeInfo & source,
-		CDestinationNodeInfo & destination,
-		const PathfinderConfig * pathfinderConfig,
-		CPathfinderHelper * pathfinderHelper) const
-	{
-		if(source.node->action == EPathNodeAction::BLOCKING_VISIT || source.node->action == EPathNodeAction::VISIT)
-		{
-			// we can not directly bypass objects, we need to interact with them first
-			destination.node->theNodeBefore = source.node;
-#ifdef VCMI_TRACE_PATHFINDER
-			logAi->trace(
-				"Link src node %s to destination node %s while bypassing visitable obj",
-				source.coord.toString(),
-				destination.coord.toString());
-#endif
-			return;
-		}
-
-		auto aiSourceNode = nodeStorage->getAINode(source.node);
-
-		if(aiSourceNode->specialAction)
-		{
-			// there is some action on source tile which should be performed before we can bypass it
-			destination.node->theNodeBefore = source.node;
-		}
-	}
-}

+ 0 - 34
AI/VCAI/Pathfinding/Rules/AIPreviousNodeRule.h

@@ -1,34 +0,0 @@
-/*
-* AIPreviousNodeRule.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 "../AINodeStorage.h"
-#include "../../VCAI.h"
-#include "../../../../lib/mapObjects/MapObjects.h"
-#include "../../../../lib/pathfinder/PathfindingRules.h"
-
-namespace AIPathfinding
-{
-	class AIPreviousNodeRule : public MovementToDestinationRule
-	{
-	private:
-		std::shared_ptr<AINodeStorage> nodeStorage;
-
-	public:
-		AIPreviousNodeRule(std::shared_ptr<AINodeStorage> nodeStorage);
-
-		virtual void process(
-			const PathNodeInfo & source,
-			CDestinationNodeInfo & destination,
-			const PathfinderConfig * pathfinderConfig,
-			CPathfinderHelper * pathfinderHelper) const override;
-	};
-}

+ 0 - 344
AI/VCAI/ResourceManager.cpp

@@ -1,344 +0,0 @@
-/*
-* ResourceManager.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 "ResourceManager.h"
-#include "Goals/Goals.h"
-
-#include "../../lib/mapObjects/MapObjects.h"
-
-ResourceObjective::ResourceObjective(const TResources & Res, Goals::TSubgoal Goal)
-	: resources(Res), goal(Goal)
-{
-}
-
-bool ResourceObjective::operator<(const ResourceObjective & ro) const
-{
-	return goal->priority < ro.goal->priority;
-}
-
-ResourceManager::ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI)
-	: ai(AI), cb(CB)
-{
-}
-
-void ResourceManager::init(CPlayerSpecificInfoCallback * CB)
-{
-	cb = CB;
-}
-
-void ResourceManager::setAI(VCAI * AI)
-{
-	ai = AI;
-}
-
-bool ResourceManager::canAfford(const TResources & cost) const
-{
-	return freeResources().canAfford(cost);
-}
-
-TResources ResourceManager::estimateIncome() const
-{
-	TResources ret;
-	for (const CGTownInstance * t : cb->getTownsInfo())
-	{
-		ret += t->dailyIncome();
-	}
-
-	for (const CGObjectInstance * obj : ai->getFlaggedObjects())
-	{
-		if (obj->ID == Obj::MINE)
-		{
-			auto mine = dynamic_cast<const CGMine*>(obj);
-			ret += mine->dailyIncome();
-		}
-	}
-
-	return ret;
-}
-
-void ResourceManager::reserveResources(const TResources & res, Goals::TSubgoal goal)
-{
-	if (!goal->invalid())
-		tryPush(ResourceObjective(res, goal));
-	else
-		logAi->warn("Attempt to reserve resources for Invalid goal");
-}
-
-Goals::TSubgoal ResourceManager::collectResourcesForOurGoal(ResourceObjective &o) const
-{
-	auto allResources = cb->getResourceAmount();
-	auto income = estimateIncome();
-	GameResID resourceType = EGameResID::NONE;
-	TResource amountToCollect = 0;
-
-	using resPair = std::pair<GameResID, TResource>;
-	std::map<GameResID, TResource> missingResources;
-
-	//TODO: unit test for complex resource sets
-
-	//sum missing resources of given type for ALL reserved objectives
-	for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
-	{
-		//choose specific resources we need for this goal (not 0)
-		for (auto r = ResourceSet::nziterator(o.resources); r.valid(); r++)
-			missingResources[r->resType] += it->resources[r->resType]; //goal it costs r units of resType
-	}
-	for (auto it = ResourceSet::nziterator(o.resources); it.valid(); it++)
-	{
-		missingResources[it->resType] -= allResources[it->resType]; //missing = (what we need) - (what we have)
-		vstd::amax(missingResources[it->resType], 0); // if we have more resources than reserved, we don't need them
-	}
-	vstd::erase_if(missingResources, [=](const resPair & p) -> bool
-	{
-		return !(p.second); //in case evaluated to 0 or less
-	});
-	if (missingResources.empty()) //FIXME: should be unit-tested out
-	{
-		logAi->error("We don't need to collect resources %s for goal %s", o.resources.toString(), o.goal->name());
-		return o.goal;
-	}
-
-	for (const resPair p : missingResources)
-	{
-		if (!income[p.first]) //prioritize resources with 0 income
-		{
-			resourceType = p.first;
-			amountToCollect = p.second;
-			break;
-		}
-	}
-	if (resourceType == EGameResID::NONE) //no needed resources has 0 income,
-	{
-		//find the one which takes longest to collect
-		using timePair = std::pair<GameResID, float>;
-		std::map<GameResID, float> daysToEarn;
-		for (auto it : missingResources)
-			daysToEarn[it.first] = (float)missingResources[it.first] / income[it.first];
-		auto incomeComparer = [](const timePair & lhs, const timePair & rhs) -> bool
-		{
-			//theoretically income can be negative, but that falls into this comparison
-			return lhs.second < rhs.second;
-		};
-
-		resourceType = boost::max_element(daysToEarn, incomeComparer)->first;
-		amountToCollect = missingResources[resourceType];
-	}
-
-	//this is abstract goal and might take some time to complete
-	return Goals::sptr(Goals::CollectRes(resourceType, amountToCollect).setisAbstract(true));
-}
-
-Goals::TSubgoal ResourceManager::whatToDo() const //suggest any goal
-{
-	if (queue.size())
-	{
-		auto o = queue.top();
-
-		auto allResources = cb->getResourceAmount(); //we don't consider savings, it's out top-priority goal
-		if (allResources.canAfford(o.resources))
-			return o.goal;
-		else //we can't afford even top-priority goal, need to collect resources
-			return collectResourcesForOurGoal(o);
-	}
-	else
-		return Goals::sptr(Goals::Invalid()); //nothing else to do
-}
-
-Goals::TSubgoal ResourceManager::whatToDo(TResources &res, Goals::TSubgoal goal)
-{
-	logAi->trace("ResourceManager: checking goal %s which requires resources %s", goal->name(), res.toString());
-
-	TResources accumulatedResources;
-	auto allResources = cb->getResourceAmount();
-
-	ResourceObjective ro(res, goal);
-	tryPush(ro);
-	//check if we can afford all the objectives with higher priority first
-	for (auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
-	{
-		accumulatedResources += it->resources;
-
-		logAi->trace(
-			"ResourceManager: checking goal %s, accumulatedResources=%s, available=%s",
-			it->goal->name(),
-			accumulatedResources.toString(),
-			allResources.toString());
-
-		if(!accumulatedResources.canBeAfforded(allResources))
-		{
-			//can't afford
-			break;
-		}
-		else //can afford all goals up to this point
-		{
-			if(it->goal == goal)
-			{
-				logAi->debug("ResourceManager: can afford goal %s", goal->name());
-				return goal; //can afford immediately
-			}
-		}
-	}
-
-	logAi->debug("ResourceManager: can not afford goal %s", goal->name());
-
-	return collectResourcesForOurGoal(ro);
-}
-
-bool ResourceManager::containsObjective(Goals::TSubgoal goal) const
-{
-	logAi->trace("Entering ResourceManager.containsObjective goal=%s", goal->name());
-	dumpToLog();
-
-	//TODO: unit tests for once
-	for (auto objective : queue)
-	{
-		if (objective.goal == goal)
-			return true;
-	}
-	return false;
-}
-
-bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal)
-{
-	logAi->trace("Entering ResourceManager.notifyGoalCompleted goal=%s", goal->name());
-
-	if (goal->invalid())
-		logAi->warn("Attempt to complete Invalid goal");
-
-	std::function<bool(const Goals::TSubgoal &)> equivalentGoalsCheck = [goal](const Goals::TSubgoal & x) -> bool
-	{
-		return x == goal || x->fulfillsMe(goal);
-	};
-
-	bool removedGoal = removeOutdatedObjectives(equivalentGoalsCheck);
-
-	dumpToLog();
-
-	return removedGoal;
-}
-
-bool ResourceManager::updateGoal(Goals::TSubgoal goal)
-{
-	//we update priority of goal if it is easier or more difficult to complete
-	if (goal->invalid())
-		logAi->warn("Attempt to update Invalid goal");
-
-	auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
-	{
-		return ro.goal == goal;
-	});
-	if (it != queue.end())
-	{
-		it->goal->setpriority(goal->priority);
-		auto handle = queue.s_handle_from_iterator(it);
-		queue.update(handle); //restore order
-		return true;
-	}
-	else
-		return false;
-}
-
-void ResourceManager::dumpToLog() const
-{
-	for(auto it = queue.ordered_begin(); it != queue.ordered_end(); it++)
-	{
-		logAi->trace("ResourceManager contains goal %s which requires resources %s", it->goal->name(), it->resources.toString());
-	}
-}
-
-bool ResourceManager::tryPush(const ResourceObjective & o)
-{
-	auto goal = o.goal;
-
-	logAi->trace("ResourceManager: Trying to add goal %s which requires resources %s", goal->name(), o.resources.toString());
-	dumpToLog();
-
-	auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool
-	{
-		return ro.goal == goal;
-	});
-	if (it != queue.end())
-	{
-		auto handle = queue.s_handle_from_iterator(it);
-		vstd::amax(goal->priority, it->goal->priority); //increase priority if case
-		//update resources with new value
-		queue.update(handle, ResourceObjective(o.resources, goal)); //restore order
-		return false;
-	}
-	else
-	{
-		queue.push(o); //add new objective
-		logAi->debug("Reserved resources (%s) for %s", o.resources.toString(), goal->name());
-		return true;
-	}
-}
-
-bool ResourceManager::hasTasksLeft() const
-{
-	return !queue.empty();
-}
-
-bool ResourceManager::removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate)
-{
-	bool removedAnything = false;
-	while(true)
-	{ //unfortunately we can't use remove_if on heap
-		auto it = boost::find_if(queue, [&](const ResourceObjective & ro) -> bool
-		{
-			return predicate(ro.goal);
-		});
-
-		if(it != queue.end()) //removed at least one
-		{
-			logAi->debug("Removing goal %s from ResourceManager.", it->goal->name());
-			queue.erase(queue.s_handle_from_iterator(it));
-			removedAnything = true;
-		}
-		else
-		{ //found nothing more to remove
-			break;
-		}
-	}
-	return removedAnything;
-}
-
-TResources ResourceManager::reservedResources() const
-{
-	TResources res;
-	for (auto it : queue) //subtract the value of reserved goals
-		res += it.resources;
-	return res;
-}
-
-TResources ResourceManager::freeResources() const
-{
-	TResources myRes = cb->getResourceAmount();
-	myRes -= reservedResources(); //subtract the value of reserved goals
-
-	for (auto & val : myRes)
-		vstd::amax(val, 0); //never negative
-
-	return myRes;
-}
-
-TResource ResourceManager::freeGold() const
-{
-	return freeResources()[EGameResID::GOLD];
-}
-
-TResources ResourceManager::allResources() const
-{
-	return cb->getResourceAmount();
-}
-
-TResource ResourceManager::allGold() const
-{
-	return cb->getResourceAmount()[EGameResID::GOLD];
-}

+ 0 - 99
AI/VCAI/ResourceManager.h

@@ -1,99 +0,0 @@
-/*
-* ResourceManager.h, part of VCMI engine
-*
-* Authors: listed in file AUTHORS in main folder
-*
-* License: GNU General Public License v2.0 or later
-* Full text of license available in license.txt file, in main folder
-*
-*/
-#pragma once
-
-#include "AIUtility.h"
-#include "../../lib/GameConstants.h"
-#include "../../lib/GameLibrary.h"
-#include "VCAI.h"
-#include <boost/heap/binomial_heap.hpp>
-
-class AIhelper;
-class IResourceManager;
-
-struct DLL_EXPORT ResourceObjective
-{
-	ResourceObjective() = default;
-	ResourceObjective(const TResources &res, Goals::TSubgoal goal);
-	bool operator < (const ResourceObjective &ro) const;
-
-	TResources resources; //how many resources do we need
-	Goals::TSubgoal goal; //what for (build, gather army etc...)
-};
-
-class DLL_EXPORT IResourceManager //: public: IAbstractManager
-{
-public:
-	virtual ~IResourceManager() = default;
-	virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
-	virtual void setAI(VCAI * AI) = 0;
-
-	virtual TResources reservedResources() const = 0;
-	virtual TResources freeResources() const = 0;
-	virtual TResource freeGold() const = 0;
-	virtual TResources allResources() const = 0;
-	virtual TResource allGold() const = 0;
-
-	virtual Goals::TSubgoal whatToDo() const = 0;//get highest-priority goal
-	virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0;
-	virtual bool containsObjective(Goals::TSubgoal goal) const = 0;
-	virtual bool hasTasksLeft() const = 0;
-	virtual bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) = 0; //remove ResourceObjectives from queue if ResourceObjective->goal meets specific criteria
-	virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0;
-};
-
-class DLL_EXPORT ResourceManager : public IResourceManager
-{
-	/*Resource Manager is a smart shopping list for AI to help
-	Uses priority queue based on CGoal.priority */
-	friend class VCAI;
-	friend class AIhelper;
-	friend struct SetGlobalState;
-
-	CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
-	VCAI * ai;
-
-public:
-	ResourceManager() = default;
-	ResourceManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
-
-	bool canAfford(const TResources & cost) const;
-	TResources reservedResources() const override;
-	TResources freeResources() const override;
-	TResource freeGold() const override;
-	TResources allResources() const override;
-	TResource allGold() const override;
-
-	Goals::TSubgoal whatToDo() const override; //peek highest-priority goal
-	Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal) override; //can we afford this goal or need to CollectRes?
-	bool containsObjective(Goals::TSubgoal goal) const override;
-	bool hasTasksLeft() const override;
-	bool removeOutdatedObjectives(std::function<bool(const Goals::TSubgoal &)> predicate) override;
-	bool notifyGoalCompleted(Goals::TSubgoal goal) override;
-
-protected: //not-const actions only for AI
-	virtual void reserveResources(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal());
-	virtual bool updateGoal(Goals::TSubgoal goal); //new goal must have same properties but different priority
-	virtual bool tryPush(const ResourceObjective &o);
-
-	//inner processing
-	virtual TResources estimateIncome() const;
-	virtual Goals::TSubgoal collectResourcesForOurGoal(ResourceObjective &o) const;
-
-	void init(CPlayerSpecificInfoCallback * CB) override;
-	void setAI(VCAI * AI) override;
-
-private:
-	TResources saving;
-
-	boost::heap::binomial_heap<ResourceObjective> queue;
-
-	void dumpToLog() const;
-};

+ 0 - 10
AI/VCAI/StdInc.cpp

@@ -1,10 +0,0 @@
-/*
- * StdInc.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"

+ 0 - 13
AI/VCAI/StdInc.h

@@ -1,13 +0,0 @@
-/*
- * StdInc.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 "../../Global.h"
-
-VCMI_LIB_USING_NAMESPACE

+ 0 - 2914
AI/VCAI/VCAI.cpp

@@ -1,2914 +0,0 @@
-/*
- * VCAI.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 "FuzzyHelper.h"
-#include "ResourceManager.h"
-#include "BuildingManager.h"
-#include "Goals/Goals.h"
-#include "Goals/CompleteQuest.h"
-
-#include "../../lib/AsyncRunner.h"
-#include "../../lib/CThreadHelper.h"
-#include "../../lib/UnlockGuard.h"
-#include "../../lib/StartInfo.h"
-#include "../../lib/battle/CPlayerBattleCallback.h"
-#include "../../lib/mapObjects/MapObjects.h"
-#include "../../lib/mapObjects/ObjectTemplate.h"
-#include "../../lib/CConfigHandler.h"
-#include "../../lib/IGameSettings.h"
-#include "../../lib/gameState/CGameState.h"
-#include "../../lib/gameState/UpgradeInfo.h"
-#include "../../lib/bonuses/Limiters.h"
-#include "../../lib/bonuses/Updaters.h"
-#include "../../lib/bonuses/Propagators.h"
-#include "../../lib/entities/artifact/ArtifactUtils.h"
-#include "../../lib/entities/artifact/CArtifact.h"
-#include "../../lib/entities/building/CBuilding.h"
-#include "../../lib/mapObjects/CQuest.h"
-#include "../../lib/mapping/TerrainTile.h"
-#include "../../lib/networkPacks/PacksForClient.h"
-#include "../../lib/networkPacks/PacksForClientBattle.h"
-#include "../../lib/networkPacks/PacksForServer.h"
-#include "../../lib/serializer/CTypeList.h"
-#include "../../lib/pathfinder/PathfinderCache.h"
-#include "../../lib/pathfinder/PathfinderOptions.h"
-
-#include "AIhelper.h"
-
-extern FuzzyHelper * fh;
-
-const double SAFE_ATTACK_CONSTANT = 1.5;
-
-//one thread may be turn of AI and another will be handling a side effect for AI2
-thread_local CCallback * cb = nullptr;
-thread_local VCAI * ai = nullptr;
-
-//std::map<int, std::map<int, int> > HeroView::infosCount;
-
-//helper RAII to manage global ai/cb ptrs
-struct SetGlobalState
-{
-	SetGlobalState(VCAI * AI)
-	{
-		assert(!ai);
-		assert(!cb);
-
-		ai = AI;
-		cb = AI->myCb.get();
-	}
-	~SetGlobalState()
-	{
-		//TODO: how to handle rm? shouldn't be called after ai is destroyed, hopefully
-		//TODO: to ensure that, make rm unique_ptr
-		ai = nullptr;
-		cb = nullptr;
-	}
-};
-
-
-#define SET_GLOBAL_STATE(ai) SetGlobalState _hlpSetState(ai)
-
-#define NET_EVENT_HANDLER SET_GLOBAL_STATE(this)
-#define MAKING_TURN SET_GLOBAL_STATE(this)
-
-VCAI::VCAI()
-{
-	LOG_TRACE(logAi);
-	asyncTasks = std::make_unique<AsyncRunner>();
-	destinationTeleport = ObjectInstanceID();
-	destinationTeleportPos = int3(-1);
-
-	ah = new AIhelper();
-	ah->setAI(this);
-}
-
-VCAI::~VCAI()
-{
-	delete ah;
-	LOG_TRACE(logAi);
-	finish();
-}
-
-void VCAI::availableCreaturesChanged(const CGDwelling * town)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::heroMoved(const TryMoveHero & details, bool verbose)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	//enemy hero may have left visible area
-	validateObject(details.id);
-	auto hero = cb->getHero(details.id);
-
-	const int3 from = hero ? hero->convertToVisitablePos(details.start) : (details.start - int3(0,1,0));
-	const int3 to   = hero ? hero->convertToVisitablePos(details.end)   : (details.end   - int3(0,1,0));
-
-	const CGObjectInstance * o1 = vstd::frontOrNull(cb->getVisitableObjs(from, verbose));
-	const CGObjectInstance * o2 = vstd::frontOrNull(cb->getVisitableObjs(to, verbose));
-
-	if(details.result == TryMoveHero::TELEPORTATION)
-	{
-		auto t1 = dynamic_cast<const CGTeleport *>(o1);
-		auto t2 = dynamic_cast<const CGTeleport *>(o2);
-		if(t1 && t2)
-		{
-			if(cb->isTeleportChannelBidirectional(t1->channel))
-			{
-				if(o1->ID == Obj::SUBTERRANEAN_GATE && o1->ID == o2->ID) // We need to only add subterranean gates in knownSubterraneanGates. Used for features not yet ported to use teleport channels
-				{
-					knownSubterraneanGates[o1] = o2;
-					knownSubterraneanGates[o2] = o1;
-					logAi->debug("Found a pair of subterranean gates between %s and %s!", from.toString(), to.toString());
-				}
-			}
-		}
-		//FIXME: teleports are not correctly visited
-		unreserveObject(hero, t1);
-		unreserveObject(hero, t2);
-	}
-	else if(details.result == TryMoveHero::EMBARK && hero)
-	{
-		//make sure AI not attempt to visit used boat
-		validateObject(hero->getBoat());
-	}
-	else if(details.result == TryMoveHero::DISEMBARK && o1)
-	{
-		auto boat = dynamic_cast<const CGBoat *>(o1);
-		if(boat)
-			addVisitableObj(boat);
-	}
-}
-
-void VCAI::heroInGarrisonChange(const CGTownInstance * town)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::centerView(int3 pos, int focusTime)
-{
-	LOG_TRACE_PARAMS(logAi, "focusTime '%i'", focusTime);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::artifactAssembled(const ArtifactLocation & al)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	status.addQuery(queryID, "TavernWindow");
-	executeActionAsync("showTavernWindow", [this, queryID](){ answerQuery(queryID, 0); });
-}
-
-void VCAI::showThievesGuildWindow(const CGObjectInstance * obj)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::playerBlocked(int reason, bool start)
-{
-	LOG_TRACE_PARAMS(logAi, "reason '%i', start '%i'", reason % start);
-	NET_EVENT_HANDLER;
-	if(start && reason == PlayerBlocked::UPCOMING_BATTLE)
-		status.setBattle(UPCOMING_BATTLE);
-
-	if(reason == PlayerBlocked::ONGOING_MOVEMENT)
-		status.setMove(start);
-}
-
-void VCAI::showPuzzleMap()
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::showShipyardDialog(const IShipyard * obj)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult)
-{
-	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf.toString());
-	NET_EVENT_HANDLER;
-	logAi->debug("Player %d (%s): I heard that player %d (%s) %s.", playerID, playerID.toString(), player, player.toString(), (victoryLossCheckResult.victory() ? "won" : "lost"));
-	if(player == playerID)
-	{
-		if(victoryLossCheckResult.victory())
-		{
-			logAi->debug("VCAI: I won! Incredible!");
-			logAi->debug("Turn nr %d", myCb->getDate());
-		}
-		else
-		{
-			logAi->debug("VCAI: Player %d (%s) lost. It's me. What a disappointment! :(", player, player.toString());
-		}
-
-		makingTurnInterrupption.interruptThread();
-	}
-}
-
-void VCAI::artifactPut(const ArtifactLocation & al)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::artifactRemoved(const ArtifactLocation & al)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::artifactDisassembled(const ArtifactLocation & al)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start)
-{
-	LOG_TRACE_PARAMS(logAi, "start '%i'; obj '%s'", start % (visitedObj ? visitedObj->getObjectName() : std::string("n/a")));
-	NET_EVENT_HANDLER;
-
-	if(start && visitedObj) //we can end visit with null object, anyway
-	{
-		markObjectVisited(visitedObj);
-		unreserveObject(visitor, visitedObj);
-		completeGoal(sptr(Goals::VisitObj(visitedObj->id.getNum()).sethero(visitor))); //we don't need to visit it anymore
-		//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..)
-		if (visitedObj->ID == Obj::HERO)
-		{
-			visitedHeroes[visitor].insert(HeroPtr(dynamic_cast<const CGHeroInstance *>(visitedObj)));
-		}
-	}
-
-	status.heroVisit(visitedObj, start);
-}
-
-void VCAI::availableArtifactsChanged(const CGBlackMarket * bm)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	//buildArmyIn(town);
-	//moveCreaturesToHero(town);
-}
-
-void VCAI::tileHidden(const FowTilesType & pos)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	validateVisitableObjs();
-	clearPathsInfo();
-}
-
-void VCAI::tileRevealed(const FowTilesType & pos)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	for(int3 tile : pos)
-	{
-		for(const CGObjectInstance * obj : myCb->getVisitableObjs(tile))
-			addVisitableObj(obj);
-	}
-
-	clearPathsInfo();
-}
-
-void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	auto firstHero = cb->getHero(hero1);
-	auto secondHero = cb->getHero(hero2);
-
-	status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->getNameTranslated() % firstHero->tempOwner % secondHero->getNameTranslated() % secondHero->tempOwner));
-
-	executeActionAsync("heroExchangeStarted", [this, firstHero, secondHero, query]()
-	{
-		float goalpriority1 = 0;
-		float goalpriority2 = 0;
-
-		auto firstGoal = getGoal(firstHero);
-		if(firstGoal->goalType == Goals::GATHER_ARMY)
-			goalpriority1 = firstGoal->priority;
-		auto secondGoal = getGoal(secondHero);
-		if(secondGoal->goalType == Goals::GATHER_ARMY)
-			goalpriority2 = secondGoal->priority;
-
-		auto transferFrom2to1 = [this](const CGHeroInstance * h1, const CGHeroInstance * h2) -> void
-		{
-			this->pickBestCreatures(h1, h2);
-			this->pickBestArtifacts(h1, h2);
-		};
-
-		//Do not attempt army or artifacts exchange if we visited ally player
-		//Visits can still be useful if hero have skills like Scholar
-		if(firstHero->tempOwner != secondHero->tempOwner)
-		{
-			logAi->debug("Heroes owned by different players. Do not exchange army or artifacts.");
-		}
-		else if(goalpriority1 > goalpriority2)
-		{
-			transferFrom2to1(firstHero, secondHero);
-		}
-		else if(goalpriority1 < goalpriority2)
-		{
-			transferFrom2to1(secondHero, firstHero);
-		}
-		else //regular criteria
-		{
-			if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && ah->canGetArmy(firstHero, secondHero))
-				transferFrom2to1(firstHero, secondHero);
-			else if(ah->canGetArmy(secondHero, firstHero))
-				transferFrom2to1(secondHero, firstHero);
-		}
-
-		completeGoal(sptr(Goals::VisitHero(firstHero->id.getNum()))); //TODO: what if we were visited by other hero in the meantime?
-		completeGoal(sptr(Goals::VisitHero(secondHero->id.getNum())));
-
-		answerQuery(query, 0);
-	});
-}
-
-void VCAI::heroExperienceChanged(const CGHeroInstance * hero, si64 val)
-{
-	LOG_TRACE_PARAMS(logAi, "val '%i'", val);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val)
-{
-	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level, QueryID queryID)
-{
-	LOG_TRACE_PARAMS(logAi, "level '%i'", level);
-	NET_EVENT_HANDLER;
-
-	status.addQuery(queryID, "RecruitmentDialog");
-	executeActionAsync("showRecruitmentDialog", [this, dwelling, dst, queryID](){
-		recruitCreatures(dwelling, dst);
-		checkHeroArmy(dynamic_cast<const CGHeroInstance*>(dst));
-		answerQuery(queryID, 0);
-	});
-}
-
-void VCAI::heroMovePointsChanged(const CGHeroInstance * hero)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::newObject(const CGObjectInstance * obj)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	if(obj->isVisitable())
-		addVisitableObj(obj);
-}
-
-//to prevent AI from accessing objects that got deleted while they became invisible (Cover of Darkness, enemy hero moved etc.) below code allows AI to know deletion of objects out of sight
-//see: RemoveObject::applyFirstCl, to keep AI "not cheating" do not use advantage of this and use this function just to prevent crashes
-void VCAI::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	vstd::erase_if_present(visitableObjs, obj);
-	vstd::erase_if_present(alreadyVisited, obj);
-
-	for(auto h : cb->getHeroesInfo())
-		unreserveObject(h, obj);
-
-	std::function<bool(const Goals::TSubgoal &)> checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool
-	{
-		if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum()))
-			return true;
-		else if(x->parent && checkRemovalValidity(x->parent)) //repeat this lambda check recursively on parent goal
-			return true;
-		else
-			return false;
-	};
-
-	//clear VCAI / main loop caches
-	vstd::erase_if(lockedHeroes, [&](const std::pair<HeroPtr, Goals::TSubgoal> & x) -> bool
-	{
-		return checkRemovalValidity(x.second);
-	});
-
-	vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool
-	{
-		return checkRemovalValidity(x.first);
-	});
-
-	vstd::erase_if(basicGoals, checkRemovalValidity);
-	vstd::erase_if(goalsToAdd, checkRemovalValidity);
-	vstd::erase_if(goalsToRemove, checkRemovalValidity);
-
-	for(auto goal : ultimateGoalsFromBasic)
-		vstd::erase_if(goal.second, checkRemovalValidity);
-
-	//clear resource manager goal cache
-	ah->removeOutdatedObjectives(checkRemovalValidity);
-
-	//TODO
-	//there are other places where CGObjectinstance ptrs are stored...
-	//
-
-	if(obj->ID == Obj::HERO && obj->tempOwner == playerID)
-	{
-		lostHero(cb->getHero(obj->id)); //we can promote, since objectRemoved is called just before actual deletion
-	}
-}
-
-void VCAI::showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	executeActionAsync("showHillFortWindow", [visitor]()
-	{
-		makePossibleUpgrades(visitor);
-	});
-}
-
-void VCAI::playerBonusChanged(const Bonus & bonus, bool gain)
-{
-	LOG_TRACE_PARAMS(logAi, "gain '%i'", gain);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::heroCreated(const CGHeroInstance * h)
-{
-	LOG_TRACE(logAi);
-	if(h->getVisitedTown())
-		townVisitsThisWeek[HeroPtr(h)].insert(h->getVisitedTown());
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::advmapSpellCast(const CGHeroInstance * caster, SpellID spellID)
-{
-	LOG_TRACE_PARAMS(logAi, "spellID '%i", spellID);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID)
-{
-	LOG_TRACE_PARAMS(logAi, "soundID '%i'", soundID);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::requestRealized(PackageApplied * pa)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	if(status.haveTurn())
-	{
-		if(pa->packType == CTypeList::getInstance().getTypeID<EndTurn>(nullptr))
-		{
-			if(pa->result)
-				status.madeTurn();
-		}
-	}
-
-	if(pa->packType == CTypeList::getInstance().getTypeID<QueryReply>(nullptr))
-	{
-		status.receivedAnswerConfirmation(pa->requestID, pa->result);
-	}
-}
-
-void VCAI::receivedResource()
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	status.addQuery(queryID, "UniversityWindow");
-	executeActionAsync("showUniversityWindow", [this, queryID](){ answerQuery(queryID, 0); });
-}
-
-void VCAI::heroManaPointsChanged(const CGHeroInstance * hero)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val)
-{
-	LOG_TRACE_PARAMS(logAi, "which '%d', val '%d'", which % val);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::battleResultsApplied()
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	assert(status.getBattle() == ENDING_BATTLE);
-}
-
-void VCAI::battleEnded()
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	assert(status.getBattle() == ENDING_BATTLE);
-	status.setBattle(NO_BATTLE);
-}
-
-void VCAI::beforeObjectPropertyChanged(const SetObjectProperty * sop)
-{
-
-}
-
-void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-	if(sop->what == ObjProperty::OWNER)
-	{
-		if(myCb->getPlayerRelations(playerID, sop->identifier.as<PlayerColor>()) == PlayerRelations::ENEMIES)
-		{
-			//we want to visit objects owned by oppponents
-			auto obj = myCb->getObj(sop->id, false);
-			if(obj)
-			{
-				addVisitableObj(obj); // TODO: Remove once save compatibility broken. In past owned objects were removed from this set
-				vstd::erase_if_present(alreadyVisited, obj);
-			}
-		}
-	}
-}
-
-void VCAI::buildChanged(const CGTownInstance * town, BuildingID buildingID, int what)
-{
-	LOG_TRACE_PARAMS(logAi, "what '%i'", what);
-	NET_EVENT_HANDLER;
-
-	if(town->getOwner() == playerID && what == 1) //built
-		completeGoal(sptr(Goals::BuildThis(buildingID, town)));
-}
-
-void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain)
-{
-	LOG_TRACE_PARAMS(logAi, "gain '%i'", gain);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
-{
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-
-	status.addQuery(queryID, "MarketWindow");
-	executeActionAsync("showMarketWindow", [this, queryID](){ answerQuery(queryID, 0); });
-}
-
-void VCAI::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain)
-{
-	//TODO: AI support for ViewXXX spell
-	LOG_TRACE(logAi);
-	NET_EVENT_HANDLER;
-}
-
-void VCAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB)
-{
-	LOG_TRACE(logAi);
-	env = ENV;
-	myCb = CB;
-	cbc = CB;
-
-	ah->init(CB.get());
-
-	NET_EVENT_HANDLER; //sets ah->rm->cb
-	playerID = *myCb->getPlayerID();
-	myCb->waitTillRealize = true;
-	pathfinderCache = std::make_unique<PathfinderCache>(myCb.get(), PathfinderOptions(*myCb));
-
-	if(!fh)
-		fh = new FuzzyHelper();
-
-	retrieveVisitableObjs();
-}
-
-std::shared_ptr<const CPathsInfo> VCAI::getPathsInfo(const CGHeroInstance * h) const
-{
-	return pathfinderCache->getPathsInfo(h);
-}
-
-void VCAI::invalidatePaths()
-{
-	pathfinderCache->invalidatePaths();
-}
-
-void VCAI::yourTurn(QueryID queryID)
-{
-	LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
-	NET_EVENT_HANDLER;
-	status.addQuery(queryID, "YourTurn");
-	executeActionAsync("yourTurn", [this, queryID](){ answerQuery(queryID, 0); });
-	status.startedTurn();
-
-	makingTurnInterrupption.reset();
-	asyncTasks->run([this]()
-	{
-		ScopedThreadName guard("VCAI::makingTurn");
-		makeTurn();
-	});
-}
-
-void VCAI::heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID)
-{
-	LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
-	NET_EVENT_HANDLER;
-	status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->getNameTranslated() % hero->level));
-	executeActionAsync("heroGotLevel", [this, queryID](){ answerQuery(queryID, 0); });
-}
-
-void VCAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
-{
-	LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
-	NET_EVENT_HANDLER;
-	status.addQuery(queryID, boost::str(boost::format("Commander %s of %s got level %d") % commander->name % commander->getArmy()->nodeName() % static_cast<int>(commander->level)));
-	executeActionAsync("commanderGotLevel", [this, queryID](){ answerQuery(queryID, 0); });
-}
-
-void VCAI::showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel, bool safeToAutoaccept)
-{
-	LOG_TRACE_PARAMS(logAi, "text '%s', askID '%i', soundID '%i', selection '%i', cancel '%i', autoaccept '%i'", text % askID % soundID % selection % cancel % safeToAutoaccept);
-	NET_EVENT_HANDLER;
-	int sel = 0;
-	status.addQuery(askID, boost::str(boost::format("Blocking dialog query with %d components - %s")
-									  % components.size() % text));
-
-	if(selection) //select from multiple components -> take the last one (they're indexed [1-size])
-		sel = static_cast<int>(components.size());
-
-	if(!selection && cancel) //yes&no -> always answer yes, we are a brave AI :)
-		sel = 1;
-
-	executeActionAsync("showBlockingDialog", [this, askID, sel]()
-	{
-		answerQuery(askID, sel);
-	});
-}
-
-void VCAI::showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
-{
-//	LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
-	NET_EVENT_HANDLER;
-	status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits")
-																			% exits.size()));
-
-	int chosenExit = -1;
-	if(impassable)
-	{
-		knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
-	}
-	else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.isValid())
-	{
-		auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos);
-		if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, neededExit))
-			chosenExit = vstd::find_pos(exits, neededExit);
-	}
-
-	for(auto exit : exits)
-	{
-		if(status.channelProbing() && exit.first == destinationTeleport)
-		{
-			chosenExit = vstd::find_pos(exits, exit);
-			break;
-		}
-		else
-		{
-			// TODO: Implement checking if visiting that teleport will uncovert any FoW
-			// So far this is the best option to handle decision about probing
-			auto obj = cb->getObj(exit.first, false);
-			if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first))
-			{
-				if(exit.first != destinationTeleport)
-					teleportChannelProbingList.push_back(exit.first);
-			}
-		}
-	}
-
-	executeActionAsync("showTeleportDialog", [this, askID, chosenExit]()
-	{
-		answerQuery(askID, chosenExit);
-	});
-}
-
-void VCAI::showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID)
-{
-	LOG_TRACE_PARAMS(logAi, "removableUnits '%i', queryID '%i'", removableUnits % queryID);
-	NET_EVENT_HANDLER;
-
-	std::string s1 = up ? up->nodeName() : "NONE";
-	std::string s2 = down ? down->nodeName() : "NONE";
-
-	status.addQuery(queryID, boost::str(boost::format("Garrison dialog with %s and %s") % s1 % s2));
-
-	//you can't request action from action-response thread
-	executeActionAsync("showGarrisonDialog", [this, down, up, removableUnits, queryID]()
-	{
-		if(removableUnits && !cb->getStartInfo()->restrictedGarrisonsForAI())
-			pickBestCreatures(down, up);
-
-		answerQuery(queryID, 0);
-	});
-}
-
-void VCAI::showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects)
-{
-	NET_EVENT_HANDLER;
-	status.addQuery(askID, "Map object select query");
-	executeActionAsync("showMapObjectSelectDialog", [this, askID](){ answerQuery(askID, selectedObject.getNum()); });
-}
-
-void makePossibleUpgrades(const CArmedInstance * obj)
-{
-	if(!obj)
-		return;
-
-	for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
-	{
-		if(const CStackInstance * s = obj->getStackPtr(SlotID(i)))
-		{
-			UpgradeInfo upgradeInfo(s->getId());
-			do
-			{
-				cb->fillUpgradeInfo(obj, SlotID(i), upgradeInfo);
-
-				if(upgradeInfo.hasUpgrades())
-				{
-					// creature at given slot might have alternative upgrades, pick best one
-					CreatureID upgID = *vstd::maxElementByFun(upgradeInfo.getAvailableUpgrades(), [](const CreatureID & id)
-						{
-							return id.toCreature()->getAIValue();
-						});
-					if(cb->getResourceAmount().canAfford(upgradeInfo.getUpgradeCostsFor(upgID) * s->getCount()))
-					{
-						cb->upgradeCreature(obj, SlotID(i), upgID);
-						logAi->debug("Upgraded %d %s to %s", s->getCount(), upgradeInfo.oldID.toCreature()->getNamePluralTranslated(),
-							upgradeInfo.getUpgrade().toCreature()->getNamePluralTranslated());
-					}
-					else
-						break;
-				}
-			}
-			while(upgradeInfo.hasUpgrades());
-		}
-	}
-}
-
-void VCAI::makeTurn()
-{
-	MAKING_TURN;
-
-	auto day = cb->getDate(Date::DAY);
-	logAi->info("Player %d (%s) starting turn, day %d", playerID, playerID.toString(), day);
-
-	std::shared_lock gsLock(CGameState::mutex);
-	setThreadName("VCAI::makeTurn");
-
-	switch(cb->getDate(Date::DAY_OF_WEEK))
-	{
-	case 1:
-	{
-		townVisitsThisWeek.clear();
-		std::vector<const CGObjectInstance *> objs;
-		retrieveVisitableObjs(objs, true);
-		for(const CGObjectInstance * obj : objs)
-		{
-			if(isWeeklyRevisitable(obj))
-			{
-				addVisitableObj(obj);
-				vstd::erase_if_present(alreadyVisited, obj);
-			}
-		}
-		break;
-	}
-	}
-	markHeroAbleToExplore(primaryHero());
-	visitedHeroes.clear();
-
-	try
-	{
-		//it looks messy here, but it's better to have armed heroes before attempting realizing goals
-		for (const CGTownInstance * t : cb->getTownsInfo())
-			moveCreaturesToHero(t);
-
-		mainLoop();
-
-		/*Below function is also responsible for hero movement via internal wander function. By design it is separate logic for heroes that have nothing to do.
-		Heroes that were not picked by striveToGoal(sptr(Goals::Win())); recently (so they do not have new goals and cannot continue/reevaluate previously locked goals) will do logic in wander().*/
-		performTypicalActions();
-
-		//for debug purpose
-		for (auto h : cb->getHeroesInfo())
-		{
-			if (h->movementPointsRemaining())
-				logAi->info("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
-		}
-	}
-	catch (const TerminationRequestedException &)
-	{
-		logAi->debug("Making turn thread has been interrupted. We'll end without calling endTurn.");
-		return;
-	}
-	catch (std::exception & e)
-	{
-		logAi->debug("Making turn thread has caught an exception: %s", e.what());
-	}
-
-	endTurn();
-}
-
-std::vector<HeroPtr> VCAI::getMyHeroes() const
-{
-	std::vector<HeroPtr> ret;
-
-	for(auto h : cb->getHeroesInfo())
-	{
-		ret.push_back(h);
-	}
-
-	return ret;
-}
-
-void VCAI::mainLoop()
-{
-	std::vector<Goals::TSubgoal> elementarGoals; //no duplicates allowed (operator ==)
-	basicGoals.clear();
-
-	validateVisitableObjs();
-
-	//get all potential and saved goals
-	//TODO: not lose
-	basicGoals.push_back(sptr(Goals::Win()));
-	for (auto goalPair : lockedHeroes)
-	{
-		fh->setPriority(goalPair.second);  //re-evaluate, as heroes moved in the meantime
-		basicGoals.push_back(goalPair.second);
-	}
-	if (ah->hasTasksLeft())
-		basicGoals.push_back(ah->whatToDo());
-	for (auto quest : myCb->getMyQuests())
-	{
-		basicGoals.push_back(sptr(Goals::CompleteQuest(quest)));
-	}
-	basicGoals.push_back(sptr(Goals::Build()));
-
-	invalidPathHeroes.clear();
-
-	for (int pass = 0; pass< 30 && basicGoals.size(); pass++)
-	{
-		vstd::removeDuplicates(basicGoals); //TODO: container which does this automagically without has would be nice
-		goalsToAdd.clear();
-		goalsToRemove.clear();
-		elementarGoals.clear();
-		ultimateGoalsFromBasic.clear();
-
-		ah->updatePaths(getMyHeroes());
-
-		logAi->debug("Main loop: decomposing %i basic goals", basicGoals.size());
-
-		for (auto basicGoal : basicGoals)
-		{
-			logAi->debug("Main loop: decomposing basic goal %s", basicGoal->name());
-
-			auto goalToDecompose = basicGoal;
-			Goals::TSubgoal elementarGoal = sptr(Goals::Invalid());
-			int maxAbstractGoals = 10;
-			while (!elementarGoal->isElementar && maxAbstractGoals)
-			{
-				try
-				{
-					elementarGoal = decomposeGoal(goalToDecompose);
-				}
-				catch (goalFulfilledException & e)
-				{
-					//it is impossible to continue some goals (like exploration, for example)
-					//complete abstract goal for now, but maybe main goal finds another path
-					logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name());
-					completeGoal(e.goal); //put in goalsToRemove
-					break;
-				}
-				catch(cannotFulfillGoalException & e)
-				{
-					//it is impossible to continue some goals (like exploration, for example)
-					//complete abstract goal for now, but maybe main goal finds another path
-					goalsToRemove.push_back(basicGoal);
-					logAi->debug("Goal %s decomposition failed: %s", goalToDecompose->name(), e.what());
-					break;
-				}
-				catch (std::exception & e) //decomposition failed, which means we can't decompose entire tree
-				{
-					goalsToRemove.push_back(basicGoal);
-					logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what());
-					break;
-				}
-				if (elementarGoal->isAbstract) //we can decompose it further
-				{
-					goalsToAdd.push_back(elementarGoal);
-					//decompose further now - this is necesssary if we can't add over 10 goals in the pool
-					goalToDecompose = elementarGoal;
-					//there is a risk of infinite abstract goal loop, though it indicates failed logic
-					maxAbstractGoals--;
-				}
-				else if (elementarGoal->isElementar) //should be
-				{
-					logAi->debug("Found elementar goal %s", elementarGoal->name());
-					elementarGoals.push_back(elementarGoal);
-					ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal?
-					break;
-				}
-				else //should never be here
-					throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name());
-			}
-		}
-
-		logAi->trace("Main loop: selecting best elementar goal");
-
-		//now choose one elementar goal to realize
-		Goals::TGoalVec possibleGoals(elementarGoals.begin(), elementarGoals.end()); //copy to vector
-		Goals::TSubgoal goalToRealize = sptr(Goals::Invalid());
-		while (possibleGoals.size())
-		{
-			//allow assign goals to heroes with 0 movement, but don't realize them
-			//maybe there are better ones left
-
-			auto bestGoal = fh->chooseSolution(possibleGoals);
-			if (bestGoal->hero) //lock this hero to fulfill goal
-			{
-				setGoal(bestGoal->hero, bestGoal);
-				if (!bestGoal->hero->movementPointsRemaining() || vstd::contains(invalidPathHeroes, bestGoal->hero))
-				{
-					if (!vstd::erase_if_present(possibleGoals, bestGoal))
-					{
-						logAi->error("erase_if_preset failed? Something very wrong!");
-						break;
-					}
-					continue; //chose next from the list
-				}
-			}
-			goalToRealize = bestGoal; //we found our goal to execute
-			break;
-		}
-
-		//realize best goal
-		if (!goalToRealize->invalid())
-		{
-			logAi->debug("Trying to realize %s (value %2.3f)", goalToRealize->name(), goalToRealize->priority);
-
-			try
-			{
-				makingTurnInterrupption.interruptionPoint();
-				goalToRealize->accept(this); //visitor pattern
-				makingTurnInterrupption.interruptionPoint();
-			}
-			catch (const TerminationRequestedException &)
-			{
-				logAi->debug("Player %d: Making turn thread received an interruption!", playerID);
-				throw; //rethrow, we want to truly end this thread
-			}
-			catch (const goalFulfilledException & e)
-			{
-				//the sub-goal was completed successfully
-				completeGoal(e.goal);
-				//local goal was also completed?
-				completeGoal(goalToRealize);
-
-				// remove abstract visit tile if we completed the elementar one
-				vstd::erase_if_present(goalsToAdd, goalToRealize);
-			}
-			catch (const std::exception & e)
-			{
-				logAi->debug("Failed to realize subgoal of type %s, I will stop.", goalToRealize->name());
-				logAi->debug("The error message was: %s", e.what());
-
-				//erase base goal if we failed to execute decomposed goal
-				for (auto basicGoal : ultimateGoalsFromBasic[goalToRealize])
-					goalsToRemove.push_back(basicGoal);
-
-				// sometimes resource manager contains an elementar goal which is not able to execute anymore and just fails each turn.
-				ai->ah->notifyGoalCompleted(goalToRealize);
-
-				//we failed to realize best goal, but maybe others are still possible?
-			}
-
-			//remove goals we couldn't decompose
-			for (auto goal : goalsToRemove)
-				vstd::erase_if_present(basicGoals, goal);
-
-			//add abstract goals
-			boost::sort(goalsToAdd, [](const Goals::TSubgoal & lhs, const Goals::TSubgoal & rhs) -> bool
-			{
-				return lhs->priority > rhs->priority; //highest priority at the beginning
-			});
-
-			//max number of goals = 10
-			int i = 0;
-			while (basicGoals.size() < 10 && goalsToAdd.size() > i)
-			{
-				if (!vstd::contains(basicGoals, goalsToAdd[i])) //don't add duplicates
-					basicGoals.push_back(goalsToAdd[i]);
-				i++;
-			}
-		}
-		else //no elementar goals possible
-		{
-			logAi->debug("Goal decomposition exhausted");
-			break;
-		}
-	}
-}
-
-void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
-{
-	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->anchorPos().toString());
-	switch(obj->ID)
-	{
-	case Obj::TOWN:
-		moveCreaturesToHero(dynamic_cast<const CGTownInstance *>(obj));
-		if(h->getVisitedTown()) //we are inside, not just attacking
-		{
-			townVisitsThisWeek[h].insert(h->getVisitedTown());
-			if(!h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
-			{
-				if(h->getVisitedTown()->hasBuilt(BuildingID::MAGES_GUILD_1))
-					cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
-			}
-		}
-		break;
-	}
-	completeGoal(sptr(Goals::VisitObj(obj->id.getNum()).sethero(h)));
-}
-
-void VCAI::moveCreaturesToHero(const CGTownInstance * t)
-{
-	if(t->getVisitingHero() && t->armedGarrison() && t->getVisitingHero()->tempOwner == t->tempOwner)
-	{
-		pickBestCreatures(t->getVisitingHero(), t);
-	}
-}
-
-void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArmedInstance * source)
-{
-	const CArmedInstance * armies[] = {destinationArmy, source};
-
-	auto bestArmy = ah->getSortedSlots(destinationArmy, source);
-
-	//foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs
-	for(SlotID i = SlotID(0); i.getNum() < bestArmy.size() && i.validSlot(); i.advance(1)) //i-th strongest creature type will go to i-th slot
-	{
-		const CCreature * targetCreature = bestArmy[i.getNum()].creature;
-
-		for(auto armyPtr : armies)
-		{
-			for(SlotID j = SlotID(0); j.validSlot(); j.advance(1))
-			{
-				if(armyPtr->getCreature(j) == targetCreature && (i != j || armyPtr != destinationArmy)) //it's a searched creature not in dst SLOT
-				{
-					//can't take away last creature without split. generate a new stack with 1 creature which is weak but fast
-					if(armyPtr == source
-						&& source->needsLastStack()
-						&& source->stacksCount() == 1
-						&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
-					{
-						auto weakest = ah->getWeakestCreature(bestArmy);
-
-						if(weakest->creature == targetCreature)
-						{
-							if(1 == source->getStackCount(j))
-								break;
-
-							// move all except 1 of weakest creature from source to destination
-							cb->splitStack(
-								source,
-								destinationArmy,
-								j,
-								destinationArmy->getSlotFor(targetCreature),
-								destinationArmy->getStackCount(i) + source->getStackCount(j) - 1);
-
-							break;
-						}
-						else
-						{
-							// Source last stack is not weakest. Move 1 of weakest creature from destination to source
-							cb->splitStack(
-								destinationArmy,
-								source,
-								destinationArmy->getSlotFor(weakest->creature),
-								source->getFreeSlot(),
-								1);
-						}
-					}
-
-					cb->mergeOrSwapStacks(armyPtr, destinationArmy, j, i);
-				}
-			}
-		}
-	}
-
-	//TODO - having now strongest possible army, we may want to think about arranging stacks
-
-	auto hero = dynamic_cast<const CGHeroInstance *>(destinationArmy);
-	if(hero)
-		checkHeroArmy(hero);
-}
-
-void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
-{
-	auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
-	{
-		bool changeMade = false;
-		do
-		{
-			changeMade = false;
-
-			//we collect gear always in same order
-			std::vector<ArtifactLocation> allArtifacts;
-			if(giveStuffToFirstHero)
-			{
-				for(auto p : h->artifactsWorn)
-				{
-					if(p.second.getArt())
-						allArtifacts.push_back(ArtifactLocation(h->id, p.first));
-				}
-			}
-			for(auto slot : h->artifactsInBackpack)
-				allArtifacts.push_back(ArtifactLocation(h->id, h->getArtPos(slot.getArt())));
-
-			if(otherh)
-			{
-				for(auto p : otherh->artifactsWorn)
-				{
-					if(p.second.getArt())
-						allArtifacts.push_back(ArtifactLocation(otherh->id, p.first));
-				}
-				for(auto slot : otherh->artifactsInBackpack)
-					allArtifacts.push_back(ArtifactLocation(otherh->id, otherh->getArtPos(slot.getArt())));
-			}
-			//we give stuff to one hero or another, depending on giveStuffToFirstHero
-
-			const CGHeroInstance * target = nullptr;
-			if(giveStuffToFirstHero || !otherh)
-				target = h;
-			else
-				target = otherh;
-
-			for(auto location : allArtifacts)
-			{
-				if(location.slot == ArtifactPosition::MACH4 || location.slot == ArtifactPosition::SPELLBOOK)
-					continue; // don't attempt to move catapult and spellbook
-
-				if(location.artHolder == target->id && ArtifactUtils::isSlotEquipment(location.slot))
-					continue; //don't reequip artifact we already wear
-
-				auto s = cb->getHero(location.artHolder)->getSlot(location.slot);
-				if(!s || s->locked) //we can't move locks
-					continue;
-				auto artifact = s->getArt();
-				if(!artifact)
-					continue;
-				//FIXME: why are the above possible to be null?
-
-				bool emptySlotFound = false;
-				for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
-				{
-					if(target->isPositionFree(slot) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
-					{
-						ArtifactLocation destLocation(target->id, slot);
-						cb->swapArtifacts(location, destLocation); //just put into empty slot
-						emptySlotFound = true;
-						changeMade = true;
-						break;
-					}
-				}
-				if(!emptySlotFound) //try to put that atifact in already occupied slot
-				{
-					for(auto slot : artifact->getType()->getPossibleSlots().at(target->bearerType()))
-					{
-						auto otherSlot = target->getSlot(slot);
-						if(otherSlot && otherSlot->getArt()) //we need to exchange artifact for better one
-						{
-							//if that artifact is better than what we have, pick it
-							if(compareArtifacts(artifact, otherSlot->getArt()) && artifact->canBePutAt(target, slot, true)) //combined artifacts are not always allowed to move
-							{
-								ArtifactLocation destLocation(target->id, slot);
-								cb->swapArtifacts(location, ArtifactLocation(target->id, target->getArtPos(otherSlot->getArt())));
-								changeMade = true;
-								break;
-							}
-						}
-					}
-				}
-				if(changeMade)
-					break; //start evaluating artifacts from scratch
-			}
-		}
-		while(changeMade);
-	};
-
-	equipBest(h, other, true);
-
-	if(other)
-		equipBest(h, other, false);
-}
-
-void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter)
-{
-	//now used only for visited dwellings / towns, not BuyArmy goal
-	for(int i = 0; i < d->creatures.size(); i++)
-	{
-		if(!d->creatures[i].second.size())
-			continue;
-
-		int count = d->creatures[i].first;
-		CreatureID creID = d->creatures[i].second.back();
-
-		vstd::amin(count, ah->freeResources() / LIBRARY->creatures()->getById(creID)->getFullRecruitCost());
-		if(count > 0)
-			cb->recruitCreatures(d, recruiter, creID, count, i);
-	}
-}
-
-bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, std::optional<float> movementCostLimit)
-{
-	int3 op = obj->visitablePos();
-	auto paths = ah->getPathsToTile(h, op);
-
-	for(const auto & path : paths)
-	{
-		if(movementCostLimit && movementCostLimit.value() < path.movementCost())
-			return false;
-
-		if(isGoodForVisit(obj, h, path))
-			return true;
-	}
-
-	return false;
-}
-
-bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const
-{
-	const int3 pos = obj->visitablePos();
-	const int3 targetPos = path.firstTileToGet();
-	if (!targetPos.isValid())
-		return false;
-	if (!isTileNotReserved(h.get(), targetPos))
-		return false;
-	if (obj->wasVisited(playerID))
-		return false;
-	if (cb->getPlayerRelations(playerID, obj->tempOwner) != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
-		return false; // Otherwise we flag or get weekly resources / creatures
-	if (!isSafeToVisit(h, pos))
-		return false;
-	if (!shouldVisit(h, obj))
-		return false;
-	if (vstd::contains(alreadyVisited, obj))
-		return false;
-	if (vstd::contains(reservedObjs, obj))
-		return false;
-
-	// TODO: looks extra if we already have AIPath
-	//if (!isAccessibleForHero(targetPos, h))
-	//	return false;
-
-	const CGObjectInstance * topObj = cb->getVisitableObjs(obj->visitablePos()).back(); //it may be hero visiting this obj
-																						//we don't try visiting object on which allied or owned hero stands
-																						// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
-	return !(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES); //all of the following is met
-}
-
-bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t) const
-{
-	if(t.isValid())
-	{
-		auto obj = cb->getTopObj(t);
-		if(obj && vstd::contains(ai->reservedObjs, obj)
-			&& vstd::contains(reservedHeroesMap, h)
-			&& !vstd::contains(reservedHeroesMap.at(h), obj))
-			return false; //do not capture object reserved by another hero
-		else
-			return true;
-	}
-	else
-	{
-		return false;
-	}
-}
-
-bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
-{
-	//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
-	if(!t)
-		t = findTownWithTavern();
-	if(!t)
-		return false;
-	if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
-		return false;
-	if(cb->getHeroesInfo().size() >= cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
-		return false;
-	if(!cb->getAvailableHeroes(t).size())
-		return false;
-
-	return true;
-}
-
-void VCAI::wander(HeroPtr h)
-{
-	auto visitTownIfAny = [this](HeroPtr h) -> bool
-	{
-		if (h->getVisitedTown())
-		{
-			townVisitsThisWeek[h].insert(h->getVisitedTown());
-			buildArmyIn(h->getVisitedTown());
-			return true;
-		}
-		return false;
-	};
-
-	//unclaim objects that are now dangerous for us
-	auto reservedObjsSetCopy = reservedHeroesMap[h];
-	for(auto obj : reservedObjsSetCopy)
-	{
-		if(!isSafeToVisit(h, obj->visitablePos()))
-			unreserveObject(h, obj);
-	}
-
-	TimeCheck tc("looking for wander destination");
-
-	for(int k = 0; k < 10 && h->movementPointsRemaining(); k++)
-	{
-		validateVisitableObjs();
-		ah->updatePaths(getMyHeroes());
-
-		std::vector<ObjectIdRef> dests;
-
-		//also visit our reserved objects - but they are not prioritized to avoid running back and forth
-		vstd::copy_if(reservedHeroesMap[h], std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
-		{
-			return ah->isTileAccessible(h, obj->visitablePos());
-		});
-
-		int pass = 0;
-		std::vector<std::optional<float>> distanceLimits = {1.0, 2.0, std::nullopt};
-
-		while(!dests.size() && pass < distanceLimits.size())
-		{
-			auto & distanceLimit = distanceLimits[pass];
-
-			logAi->debug("Looking for wander destination pass=%i, cost limit=%f", pass, distanceLimit.value_or(-1.0));
-
-			vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
-			{
-				return isGoodForVisit(obj, h, distanceLimit);
-			});
-
-			pass++;
-		}
-
-		if(!dests.size())
-		{
-			logAi->debug("Looking for town destination");
-
-			if(cb->getVisitableObjs(h->visitablePos()).size() > 1)
-				moveHeroToTile(h->visitablePos(), h); //just in case we're standing on blocked subterranean gate
-
-			auto compareReinforcements = [&](const CGTownInstance * lhs, const CGTownInstance * rhs) -> bool
-			{
-				const CGHeroInstance * hptr = h.get();
-				auto r1 = ah->howManyReinforcementsCanGet(hptr, lhs);
-				auto r2 = ah->howManyReinforcementsCanGet(hptr, rhs);
-				if (r1 != r2)
-					return r1 < r2;
-				else
-					return ah->howManyReinforcementsCanBuy(hptr, lhs) < ah->howManyReinforcementsCanBuy(hptr, rhs);
-			};
-
-			std::vector<const CGTownInstance *> townsReachable;
-			std::vector<const CGTownInstance *> townsNotReachable;
-			for(const CGTownInstance * t : cb->getTownsInfo())
-			{
-				if(!t->getVisitingHero() && !vstd::contains(townVisitsThisWeek[h], t))
-				{
-					if(isAccessibleForHero(t->visitablePos(), h))
-						townsReachable.push_back(t);
-					else
-						townsNotReachable.push_back(t);
-				}
-			}
-			if(townsReachable.size()) //travel to town with largest garrison, or empty - better than nothing
-			{
-				dests.push_back(*boost::max_element(townsReachable, compareReinforcements));
-			}
-			else if(townsNotReachable.size())
-			{
-				//TODO pick the truly best
-				const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements);
-				logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->getNameTranslated(), t->getNameTranslated(), t->visitablePos().toString());
-				int3 posBefore = h->visitablePos();
-				striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop
-				//if out hero is stuck, we may need to request another hero to clear the way we see
-
-				if(posBefore == h->visitablePos() && h == primaryHero()) //hero can't move
-				{
-					if(canRecruitAnyHero(t))
-						recruitHero(t);
-				}
-				break;
-			}
-			else if(cb->getResourceAmount(EGameResID::GOLD) >= GameConstants::HERO_GOLD_COST)
-			{
-				std::vector<const CGTownInstance *> towns = cb->getTownsInfo();
-				vstd::erase_if(towns, [&](const CGTownInstance * t) -> bool
-				{
-					for(const CGHeroInstance * h : cb->getHeroesInfo())
-					{
-						if(!t->getArmyStrength() || ah->howManyReinforcementsCanGet(h, t))
-							return true;
-					}
-					return false;
-				});
-				if (towns.size())
-				{
-					recruitHero(*boost::max_element(towns, compareArmyStrength));
-				}
-				break;
-			}
-			else
-			{
-				logAi->debug("Nowhere more to go...");
-				break;
-			}
-		}
-		//end of objs empty
-
-		if(dests.size()) //performance improvement
-		{
-			Goals::TGoalVec targetObjectGoals;
-			for(auto destination : dests)
-			{
-				vstd::concatenate(targetObjectGoals, ah->howToVisitObj(h, destination, false));
-			}
-
-			if(targetObjectGoals.size())
-			{
-				auto bestObjectGoal = fh->chooseSolution(targetObjectGoals);
-
-				//wander should not cause heroes to be reserved - they are always considered free
-				if(bestObjectGoal->goalType == Goals::VISIT_OBJ)
-				{
-					auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid));
-					if(chosenObject != nullptr)
-						logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->anchorPos().toString());
-				}
-				else
-					logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name());
-
-				try
-				{
-					decomposeGoal(bestObjectGoal)->accept(this);
-				}
-				catch(const goalFulfilledException & e)
-				{
-					if(e.goal->goalType == Goals::EGoals::VISIT_TILE || e.goal->goalType == Goals::EGoals::VISIT_OBJ)
-						continue;
-
-					throw;
-				}
-			}
-			else
-			{
-				logAi->debug("Nowhere more to go...");
-				break;
-			}
-
-			visitTownIfAny(h);
-		}
-	}
-
-	visitTownIfAny(h); //in case hero is just sitting in town
-}
-
-void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
-{
-	if(goal->invalid())
-	{
-		vstd::erase_if_present(lockedHeroes, h);
-	}
-	else
-	{
-		lockedHeroes[h] = goal;
-		goal->setisElementar(false); //Force always evaluate goals before realizing
-	}
-}
-void VCAI::evaluateGoal(HeroPtr h)
-{
-	if(vstd::contains(lockedHeroes, h))
-		fh->setPriority(lockedHeroes[h]);
-}
-
-void VCAI::completeGoal(Goals::TSubgoal goal)
-{
-	if (goal->goalType == Goals::WIN) //we can never complete this goal - unless we already won
-		return;
-
-	logAi->debug("Completing goal: %s", goal->name());
-
-	//notify Managers
-	ah->notifyGoalCompleted(goal);
-	//notify mainLoop()
-	goalsToRemove.push_back(goal); //will be removed from mainLoop() goals
-	for (auto basicGoal : basicGoals) //we could luckily fulfill any of our goals
-	{
-		if (basicGoal->fulfillsMe(goal))
-			goalsToRemove.push_back(basicGoal);
-	}
-
-	//unreserve heroes
-	if(const CGHeroInstance * h = goal->hero.get(true))
-	{
-		auto it = lockedHeroes.find(h);
-		if(it != lockedHeroes.end())
-		{
-			if(it->second == goal || it->second->fulfillsMe(goal)) //FIXME this is overspecified, fulfillsMe should be complete
-			{
-				logAi->debug(goal->completeMessage());
-				lockedHeroes.erase(it); //goal fulfilled, free hero
-			}
-		}
-	}
-	else //complete goal for all heroes maybe?
-	{
-		vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p)
-		{
-			if(p.second == goal || p.second->fulfillsMe(goal)) //we could have fulfilled goals of other heroes by chance
-			{
-				logAi->debug(p.second->completeMessage());
-				return true;
-			}
-			return false;
-		});
-	}
-
-}
-
-void VCAI::battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed)
-{
-	NET_EVENT_HANDLER;
-	assert(!playerID.isValidPlayer() || status.getBattle() == UPCOMING_BATTLE);
-	status.setBattle(ONGOING_BATTLE);
-	const CGObjectInstance * presumedEnemy = vstd::backOrNull(cb->getVisitableObjs(tile)); //may be nullptr in some very are cases -> eg. visited monolith and fighting with an enemy at the FoW covered exit
-	battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->getNameTranslated() : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
-	CAdventureAI::battleStart(battleID, army1, army2, tile, hero1, hero2, side, replayAllowed);
-}
-
-void VCAI::battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID)
-{
-	NET_EVENT_HANDLER;
-	assert(status.getBattle() == ONGOING_BATTLE);
-	status.setBattle(ENDING_BATTLE);
-	bool won = br->winner == myCb->getBattle(battleID)->battleGetMySide();
-	logAi->debug("Player %d (%s): I %s the %s!", playerID, playerID.toString(), (won ? "won" : "lost"), battlename);
-	battlename.clear();
-
-	CAdventureAI::battleEnd(battleID, br, queryID);
-}
-
-void VCAI::waitTillFree()
-{
-	auto unlock = vstd::makeUnlockSharedGuard(CGameState::mutex);
-	status.waitTillFree();
-}
-
-void VCAI::markObjectVisited(const CGObjectInstance * obj)
-{
-	if(!obj)
-		return;
-
-	if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj)) //we may want to visit it with another hero
-	{
-		if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_HERO) //we may want to visit it with another hero
-			return;
-
-		if (rewardable->configuration.getVisitMode() == Rewardable::VISIT_BONUS) //or another time
-			return;
-	}
-
-	if(obj->ID == Obj::MONSTER)
-		return;
-
-	alreadyVisited.insert(obj);
-}
-
-void VCAI::reserveObject(HeroPtr h, const CGObjectInstance * obj)
-{
-	reservedObjs.insert(obj);
-	reservedHeroesMap[h].insert(obj);
-	logAi->debug("reserved object id=%d; address=%p; name=%s", obj->id, obj, obj->getObjectName());
-}
-
-void VCAI::unreserveObject(HeroPtr h, const CGObjectInstance * obj)
-{
-	vstd::erase_if_present(reservedObjs, obj); //unreserve objects
-	vstd::erase_if_present(reservedHeroesMap[h], obj);
-}
-
-void VCAI::markHeroUnableToExplore(HeroPtr h)
-{
-	heroesUnableToExplore.insert(h);
-}
-void VCAI::markHeroAbleToExplore(HeroPtr h)
-{
-	vstd::erase_if_present(heroesUnableToExplore, h);
-}
-bool VCAI::isAbleToExplore(HeroPtr h)
-{
-	return !vstd::contains(heroesUnableToExplore, h);
-}
-void VCAI::clearPathsInfo()
-{
-	heroesUnableToExplore.clear();
-}
-
-void VCAI::validateVisitableObjs()
-{
-	std::string errorMsg;
-	auto shouldBeErased = [&](const CGObjectInstance * obj) -> bool
-	{
-		if(obj)
-			return !cb->getObj(obj->id, false); // no verbose output needed as we check object visibility
-		else
-			return true;
-	};
-
-	//errorMsg is captured by ref so lambda will take the new text
-	errorMsg = " shouldn't be on the visitable objects list!";
-	vstd::erase_if(visitableObjs, shouldBeErased);
-
-	//FIXME: how comes our own heroes become inaccessible?
-	vstd::erase_if(reservedHeroesMap, [](std::pair<HeroPtr, std::set<const CGObjectInstance *>> hp) -> bool
-	{
-		return !hp.first.get(true);
-	});
-	for(auto & p : reservedHeroesMap)
-	{
-		errorMsg = " shouldn't be on list for hero " + p.first->getNameTranslated() + "!";
-		vstd::erase_if(p.second, shouldBeErased);
-	}
-
-	errorMsg = " shouldn't be on the reserved objs list!";
-	vstd::erase_if(reservedObjs, shouldBeErased);
-
-	//TODO overkill, hidden object should not be removed. However, we can't know if hidden object is erased from game.
-	errorMsg = " shouldn't be on the already visited objs list!";
-	vstd::erase_if(alreadyVisited, shouldBeErased);
-}
-
-void VCAI::retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned) const
-{
-	foreach_tile_pos([&](const int3 & pos)
-	{
-		for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
-		{
-			if(includeOwned || obj->tempOwner != playerID)
-				out.push_back(obj);
-		}
-	});
-}
-
-void VCAI::retrieveVisitableObjs()
-{
-	foreach_tile_pos([&](const int3 & pos)
-	{
-		for(const CGObjectInstance * obj : myCb->getVisitableObjs(pos, false))
-		{
-			if(obj->tempOwner != playerID)
-				addVisitableObj(obj);
-		}
-	});
-}
-
-std::vector<const CGObjectInstance *> VCAI::getFlaggedObjects() const
-{
-	std::vector<const CGObjectInstance *> ret;
-	for(const CGObjectInstance * obj : visitableObjs)
-	{
-		if(obj->tempOwner == playerID)
-			ret.push_back(obj);
-	}
-	return ret;
-}
-
-void VCAI::addVisitableObj(const CGObjectInstance * obj)
-{
-	if(obj->ID == Obj::EVENT)
-		return;
-
-	visitableObjs.insert(obj);
-
-	// All teleport objects seen automatically assigned to appropriate channels
-	auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
-	if(teleportObj)
-		CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
-}
-
-const CGObjectInstance * VCAI::lookForArt(ArtifactID aid) const
-{
-	for(const CGObjectInstance * obj : ai->visitableObjs)
-	{
-		if(obj->ID == Obj::ARTIFACT && dynamic_cast<const CGArtifact *>(obj)->getArtifactType()  == aid)
-			return obj;
-	}
-
-	return nullptr;
-
-	//TODO what if more than one artifact is available? return them all or some selection criteria
-}
-
-bool VCAI::isAccessible(const int3 & pos) const
-{
-	//TODO precalculate for speed
-
-	for(const CGHeroInstance * h : cb->getHeroesInfo())
-	{
-		if(isAccessibleForHero(pos, h))
-			return true;
-	}
-
-	return false;
-}
-
-HeroPtr VCAI::getHeroWithGrail() const
-{
-	for(const CGHeroInstance * h : cb->getHeroesInfo())
-	{
-		if(h->hasArt(ArtifactID::GRAIL))
-			return h;
-	}
-	return nullptr;
-}
-
-const CGObjectInstance * VCAI::getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate)
-{
-	//TODO smarter definition of unvisited
-	for(const CGObjectInstance * obj : visitableObjs)
-	{
-		if(predicate(obj) && !vstd::contains(alreadyVisited, obj))
-			return obj;
-	}
-	return nullptr;
-}
-
-bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies) const
-{
-	// Don't visit tile occupied by allied hero
-	if(!includeAllies)
-	{
-		for(auto obj : cb->getVisitableObjs(pos))
-		{
-			if(obj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES)
-			{
-				if(obj != h.get())
-					return false;
-			}
-		}
-	}
-	return getPathsInfo(h.get())->getPathInfo(pos)->reachable();
-}
-
-bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
-{
-	//TODO: consider if blockVisit objects change something in our checks: AIUtility::isBlockVisitObj()
-
-	auto afterMovementCheck = [&]() -> void
-	{
-		waitTillFree(); //movement may cause battle or blocking dialog
-		if(!h)
-		{
-			lostHero(h);
-			teleportChannelProbingList.clear();
-			if(status.channelProbing()) // if hero lost during channel probing we need to switch this mode off
-				status.setChannelProbing(false);
-			throw cannotFulfillGoalException("Hero was lost!");
-		}
-	};
-
-	logAi->debug("Moving hero %s to tile %s", h->getNameTranslated(), dst.toString());
-	int3 startHpos = h->visitablePos();
-	bool ret = false;
-	if(startHpos == dst)
-	{
-		//FIXME: this assertion fails also if AI moves onto defeated guarded object
-		assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
-		cb->moveHero(*h, h->convertFromVisitablePos(dst), false);
-		afterMovementCheck(); // TODO: is it feasible to hero get killed there if game work properly?
-		// If revisiting, teleport probing is never done, and so the entries into the list would remain unused and uncleared
-		teleportChannelProbingList.clear();
-		// not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
-		ret = true;
-	}
-	else
-	{
-		CGPath path;
-		getPathsInfo(h.get())->getPath(path, dst);
-		if(path.nodes.empty())
-		{
-			logAi->error("Hero %s cannot reach %s.", h->getNameTranslated(), dst.toString());
-			throw goalFulfilledException(sptr(Goals::VisitTile(dst).sethero(h)));
-		}
-		int i = (int)path.nodes.size() - 1;
-
-		auto getObj = [&](int3 coord, bool ignoreHero)
-		{
-			return cb->getObj(cb->getTile(coord)->topVisitableObj(ignoreHero));
-		};
-
-		auto isTeleportAction = [&](EPathNodeAction action) -> bool
-		{
-			if(action != EPathNodeAction::TELEPORT_NORMAL && action != EPathNodeAction::TELEPORT_BLOCKING_VISIT)
-			{
-				if(action != EPathNodeAction::TELEPORT_BATTLE)
-				{
-					return false;
-				}
-			}
-
-			return true;
-		};
-
-		auto getDestTeleportObj = [&](const CGObjectInstance * currentObject, const CGObjectInstance * nextObjectTop, const CGObjectInstance * nextObject) -> const CGObjectInstance *
-		{
-			if(CGTeleport::isConnected(currentObject, nextObjectTop))
-				return nextObjectTop;
-			if(nextObjectTop && nextObjectTop->ID == Obj::HERO)
-			{
-				if(CGTeleport::isConnected(currentObject, nextObject))
-					return nextObject;
-			}
-
-			return nullptr;
-		};
-
-		auto doMovement = [&](int3 dst, bool transit)
-		{
-			cb->moveHero(*h, h->convertFromVisitablePos(dst), transit);
-		};
-
-		auto doTeleportMovement = [&](ObjectInstanceID exitId, int3 exitPos)
-		{
-			destinationTeleport = exitId;
-			if(exitPos.isValid())
-				destinationTeleportPos = exitPos;
-			cb->moveHero(*h, h->pos, false);
-			destinationTeleport = ObjectInstanceID();
-			destinationTeleportPos = int3(-1);
-			afterMovementCheck();
-		};
-
-		auto doChannelProbing = [&]() -> void
-		{
-			auto currentPos = h->visitablePos();
-			auto currentExit = getObj(currentPos, true)->id;
-
-			status.setChannelProbing(true);
-			for(auto exit : teleportChannelProbingList)
-				doTeleportMovement(exit, int3(-1));
-			teleportChannelProbingList.clear();
-			status.setChannelProbing(false);
-
-			doTeleportMovement(currentExit, currentPos);
-		};
-
-		for(; i > 0; i--)
-		{
-			int3 currentCoord = path.nodes[i].coord;
-			int3 nextCoord = path.nodes[i - 1].coord;
-
-			auto currentObject = getObj(currentCoord, currentCoord == h->visitablePos());
-			auto nextObjectTop = getObj(nextCoord, false);
-			auto nextObject = getObj(nextCoord, true);
-			auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
-			if(isTeleportAction(path.nodes[i - 1].action) && destTeleportObj != nullptr)
-			{
-				//we use special login if hero standing on teleporter it's mean we need
-				doTeleportMovement(destTeleportObj->id, nextCoord);
-				if(teleportChannelProbingList.size())
-					doChannelProbing();
-				markObjectVisited(destTeleportObj); //FIXME: Monoliths are not correctly visited
-
-				continue;
-			}
-
-			//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
-			if(path.nodes[i - 1].turns)
-			{
-				//blockedHeroes.insert(h); //to avoid attempts of moving heroes with very little MPs
-				break;
-			}
-
-			int3 endpos = path.nodes[i - 1].coord;
-			if(endpos == h->visitablePos())
-				continue;
-
-			bool isConnected = false;
-			bool isNextObjectTeleport = false;
-			// Check there is node after next one; otherwise transit is pointless
-			if(i - 2 >= 0)
-			{
-				isConnected = CGTeleport::isConnected(nextObjectTop, getObj(path.nodes[i - 2].coord, false));
-				isNextObjectTeleport = CGTeleport::isTeleport(nextObjectTop);
-			}
-			if(isConnected || isNextObjectTeleport)
-			{
-				// Hero should be able to go through object if it's allow transit
-				doMovement(endpos, true);
-			}
-			else if(path.nodes[i - 1].layer == EPathfindingLayer::AIR)
-			{
-				doMovement(endpos, true);
-			}
-			else
-			{
-				doMovement(endpos, false);
-			}
-
-			afterMovementCheck();
-
-			if(teleportChannelProbingList.size())
-				doChannelProbing();
-		}
-
-		if(path.nodes[0].action == EPathNodeAction::BLOCKING_VISIT)
-		{
-			ret = h && i == 0; // when we take resource we do not reach its position. We even might not move
-		}
-	}
-	if(h)
-	{
-		if(auto visitedObject = vstd::frontOrNull(cb->getVisitableObjs(h->visitablePos()))) //we stand on something interesting
-		{
-			if(visitedObject != *h)
-				performObjectInteraction(visitedObject, h);
-		}
-	}
-	if(h) //we could have lost hero after last move
-	{
-		completeGoal(sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway
-		completeGoal(sptr(Goals::ClearWayTo(dst).sethero(h)));
-
-		ret = ret || (dst == h->visitablePos());
-
-		if(!ret) //reserve object we are heading towards
-		{
-			auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst));
-			if(obj && obj != *h)
-				reserveObject(h, obj);
-		}
-
-		if(startHpos == h->visitablePos() && !ret) //we didn't move and didn't reach the target
-		{
-			vstd::erase_if_present(lockedHeroes, h); //hero seemingly is confused or has only 95mp which is not enough to move
-			invalidPathHeroes.insert(h);
-			throw cannotFulfillGoalException("Invalid path found!");
-		}
-		evaluateGoal(h); //new hero position means new game situation
-		logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->getNameTranslated(), startHpos.toString(), h->visitablePos().toString(), ret);
-	}
-	return ret;
-}
-
-void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
-{
-	auto name = t->getTown()->buildings.at(building)->getNameTranslated();
-	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->anchorPos().toString());
-	cb->buildBuilding(t, building); //just do this;
-}
-
-void VCAI::tryRealize(Goals::Explore & g)
-{
-	throw cannotFulfillGoalException("EXPLORE is not an elementar goal!");
-}
-
-void VCAI::tryRealize(Goals::RecruitHero & g)
-{
-	if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST)
-		throw cannotFulfillGoalException("Not enough gold to recruit hero!");
-
-	if(const CGTownInstance * t = findTownWithTavern())
-	{
-		recruitHero(t, true);
-		//TODO try to free way to blocked town
-		//TODO: adventure map tavern or prison?
-	}
-	else
-	{
-		throw cannotFulfillGoalException("No town to recruit hero!");
-	}
-}
-
-void VCAI::tryRealize(Goals::VisitTile & g)
-{
-	if(!g.hero->movementPointsRemaining())
-		throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
-	if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
-	{
-		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->getNameTranslated(), g.tile.toString());
-		throw goalFulfilledException(sptr(g));
-	}
-	if(ai->moveHeroToTile(g.tile, g.hero.get()))
-	{
-		throw goalFulfilledException(sptr(g));
-	}
-}
-
-void VCAI::tryRealize(Goals::VisitObj & g)
-{
-	auto position = g.tile;
-	if(!g.hero->movementPointsRemaining())
-		throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!");
-	if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
-	{
-		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->getNameTranslated(), g.tile.toString());
-		throw goalFulfilledException(sptr(g));
-	}
-	if(ai->moveHeroToTile(position, g.hero.get()))
-	{
-		throw goalFulfilledException(sptr(g));
-	}
-}
-
-void VCAI::tryRealize(Goals::VisitHero & g)
-{
-	if(!g.hero->movementPointsRemaining())
-		throw cannotFulfillGoalException("Cannot visit target hero: hero is out of MPs!");
-
-	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid));
-	if(obj)
-	{
-		if(ai->moveHeroToTile(obj->visitablePos(), g.hero.get()))
-		{
-			throw goalFulfilledException(sptr(g));
-		}
-	}
-	else
-	{
-		throw cannotFulfillGoalException("Cannot visit hero: object not found!");
-	}
-}
-
-void VCAI::tryRealize(Goals::BuildThis & g)
-{
-	auto b = BuildingID(g.bid);
-	auto t = g.town;
-
-	if (t)
-	{
-		if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
-		{
-			logAi->debug("Player %d will build %s in town of %s at %s",
-				playerID, t->getTown()->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->anchorPos().toString());
-			cb->buildBuilding(t, b);
-			throw goalFulfilledException(sptr(g));
-		}
-	}
-	throw cannotFulfillGoalException("Cannot build a given structure!");
-}
-
-void VCAI::tryRealize(Goals::DigAtTile & g)
-{
-	assert(g.hero->visitablePos() == g.tile); //surely we want to crash here?
-	if(g.hero->diggingStatus() == EDiggingStatus::CAN_DIG)
-	{
-		cb->dig(g.hero.get());
-		completeGoal(sptr(g)); // finished digging
-	}
-	else
-	{
-		ai->lockedHeroes[g.hero] = sptr(g); //hero who tries to dig shouldn't do anything else
-		throw cannotFulfillGoalException("A hero can't dig!\n");
-	}
-}
-
-void VCAI::tryRealize(Goals::Trade & g) //trade
-{
-	if(ah->freeResources()[g.resID] >= g.value) //goal is already fulfilled. Why we need this check, anyway?
-		throw goalFulfilledException(sptr(g));
-
-	int acquiredResources = 0;
-	if(const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid), false))
-	{
-		if(const auto * m = dynamic_cast<const IMarket*>(obj))
-		{
-			auto freeRes = ah->freeResources(); //trade only resources which are not reserved
-			for(auto it = ResourceSet::nziterator(freeRes); it.valid(); it++)
-			{
-				auto res = it->resType;
-				if(res.getNum() == g.resID) //sell any other resource
-					continue;
-
-				int toGive;
-				int toGet;
-				m->getOffer(res.getNum(), g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
-				toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down
-				//TODO trade only as much as needed
-				if (toGive) //don't try to sell 0 resources
-				{
-					cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
-					acquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
-					logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, acquiredResources, g.resID, obj->getObjectName());
-				}
-				if (ah->freeResources()[g.resID] >= g.value)
-					throw goalFulfilledException(sptr(g)); //we traded all we needed
-			}
-
-			throw cannotFulfillGoalException("I cannot get needed resources by trade!");
-		}
-		else
-		{
-			throw cannotFulfillGoalException("I don't know how to use this object to raise resources!");
-		}
-	}
-	else
-	{
-		throw cannotFulfillGoalException("No object that could be used to raise resources!");
-	}
-}
-
-void VCAI::tryRealize(Goals::BuyArmy & g)
-{
-	auto t = g.town;
-
-	ui64 valueBought = 0;
-	//buy the stacks with largest AI value
-
-	makePossibleUpgrades(t);
-
-	while (valueBought < g.value)
-	{
-		auto res = ah->allResources();
-		std::vector<creInfo> creaturesInDwellings;
-
-		for (int i = 0; i < t->creatures.size(); i++)
-		{
-			auto ci = infoFromDC(t->creatures[i]);
-
-			if(!ci.count
-				|| ci.creID == CreatureID::NONE
-				|| (g.objid != -1 && ci.creID.getNum() != g.objid)
-				|| t->getUpperArmy()->getSlotFor(ci.creID) == SlotID())
-				continue;
-
-			vstd::amin(ci.count, res / ci.cre->getFullRecruitCost()); //max count we can afford
-
-			if(!ci.count)
-				continue;
-
-			ci.level = i; //this is important for Dungeon Summoning Portal
-			creaturesInDwellings.push_back(ci);
-		}
-
-		if (creaturesInDwellings.empty())
-			throw cannotFulfillGoalException("Can't buy any more creatures!");
-
-		creInfo ci =
-			*boost::max_element(creaturesInDwellings, [](const creInfo & lhs, const creInfo & rhs)
-		{
-			//max value of creatures we can buy with our res
-			int value1 = lhs.cre->getAIValue() * lhs.count;
-			int value2 = rhs.cre->getAIValue() * rhs.count;
-
-			return value1 < value2;
-		});
-
-
-		cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
-		valueBought += ci.count * ci.cre->getAIValue();
-	}
-
-	throw goalFulfilledException(sptr(g)); //we bought as many creatures as we wanted
-}
-
-void VCAI::tryRealize(Goals::Invalid & g)
-{
-	throw cannotFulfillGoalException("I don't know how to fulfill this!");
-}
-
-void VCAI::tryRealize(Goals::AbstractGoal & g)
-{
-	logAi->debug("Attempting realizing goal with code %s", g.name());
-	throw cannotFulfillGoalException("Unknown type of goal !");
-}
-
-const CGTownInstance * VCAI::findTownWithTavern() const
-{
-	for(const CGTownInstance * t : cb->getTownsInfo())
-		if(t->hasBuilt(BuildingID::TAVERN) && !t->getVisitingHero())
-			return t;
-
-	return nullptr;
-}
-
-Goals::TSubgoal VCAI::getGoal(HeroPtr h) const
-{
-	auto it = lockedHeroes.find(h);
-	if(it != lockedHeroes.end())
-		return it->second;
-	else
-		return sptr(Goals::Invalid());
-}
-
-
-std::vector<HeroPtr> VCAI::getUnblockedHeroes() const
-{
-	std::vector<HeroPtr> ret;
-	for(auto h : cb->getHeroesInfo())
-	{
-		//&& !vstd::contains(lockedHeroes, h)
-		//at this point we assume heroes exhausted their locked goals
-		if(canAct(h))
-			ret.push_back(h);
-	}
-	return ret;
-}
-
-bool VCAI::canAct(HeroPtr h) const
-{
-	auto mission = lockedHeroes.find(h);
-	if(mission != lockedHeroes.end())
-	{
-		//FIXME: I'm afraid there can be other conditions when heroes can act but not move :?
-		if(mission->second->goalType == Goals::DIG_AT_TILE && !mission->second->isElementar)
-			return false;
-	}
-
-	return h->movementPointsRemaining();
-}
-
-HeroPtr VCAI::primaryHero() const
-{
-	auto hs = cb->getHeroesInfo();
-	if (hs.empty())
-		return nullptr;
-	else
-		return *boost::max_element(hs, compareHeroStrength);
-}
-
-void VCAI::endTurn()
-{
-	logAi->info("Player %d (%s) ends turn", playerID, playerID.toString());
-	if(!status.haveTurn())
-	{
-		logAi->error("Not having turn at the end of turn???");
-	}
-	logAi->debug("Resources at the end of turn: %s", cb->getResourceAmount().toString());
-	do
-	{
-		cb->endTurn();
-	}
-	while(status.haveTurn()); //for some reasons, our request may fail -> stop requesting end of turn only after we've received a confirmation that it's over
-
-	logGlobal->info("Player %d (%s) ended turn", playerID, playerID.toString());
-}
-
-void VCAI::striveToGoal(Goals::TSubgoal basicGoal)
-{
-	//TODO: this function is deprecated and should be dropped altogether
-
-	auto goalToDecompose = basicGoal;
-	Goals::TSubgoal elementarGoal = sptr(Goals::Invalid());
-	int maxAbstractGoals = 10;
-	while (!elementarGoal->isElementar && maxAbstractGoals)
-	{
-		try
-		{
-			elementarGoal = decomposeGoal(goalToDecompose);
-		}
-		catch (goalFulfilledException & e)
-		{
-			//it is impossible to continue some goals (like exploration, for example)
-			completeGoal(e.goal); //put in goalsToRemove
-			logAi->debug("Goal %s decomposition failed: goal was completed as much as possible", e.goal->name());
-			return;
-		}
-		catch (std::exception & e)
-		{
-			goalsToRemove.push_back(basicGoal);
-			logAi->debug("Goal %s decomposition failed: %s", basicGoal->name(), e.what());
-			return;
-		}
-		if (elementarGoal->isAbstract) //we can decompose it further
-		{
-			goalsToAdd.push_back(elementarGoal);
-			//decompose further now - this is necesssary if we can't add over 10 goals in the pool
-			goalToDecompose = elementarGoal;
-			//there is a risk of infinite abstract goal loop, though it indicates failed logic
-			maxAbstractGoals--;
-		}
-		else if (elementarGoal->isElementar) //should be
-		{
-			logAi->debug("Found elementar goal %s", elementarGoal->name());
-			ultimateGoalsFromBasic[elementarGoal].push_back(goalToDecompose); //TODO: how about indirect basicGoal?
-			break;
-		}
-		else //should never be here
-			throw cannotFulfillGoalException("Goal %s is neither abstract nor elementar!" + basicGoal->name());
-	}
-
-	//realize best goal
-	if (!elementarGoal->invalid())
-	{
-		logAi->debug("Trying to realize %s (value %2.3f)", elementarGoal->name(), elementarGoal->priority);
-
-		try
-		{
-			makingTurnInterrupption.interruptionPoint();
-			elementarGoal->accept(this); //visitor pattern
-			makingTurnInterrupption.interruptionPoint();
-		}
-		catch (const TerminationRequestedException &)
-		{
-			logAi->debug("Player %d: Making turn thread received an interruption!", playerID);
-			throw; //rethrow, we want to truly end this thread
-		}
-		catch (const goalFulfilledException & e)
-		{
-			//the sub-goal was completed successfully
-			completeGoal(e.goal);
-			//local goal was also completed
-			completeGoal(elementarGoal);
-		}
-		catch (const std::exception & e)
-		{
-			logAi->debug("Failed to realize subgoal of type %s, I will stop.", elementarGoal->name());
-			logAi->debug("The error message was: %s", e.what());
-
-			//erase base goal if we failed to execute decomposed goal
-			for (auto basicGoalToRemove : ultimateGoalsFromBasic[elementarGoal])
-				goalsToRemove.push_back(basicGoalToRemove);
-		}
-	}
-}
-
-Goals::TSubgoal VCAI::decomposeGoal(Goals::TSubgoal ultimateGoal)
-{
-	if(ultimateGoal->isElementar)
-	{
-		logAi->warn("Trying to decompose elementar goal %s", ultimateGoal->name());
-
-		return ultimateGoal;
-	}
-
-	const int searchDepth = 30;
-
-	Goals::TSubgoal goal = ultimateGoal;
-	logAi->debug("Decomposing goal %s", ultimateGoal->name());
-	int maxGoals = searchDepth; //preventing deadlock for mutually dependent goals
-	while (maxGoals)
-	{
-		makingTurnInterrupption.interruptionPoint();
-
-		goal = goal->whatToDoToAchieve(); //may throw if decomposition fails
-		--maxGoals;
-		if (goal == ultimateGoal) //compare objects by value
-			if (goal->isElementar == ultimateGoal->isElementar)
-				throw cannotFulfillGoalException((boost::format("Goal dependency loop detected for %s!")
-												% ultimateGoal->name()).str());
-		if (goal->isAbstract || goal->isElementar)
-			return goal;
-		else
-			logAi->debug("Considering: %s", goal->name());
-	}
-
-	throw cannotFulfillGoalException("Too many subgoals, don't know what to do");
-}
-
-void VCAI::performTypicalActions()
-{
-	for(auto h : getUnblockedHeroes())
-	{
-		if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
-			continue;
-
-		logAi->debug("Hero %s started wandering, MP=%d", h->getNameTranslated(), h->movementPointsRemaining());
-		makePossibleUpgrades(*h);
-		pickBestArtifacts(*h);
-		try
-		{
-			wander(h);
-		}
-		catch(std::exception & e)
-		{
-			logAi->debug("Cannot use this hero anymore, received exception: %s", e.what());
-			continue;
-		}
-	}
-}
-
-void VCAI::buildArmyIn(const CGTownInstance * t)
-{
-	makePossibleUpgrades(t->getVisitingHero());
-	makePossibleUpgrades(t);
-	recruitCreatures(t, t->getUpperArmy());
-	moveCreaturesToHero(t);
-}
-
-void VCAI::checkHeroArmy(HeroPtr h)
-{
-	auto it = lockedHeroes.find(h);
-	if(it != lockedHeroes.end())
-	{
-		if(it->second->goalType == Goals::GATHER_ARMY && it->second->value <= h->getArmyStrength())
-			completeGoal(sptr(Goals::GatherArmy(it->second->value).sethero(h)));
-	}
-}
-
-void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
-{
-	logAi->debug("Trying to recruit a hero in %s at %s", t->getNameTranslated(), t->visitablePos().toString());
-
-	auto heroes = cb->getAvailableHeroes(t);
-	if(heroes.size())
-	{
-		auto hero = heroes[0];
-		if(heroes.size() >= 2) //makes sense to recruit two heroes with starting amries in first week
-		{
-			if(heroes[1]->getTotalStrength() > hero->getTotalStrength())
-				hero = heroes[1];
-		}
-		cb->recruitHero(t, hero);
-		throw goalFulfilledException(sptr(Goals::RecruitHero().settown(t)));
-	}
-	else if(throwing)
-	{
-		throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
-	}
-}
-
-void VCAI::finish()
-{
-	makingTurnInterrupption.interruptThread();
-
-	if (asyncTasks)
-	{
-		asyncTasks->wait();
-		asyncTasks.reset();
-	}
-}
-
-void VCAI::executeActionAsync(const std::string & description, const std::function<void()> & whatToDo)
-{
-	if (!asyncTasks)
-		throw std::runtime_error("Attempt to execute task on shut down AI state!");
-
-	asyncTasks->run([this, description, whatToDo]()
-	{
-		ScopedThreadName guard("VCAI::" + description);
-		SET_GLOBAL_STATE(this);
-		std::shared_lock gsLock(CGameState::mutex);
-		whatToDo();
-	});
-}
-
-void VCAI::lostHero(HeroPtr h)
-{
-	logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name);
-
-	vstd::erase_if_present(lockedHeroes, h);
-	for(auto obj : reservedHeroesMap[h])
-	{
-		vstd::erase_if_present(reservedObjs, obj); //unreserve all objects for that hero
-	}
-	vstd::erase_if_present(reservedHeroesMap, h);
-	vstd::erase_if_present(visitedHeroes, h);
-	for (auto heroVec : visitedHeroes)
-	{
-		vstd::erase_if_present(heroVec.second, h);
-	}
-
-	//remove goals with removed hero assigned from main loop
-	vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair<Goals::TSubgoal, Goals::TGoalVec> & x) -> bool
-	{
-		if(x.first->hero == h)
-			return true;
-		else
-			return false;
-	});
-
-	auto removedHeroGoalPredicate = [&](const Goals::TSubgoal & x) ->bool
-	{
-		if(x->hero == h)
-			return true;
-		else
-			return false;
-	};
-
-	vstd::erase_if(basicGoals, removedHeroGoalPredicate);
-	vstd::erase_if(goalsToAdd, removedHeroGoalPredicate);
-	vstd::erase_if(goalsToRemove, removedHeroGoalPredicate);
-
-	for(auto goal : ultimateGoalsFromBasic)
-		vstd::erase_if(goal.second, removedHeroGoalPredicate);
-}
-
-void VCAI::answerQuery(QueryID queryID, int selection)
-{
-	logAi->debug("I'll answer the query %d giving the choice %d", queryID, selection);
-	if(queryID != QueryID(-1))
-	{
-		cb->selectionMade(selection, queryID);
-	}
-	else
-	{
-		logAi->debug("Since the query ID is %d, the answer won't be sent. This is not a real query!", queryID);
-		//do nothing
-	}
-}
-
-void VCAI::requestSent(const CPackForServer * pack, int requestID)
-{
-	//BNLOG("I have sent request of type %s", typeid(*pack).name());
-	if(auto reply = dynamic_cast<const QueryReply *>(pack))
-	{
-		status.attemptedAnsweringQuery(reply->qid, requestID);
-	}
-}
-
-std::string VCAI::getBattleAIName() const
-{
-	if(settings["server"]["enemyAI"].getType() == JsonNode::JsonType::DATA_STRING)
-		return settings["server"]["enemyAI"].String();
-	else
-		return "BattleAI";
-}
-
-void VCAI::validateObject(const CGObjectInstance * obj)
-{
-	validateObject(obj->id);
-}
-
-void VCAI::validateObject(ObjectIdRef obj)
-{
-	auto matchesId = [&](const CGObjectInstance * hlpObj) -> bool
-	{
-		return hlpObj->id == obj.id;
-	};
-	if(!obj)
-	{
-		vstd::erase_if(visitableObjs, matchesId);
-
-		for(auto & p : reservedHeroesMap)
-			vstd::erase_if(p.second, matchesId);
-
-		vstd::erase_if(reservedObjs, matchesId);
-	}
-}
-
-AIStatus::AIStatus()
-{
-	battle = NO_BATTLE;
-	havingTurn = false;
-	ongoingHeroMovement = false;
-	ongoingChannelProbing = false;
-}
-
-AIStatus::~AIStatus()
-{
-
-}
-
-void AIStatus::setBattle(BattleState BS)
-{
-	std::unique_lock<std::mutex> lock(mx);
-	LOG_TRACE_PARAMS(logAi, "battle state=%d", (int)BS);
-	battle = BS;
-	cv.notify_all();
-}
-
-BattleState AIStatus::getBattle()
-{
-	std::unique_lock<std::mutex> lock(mx);
-	return battle;
-}
-
-void AIStatus::addQuery(QueryID ID, std::string description)
-{
-	if(ID == QueryID(-1))
-	{
-		logAi->debug("The \"query\" has an id %d, it'll be ignored as non-query. Description: %s", ID, description);
-		return;
-	}
-
-	assert(ID.getNum() >= 0);
-	std::unique_lock<std::mutex> lock(mx);
-
-	assert(!vstd::contains(remainingQueries, ID));
-
-	remainingQueries[ID] = description;
-
-	cv.notify_all();
-	logAi->debug("Adding query %d - %s. Total queries count: %d", ID, description, remainingQueries.size());
-}
-
-void AIStatus::removeQuery(QueryID ID)
-{
-	std::unique_lock<std::mutex> lock(mx);
-	assert(vstd::contains(remainingQueries, ID));
-
-	std::string description = remainingQueries[ID];
-	remainingQueries.erase(ID);
-
-	cv.notify_all();
-	logAi->debug("Removing query %d - %s. Total queries count: %d", ID, description, remainingQueries.size());
-}
-
-int AIStatus::getQueriesCount()
-{
-	std::unique_lock<std::mutex> lock(mx);
-	return static_cast<int>(remainingQueries.size());
-}
-
-void AIStatus::startedTurn()
-{
-	std::unique_lock<std::mutex> lock(mx);
-	havingTurn = true;
-	cv.notify_all();
-}
-
-void AIStatus::madeTurn()
-{
-	std::unique_lock<std::mutex> lock(mx);
-	havingTurn = false;
-	cv.notify_all();
-}
-
-void AIStatus::waitTillFree()
-{
-	std::unique_lock<std::mutex> lock(mx);
-	while(battle != NO_BATTLE || !remainingQueries.empty() || !objectsBeingVisited.empty() || ongoingHeroMovement)
-		cv.wait_for(lock, std::chrono::milliseconds(100));
-}
-
-bool AIStatus::haveTurn()
-{
-	std::unique_lock<std::mutex> lock(mx);
-	return havingTurn;
-}
-
-void AIStatus::attemptedAnsweringQuery(QueryID queryID, int answerRequestID)
-{
-	std::unique_lock<std::mutex> lock(mx);
-	assert(vstd::contains(remainingQueries, queryID));
-	std::string description = remainingQueries[queryID];
-	logAi->debug("Attempted answering query %d - %s. Request id=%d. Waiting for results...", queryID, description, answerRequestID);
-	requestToQueryID[answerRequestID] = queryID;
-}
-
-void AIStatus::receivedAnswerConfirmation(int answerRequestID, int result)
-{
-	QueryID query;
-
-	{
-		std::unique_lock<std::mutex> lock(mx);
-
-		assert(vstd::contains(requestToQueryID, answerRequestID));
-		query = requestToQueryID[answerRequestID];
-		assert(vstd::contains(remainingQueries, query));
-		requestToQueryID.erase(answerRequestID);
-	}
-
-	if(result)
-	{
-		removeQuery(query);
-	}
-	else
-	{
-		logAi->error("Something went really wrong, failed to answer query %d : %s", query.getNum(), remainingQueries[query]);
-		//TODO safely retry
-	}
-}
-
-void AIStatus::heroVisit(const CGObjectInstance * obj, bool started)
-{
-	std::unique_lock<std::mutex> lock(mx);
-	if(started)
-	{
-		objectsBeingVisited.push_back(obj);
-	}
-	else
-	{
-		// There can be more than one object visited at the time (eg. hero visits Subterranean Gate
-		// causing visit to hero on the other side.
-		// However, we are guaranteed that start/end visit notification maintain stack order.
-		assert(!objectsBeingVisited.empty());
-		objectsBeingVisited.pop_back();
-	}
-	cv.notify_all();
-}
-
-void AIStatus::setMove(bool ongoing)
-{
-	std::unique_lock<std::mutex> lock(mx);
-	ongoingHeroMovement = ongoing;
-	cv.notify_all();
-}
-
-void AIStatus::setChannelProbing(bool ongoing)
-{
-	std::unique_lock<std::mutex> lock(mx);
-	ongoingChannelProbing = ongoing;
-	cv.notify_all();
-}
-
-bool AIStatus::channelProbing()
-{
-	return ongoingChannelProbing;
-}
-
-
-
-bool isWeeklyRevisitable(const CGObjectInstance * obj)
-{
-	//TODO: allow polling of remaining creatures in dwelling
-	if(const auto * rewardable = dynamic_cast<const CRewardableObject *>(obj))
-		return rewardable->configuration.getResetDuration() == 7;
-
-	if(dynamic_cast<const CGDwelling *>(obj))
-		return true;
-
-	switch(obj->ID)
-	{
-	case Obj::STABLES:
-	case Obj::MAGIC_WELL:
-	case Obj::HILL_FORT:
-		return true;
-	case Obj::BORDER_GATE:
-	case Obj::BORDERGUARD:
-		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
-	}
-	return false;
-}
-
-bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
-{
-	switch(obj->ID)
-	{
-	case Obj::TOWN:
-	case Obj::HERO: //never visit our heroes at random
-		return obj->tempOwner != h->tempOwner; //do not visit our towns at random
-	case Obj::BORDER_GATE:
-	{
-		for(auto q : ai->myCb->getMyQuests())
-		{
-			if(q.getObject(cb) == obj)
-			{
-				return false; // do not visit guards or gates when wandering
-			}
-		}
-		return true; //we don't have this quest yet
-	}
-	case Obj::BORDERGUARD: //open borderguard if possible
-		return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
-	case Obj::SEER_HUT:
-	case Obj::QUEST_GUARD:
-	{
-		for(auto q : ai->myCb->getMyQuests())
-		{
-			if(q.getObject(cb) == obj)
-			{
-				if(q.getQuest(cb)->checkQuest(h.h))
-					return true; //we completed the quest
-				else
-					return false; //we can't complete this quest
-			}
-		}
-		return true; //we don't have this quest yet
-	}
-	case Obj::CREATURE_GENERATOR1:
-	{
-		if(obj->tempOwner != h->tempOwner)
-			return true; //flag just in case
-		bool canRecruitCreatures = false;
-		const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
-		for(auto level : d->creatures)
-		{
-			for(auto c : level.second)
-			{
-				if(h->getSlotFor(CreatureID(c)) != SlotID())
-					canRecruitCreatures = true;
-			}
-		}
-		return canRecruitCreatures;
-	}
-	case Obj::HILL_FORT:
-	{
-		for(const auto & slot : h->Slots())
-		{
-			if(slot.second->getType()->hasUpgrades())
-				return true; //TODO: check price?
-		}
-		return false;
-	}
-	case Obj::MONOLITH_ONE_WAY_ENTRANCE:
-	case Obj::MONOLITH_ONE_WAY_EXIT:
-	case Obj::MONOLITH_TWO_WAY:
-	case Obj::WHIRLPOOL:
-		return false;
-	case Obj::SCHOOL_OF_MAGIC:
-	case Obj::SCHOOL_OF_WAR:
-	{
-		if (ai->ah->freeGold() < 1000)
-			return false;
-		break;
-	}
-	case Obj::LIBRARY_OF_ENLIGHTENMENT:
-		if(h->level < 12)
-			return false;
-		break;
-	case Obj::TREE_OF_KNOWLEDGE:
-	{
-		TResources myRes = ai->ah->freeResources();
-		if(myRes[EGameResID::GOLD] < 2000 || myRes[EGameResID::GEMS] < 10)
-			return false;
-		break;
-	}
-	case Obj::MAGIC_WELL:
-		return h->mana < h->manaLimit();
-	case Obj::PRISON:
-		return ai->myCb->getHeroesInfo().size() < cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP);
-	case Obj::TAVERN:
-	{
-		//TODO: make AI actually recruit heroes
-		//TODO: only on request
-		if(ai->myCb->getHeroesInfo().size() >= cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
-			return false;
-		else if(ai->ah->freeGold() < GameConstants::HERO_GOLD_COST)
-			return false;
-		break;
-	}
-	case Obj::BOAT:
-		return false;
-	//Boats are handled by pathfinder
-	case Obj::EYE_OF_MAGI:
-		return false; //this object is useless to visit, but could be visited indefinitely
-	}
-
-	if(obj->wasVisited(*h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
-		return false;
-
-	return true;
-}
-
-std::optional<BattleAction> VCAI::makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState)
-{
-	return std::nullopt;
-}

+ 0 - 304
AI/VCAI/VCAI.h

@@ -1,304 +0,0 @@
-/*
- * VCAI.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-#pragma once
-
-#include "AIUtility.h"
-#include "Goals/AbstractGoal.h"
-
-#include "../../lib/ConditionalWait.h"
-#include "../../lib/GameConstants.h"
-#include "../../lib/GameLibrary.h"
-#include "../../lib/CCreatureHandler.h"
-#include "../../lib/callback/CAdventureAI.h"
-#include "../../lib/mapObjects/MiscObjects.h"
-#include "../../lib/spells/CSpellHandler.h"
-#include "Pathfinding/AIPathfinder.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-struct QuestInfo;
-class PathfinderCache;
-class AsyncRunner;
-
-VCMI_LIB_NAMESPACE_END
-
-class AIhelper;
-
-class AIStatus
-{
-	std::mutex mx;
-	std::condition_variable cv;
-
-	BattleState battle;
-	std::map<QueryID, std::string> remainingQueries;
-	std::map<int, QueryID> requestToQueryID; //IDs of answer-requests sent to server => query ids (so we can match answer confirmation from server to the query)
-	std::vector<const CGObjectInstance *> objectsBeingVisited;
-	bool ongoingHeroMovement;
-	bool ongoingChannelProbing; // true if AI currently explore bidirectional teleport channel exits
-
-	bool havingTurn;
-
-public:
-	AIStatus();
-	~AIStatus();
-	void setBattle(BattleState BS);
-	void setMove(bool ongoing);
-	void setChannelProbing(bool ongoing);
-	bool channelProbing();
-	BattleState getBattle();
-	void addQuery(QueryID ID, std::string description);
-	void removeQuery(QueryID ID);
-	int getQueriesCount();
-	void startedTurn();
-	void madeTurn();
-	void waitTillFree();
-	bool haveTurn();
-	void attemptedAnsweringQuery(QueryID queryID, int answerRequestID);
-	void receivedAnswerConfirmation(int answerRequestID, int result);
-	void heroVisit(const CGObjectInstance * obj, bool started);
-};
-
-class DLL_EXPORT VCAI : public CAdventureAI
-{
-public:
-
-	friend class FuzzyHelper;
-	friend class ResourceManager;
-	friend class BuildingManager;
-
-	std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
-	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
-	ObjectInstanceID destinationTeleport;
-	int3 destinationTeleportPos;
-	std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
-	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
-	std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
-	std::unique_ptr<PathfinderCache> pathfinderCache;
-
-	//part of mainLoop, but accessible from outside
-	std::vector<Goals::TSubgoal> basicGoals;
-	Goals::TGoalVec goalsToRemove;
-	Goals::TGoalVec goalsToAdd;
-	std::map<Goals::TSubgoal, Goals::TGoalVec> ultimateGoalsFromBasic; //theoreticlaly same goal can fulfill multiple basic goals
-
-	std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
-	std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
-	std::map<HeroPtr, std::set<const CGObjectInstance *>> reservedHeroesMap; //objects reserved by specific heroes
-	std::set<HeroPtr> heroesUnableToExplore; //these heroes will not be polled for exploration in current state of game
-
-	//sets are faster to search, also do not contain duplicates
-	std::set<const CGObjectInstance *> visitableObjs;
-	std::set<const CGObjectInstance *> alreadyVisited;
-	std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
-	std::map<HeroPtr, std::set<HeroPtr>> visitedHeroes; //visited this turn //FIXME: this is just bug workaround
-
-	AIStatus status;
-	std::string battlename;
-
-	std::shared_ptr<CCallback> myCb;
-
-	std::unique_ptr<AsyncRunner> asyncTasks;
-	ThreadInterruption makingTurnInterrupption;
-
-public:
-	ObjectInstanceID selectedObject;
-
-	AIhelper * ah;
-
-	VCAI();
-	virtual ~VCAI();
-
-	//TODO: use only smart pointers?
-	void tryRealize(Goals::Explore & g);
-	void tryRealize(Goals::RecruitHero & g);
-	void tryRealize(Goals::VisitTile & g);
-	void tryRealize(Goals::VisitObj & g);
-	void tryRealize(Goals::VisitHero & g);
-	void tryRealize(Goals::BuildThis & g);
-	void tryRealize(Goals::DigAtTile & g);
-	void tryRealize(Goals::Trade & g);
-	void tryRealize(Goals::BuyArmy & g);
-	void tryRealize(Goals::Invalid & g);
-	void tryRealize(Goals::AbstractGoal & g);
-
-	bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved
-
-	std::string getBattleAIName() const override;
-
-	void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
-	void yourTurn(QueryID queryID) override;
-
-	void heroGotLevel(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, QueryID queryID) override; //pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
-	void commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID) override; //TODO
-	void showBlockingDialog(const std::string & text, const std::vector<Component> & components, QueryID askID, const int soundID, bool selection, bool cancel, bool safeToAutoaccept) override; //Show a dialog, player must take decision. If selection then he has to choose between one of given components, if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called with number of selected component (1 - n) or 0 for cancel (if allowed) and askID.
-	void showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance * down, bool removableUnits, QueryID queryID) override; //all stacks operations between these objects become allowed, interface has to call onEnd when done
-	void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
-	void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
-	void finish() override;
-
-	void availableCreaturesChanged(const CGDwelling * town) override;
-	void heroMoved(const TryMoveHero & details, bool verbose = true) override;
-	void heroInGarrisonChange(const CGTownInstance * town) override;
-	void centerView(int3 pos, int focusTime) override;
-	void tileHidden(const FowTilesType & pos) override;
-	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
-	void artifactAssembled(const ArtifactLocation & al) override;
-	void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
-	void showThievesGuildWindow(const CGObjectInstance * obj) override;
-	void playerBlocked(int reason, bool start) override;
-	void showPuzzleMap() override;
-	void showShipyardDialog(const IShipyard * obj) override;
-	void gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult) override;
-	void artifactPut(const ArtifactLocation & al) override;
-	void artifactRemoved(const ArtifactLocation & al) override;
-	void artifactDisassembled(const ArtifactLocation & al) override;
-	void heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * visitedObj, bool start) override;
-	void availableArtifactsChanged(const CGBlackMarket * bm = nullptr) override;
-	void heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * town) override;
-	void tileRevealed(const FowTilesType & pos) override;
-	void heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, QueryID query) override;
-	void heroExperienceChanged(const CGHeroInstance * hero, si64 val) override;
-	void heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val) override;
-	void showRecruitmentDialog(const CGDwelling * dwelling, const CArmedInstance * dst, int level, QueryID queryID) override;
-	void heroMovePointsChanged(const CGHeroInstance * hero) override;
-	void garrisonsChanged(ObjectInstanceID id1, ObjectInstanceID id2) override;
-	void newObject(const CGObjectInstance * obj) override;
-	void showHillFortWindow(const CGObjectInstance * object, const CGHeroInstance * visitor) override;
-	void playerBonusChanged(const Bonus & bonus, bool gain) override;
-	void heroCreated(const CGHeroInstance *) override;
-	void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override;
-	void showInfoDialog(EInfoWindowMode type, const std::string & text, const std::vector<Component> & components, int soundID) override;
-	void requestRealized(PackageApplied * pa) override;
-	void receivedResource() override;
-	void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
-	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
-	void heroManaPointsChanged(const CGHeroInstance * hero) override;
-	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
-	void battleResultsApplied() override;
-	void battleEnded() override;
-	void beforeObjectPropertyChanged(const SetObjectProperty * sop) override;
-	void objectPropertyChanged(const SetObjectProperty * sop) override;
-	void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
-	void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
-	void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
-	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
-
-	void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) override;
-	void battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID) override;
-	std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
-
-	void makeTurn();
-	void mainLoop();
-	void performTypicalActions();
-
-	void buildArmyIn(const CGTownInstance * t);
-	void striveToGoal(Goals::TSubgoal ultimateGoal);
-	Goals::TSubgoal decomposeGoal(Goals::TSubgoal ultimateGoal);
-	void endTurn();
-	void wander(HeroPtr h);
-	void setGoal(HeroPtr h, Goals::TSubgoal goal);
-	void evaluateGoal(HeroPtr h); //evaluates goal assigned to hero, if any
-	void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
-
-	void recruitHero(const CGTownInstance * t, bool throwing = false);
-	bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, std::optional<float> movementCostLimit = std::nullopt);
-	bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
-	//void recruitCreatures(const CGTownInstance * t);
-	void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
-	void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
-	void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
-	void moveCreaturesToHero(const CGTownInstance * t);
-	void performObjectInteraction(const CGObjectInstance * obj, HeroPtr h);
-
-	bool moveHeroToTile(int3 dst, HeroPtr h);
-	void buildStructure(const CGTownInstance * t, BuildingID building); //TODO: move to BuildingManager
-
-	void lostHero(HeroPtr h); //should remove all references to hero (assigned tasks and so on)
-	void waitTillFree();
-
-	void addVisitableObj(const CGObjectInstance * obj);
-	void markObjectVisited(const CGObjectInstance * obj);
-	void reserveObject(HeroPtr h, const CGObjectInstance * obj); //TODO: reserve all objects that heroes attempt to visit
-	void unreserveObject(HeroPtr h, const CGObjectInstance * obj);
-
-	void markHeroUnableToExplore(HeroPtr h);
-	void markHeroAbleToExplore(HeroPtr h);
-	bool isAbleToExplore(HeroPtr h);
-	void clearPathsInfo();
-
-	void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
-	void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
-	void validateVisitableObjs();
-	void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
-	void retrieveVisitableObjs();
-	virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
-
-	const CGObjectInstance * lookForArt(ArtifactID aid) const;
-	bool isAccessible(const int3 & pos) const;
-	HeroPtr getHeroWithGrail() const;
-
-	const CGObjectInstance * getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate);
-	bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
-	//optimization - use one SM for every hero call
-
-	const CGTownInstance * findTownWithTavern() const;
-	bool canRecruitAnyHero(const CGTownInstance * t = nullptr) const;
-
-	Goals::TSubgoal getGoal(HeroPtr h) const;
-	bool canAct(HeroPtr h) const;
-	std::vector<HeroPtr> getUnblockedHeroes() const;
-	std::vector<HeroPtr> getMyHeroes() const;
-	HeroPtr primaryHero() const;
-	void checkHeroArmy(HeroPtr h);
-	std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h) const;
-	void invalidatePaths() override;
-
-	void requestSent(const CPackForServer * pack, int requestID) override;
-	void answerQuery(QueryID queryID, int selection);
-	//special function that can be called ONLY from game events handling thread and will send request ASAP
-	void executeActionAsync(const std::string & description, const std::function<void()> & whatToDo);
-};
-
-class cannotFulfillGoalException : public std::exception
-{
-	std::string msg;
-
-public:
-	explicit cannotFulfillGoalException(crstring _Message)
-		: msg(_Message)
-	{
-	}
-
-	const char * what() const noexcept override
-	{
-		return msg.c_str();
-	}
-};
-
-class goalFulfilledException : public std::exception
-{
-	std::string msg;
-
-public:
-	Goals::TSubgoal goal;
-
-	explicit goalFulfilledException(Goals::TSubgoal Goal)
-		: goal(Goal)
-	{
-		msg = goal->name();
-	}
-
-	const char * what() const noexcept override
-	{
-		return msg.c_str();
-	}
-};
-
-void makePossibleUpgrades(const CArmedInstance * obj);

+ 0 - 32
AI/VCAI/main.cpp

@@ -1,32 +0,0 @@
-/*
- * main.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"
-
-#ifdef __GNUC__
-#define strcpy_s(a, b, c) strncpy(a, c, b)
-#endif
-
-static const char * const g_cszAiName = "VCAI";
-
-extern "C" DLL_EXPORT int GetGlobalAiVersion()
-{
-	return AI_INTERFACE_VER;
-}
-
-extern "C" DLL_EXPORT void GetAiName(char * name)
-{
-	strcpy_s(name, strlen(g_cszAiName) + 1, g_cszAiName);
-}
-
-extern "C" DLL_EXPORT void GetNewAI(std::shared_ptr<CGlobalAI> & out)
-{
-	out = std::make_shared<VCAI>();
-}

+ 20 - 1
CMakeLists.txt

@@ -46,10 +46,16 @@ option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
 option(ENABLE_LUA "Enable compilation of LUA scripting module" OFF)
 option(ENABLE_VIDEO "Enable video support using ffmpeg" ON)
 option(ENABLE_TRANSLATIONS "Enable generation of translations for launcher and editor" ON)
-option(ENABLE_NULLKILLER_AI "Enable compilation of Nullkiller AI library" ON)
 option(ENABLE_MINIMAL_LIB "Build only core parts of vcmi library that are required for game lobby" OFF)
 option(ENABLE_GOLDMASTER "Build in public release mode in which some debug routines are disabled" OFF)
 
+# AI libraries. Empty AI is always on
+
+option(ENABLE_NULLKILLER_AI "Enable compilation of NullkillerAI library" ON)
+option(ENABLE_NULLKILLER2_AI "Enable compilation of Nullkiller2AI library" ON)
+option(ENABLE_STUPID_AI "Enable compilation of StupidAI library" ON)
+option(ENABLE_BATTLE_AI "Enable compilation of BattleAI library" ON)
+
 # Compilation options
 
 option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
@@ -284,6 +290,19 @@ if(ENABLE_GOLDMASTER)
 	add_definitions(-DENABLE_GOLDMASTER)
 endif()
 
+if(ENABLE_NULLKILLER_AI)
+	add_definitions(-DENABLE_NULLKILLER_AI)
+endif()
+if(ENABLE_NULLKILLER2_AI)
+	add_definitions(-DENABLE_NULLKILLER2_AI)
+endif()
+if(ENABLE_STUPID_AI)
+	add_definitions(-DENABLE_STUPID_AI)
+endif()
+if(ENABLE_BATTLE_AI)
+	add_definitions(-DENABLE_BATTLE_AI)
+endif()
+
 if(IOS)
 	set(CMAKE_MACOSX_RPATH 1)
 	set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)

+ 14 - 6
client/CMakeLists.txt

@@ -452,16 +452,24 @@ assign_source_group(${vcmiclientcommon_SRCS} ${vcmiclientcommon_HEADERS})
 add_library(vcmiclientcommon STATIC ${vcmiclientcommon_SRCS} ${vcmiclientcommon_HEADERS})
 
 if(NOT ENABLE_STATIC_LIBS)
-	add_dependencies(vcmiclientcommon
-		BattleAI
-		EmptyAI
-		StupidAI
-		VCAI
-	)
+	add_dependencies(vcmiclientcommon EmptyAI)
+
 	if(ENABLE_NULLKILLER_AI)
 		add_dependencies(vcmiclientcommon Nullkiller)
+	endif()
+
+	if(ENABLE_NULLKILLER2_AI)
 		add_dependencies(vcmiclientcommon Nullkiller2)
 	endif()
+
+	if(ENABLE_STUPID_AI)
+		add_dependencies(vcmiclientcommon StupidAI)
+	endif()
+
+	if(ENABLE_BATTLE_AI)
+		add_dependencies(vcmiclientcommon BattleAI)
+	endif()
+
 endif()
 if(IOS)
 	if(ENABLE_ERM)

+ 7 - 2
config/schemas/settings.json

@@ -589,22 +589,27 @@
 				},
 				"playerAI" : {
 					"type" : "string",
-					"default" : "Nullkiller"
+					"enum" : [ "EmptyAI", "Nullkiller", "Nullkiller2" ],
+					"default" : "Nullkiller2"
 				},
 				"alliedAI" : {
 					"type" : "string",
-					"default" : "Nullkiller"
+					"enum" : [ "EmptyAI", "Nullkiller", "Nullkiller2" ],
+					"default" : "Nullkiller2"
 				},
 				"friendlyAI" : {
 					"type" : "string",
+					"enum" : [ "EmptyAI", "StupidAI", "BattleAI" ],
 					"default" : "BattleAI"
 				},
 				"neutralAI" : {
 					"type" : "string",
+					"enum" : [ "EmptyAI", "StupidAI", "BattleAI" ],
 					"default" : "BattleAI"
 				},
 				"enemyAI" : {
 					"type" : "string",
+					"enum" : [ "EmptyAI", "StupidAI", "BattleAI" ],
 					"default" : "BattleAI"
 				}
 			}

+ 78 - 25
launcher/settingsView/csettingsview_moc.cpp

@@ -91,6 +91,60 @@ void CSettingsView::updateCheckbuttonText(QToolButton * button)
 		button->setText(tr("Off"));
 }
 
+void CSettingsView::fillValidCombatAILibraries(QComboBox * comboBox, QString activeAI)
+{
+	comboBox->clear();
+
+#ifdef ENABLE_STUPID_AI
+	comboBox->addItem(tr("StupidAI (deprecated)"), "StupidAI");
+#endif
+
+#ifdef ENABLE_BATTLE_AI
+	comboBox->addItem(tr("BattleAI (default, recommended)"), "BattleAI");
+#endif
+
+	fillValidAnyAILibraries(comboBox, activeAI);
+}
+
+void CSettingsView::fillValidAdventureAILibraries(QComboBox * comboBox, QString activeAI)
+{
+	comboBox->clear();
+
+#ifdef ENABLE_NULLKILLER_AI
+	comboBox->addItem(tr("Nullkiller (superseded by Nullkiller2)"), "Nullkiller");
+#endif
+
+#ifdef ENABLE_NULLKILLER2_AI
+	comboBox->addItem(tr("Nullkiller2 (default, recommended)"), "Nullkiller2");
+#endif
+
+	fillValidAnyAILibraries(comboBox, activeAI);
+}
+
+void CSettingsView::fillValidAnyAILibraries(QComboBox * comboBox, QString activeAI)
+{
+	if (comboBox->count() == 0)
+		comboBox->addItem(tr("EmptyAI - No valid AI libraries found!"), "EmptyAI");
+
+	int indexToSelect = comboBox->findData(activeAI);
+
+	if (indexToSelect == -1)
+		comboBox->setCurrentIndex(0);
+	else
+		comboBox->setCurrentIndex(indexToSelect);
+}
+
+void CSettingsView::fillValidAILibraries()
+{
+	const auto & serverSettings = settings["server"];
+
+	fillValidAdventureAILibraries(ui->comboBoxAlliedPlayerAI,QString::fromStdString(serverSettings["alliedAI"].String()));
+	fillValidAdventureAILibraries(ui->comboBoxEnemyPlayerAI, QString::fromStdString(serverSettings["playerAI"].String()));
+	fillValidCombatAILibraries(ui->comboBoxEnemyAI, QString::fromStdString(serverSettings["enemyAI"].String()));
+	fillValidCombatAILibraries(ui->comboBoxFriendlyAI, QString::fromStdString(serverSettings["friendlyAI"].String()));
+	fillValidCombatAILibraries(ui->comboBoxNeutralAI, QString::fromStdString(serverSettings["neutralAI"].String()));
+}
+
 void CSettingsView::loadSettings()
 {
 #ifdef VCMI_MOBILE
@@ -129,6 +183,7 @@ void CSettingsView::loadSettings()
 	ui->buttonIgnoreMuteSwitch->hide();
 #endif
 	fillValidScalingRange();
+	fillValidAILibraries();
 
 	ui->buttonScalingAuto->setChecked(settings["video"]["resolution"]["scaling"].Integer() == 0);
 	if (settings["video"]["resolution"]["scaling"].Integer() == 0)
@@ -140,13 +195,6 @@ void CSettingsView::loadSettings()
 	ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
 	ui->sliderReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100));
 
-	ui->comboBoxFriendlyAI->setCurrentText(QString::fromStdString(settings["server"]["friendlyAI"].String()));
-	ui->comboBoxNeutralAI->setCurrentText(QString::fromStdString(settings["server"]["neutralAI"].String()));
-	ui->comboBoxEnemyAI->setCurrentText(QString::fromStdString(settings["server"]["enemyAI"].String()));
-
-	ui->comboBoxEnemyPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["playerAI"].String()));
-	ui->comboBoxAlliedPlayerAI->setCurrentText(QString::fromStdString(settings["server"]["alliedAI"].String()));
-
 	ui->spinBoxNetworkPort->setValue(settings["server"]["localPort"].Integer());
 
 	ui->lineEditRepositoryDefault->setText(QString::fromStdString(settings["launcher"]["defaultRepositoryURL"].String()));
@@ -448,22 +496,39 @@ void CSettingsView::on_comboBoxDisplayIndex_currentIndexChanged(int index)
 	fillValidResolutionsForScreen(index);
 }
 
-void CSettingsView::on_comboBoxFriendlyAI_currentTextChanged(const QString & arg1)
+void CSettingsView::on_comboBoxFriendlyAI_currentIndexChanged(int index)
 {
+	QString aiName = ui->comboBoxFriendlyAI->itemData(index).toString();
 	Settings node = settings.write["server"]["friendlyAI"];
-	node->String() = arg1.toUtf8().data();
+	node->String() = aiName.toUtf8().data();
 }
 
-void CSettingsView::on_comboBoxNeutralAI_currentTextChanged(const QString & arg1)
+void CSettingsView::on_comboBoxNeutralAI_currentIndexChanged(int index)
 {
+	QString aiName = ui->comboBoxNeutralAI->itemData(index).toString();
 	Settings node = settings.write["server"]["neutralAI"];
-	node->String() = arg1.toUtf8().data();
+	node->String() = aiName.toUtf8().data();
 }
 
-void CSettingsView::on_comboBoxEnemyAI_currentTextChanged(const QString & arg1)
+void CSettingsView::on_comboBoxEnemyAI_currentIndexChanged(int index)
 {
+	QString aiName = ui->comboBoxEnemyAI->itemData(index).toString();
 	Settings node = settings.write["server"]["enemyAI"];
-	node->String() = arg1.toUtf8().data();
+	node->String() = aiName.toUtf8().data();
+}
+
+void CSettingsView::on_comboBoxEnemyPlayerAI_currentIndexChanged(int index)
+{
+	QString aiName = ui->comboBoxEnemyPlayerAI->itemData(index).toString();
+	Settings node = settings.write["server"]["playerAI"];
+	node->String() = aiName.toUtf8().data();
+}
+
+void CSettingsView::on_comboBoxAlliedPlayerAI_currentIndexChanged(int index)
+{
+	QString aiName = ui->comboBoxAlliedPlayerAI->itemData(index).toString();
+	Settings node = settings.write["server"]["alliedAI"];
+	node->String() = aiName.toUtf8().data();
 }
 
 void CSettingsView::on_spinBoxNetworkPort_valueChanged(int arg1)
@@ -657,18 +722,6 @@ void CSettingsView::on_buttonVSync_toggled(bool value)
 	ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
 }
 
-void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1)
-{
-	Settings node = settings.write["server"]["playerAI"];
-	node->String() = arg1.toUtf8().data();
-}
-
-void CSettingsView::on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1)
-{
-	Settings node = settings.write["server"]["alliedAI"];
-	node->String() = arg1.toUtf8().data();
-}
-
 void CSettingsView::on_buttonAutoSavePrefix_toggled(bool value)
 {
 	Settings node = settings.write["general"]["useSavePrefix"];

+ 10 - 5
launcher/settingsView/csettingsview_moc.h

@@ -42,9 +42,11 @@ public slots:
 private slots:
 	void on_comboBoxResolution_currentTextChanged(const QString & arg1);
 	void on_comboBoxFullScreen_currentIndexChanged(int index);
-	void on_comboBoxFriendlyAI_currentTextChanged(const QString & arg1);
-	void on_comboBoxNeutralAI_currentTextChanged(const QString & arg1);
-	void on_comboBoxEnemyAI_currentTextChanged(const QString & arg1);
+	void on_comboBoxFriendlyAI_currentIndexChanged(int index);
+	void on_comboBoxNeutralAI_currentIndexChanged(int index);
+	void on_comboBoxEnemyAI_currentIndexChanged(int index);
+	void on_comboBoxAlliedPlayerAI_currentIndexChanged(int index);
+	void on_comboBoxEnemyPlayerAI_currentIndexChanged(int index);
 	void on_spinBoxNetworkPort_valueChanged(int arg1);
 	void on_buttonShowIntro_toggled(bool value);
 	void on_buttonAllowPortrait_toggled(bool value);
@@ -63,8 +65,6 @@ private slots:
 	void on_buttonConfigEditor_clicked();
 	void on_spinBoxFramerateLimit_valueChanged(int arg1);
 	void on_buttonVSync_toggled(bool value);
-	void on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1);
-	void on_comboBoxAlliedPlayerAI_currentTextChanged(const QString &arg1);
 	void on_buttonAutoSavePrefix_toggled(bool value);
 	void on_spinBoxAutoSaveLimit_valueChanged(int arg1);
 	void on_lineEditAutoSavePrefix_textEdited(const QString &arg1);
@@ -107,4 +107,9 @@ private:
 	void fillValidResolutionsForScreen(int screenIndex);
 	void fillValidScalingRange();
 	QSize getPreferredRenderingResolution();
+
+	void fillValidAILibraries();
+	void fillValidCombatAILibraries(QComboBox *, QString activeAI);
+	void fillValidAdventureAILibraries(QComboBox *, QString activeAI);
+	void fillValidAnyAILibraries(QComboBox *, QString activeAI);
 };

+ 9 - 69
launcher/settingsView/csettingsview_moc.ui

@@ -47,9 +47,9 @@
       <property name="geometry">
        <rect>
         <x>0</x>
-        <y>0</y>
+        <y>-860</y>
         <width>729</width>
-        <height>1503</height>
+        <height>1758</height>
        </rect>
       </property>
       <layout class="QGridLayout" name="gridLayout" columnstretch="20,5,5,5,5,10">
@@ -83,7 +83,7 @@
          </property>
         </widget>
        </item>
-      <item row="64" column="1" colspan="5">
+       <item row="64" column="1" colspan="5">
         <widget class="QPushButton" name="buttonConfigEditor">
          <property name="text">
           <string>Open editor</string>
@@ -396,23 +396,8 @@
        <item row="50" column="1" colspan="5">
         <widget class="QComboBox" name="comboBoxEnemyPlayerAI">
          <property name="currentText">
-          <string notr="true">VCAI</string>
+          <string notr="true"/>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">VCAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">Nullkiller</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">Nullkiller2</string>
-          </property>
-         </item>
         </widget>
        </item>
        <item row="58" column="2" colspan="4">
@@ -428,23 +413,8 @@
        <item row="51" column="1" colspan="5">
         <widget class="QComboBox" name="comboBoxAlliedPlayerAI">
          <property name="currentText">
-          <string notr="true">VCAI</string>
+          <string notr="true"/>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">VCAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">Nullkiller</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">Nullkiller2</string>
-          </property>
-         </item>
         </widget>
        </item>
        <item row="4" column="0">
@@ -924,18 +894,8 @@
        <item row="52" column="1" colspan="5">
         <widget class="QComboBox" name="comboBoxNeutralAI">
          <property name="currentText">
-          <string notr="true">BattleAI</string>
+          <string notr="true"/>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">BattleAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">StupidAI</string>
-          </property>
-         </item>
         </widget>
        </item>
        <item row="26" column="1" colspan="5">
@@ -1002,18 +962,8 @@
           <bool>false</bool>
          </property>
          <property name="currentText">
-          <string notr="true">BattleAI</string>
+          <string notr="true"/>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">BattleAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">StupidAI</string>
-          </property>
-         </item>
         </widget>
        </item>
        <item row="4" column="5">
@@ -1122,18 +1072,8 @@
           <bool>false</bool>
          </property>
          <property name="currentText">
-          <string notr="true">BattleAI</string>
+          <string notr="true"/>
          </property>
-         <item>
-          <property name="text">
-           <string notr="true">BattleAI</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string notr="true">StupidAI</string>
-          </property>
-         </item>
         </widget>
        </item>
        <item row="59" column="2" colspan="4">
@@ -1548,7 +1488,7 @@ Fullscreen Exclusive Mode - the game will cover the entirety of your screen and
  <resources/>
  <connections/>
  <buttongroups>
-  <buttongroup name="buttonGroupFonts"/>
   <buttongroup name="buttonGroupValidation"/>
+  <buttongroup name="buttonGroupFonts"/>
  </buttongroups>
 </ui>

+ 13 - 6
lib/CMakeLists.txt

@@ -835,16 +835,23 @@ target_link_libraries(vcmi PUBLIC
 
 if(ENABLE_STATIC_LIBS AND ENABLE_CLIENT)
 	target_compile_definitions(vcmi PRIVATE STATIC_AI)
-	target_link_libraries(vcmi PRIVATE
-		BattleAI
-		EmptyAI
-		StupidAI
-		VCAI
-	)
+	target_link_libraries(vcmi PRIVATE EmptyAI)
 	if(ENABLE_NULLKILLER_AI)
 		target_link_libraries(vcmi PRIVATE Nullkiller)
+	endif()
+
+	if(ENABLE_NULLKILLER2_AI)
 		target_link_libraries(vcmi PRIVATE Nullkiller2)
 	endif()
+
+	if(ENABLE_STUPID_AI)
+		target_link_libraries(vcmi PRIVATE StupidAI)
+	endif()
+
+	if(ENABLE_BATTLE_AI)
+		target_link_libraries(vcmi PRIVATE BattleAI)
+	endif()
+
 endif()
 
 # no longer necessary, but might be useful to keep in future

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini