Przeglądaj źródła

Nullkiller: initial decomposition

Andrii Danylchenko 4 lat temu
rodzic
commit
8f8c5ca255
52 zmienionych plików z 736 dodań i 425 usunięć
  1. 2 2
      AI/Nullkiller/AIUtility.h
  2. 0 1
      AI/Nullkiller/Behaviors/BuildingBehavior.cpp
  3. 1 0
      AI/Nullkiller/Behaviors/BuildingBehavior.h
  4. 0 1
      AI/Nullkiller/Behaviors/BuyArmyBehavior.cpp
  5. 44 14
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp
  6. 4 4
      AI/Nullkiller/Behaviors/CaptureObjectsBehavior.h
  7. 1 1
      AI/Nullkiller/Behaviors/CompleteQuestBehavior.h
  8. 1 0
      AI/Nullkiller/Behaviors/DefenceBehavior.h
  9. 5 5
      AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp
  10. 1 0
      AI/Nullkiller/Behaviors/GatherArmyBehavior.h
  11. 1 0
      AI/Nullkiller/Behaviors/RecruitHeroBehavior.h
  12. 1 0
      AI/Nullkiller/Behaviors/StartupBehavior.h
  13. 4 3
      AI/Nullkiller/CMakeLists.txt
  14. 27 10
      AI/Nullkiller/Engine/Nullkiller.cpp
  15. 82 34
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  16. 22 3
      AI/Nullkiller/Engine/PriorityEvaluator.h
  17. 1 67
      AI/Nullkiller/Goals/AbstractGoal.cpp
  18. 21 33
      AI/Nullkiller/Goals/AbstractGoal.h
  19. 18 18
      AI/Nullkiller/Goals/BuildBoat.cpp
  20. 0 1
      AI/Nullkiller/Goals/BuildBoat.h
  21. 19 0
      AI/Nullkiller/Goals/BuildThis.cpp
  22. 1 0
      AI/Nullkiller/Goals/BuildThis.h
  23. 45 0
      AI/Nullkiller/Goals/BuyArmy.cpp
  24. 2 0
      AI/Nullkiller/Goals/BuyArmy.h
  25. 17 16
      AI/Nullkiller/Goals/CGoal.h
  26. 114 0
      AI/Nullkiller/Goals/Composition.cpp
  27. 40 0
      AI/Nullkiller/Goals/Composition.h
  28. 0 1
      AI/Nullkiller/Goals/DigAtTile.cpp
  29. 9 14
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  30. 2 0
      AI/Nullkiller/Goals/ExecuteHeroChain.h
  31. 0 1
      AI/Nullkiller/Goals/FindObj.cpp
  32. 1 29
      AI/Nullkiller/Goals/FindObj.h
  33. 0 1
      AI/Nullkiller/Goals/GetArtOfType.cpp
  34. 0 1
      AI/Nullkiller/Goals/Goals.h
  35. 18 0
      AI/Nullkiller/Goals/RecruitHero.cpp
  36. 1 0
      AI/Nullkiller/Goals/RecruitHero.h
  37. 9 6
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  38. 7 7
      AI/Nullkiller/Pathfinding/AINodeStorage.h
  39. 34 3
      AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp
  40. 26 3
      AI/Nullkiller/Pathfinding/Actions/BattleAction.h
  41. 59 5
      AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp
  42. 16 5
      AI/Nullkiller/Pathfinding/Actions/BoatActions.h
  43. 25 0
      AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp
  44. 8 4
      AI/Nullkiller/Pathfinding/Actions/SpecialAction.h
  45. 36 4
      AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp
  46. 5 3
      AI/Nullkiller/Pathfinding/Actions/TownPortalAction.h
  47. 0 1
      AI/Nullkiller/Pathfinding/Actors.cpp
  48. 2 2
      AI/Nullkiller/Pathfinding/Actors.h
  49. 1 1
      AI/Nullkiller/Pathfinding/Rules/AILayerTransitionRule.cpp
  50. 3 21
      AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp
  51. 0 97
      AI/Nullkiller/VCAI.cpp
  52. 0 3
      AI/Nullkiller/VCAI.h

+ 2 - 2
AI/Nullkiller/AIUtility.h

@@ -36,9 +36,9 @@ extern const int GOLD_RESERVE;
 
 enum HeroRole
 {
-	MAIN,
+	SCOUT,
 
-	SCOUT
+	MAIN
 };
 
 //provisional class for AI to store a reference to an owned hero object

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

@@ -13,7 +13,6 @@
 #include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/BuyArmy.h"
-#include "../Goals/VisitTile.h"
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"
 #include "../Engine/Nullkiller.h"

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

@@ -19,6 +19,7 @@ namespace Goals
 	{
 	public:
 		BuildingBehavior()
+			:CGoal(Goals::BUILD)
 		{
 		}
 

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

@@ -13,7 +13,6 @@
 #include "../AIhelper.h"
 #include "../AIUtility.h"
 #include "../Goals/BuyArmy.h"
-#include "../Goals/VisitTile.h"
 #include "../Engine/Nullkiller.h"
 #include "lib/mapping/CMap.h" //for victory conditions
 #include "lib/CPathfinder.h"

+ 44 - 14
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -11,6 +11,7 @@
 #include "../VCAI.h"
 #include "../Engine/Nullkiller.h"
 #include "../AIhelper.h"
+#include "../Goals/Composition.h"
 #include "../Goals/ExecuteHeroChain.h"
 #include "CaptureObjectsBehavior.h"
 #include "../AIUtility.h"
@@ -23,11 +24,32 @@ extern FuzzyHelper * fh;
 
 using namespace Goals;
 
+template <typename T>
+bool vectorEquals(const std::vector<T> & v1, const std::vector<T> & v2)
+{
+	return vstd::contains_if(v1, [&](T o) -> bool
+	{
+		return vstd::contains(v2, o);
+	});
+}
+
 std::string CaptureObjectsBehavior::toString() const
 {
 	return "Capture objects";
 }
 
+bool CaptureObjectsBehavior::operator==(const CaptureObjectsBehavior & other) const
+{
+	if(specificObjects != other.specificObjects)
+		return false;
+
+	if(specificObjects)
+		return vectorEquals(objectsToCapture, other.objectsToCapture);
+
+	return vectorEquals(objectTypes, other.objectTypes)
+		&& vectorEquals(objectSubTypes, other.objectSubTypes);
+}
+
 Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 {
 	Goals::TGoalVec tasks;
@@ -65,15 +87,6 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 				logAi->trace("Path found %s", path.toString());
 #endif
 
-				if(path.getFirstBlockedAction())
-				{
-#if AI_TRACE_LEVEL >= 2
-					// TODO: decomposition?
-					logAi->trace("Ignore path. Action is blocked.");
-#endif
-					continue;
-				}
-
 				if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 				{
 #if AI_TRACE_LEVEL >= 2
@@ -91,9 +104,26 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 				if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
 					continue;
 
-				if(path.specialAction && !path.specialAction->canAct(path.targetHero))
+				auto firstBlockedAction = path.getFirstBlockedAction();
+				if(firstBlockedAction)
 				{
-					auto subGoal = path.specialAction->whatToDo(path.targetHero);
+					auto subGoal = firstBlockedAction->decompose(path.targetHero);
+
+#if AI_TRACE_LEVEL >= 2
+					logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());
+#endif
+
+					if(!subGoal->invalid())
+					{
+						Composition composition;
+
+						composition.addNext(ExecuteHeroChain(path, objToVisit));
+						composition.addNext(subGoal);
+
+						tasks.push_back(sptr(composition));
+					}
+
+					continue;
 				}
 
 				auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
@@ -115,7 +145,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 
 					waysToVisitObj.push_back(newWay);
 
-					if(!closestWay || closestWay->evaluationContext.movementCost > newWay->evaluationContext.movementCost)
+					if(!closestWay || closestWay->getPath().movementCost() > newWay->getPath().movementCost())
 						closestWay = newWay;
 				}
 			}
@@ -128,8 +158,8 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
 				if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
 					continue;
 
