Browse Source

NKAI: fix retreat logic

Andrii Danylchenko 3 years ago
parent
commit
e9c725181c

+ 5 - 2
AI/BattleAI/BattleAI.cpp

@@ -734,8 +734,8 @@ boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
 	bs.canFlee = cb->battleCanFlee();
 	bs.canSurrender = cb->battleCanSurrender(playerID);
 	bs.ourSide = cb->battleGetMySide();
-	bs.ourHero = cb->battleGetMyHero();
-	bs.enemyHero = cb->battleGetFightingHero(!bs.ourSide);
+	bs.ourHero = cb->battleGetMyHero(); 
+	bs.enemyHero = nullptr;
 
 	for(auto stack : cb->battleGetAllStacks(false))
 	{
@@ -744,7 +744,10 @@ boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
 			if(stack->side == bs.ourSide)
 				bs.ourStacks.push_back(stack);
 			else
+			{
 				bs.enemyStacks.push_back(stack);
+				bs.enemyHero = cb->battleGetOwnerHero(stack);
+			}
 		}
 	}
 

+ 6 - 3
AI/Nullkiller/AIGateway.cpp

@@ -497,7 +497,8 @@ boost::optional<BattleAction> AIGateway::makeSurrenderRetreatDecision(
 
 	double fightRatio = battleState.getOurStrength() / (double)battleState.getEnemyStrength();
 
-	if(fightRatio < 0.3 && battleState.canFlee)
+	// if we have no towns - things are already bad, so retreat is not an option.
+	if(cb->getTownsInfo().size() && fightRatio < 0.3 && battleState.canFlee)
 	{
 		return BattleAction::makeRetreat(battleState.ourSide);
 	}
@@ -1035,8 +1036,10 @@ bool AIGateway::canRecruitAnyHero(const CGTownInstance * t) const
 	//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
 	if(!t)
 		t = findTownWithTavern();
-	if(!t)
+
+	if(!t || !townHasFreeTavern(t))
 		return false;
+
 	if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
 		return false;
 	if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
@@ -1401,7 +1404,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 || !t->garrisonHero))
+		if(townHasFreeTavern(t))
 			return t;
 
 	return nullptr;

+ 10 - 0
AI/Nullkiller/AIUtility.cpp

@@ -450,4 +450,14 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
 	return true;
 }
 
+bool townHasFreeTavern(const CGTownInstance * town)
+{
+	if(!town->hasBuilt(BuildingID::TAVERN)) return false;
+	if(!town->visitingHero) return true;
+
+	bool canMoveVisitingHeroToGarnison = !town->getUpperArmy()->stacksCount();
+
+	return canMoveVisitingHeroToGarnison;
+}
+
 }

+ 1 - 0
AI/Nullkiller/AIUtility.h

@@ -238,6 +238,7 @@ bool isSafeToVisit(HeroPtr h, const CCreatureSet *, uint64_t dangerStrength);
 bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
 bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
 bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
+bool townHasFreeTavern(const CGTownInstance * town);
 
 uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start);
 

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

@@ -53,7 +53,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
 
 	for(auto town : towns)
 	{
-		if((!town->garrisonHero || !town->visitingHero) && ai->canRecruitAnyHero(town))
+		if(ai->canRecruitAnyHero(town))
 		{
 			auto availableHeroes = cb->getAvailableHeroes(town);
 

+ 3 - 3
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -65,12 +65,12 @@ void RecruitHero::accept(AIGateway * ai)
 
 	if(t->visitingHero)
 	{
-		if(t->garrisonHero)
-			throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!");
-
 		cb->swapGarrisonHero(t);
 	}
 
+	if(t->visitingHero)
+		throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!");
+
 	cb->recruitHero(t, heroToHire);
 	ai->nullkiller->heroManager->update();