diff --git a/AI/BattleAI/BattleAI.cpp b/AI/BattleAI/BattleAI.cpp index 61327e548..366c07f6a 100644 --- a/AI/BattleAI/BattleAI.cpp +++ b/AI/BattleAI/BattleAI.cpp @@ -734,8 +734,8 @@ boost::optional 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 CBattleAI::considerFleeingOrSurrendering() if(stack->side == bs.ourSide) bs.ourStacks.push_back(stack); else + { bs.enemyStacks.push_back(stack); + bs.enemyHero = cb->battleGetOwnerHero(stack); + } } } diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 269de851a..f22efda7c 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -497,7 +497,8 @@ boost::optional 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; diff --git a/AI/Nullkiller/AIUtility.cpp b/AI/Nullkiller/AIUtility.cpp index 52c127c75..6343316fa 100644 --- a/AI/Nullkiller/AIUtility.cpp +++ b/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; +} + } diff --git a/AI/Nullkiller/AIUtility.h b/AI/Nullkiller/AIUtility.h index 68fa115f6..e7cc57ecb 100644 --- a/AI/Nullkiller/AIUtility.h +++ b/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 start); diff --git a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp index f4c753510..82a146549 100644 --- a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp +++ b/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); diff --git a/AI/Nullkiller/Goals/RecruitHero.cpp b/AI/Nullkiller/Goals/RecruitHero.cpp index 3e011b3b8..f01776252 100644 --- a/AI/Nullkiller/Goals/RecruitHero.cpp +++ b/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();