-				way->evaluationContext.closestWayRatio
-					= closestWay->evaluationContext.movementCost / way->evaluationContext.movementCost;
+				way->closestWayRatio
+					= closestWay->getPath().movementCost() / way->getPath().movementCost();
 
 				tasks.push_back(sptr(*way));
 			}

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

@@ -24,18 +24,21 @@ namespace Goals
 		bool specificObjects;
 	public:
 		CaptureObjectsBehavior()
+			:CGoal(CAPTURE_OBJECTS)
 		{
 			objectTypes = std::vector<int>();
 			specificObjects = false;
 		}
 
 		CaptureObjectsBehavior(std::vector<const CGObjectInstance *> objectsToCapture)
+			:CGoal(CAPTURE_OBJECTS)
 		{
 			this->objectsToCapture = objectsToCapture;
 			specificObjects = true;
 		}
 
 		CaptureObjectsBehavior(const CGObjectInstance * objectToCapture)
+			:CGoal(CAPTURE_OBJECTS)
 		{
 			objectsToCapture = std::vector<const CGObjectInstance *>();
 			objectsToCapture.push_back(objectToCapture);
@@ -59,10 +62,7 @@ namespace Goals
 			return *this;
 		}
 
-		virtual bool operator==(const CaptureObjectsBehavior & other) const override
-		{
-			return false;
-		}
+		virtual bool operator==(const CaptureObjectsBehavior & other) const override;
 
 	private:
 		bool shouldVisitObject(ObjectIdRef obj) const;

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

@@ -9,7 +9,6 @@
 */
 #pragma once
 
-#include "lib/VCMI_Lib.h"
 #include "../AIUtility.h"
 #include "../../../lib/VCMI_Lib.h"
 #include "../../../CCallback.h"
@@ -21,6 +20,7 @@ namespace Goals
 	{
 	public:
 		CompleteQuestBehavior()
+			:CGoal(COMPLETE_QUEST)
 		{
 		}
 

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

@@ -19,6 +19,7 @@ namespace Goals
 	{
 	public:
 		DefenceBehavior()
+			:CGoal(DEFENCE)
 		{
 		}
 

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

@@ -135,7 +135,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		{
 			auto newWay = std::make_shared<ExecuteHeroChain>(path, hero);
 
-			newWay->evaluationContext.strategicalValue = armyValue;
+			newWay->strategicalValue = armyValue;
 			waysToVisitObj.push_back(newWay);
 		}
 	}
@@ -148,7 +148,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
 		if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
 			continue;
 
-		way->evaluationContext.closestWayRatio = 1;
+		way->closestWayRatio = 1;
 
 		tasks.push_back(sptr(*way));
 	}
@@ -228,8 +228,8 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 		{
 			auto newWay = std::make_shared<ExecuteHeroChain>(path, upgrader);
 
-			newWay->evaluationContext.strategicalValue = armyValue;
-			newWay->evaluationContext.goldCost = upgrade.upgradeCost[Res::GOLD];
+			newWay->strategicalValue = armyValue;
+			newWay->goldCost = upgrade.upgradeCost[Res::GOLD];
 
 			waysToVisitObj.push_back(newWay);
 		}
@@ -243,7 +243,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
 		if(ai->nullkiller->arePathHeroesLocked(way->getPath()))
 			continue;
 
-		way->evaluationContext.closestWayRatio = 1;
+		way->closestWayRatio = 1;
 
 		tasks.push_back(sptr(*way));
 	}

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

