Browse Source

NKAI: hire retreated hero

Andrii Danylchenko 3 years ago
parent
commit
153cccdf46

+ 1 - 29
AI/Nullkiller/AIGateway.cpp

@@ -1401,7 +1401,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
 const CGTownInstance * AIGateway::findTownWithTavern() const
 {
 	for(const CGTownInstance * t : cb->getTownsInfo())
-		if(t->hasBuilt(BuildingID::TAVERN) && !t->visitingHero)
+		if(t->hasBuilt(BuildingID::TAVERN) && (!t->visitingHero || !t->garrisonHero))
 			return t;
 
 	return nullptr;
@@ -1432,34 +1432,6 @@ void AIGateway::buildArmyIn(const CGTownInstance * t)
 	moveCreaturesToHero(t);
 }
 
-void AIGateway::recruitHero(const CGTownInstance * t, bool throwing)
-{
-	logAi->debug("Trying to recruit a hero in %s at %s", t->name, 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);
-		nullkiller->heroManager->update();
-
-		if(t->visitingHero)
-			moveHeroToTile(t->visitablePos(), t->visitingHero.get());
-
-		throw goalFulfilledException(sptr(Goals::RecruitHero(t)));
-	}
-	else if(throwing)
-	{
-		throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
-	}
-}
-
 void AIGateway::finish()
 {
 	//we want to lock to avoid multiple threads from calling makingTurn->join() at same time

+ 0 - 1
AI/Nullkiller/AIGateway.h

@@ -177,7 +177,6 @@ public:
 	void endTurn();
 
 	// TODO: all the routines like recruiting hero or building army should be removed from here and extracted to elementar goals or whatever
-	void recruitHero(const CGTownInstance * t, bool throwing = false);
 	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);

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

@@ -34,10 +34,40 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 	Goals::TGoalVec tasks;
 	auto towns = cb->getTownsInfo();
 
+	auto ourHeroes = ai->nullkiller->heroManager->getHeroRoles();
+	auto minScoreToHireMain = std::numeric_limits<float>::max();
+
+	for(auto hero : ourHeroes)
+	{
+		if(hero.second != HeroRole::MAIN)
+			continue;
+
+		auto newScore = ai->nullkiller->heroManager->evaluateHero(hero.first.get());
+
+		if(minScoreToHireMain > newScore)
+		{
+			// weakest main hero score
+			minScoreToHireMain = newScore;
+		}
+	}
+
 	for(auto town : towns)
 	{
-		if(!town->garrisonHero && !town->visitingHero && ai->canRecruitAnyHero(town))
+		if((!town->garrisonHero || !town->visitingHero) && ai->canRecruitAnyHero(town))
 		{
+			auto availableHeroes = cb->getAvailableHeroes(town);
+
+			for(auto hero : availableHeroes)
+			{
+				auto score = ai->nullkiller->heroManager->evaluateHero(hero);
+
+				if(score > minScoreToHireMain)
+				{
+					tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(200)));
+					break;
+				}
+			}
+
 			if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
 				|| (ai->nullkiller->getFreeResources()[Res::GOLD] > 10000
 					&& ai->nullkiller->buildAnalyzer->getGoldPreasure() < MAX_GOLD_PEASURE))

+ 38 - 7
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -35,16 +35,47 @@ void RecruitHero::accept(AIGateway * ai)
 
 	if(!t) t = ai->findTownWithTavern();
 
-	if(t)
+	if(!t)
 	{
-		ai->recruitHero(t, true);
-		//TODO try to free way to blocked town
-		//TODO: adventure map tavern or prison?
+		throw cannotFulfillGoalException("No town to recruit hero!");
 	}
-	else
+
+	logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
+
+	auto heroes = cb->getAvailableHeroes(t);
+
+	if(!heroes.size())
 	{
-		throw cannotFulfillGoalException("No town to recruit hero!");
+		throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
 	}
-}
 
+	auto heroToHire = heroes[0];
+
+	for(auto hero : heroes)
+	{
+		if(objid == hero->id.getNum())
+		{
+			heroToHire = hero;
+			break;
+		}
+
+		if(hero->getTotalStrength() > heroToHire->getTotalStrength())
+			heroToHire = hero;
+	}
+
+	if(t->visitingHero)
+	{
+		if(t->garrisonHero)
+			throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!");
+
+		cb->swapGarrisonHero(t);
+	}
+
+	cb->recruitHero(t, heroToHire);
+	ai->nullkiller->heroManager->update();
+
+	if(t->visitingHero)
+		ai->moveHeroToTile(t->visitablePos(), t->visitingHero.get());
 }
+
+}