@@ -19,6 +19,7 @@ namespace Goals
 	{
 	public:
 		GatherArmyBehavior()
+			:CGoal(Goals::GATHER_ARMY)
 		{
 		}
 

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

@@ -19,6 +19,7 @@ namespace Goals
 	{
 	public:
 		RecruitHeroBehavior()
+			:CGoal(RECRUIT_HERO_BEHAVIOR)
 		{
 		}
 

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

@@ -19,6 +19,7 @@ namespace Goals
 	{
 	public:
 		StartupBehavior()
+			:CGoal(STARTUP)
 		{
 		}
 

+ 4 - 3
AI/Nullkiller/CMakeLists.txt

@@ -6,6 +6,7 @@ set(VCAI_SRCS
 		Pathfinding/AINodeStorage.cpp
 		Pathfinding/PathfindingManager.cpp
 		Pathfinding/Actors.cpp
+		Pathfinding/Actions/SpecialAction.cpp
 		Pathfinding/Actions/BattleAction.cpp
 		Pathfinding/Actions/BoatActions.cpp
 		Pathfinding/Actions/TownPortalAction.cpp
@@ -21,6 +22,7 @@ set(VCAI_SRCS
 		FuzzyEngines.cpp
 		FuzzyHelper.cpp
 		Goals/AbstractGoal.cpp
+		Goals/Composition.cpp
 		Goals/BuildBoat.cpp
 		Goals/BuildThis.cpp
 		Goals/DismissHero.cpp
@@ -32,7 +34,6 @@ set(VCAI_SRCS
 		Goals/RecruitHero.cpp
 		Goals/DigAtTile.cpp
 		Goals/GetArtOfType.cpp
-		Goals/FindObj.cpp
 		Goals/ExecuteHeroChain.cpp
 		Goals/ExchangeSwapTownHeroes.cpp
 		Engine/Nullkiller.cpp
@@ -59,7 +60,7 @@ set(VCAI_HEADERS
 		Pathfinding/AINodeStorage.h
 		Pathfinding/PathfindingManager.h
 		Pathfinding/Actors.h
-		Pathfinding/Actions/ISpecialAction.h
+		Pathfinding/Actions/SpecialAction.h
 		Pathfinding/Actions/BattleAction.h
 		Pathfinding/Actions/BoatActions.h
 		Pathfinding/Actions/TownPortalAction.h
@@ -76,6 +77,7 @@ set(VCAI_HEADERS
 		FuzzyHelper.h
 		Goals/AbstractGoal.h
 		Goals/CGoal.h
+		Goals/Composition.h
 		Goals/Invalid.h
 		Goals/BuildBoat.h
 		Goals/BuildThis.h
@@ -88,7 +90,6 @@ set(VCAI_HEADERS
 		Goals/RecruitHero.h
 		Goals/DigAtTile.h
 		Goals/GetArtOfType.h
-		Goals/FindObj.h
 		Goals/ExecuteHeroChain.h
 		Goals/ExchangeSwapTownHeroes.h
 		Goals/Goals.h

+ 27 - 10
AI/Nullkiller/Engine/Nullkiller.cpp

@@ -51,29 +51,33 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
 
 	goals[0] = {behavior};
 
-	if(tasks.empty())
-	{
-		logAi->debug("Behavior %s found no tasks", behavior->toString());
-
-		return Goals::taskptr(Goals::Invalid());
-	}
-
-	logAi->trace("Evaluating priorities, tasks count %d", tasks.size());
-
 	int depth = 0;
 	while(goals[0].size())
 	{
 		TSubgoal current = goals[depth].back();
+
+#if AI_TRACE_LEVEL >= 1
+		logAi->trace("Decomposing %s, level: %d", current->toString(), depth);
+#endif
+
 		TGoalVec subgoals = current->decompose();
 
+#if AI_TRACE_LEVEL >= 1
+		logAi->trace("Found %d goals", subgoals.size());
+#endif
+
 		goals[depth + 1].clear();
 
 		for(auto subgoal : subgoals)
 		{
-			if(subgoal->isElementar)
+			if(subgoal->isElementar())
 			{
 				auto task = taskptr(*subgoal);
 
+#if AI_TRACE_LEVEL >= 1
+		logAi->trace("Found task %s", task->toString());
+#endif
+
 				if(task->priority <= 0)
 					task->priority = priorityEvaluator->evaluate(subgoal);
 
@@ -81,6 +85,9 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
 			}
 			else
 			{
+#if AI_TRACE_LEVEL >= 1
+				logAi->trace("Found abstract goal %s", subgoal->toString());
+#endif
 				goals[depth + 1].push_back(subgoal);
 			}
 		}
@@ -91,13 +98,23 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
 		}
 		else
 		{
+			goals[depth].pop_back();
+
 			while(depth > 0 && goals[depth].empty())
 			{
 				depth--;
+				goals[depth].pop_back();
 			}
 		}
 	}
 
+	if(tasks.empty())
+	{
+		logAi->debug("Behavior %s found no tasks", behavior->toString());
+
+		return Goals::taskptr(Goals::Invalid());
+	}
+
 	auto task = choseBestTask(tasks);
 
 	logAi->debug("Behavior %s returns %s, priority %f", behavior->toString(), task->toString(), task->priority);

+ 82 - 34
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -36,6 +36,23 @@ class CGTownInstance;
 extern boost::thread_specific_ptr<CCallback> cb;
 extern boost::thread_specific_ptr<VCAI> ai;
 
+EvaluationContext::EvaluationContext()
+	: movementCost(0.0),
+	manaCost(0),
+	danger(0),
+	closestWayRatio(1),
+	movementCostByRole(),
+	skillReward(0),
+	goldReward(0),
+	goldCost(0),
+	armyReward(0),
+	armyLossPersentage(0),
+	heroRole(HeroRole::SCOUT),
+	turn(0),
+	strategicalValue(0)
+{
+}
+
 PriorityEvaluator::~PriorityEvaluator()
 {
 	delete engine;
@@ -430,46 +447,59 @@ int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * he
 class ExecuteHeroChainEvaluationContextBuilder : public IEvaluationContextBuilder
 {
 public:
-	virtual Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal task) const override
+	virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
 	{
+		if(task->goalType != Goals::EXECUTE_HERO_CHAIN)
+			return;
+
 		Goals::ExecuteHeroChain & chain = dynamic_cast<Goals::ExecuteHeroChain &>(*task);
-		auto evaluationContext = task->evaluationContext;
+		const AIPath & path = chain.getPath();
+
+		vstd::amax(evaluationContext.danger, path.getTotalDanger());
+		evaluationContext.movementCost += path.movementCost();
+		evaluationContext.closestWayRatio = chain.closestWayRatio;
+
+		for(auto & node : path.nodes)
+		{
+			auto role = ai->ah->getHeroRole(node.targetHero);
+
+			evaluationContext.movementCostByRole[role] += node.cost;
+		}
 
 		auto heroPtr = task->hero;
 		const CGObjectInstance * target = cb->getObj((ObjectInstanceID)task->objid, false);
 		auto day = cb->getDate(Date::DAY);
 		auto hero = heroPtr.get();
 		bool checkGold = evaluationContext.danger == 0;
-		auto army = chain.getPath().heroArmy;
+		auto army = path.heroArmy;
 
-		evaluationContext.armyLossPersentage = task->evaluationContext.armyLoss / (double)task->evaluationContext.heroStrength;
-		evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
-		evaluationContext.goldReward = getGoldReward(target, hero);
-		evaluationContext.armyReward = getArmyReward(target, hero, army, checkGold);
-		evaluationContext.skillReward = getSkillReward(target, hero, evaluationContext.heroRole);
+		vstd::amax(evaluationContext.armyLossPersentage, path.getTotalArmyLoss() / (double)path.getHeroStrength());
+		vstd::amax(evaluationContext.heroRole, ai->ah->getHeroRole(heroPtr));
+		evaluationContext.goldReward += getGoldReward(target, hero);
+		evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold);
+		evaluationContext.skillReward += getSkillReward(target, hero, evaluationContext.heroRole);
 		evaluationContext.strategicalValue += getStrategicalValue(target);
-		evaluationContext.goldCost = getGoldCost(target, hero, army);
-		evaluationContext.turn = chain.getPath().turn();
-
-		return evaluationContext;
+		evaluationContext.goldCost += getGoldCost(target, hero, army);
+		vstd::amax(evaluationContext.turn, path.turn());
 	}
 };
 
 class BuildThisEvaluationContextBuilder : public IEvaluationContextBuilder
 {
 public:
-	virtual Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal task) const override
+	virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal task) const override
 	{
-		Goals::EvaluationContext evaluationContext;
+		if(task->goalType != Goals::BUILD_STRUCTURE)
+			return;
+
 		Goals::BuildThis & buildThis = dynamic_cast<Goals::BuildThis &>(*task);
 		auto & bi = buildThis.buildingInfo;
 		
-		evaluationContext.goldReward = 7 * bi.dailyIncome[Res::GOLD] / 2; // 7 day income but half we already have
+		evaluationContext.goldReward += 7 * bi.dailyIncome[Res::GOLD] / 2; // 7 day income but half we already have
 		evaluationContext.heroRole = HeroRole::MAIN;
-		evaluationContext.movementCostByRole[evaluationContext.heroRole] = bi.prerequisitesCount;
-		evaluationContext.armyReward = 0;
-		evaluationContext.strategicalValue = buildThis.townInfo.armyScore / 50000.0;
-		evaluationContext.goldCost = bi.buildCostWithPrerequisits[Res::GOLD];
+		evaluationContext.movementCostByRole[evaluationContext.heroRole] += bi.prerequisitesCount;
+		evaluationContext.strategicalValue += buildThis.townInfo.armyScore / 50000.0;
+		evaluationContext.goldCost += bi.buildCostWithPrerequisits[Res::GOLD];
 
 		if(bi.creatureID != CreatureID::NONE)
 		{
@@ -477,38 +507,56 @@ public:
 
 			if(bi.baseCreatureID == bi.creatureID)
 			{
-				evaluationContext.armyReward = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
+				evaluationContext.armyReward += ai->ah->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
 			}
-			
-			auto creaturesToUpgrade = ai->ah->getTotalCreaturesAvailable(bi.baseCreatureID);
-			auto upgradedPower = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
+			else
+			{
+				auto creaturesToUpgrade = ai->ah->getTotalCreaturesAvailable(bi.baseCreatureID);
+				auto upgradedPower = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
 
-			evaluationContext.armyReward = upgradedPower - creaturesToUpgrade.power;
+				evaluationContext.armyReward += upgradedPower - creaturesToUpgrade.power;
+			}
 		}
 		else
 		{
-			evaluationContext.strategicalValue = ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
+			evaluationContext.strategicalValue += ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
 		}
-
-		return evaluationContext;
 	}
 };
 
 PriorityEvaluator::PriorityEvaluator()
 {
 	initVisitTile();
-	evaluationContextBuilders[Goals::EXECUTE_HERO_CHAIN] = std::make_shared<ExecuteHeroChainEvaluationContextBuilder>();
-	evaluationContextBuilders[Goals::BUILD_STRUCTURE] = std::make_shared<BuildThisEvaluationContextBuilder>();
+	evaluationContextBuilders.push_back(std::make_shared<ExecuteHeroChainEvaluationContextBuilder>());
+	evaluationContextBuilders.push_back(std::make_shared<BuildThisEvaluationContextBuilder>());
 }
 
-Goals::EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
+EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
 {
-	auto builder = evaluationContextBuilders.find(goal->goalType);
+	Goals::TGoalVec parts;
+	EvaluationContext context;
+
+	if(goal->goalType == Goals::COMPOSITION)
+	{
+		parts = goal->decompose();
+	}
+	else
+	{
+		parts.push_back(goal);
+	}
+
+	for(auto goal : parts)
+	{
+		context.strategicalValue += goal->strategicalValue;
+		context.goldCost += goal->goldCost;
 
-	if(builder == evaluationContextBuilders.end())
-		return goal->evaluationContext;
+		for(auto builder : evaluationContextBuilders)
+		{
+			builder->buildEvaluationContext(context, goal);
+		}
+	}
 
-	return builder->second->buildEvaluationContext(goal);
+	return context;
 }
 
 /// distance

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

@@ -11,10 +11,29 @@
 #include "fl/Headers.h"
 #include "../Goals/Goals.h"
 
+struct DLL_EXPORT EvaluationContext
+{
+	float movementCost;
+	std::map<HeroRole, float> movementCostByRole;
+	int manaCost;
+	uint64_t danger;
+	float closestWayRatio;
+	float armyLossPersentage;
+	float armyReward;
+	int32_t goldReward;
+	int32_t goldCost;
+	float skillReward;
+	float strategicalValue;
+	HeroRole heroRole;
+	uint8_t turn;
+
+	EvaluationContext();
+};
+
 class IEvaluationContextBuilder
 {
 public:
-	virtual Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const = 0;
+	virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal goal) const = 0;
 };
 
 class PriorityEvaluator
@@ -43,7 +62,7 @@ private:
 	fl::InputVariable * goldPreasureVariable;
 	fl::InputVariable * goldCostVariable;
 	fl::OutputVariable * value;
-	std::map<Goals::EGoals, std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
+	std::vector<std::shared_ptr<IEvaluationContextBuilder>> evaluationContextBuilders;
 
-	Goals::EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const;
+	EvaluationContext buildEvaluationContext(Goals::TSubgoal goal) const;
 };

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

@@ -33,7 +33,7 @@ TTask Goals::taskptr(const AbstractGoal & tmp)
 {
 	TTask ptr;
 
-	if(!tmp.isElementar)
+	if(!tmp.isElementar())
 		throw cannotFulfillGoalException(tmp.toString() + " is not elementar");
 
 	ptr.reset(dynamic_cast<ITask *>(tmp.clone()));
@@ -46,30 +46,6 @@ std::string AbstractGoal::toString() 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 " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
 		break;
@@ -83,32 +59,9 @@ std::string AbstractGoal::toString() const //TODO: virtualize
 	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 " + boost::lexical_cast<std::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 " + VLC->arth->artifacts[aid]->Name();
 		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;
@@ -134,23 +87,4 @@ bool TSubgoal::operator==(const TSubgoal & rhs) const
 bool AbstractGoal::invalid() const
 {
 	return goalType == EGoals::INVALID;
-}
-
-EvaluationContext::EvaluationContext()
-	: movementCost(0.0),
-	manaCost(0),
-	danger(0),
-	closestWayRatio(1),
-	armyLoss(0),
-	heroStrength(0),
-	movementCostByRole(),
-	skillReward(0),
-	goldReward(0),
-	goldCost(0),
-	armyReward(0),
-	armyLossPersentage(0),
-	heroRole(HeroRole::SCOUT),
-	turn(0),
-	strategicalValue(0)
-{
 }

+ 21 - 33
AI/Nullkiller/Goals/AbstractGoal.h

@@ -29,7 +29,6 @@ namespace Goals
 	class CollectRes;
 	class BuyArmy;
 	class BuildBoat;
-	class ClearWayTo;
 	class Invalid;
 	class Trade;
 	class AdventureSpellCast;
@@ -42,18 +41,17 @@ namespace Goals
 		EXPLORE, GATHER_ARMY,
 		BOOST_HERO,
 		RECRUIT_HERO,
+		RECRUIT_HERO_BEHAVIOR,
 		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
+		CAPTURE_OBJECTS,
 
 		GET_ART_TYPE,
 
-		VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
-		CLEAR_WAY_TO,
+		DEFENCE,
+		STARTUP,
 		DIG_AT_TILE,//elementar with hero on tile
 		BUY_ARMY, //at specific town
 		TRADE, //val resID at object objid
@@ -62,7 +60,8 @@ namespace Goals
 		ADVENTURE_SPELL_CAST,
 		EXECUTE_HERO_CHAIN,
 		EXCHANGE_SWAP_TOWN_HEROES,
-		DISMISS_HERO
+		DISMISS_HERO,
+		COMPOSITION
 	};
 
 	class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
@@ -89,34 +88,14 @@ namespace Goals
 
 	DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
 	DLL_EXPORT TTask taskptr(const AbstractGoal & tmp);
-
-	struct DLL_EXPORT EvaluationContext
-	{
-		float movementCost;
-		std::map<HeroRole, float> movementCostByRole;
-		int manaCost;
-		uint64_t danger;
-		float closestWayRatio;
-		uint64_t armyLoss;
-		uint64_t heroStrength;
-		float armyLossPersentage;
-		float armyReward;
-		int32_t goldReward;
-		int32_t goldCost;
-		float skillReward;
-		float strategicalValue;
-		HeroRole heroRole;
-		uint8_t turn;
-
-		EvaluationContext();
-	};
-
+	
 	class DLL_EXPORT AbstractGoal
 	{
 	public:
-		bool isElementar; VSETTER(bool, isElementar)
 		bool isAbstract; VSETTER(bool, isAbstract)
 		int value; VSETTER(int, value)
+		float strategicalValue; VSETTER(float, strategicalValue)
+		ui64 goldCost; VSETTER(ui64, goldCost)
 		int resID; VSETTER(int, resID)
 		int objid; VSETTER(int, objid)
 		int aid; VSETTER(int, aid)
@@ -125,12 +104,11 @@ namespace Goals
 		const CGTownInstance *town; VSETTER(CGTownInstance *, town)
 		int bid; VSETTER(int, bid)
 		TSubgoal parent; VSETTER(TSubgoal, parent)
-		EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
+		//EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
 
 		AbstractGoal(EGoals goal = EGoals::INVALID)
-			: goalType(goal), evaluationContext()
+			: goalType(goal), hero()
 		{
-			isElementar = false;
 			isAbstract = false;
 			value = 0;
 			aid = -1;
@@ -139,6 +117,8 @@ namespace Goals
 			tile = int3(-1, -1, -1);
 			town = nullptr;
 			bid = -1;
+			strategicalValue = 0;
+			goldCost = 0;
 		}
 		virtual ~AbstractGoal() {}
 		//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
@@ -159,6 +139,8 @@ namespace Goals
 		bool invalid() const;
 		
 		virtual bool operator==(const AbstractGoal & g) const;
+
+		virtual bool isElementar() const { return false; }
 		
 		bool operator!=(const AbstractGoal & g) const
 		{
@@ -167,6 +149,9 @@ namespace Goals
 
 		template<typename Handler> void serialize(Handler & h, const int version)
 		{
+			float priority;
+			bool isElementar;
+
 			h & goalType;
 			h & isElementar;
 			h & isAbstract;
@@ -187,9 +172,12 @@ namespace Goals
 	public:
 		float priority;
 
+		ITask() : priority(0) {}
+
 		///Visitor pattern
 		//TODO: make accept work for std::shared_ptr... somehow
 		virtual void accept(VCAI * ai) = 0; //unhandled goal will report standard error
 		virtual std::string toString() const = 0;
+		virtual ~ITask() {}
 	};
 }

+ 18 - 18
AI/Nullkiller/Goals/BuildBoat.cpp

@@ -26,24 +26,24 @@ bool BuildBoat::operator==(const BuildBoat & other) const
 {
 	return shipyard->o->id == other.shipyard->o->id;
 }
-
-TSubgoal BuildBoat::decomposeSingle() const
-{
-	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
-	{
-		return sptr(CaptureObjectsBehavior(shipyard->o));
-	}
-
-	if(shipyard->shipyardStatus() != IShipyard::GOOD)
-	{
-		throw cannotFulfillGoalException("Shipyard is busy.");
-	}
-
-	TResources boatCost;
-	shipyard->getBoatCost(boatCost);
-
-	return iAmElementar();
-}
+//
+//TSubgoal BuildBoat::decomposeSingle() const
+//{
+//	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
+//	{
+//		return sptr(CaptureObjectsBehavior(shipyard->o));
+//	}
+//
+//	if(shipyard->shipyardStatus() != IShipyard::GOOD)
+//	{
+//		throw cannotFulfillGoalException("Shipyard is busy.");
+//	}
+//
+//	TResources boatCost;
+//	shipyard->getBoatCost(boatCost);
+//
+//	return iAmElementar();
+//}
 
 void BuildBoat::accept(VCAI * ai)
 {

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

@@ -17,7 +17,6 @@ namespace Goals
 	{
 	private:
 		const IShipyard * shipyard;
-		TSubgoal decomposeSingle() const override;
 
 	public:
 		BuildBoat(const IShipyard * shipyard)

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

@@ -32,4 +32,23 @@ bool BuildThis::operator==(const BuildThis & other) const
 std::string BuildThis::toString() const
 {
 	return "Build " + buildingInfo.name + "(" + std::to_string(bid) + ") in " + town->name;
+}
+
+void BuildThis::accept(VCAI * ai)
+{
+	auto b = BuildingID(bid);
+
+	if(town)
+	{
+		if(cb->canBuildStructure(town, b) == EBuildingState::ALLOWED)
+		{
+			logAi->debug("Player %d will build %s in town of %s at %s",
+				ai->playerID, town->town->buildings.at(b)->Name(), town->name, town->pos.toString());
+			cb->buildBuilding(town, b);
+
+			return;
+		}
+	}
+
+	throw cannotFulfillGoalException("Cannot build a given structure!");
 }

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

@@ -49,5 +49,6 @@ namespace Goals
 		}
 		virtual bool operator==(const BuildThis & other) const override;
 		virtual std::string toString() const override;
+		void accept(VCAI * ai) override;
 	};
 }

+ 45 - 0
AI/Nullkiller/Goals/BuyArmy.cpp

@@ -28,4 +28,49 @@ bool BuyArmy::operator==(const BuyArmy & other) const
 std::string BuyArmy::toString() const
 {
 	return "Buy army at " + town->name;
+}
+
+void BuyArmy::accept(VCAI * ai)
+{
+	ui64 valueBought = 0;
+	//buy the stacks with largest AI value
+
+	auto upgradeSuccessfull = ai->makePossibleUpgrades(town);
+
+	auto armyToBuy = ai->ah->getArmyAvailableToBuy(town->getUpperArmy(), town);
+
+	if(armyToBuy.empty())
+	{
+		if(upgradeSuccessfull)
+			return;
+
+		throw cannotFulfillGoalException("No creatures to buy.");
+	}
+
+	for(int i = 0; valueBought < value && i < armyToBuy.size(); i++)
+	{
+		auto res = cb->getResourceAmount();
+		auto & ci = armyToBuy[i];
+
+		if(objid != -1 && ci.creID != objid)
+			continue;
+
+		vstd::amin(ci.count, res / ci.cre->cost);
+
+		if(ci.count)
+		{
+			cb->recruitCreatures(town, town->getUpperArmy(), ci.creID, ci.count, ci.level);
+			valueBought += ci.count * ci.cre->AIValue;
+		}
+	}
+
+	if(!valueBought)
+	{
+		throw cannotFulfillGoalException("No creatures to buy.");
+	}
+
+	if(town->visitingHero)
+	{
+		ai->moveHeroToTile(town->visitablePos(), town->visitingHero.get());
+	}
 }

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

@@ -36,5 +36,7 @@ namespace Goals
 		virtual bool operator==(const BuyArmy & other) const override;
 
 		virtual std::string toString() const override;
+
+		virtual void accept(VCAI * ai) override;
 	};
 }

+ 17 - 16
AI/Nullkiller/Goals/CGoal.h

@@ -23,7 +23,6 @@ namespace Goals
 	public:
 		CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
 		{
-			isElementar = false;
 			isAbstract = true;
 			value = 0;
 			aid = -1;
@@ -33,7 +32,6 @@ namespace Goals
 			town = nullptr;
 		}
 
-		OSETTER(bool, isElementar)
 		OSETTER(bool, isAbstract)
 		OSETTER(int, value)
 		OSETTER(int, resID)
@@ -48,15 +46,6 @@ namespace Goals
 		{
 			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;
-		}
 		template<typename Handler> void serialize(Handler & h, const int version)
 		{
 			h & static_cast<AbstractGoal &>(*this);
@@ -76,7 +65,12 @@ namespace Goals
 
 		virtual TGoalVec decompose() const override
 		{
-			return {decomposeSingle()};
+			TSubgoal single = decomposeSingle();
+
+			if(single->invalid())
+				return {};
+			
+			return {single};
 		}
 
 	protected:
@@ -89,18 +83,20 @@ namespace Goals
 	template<typename T> class DLL_EXPORT ElementarGoal : public CGoal<T>, public ITask
 	{
 	public:
-		ElementarGoal<T>(EGoals goal = INVALID) : CGoal(goal)
+		ElementarGoal<T>(EGoals goal = INVALID) : CGoal(goal), ITask()
 		{
-			priority = 0;
-			isElementar = true;
 			isAbstract = false;
 		}
 
+		ElementarGoal<T>(const ElementarGoal<T> & other) : CGoal(other), ITask(other)
+		{
+		}
+
 		///Visitor pattern
 		//TODO: make accept work for std::shared_ptr... somehow
 		virtual void accept(VCAI * ai) override //unhandled goal will report standard error
 		{
-			ai->tryRealize(*this);
+			ai->tryRealize(*((T *)this));
 		}
 
 		T & setpriority(float p)
@@ -109,6 +105,11 @@ namespace Goals
 
 			return *((T *)this);
 		}
+
+		virtual bool isElementar() const override
+		{
+			return true;
+		}
 	};
 
 	class DLL_EXPORT Invalid : public ElementarGoal<Invalid>

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

@@ -0,0 +1,114 @@
+/*
+* 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 "Composition.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool Composition::operator==(const Composition & other) const
+{
+	return false;
+}
+
+std::string Composition::toString() const
+{
+	std::string result = "Composition";
+
+	for(auto goal : subtasks)
+	{
+		result += " " + goal->toString();
+	}
+
+	return result;
+}
+
+void Composition::accept(VCAI * ai)
+{
+	taskptr(*subtasks.back())->accept(ai);
+}
+
+TGoalVec Composition::decompose() const
+{
+	if(isElementar())
+		return subtasks;
+
+	auto tasks = subtasks;
+	tasks.pop_back();
+
+	TSubgoal last = subtasks.back();
+	auto decomposed = last->decompose();
+	TGoalVec result;
+
+	for(TSubgoal goal : decomposed)
+	{
+		if(goal->invalid() || goal == last || vstd::contains(tasks, goal))
+			continue;
+
+		auto newComposition = Composition(tasks);
+
+		if(goal->goalType == COMPOSITION)
+		{
+			Composition & other = dynamic_cast<Composition &>(*goal);
+			bool cancel = false;
+
+			for(auto goal : other.subtasks)
+			{
+				if(goal == last || vstd::contains(tasks, goal))
+				{
+					cancel = true;
+
+					break;
+				}
+
+				newComposition.addNext(goal);
+			}
+
+			if(cancel)
+				continue;
+		}
+		else
+		{
+			newComposition.addNext(goal);
+		}
+
+		result.push_back(sptr(newComposition));
+	}
+
+	return result;
+}
+
+Composition & Composition::addNext(AbstractGoal & goal)
+{
+	return addNext(sptr(goal));
+}
+
+Composition & Composition::addNext(TSubgoal goal)
+{
+	subtasks.push_back(goal);
+
+	return *this;
+}
+
+bool Composition::isElementar() const
+{
+	return subtasks.back()->isElementar();
+}

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

@@ -0,0 +1,40 @@
+/*
+* 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"
+
+namespace Goals
+{
+	class DLL_EXPORT Composition : public ElementarGoal<Composition>
+	{
+	private:
+		TGoalVec subtasks;
+
+	public:
+		Composition()
+			: ElementarGoal(Goals::COMPOSITION), subtasks()
+		{
+		}
+
+		Composition(TGoalVec subtasks)
+			: ElementarGoal(Goals::COMPOSITION), subtasks(subtasks)
+		{
+		}
+
+		virtual bool operator==(const Composition & other) const override;
+		virtual std::string toString() const override;
+		void accept(VCAI * ai) override;
+		Composition & addNext(AbstractGoal & goal);
+		Composition & addNext(TSubgoal goal);
+		virtual TGoalVec decompose() const override;
+		virtual bool isElementar() const override;
+	};
+}

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

@@ -9,7 +9,6 @@
 */
 #include "StdInc.h"
 #include "DigAtTile.h"
-#include "VisitTile.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
 

+ 9 - 14
AI/Nullkiller/Goals/ExecuteHeroChain.cpp

@@ -9,7 +9,6 @@
 */
 #include "StdInc.h"
 #include "ExecuteHeroChain.h"
-#include "VisitTile.h"
 #include "../VCAI.h"
 #include "../FuzzyHelper.h"
 #include "../AIhelper.h"
@@ -26,7 +25,6 @@ using namespace Goals;
 ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj)
 	:ElementarGoal(Goals::EXECUTE_HERO_CHAIN), chainPath(path)
 {
-
 	hero = path.targetHero;
 	tile = path.targetTile();
 
@@ -43,7 +41,10 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance *
 
 bool ExecuteHeroChain::operator==(const ExecuteHeroChain & other) const
 {
-	return false;
+	return tile == other.tile 
+		&& chainPath.targetHero == other.chainPath.targetHero
+		&& chainPath.nodes.size() == other.chainPath.nodes.size()
+		&& chainPath.chainMask == other.chainPath.chainMask;
 }
 
 void ExecuteHeroChain::accept(VCAI * ai)
@@ -79,19 +80,13 @@ void ExecuteHeroChain::accept(VCAI * ai)
 
 				if(node.specialAction)
 				{
-					if(node.specialAction->canAct(hero))
-					{
-						auto specialGoal = node.specialAction->whatToDo(hero);
-
-						if(!specialGoal->isElementar)
-
-						specialGoal->accept(ai);
-					}
-					else
+					if(node.actionIsBlocked)
 					{
 						throw cannotFulfillGoalException("Path is nondeterministic.");
 					}
 					
+					node.specialAction->execute(hero);
+					
 					if(!heroPtr.validAndSet())
 					{
 						logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name);
@@ -192,9 +187,9 @@ std::string ExecuteHeroChain::toString() const
 
 bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile)
 {
-	if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
+	if(tile == hero->visitablePos() && cb->getVisitableObjs(hero->visitablePos()).size() < 2)
 	{
-		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
+		logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", hero->name, tile.toString());
 
 		return true;
 	}

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

@@ -20,6 +20,8 @@ namespace Goals
 		std::string targetName;
 
 	public:
+		float closestWayRatio;
+
 		ExecuteHeroChain(const AIPath & path, const CGObjectInstance * obj = nullptr);
 
 		

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

@@ -9,7 +9,6 @@
 */
 #include "StdInc.h"
 #include "FindObj.h"
-#include "VisitObj.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
 

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

@@ -11,32 +11,4 @@
 
 #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
-		}
-		FindObj(int ID, int subID)
-			: CGoal(Goals::FIND_OBJ)
-		{
-			objid = ID;
-			resID = subID;
-		}
-		virtual bool operator==(const FindObj & other) const override;
-
-	private:
-		//TSubgoal decomposeSingle() const override;
-	};
-}
+#error not supported

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

@@ -9,7 +9,6 @@
 */
 #include "StdInc.h"
 #include "GetArtOfType.h"
-#include "FindObj.h"
 #include "../VCAI.h"
 #include "../AIUtility.h"
 

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

@@ -20,5 +20,4 @@
 #include "RecruitHero.h"
 #include "GetArtOfType.h"
 #include "DigAtTile.h"
-#include "FindObj.h"
 #include "AdventureSpellCast.h"

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

@@ -27,4 +27,22 @@ using namespace Goals;
 std::string RecruitHero::toString() const
 {
 	return "Recruit hero at " + town->name;
+}
+
+void RecruitHero::accept(VCAI * ai)
+{
+	auto t = town;
+
+	if(!t) t = ai->findTownWithTavern();
+
+	if(t)
+	{
+		ai->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!");
+	}
 }

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

@@ -39,5 +39,6 @@ namespace Goals
 		}
 
 		virtual std::string toString() const override;
+		void accept(VCAI * ai) override;
 	};
 }

+ 9 - 6
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -1033,13 +1033,16 @@ void AINodeStorage::fillChainInfo(const AIPathNode * node, AIPath & path, int pa
 			pathNode.coord = node->coord;
 			pathNode.parentIndex = parentIndex;
 
+			if(pathNode.specialAction)
+			{
+				pathNode.actionIsBlocked = !pathNode.specialAction->canAct(node);
+			}
+
 			parentIndex = path.nodes.size();
 
 			path.nodes.push_back(pathNode);
 		}
-
-		path.specialAction = node->specialAction;
-
+		
 		node = getAINode(node->theNodeBefore);
 	}
 }
@@ -1049,15 +1052,15 @@ AIPath::AIPath()
 {
 }
 
-std::shared_ptr<const ISpecialAction> AIPath::getFirstBlockedAction() const
+std::shared_ptr<const SpecialAction> AIPath::getFirstBlockedAction() const
 {
 	for(auto node : nodes)
 	{
-		if(node.specialAction && !node.specialAction->canAct(node.targetHero))
+		if(node.specialAction && node.actionIsBlocked)
 			return node.specialAction;
 	}
 
-	return std::shared_ptr<const ISpecialAction>();
+	return std::shared_ptr<const SpecialAction>();
 }
 
 int3 AIPath::firstTileToGet() const

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

@@ -10,15 +10,15 @@
 
 #pragma once
 
-#define VCMI_TRACE_PATHFINDER 1
-#define AI_TRACE_LEVEL 1
+#define VCMI_TRACE_PATHFINDER 2
+#define AI_TRACE_LEVEL 2
 
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../AIUtility.h"
 #include "../FuzzyHelper.h"
 #include "../Goals/AbstractGoal.h"
-#include "Actions/ISpecialAction.h"
+#include "Actions/SpecialAction.h"
 #include "Actors.h"
 
 struct AIPathNode : public CGPathNode
@@ -27,7 +27,7 @@ struct AIPathNode : public CGPathNode
 	uint64_t armyLoss;
 	uint32_t manaCost;
 	const AIPathNode * chainOther;
-	std::shared_ptr<const ISpecialAction> specialAction;
+	std::shared_ptr<const SpecialAction> specialAction;
 	const ChainActor * actor;
 };
 
@@ -40,13 +40,13 @@ struct AIPathNodeInfo
 	const CGHeroInstance * targetHero;
 	int parentIndex;
 	uint64_t chainMask;
-	std::shared_ptr<const ISpecialAction> specialAction;
+	std::shared_ptr<const SpecialAction> specialAction;
+	bool actionIsBlocked;
 };
 
 struct AIPath
 {
 	std::vector<AIPathNodeInfo> nodes;
-	std::shared_ptr<const ISpecialAction> specialAction;
 	uint64_t targetObjectDanger;
 	uint64_t armyLoss;
 	uint64_t targetObjectArmyLoss;
@@ -81,7 +81,7 @@ struct AIPath
 
 	std::string toString() const;
 
-	std::shared_ptr<const ISpecialAction> getFirstBlockedAction() const;
+	std::shared_ptr<const SpecialAction> getFirstBlockedAction() const;
 
 	bool containsHero(const CGHeroInstance * hero) const;
 };

+ 34 - 3
AI/Nullkiller/Pathfinding/Actions/BattleAction.cpp

@@ -9,13 +9,44 @@
 */
 
 #include "StdInc.h"
-#include "../../Goals/VisitTile.h"
 #include "BattleAction.h"
+#include "../../VCAI.h"
+#include "../../Behaviors/CompleteQuestBehavior.h"
+#include "../../../../lib/mapping/CMap.h" //for victory conditions
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
 
 namespace AIPathfinding
 {
-	Goals::TSubgoal BattleAction::whatToDo(const CGHeroInstance * hero) const
+	void BattleAction::execute(const CGHeroInstance * hero) const
+	{
+		ai->moveHeroToTile(targetTile, hero);
+	}
+
+	std::string BattleAction::toString() const
+	{
+		return "Battle at " + targetTile.toString();
+	}
+
+	bool QuestAction::canAct(const AIPathNode * node) const
+	{
+		QuestInfo q = questInfo;
+		return q.quest->checkQuest(node->actor->hero);
+	}
+
+	Goals::TSubgoal QuestAction::decompose(const CGHeroInstance * hero) const
+	{
+		return Goals::sptr(Goals::Invalid());
+	}
+
+	void QuestAction::execute(const CGHeroInstance * hero) const
+	{
+		ai->moveHeroToTile(questInfo.obj->visitablePos(), hero);
+	}
+
+	std::string QuestAction::toString() const
 	{
-		return Goals::sptr(Goals::VisitTile(targetTile).sethero(hero));
+		return "Complete Quest";
 	}
 }

+ 26 - 3
AI/Nullkiller/Pathfinding/Actions/BattleAction.h

@@ -10,11 +10,12 @@
 
 #pragma once
 
-#include "ISpecialAction.h"
+#include "SpecialAction.h"
+#include "../../../../lib/CGameState.h"
 
 namespace AIPathfinding
 {
-	class BattleAction : public ISpecialAction
+	class BattleAction : public SpecialAction
 	{
 	private:
 		const int3 targetTile;
@@ -25,6 +26,28 @@ namespace AIPathfinding
 		{
 		}
 
-		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
+		virtual void execute(const CGHeroInstance * hero) const override;
+
+		virtual std::string toString() const override;
+	};
+
+	class QuestAction : public SpecialAction
+	{
+	private:
+		QuestInfo questInfo;
+
+	public:
+		QuestAction(QuestInfo questInfo)
+			:questInfo(questInfo)
+		{
+		}
+
+		virtual bool canAct(const AIPathNode * node) const override;
+
+		virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
+
+		virtual void execute(const CGHeroInstance * hero) const override;
+
+		virtual std::string toString() const override;
 	};
 }

+ 59 - 5
AI/Nullkiller/Pathfinding/Actions/BoatActions.cpp

@@ -10,16 +10,30 @@
 
 #include "StdInc.h"
 #include "../../Goals/AdventureSpellCast.h"
+#include "../../Behaviors/CaptureObjectsBehavior.h"
 #include "../../Goals/BuildBoat.h"
 #include "../../../../lib/mapping/CMap.h"
 #include "../../../../lib/mapObjects/MapObjects.h"
 #include "BoatActions.h"
 
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+
 namespace AIPathfinding
 {
-	Goals::TSubgoal BuildBoatAction::whatToDo(const CGHeroInstance * hero) const
+	void BuildBoatAction::execute(const CGHeroInstance * hero) const
+	{
+		return Goals::BuildBoat(shipyard).accept(ai.get());
+	}
+
+	Goals::TSubgoal BuildBoatAction::decompose(const CGHeroInstance * hero) const
 	{
-		return Goals::sptr(Goals::BuildBoat(shipyard));
+		if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
+		{
+			return Goals::sptr(Goals::CaptureObjectsBehavior(shipyard->o));
+		}
+		
+		return sptr(Goals::Invalid());
 	}
 
 	const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const
@@ -27,9 +41,9 @@ namespace AIPathfinding
 		return sourceActor->resourceActor;
 	}
 
-	Goals::TSubgoal SummonBoatAction::whatToDo(const CGHeroInstance * hero) const
+	void SummonBoatAction::execute(const CGHeroInstance * hero) const
 	{
-		return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT));
+		Goals::AdventureSpellCast(hero, SpellID::SUMMON_BOAT).accept(ai.get());
 	}
 
 	const ChainActor * SummonBoatAction::getActor(const ChainActor * sourceActor) const
@@ -48,8 +62,43 @@ namespace AIPathfinding
 		dstMode->theNodeBefore = source.node;
 	}
 
-	bool SummonBoatAction::isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const
+	bool BuildBoatAction::canAct(const AIPathNode * source) const
+	{
+		auto hero = source->actor->hero;
+
+		if(cb->getPlayerRelations(hero->tempOwner, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
+		{
+#if AI_TRACE_LEVEL > 1
+			logAi->trace("Can not build a boat. Shipyard is enemy.");
+#endif
+			return false;
+		}
+
+		TResources boatCost;
+
+		shipyard->getBoatCost(boatCost);
+
+		if(!cb->getResourceAmount().canAfford(source->actor->armyCost + boatCost))
+		{
+#if AI_TRACE_LEVEL > 1
+			logAi->trace("Can not build a boat. Not enough resources.");
+#endif
+
+			return false;
+		}
+
+		return true;
+	}
+
+	std::string BuildBoatAction::toString() const
 	{
+		return "Build Boat at " + shipyard->o->getObjectName();
+	}
+
+	bool SummonBoatAction::canAct(const AIPathNode * source) const
+	{
+		auto hero = source->actor->hero;
+
 #ifdef VCMI_TRACE_PATHFINDER
 		logAi->trace(
 			"Hero %s has %d mana and needed %d and already spent %d",
@@ -62,6 +111,11 @@ namespace AIPathfinding
 		return hero->mana >= source->manaCost + getManaCost(hero);
 	}
 
+	std::string SummonBoatAction::toString() const
+	{
+		return "Summon Boat";
+	}
+
 	uint32_t SummonBoatAction::getManaCost(const CGHeroInstance * hero) const
 	{
 		SpellID summonBoat = SpellID::SUMMON_BOAT;

+ 16 - 5
AI/Nullkiller/Pathfinding/Actions/BoatActions.h

@@ -10,13 +10,13 @@
 
 #pragma once
 
-#include "ISpecialAction.h"
+#include "SpecialAction.h"
 #include "../../../../lib/mapping/CMap.h"
 #include "../../../../lib/mapObjects/MapObjects.h"
 
 namespace AIPathfinding
 {
-	class VirtualBoatAction : public ISpecialAction
+	class VirtualBoatAction : public SpecialAction
 	{
 	public:
 		virtual const ChainActor * getActor(const ChainActor * sourceActor) const = 0;
@@ -24,8 +24,11 @@ namespace AIPathfinding
 	
 	class SummonBoatAction : public VirtualBoatAction
 	{
+	private:
+		const CGHeroInstance * hero;
+
 	public:
-		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
+		virtual void execute(const CGHeroInstance * hero) const override;
 
 		virtual void applyOnDestination(
 			const CGHeroInstance * hero,
@@ -34,10 +37,12 @@ namespace AIPathfinding
 			AIPathNode * dstMode,
 			const AIPathNode * srcNode) const override;
 
-		bool isAffordableBy(const CGHeroInstance * hero, const AIPathNode * source) const;
+		virtual bool canAct(const AIPathNode * source) const;
 
 		virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
 
+		virtual std::string toString() const override;
+
 	private:
 		uint32_t getManaCost(const CGHeroInstance * hero) const;
 	};
@@ -53,8 +58,14 @@ namespace AIPathfinding
 		{
 		}
 
-		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
+		virtual bool canAct(const AIPathNode * source) const;
+
+		virtual void execute(const CGHeroInstance * hero) const override;
+
+		virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const override;
 
 		virtual const ChainActor * getActor(const ChainActor * sourceActor) const override;
+
+		virtual std::string toString() const override;
 	};
 }

+ 25 - 0
AI/Nullkiller/Pathfinding/Actions/SpecialAction.cpp

@@ -0,0 +1,25 @@
+/*
+* SpecialAction.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
+*
+*/
+
+#pragma once
+
+#include "SpecialAction.h"
+#include "../../VCAI.h"
+#include "../../Goals/CGoal.h"
+
+Goals::TSubgoal SpecialAction::decompose(const CGHeroInstance * hero) const
+{
+	return Goals::sptr(Goals::Invalid());
+}
+
+void SpecialAction::execute(const CGHeroInstance * hero) const
+{
+	throw cannotFulfillGoalException("Can not execute " + toString());
+}

+ 8 - 4
AI/Nullkiller/Pathfinding/Actions/ISpecialAction.h → AI/Nullkiller/Pathfinding/Actions/SpecialAction.h

@@ -1,5 +1,5 @@
 /*
-* ISpecialAction.h, part of VCMI engine
+* SpecialAction.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -15,15 +15,17 @@
 
 struct AIPathNode;
 
-class ISpecialAction
+class SpecialAction
 {
 public:
-	virtual bool canAct(const CGHeroInstance * hero) const
+	virtual bool canAct(const AIPathNode * source) const
 	{
 		return true;
 	}
 
-	virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const = 0;
+	virtual Goals::TSubgoal decompose(const CGHeroInstance * hero) const;
+
+	virtual void execute(const CGHeroInstance * hero) const;
 
 	virtual void applyOnDestination(
 		const CGHeroInstance * hero,
@@ -33,4 +35,6 @@ public:
 		const AIPathNode * srcNode) const
 	{
 	}
+
+	virtual std::string toString() const = 0;
 };

+ 36 - 4
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp

@@ -16,9 +16,41 @@
 
 using namespace AIPathfinding;
 
-Goals::TSubgoal TownPortalAction::whatToDo(const CGHeroInstance * hero) const
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+
+void TownPortalAction::execute(const CGHeroInstance * hero) const
+{
+	auto goal = Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL);
+	
+	goal.town = target;
+	goal.tile = target->visitablePos();
+
+	goal.accept(ai.get());
+}
+
+std::string TownPortalAction::toString() const
+{
+	return "Town Portal to " + target->name;
+}
+/*
+bool TownPortalAction::canAct(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 >= source->manaCost + getManaCost(hero);
+}
+
+uint32_t TownPortalAction::getManaCost(const CGHeroInstance * hero) const
 {
-	const CGTownInstance * targetTown = target; // const pointer is not allowed in settown
+	SpellID summonBoat = SpellID::TOWN_PORTAL;
 
-	return Goals::sptr(Goals::AdventureSpellCast(hero, SpellID::TOWN_PORTAL).settown(targetTown).settile(targetTown->visitablePos()));
-}
+	return hero->getSpellCost(summonBoat.toSpell());
+}*/

+ 5 - 3
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.h

@@ -10,14 +10,14 @@
 
 #pragma once
 
-#include "ISpecialAction.h"
+#include "SpecialAction.h"
 #include "../../../../lib/mapping/CMap.h"
 #include "../../../../lib/mapObjects/MapObjects.h"
 #include "../../Goals/AdventureSpellCast.h"
 
 namespace AIPathfinding
 {
-	class TownPortalAction : public ISpecialAction
+	class TownPortalAction : public SpecialAction
 	{
 	private:
 		const CGTownInstance * target;
@@ -28,6 +28,8 @@ namespace AIPathfinding
 		{
 		}
 
-		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override;
+		virtual void execute(const CGHeroInstance * hero) const override;
+
+		virtual std::string toString() const override;
 	};
 }

+ 0 - 1
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -9,7 +9,6 @@
 */
 #include "StdInc.h"
 #include "Actors.h"
-#include "../Goals/VisitHero.h"
 #include "../VCAI.h"
 #include "../AIhelper.h"
 #include "../../../CCallback.h"

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

@@ -13,7 +13,7 @@
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../AIUtility.h"
-#include "Actions/ISpecialAction.h"
+#include "Actions/SpecialAction.h"
 
 class HeroActor;
 class VCAI;
@@ -102,7 +102,7 @@ private:
 	void setupSpecialActors();
 
 public:
-	std::shared_ptr<ISpecialAction> exchangeAction;
+	std::shared_ptr<SpecialAction> exchangeAction;
 	// chain flags, can be combined meaning hero exchange and so on
 
 	HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai);

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

@@ -101,7 +101,7 @@ namespace AIPathfinding
 			const CGHeroInstance * hero = nodeStorage->getHero(source.node);
 
 			if(vstd::contains(summonableVirtualBoats, hero)
-				&& summonableVirtualBoats.at(hero)->isAffordableBy(hero, nodeStorage->getAINode(source.node)))
+				&& summonableVirtualBoats.at(hero)->canAct(nodeStorage->getAINode(source.node)))
 			{
 				virtualBoat = summonableVirtualBoats.at(hero);
 			}

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

@@ -14,24 +14,6 @@
 
 namespace AIPathfinding
 {
-	class QuestAction : public ISpecialAction
-	{
-	public:
-		QuestAction(QuestInfo questInfo)
-		{
-		}
-
-		virtual bool canAct(const CGHeroInstance * hero) const override
-		{
-			return false;
-		}
-
-		virtual Goals::TSubgoal whatToDo(const CGHeroInstance * hero) const override
-		{
-			return Goals::sptr(Goals::Invalid());
-		}
-	};
-
 	AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
 		CPlayerSpecificInfoCallback * cb, 
 		std::shared_ptr<AINodeStorage> nodeStorage)
@@ -218,9 +200,9 @@ namespace AIPathfinding
 		}
 
 		auto hero = nodeStorage->getHero(source.node);
-		auto danger = nodeStorage->evaluateDanger(destination.coord, hero, true);
-		double actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
-		double loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
+		uint64_t danger = nodeStorage->evaluateDanger(destination.coord, hero, true);
+		uint64_t actualArmyValue = srcNode->actor->armyValue - srcNode->armyLoss;
+		uint64_t loss = nodeStorage->evaluateArmyLoss(hero, actualArmyValue, danger);
 
 		if(loss < actualArmyValue)
 		{

+ 0 - 97
AI/Nullkiller/VCAI.cpp

@@ -36,8 +36,6 @@ const float SAFE_ATTACK_CONSTANT = 1.5;
 boost::thread_specific_ptr<CCallback> cb;
 boost::thread_specific_ptr<VCAI> ai;
 
-//std::map<int, std::map<int, int> > HeroView::infosCount;
-
 //helper RAII to manage global ai/cb ptrs
 struct SetGlobalState
 {
@@ -365,16 +363,6 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
 	vstd::erase_if_present(visitableObjs, obj);
 	vstd::erase_if_present(alreadyVisited, 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;
-	};
-
 	//TODO: Find better way to handle hero boat removal
 	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
 	{
@@ -1544,42 +1532,6 @@ void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
 	cb->buildBuilding(t, building); //just do this;
 }
 
-void VCAI::tryRealize(Goals::RecruitHero & g)
-{
-	const CGTownInstance * t = g.town;
-
-	if(!t) t = findTownWithTavern();
-
-	if(t)
-	{
-		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::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->town->buildings.at(b)->Name(), t->name, t->pos.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?
@@ -1638,55 +1590,6 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
 	}
 }
 
-void VCAI::tryRealize(Goals::BuyArmy & g)
-{
-	auto t = g.town;
-
-	ui64 valueBought = 0;
-	//buy the stacks with largest AI value
-
-	auto upgradeSuccessfull = makePossibleUpgrades(t);
-
-	auto armyToBuy = ah->getArmyAvailableToBuy(t->getUpperArmy(), t);
-
-	if(armyToBuy.empty())
-	{
-		if(upgradeSuccessfull)
-			throw goalFulfilledException(sptr(g));
-
-		throw cannotFulfillGoalException("No creatures to buy.");
-	}
-
-	for (int i = 0; valueBought < g.value && i < armyToBuy.size(); i++)
-	{
-		auto res = cb->getResourceAmount();
-		auto & ci = armyToBuy[i];
-
-		if(g.objid != -1 && ci.creID != g.objid)
-			continue;
-
-		vstd::amin(ci.count, res / ci.cre->cost);
-
-		if(ci.count)
-		{
-			cb->recruitCreatures(t, t->getUpperArmy(), ci.creID, ci.count, ci.level);
-			valueBought += ci.count * ci.cre->AIValue;
-		}
-	}
-
-	if(!valueBought)
-	{
-		throw cannotFulfillGoalException("No creatures to buy.");
-	}
-
-	if(t->visitingHero)
-	{
-		moveHeroToTile(t->visitablePos(), t->visitingHero.get());
-	}
-
-	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!");

+ 0 - 3
AI/Nullkiller/VCAI.h

@@ -117,11 +117,8 @@ public:
 	virtual ~VCAI();
 
 	//TODO: use only smart pointers?
-	void tryRealize(Goals::RecruitHero & 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);