From d347db4c164dc5a61fac0d7d0b96e0f018356923 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 22 Apr 2023 14:47:02 +0300 Subject: [PATCH] Fix hero count calculation for resourceful ai mod --- AI/Nullkiller/AIGateway.cpp | 40 ---------------- AI/Nullkiller/AIGateway.h | 5 -- AI/Nullkiller/Analyzers/HeroManager.cpp | 47 +++++++++++++++++++ AI/Nullkiller/Analyzers/HeroManager.h | 8 +++- .../Behaviors/RecruitHeroBehavior.cpp | 2 +- AI/Nullkiller/Behaviors/StartupBehavior.cpp | 2 +- AI/Nullkiller/Goals/RecruitHero.cpp | 2 - AI/Nullkiller/Pathfinding/AINodeStorage.cpp | 2 +- 8 files changed, 57 insertions(+), 51 deletions(-) diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 598be14ad..49e0f81d0 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -1058,27 +1058,6 @@ void AIGateway::recruitCreatures(const CGDwelling * d, const CArmedInstance * re } } -bool AIGateway::canRecruitAnyHero(const CGTownInstance * t) const -{ - //TODO: make gathering gold, building tavern or conquering town (?) possible subgoals - if(!t) - t = findTownWithTavern(); - - if(!t || !townHasFreeTavern(t)) - return false; - - if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager - return false; - if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES) - return false; - if(cb->getHeroesInfo().size() >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP)) - return false; - if(!cb->getAvailableHeroes(t).size()) - return false; - - return true; -} - void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) { NET_EVENT_HANDLER; @@ -1160,16 +1139,6 @@ void AIGateway::addVisitableObj(const CGObjectInstance * obj) } } -HeroPtr AIGateway::getHeroWithGrail() const -{ - for(const CGHeroInstance * h : cb->getHeroesInfo()) - { - if(h->hasArt(ArtifactID::GRAIL)) - return h; - } - return nullptr; -} - bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h) { if(h->inTownGarrison && h->visitedTown) @@ -1437,15 +1406,6 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade } } -const CGTownInstance * AIGateway::findTownWithTavern() const -{ - for(const CGTownInstance * t : cb->getTownsInfo()) - if(townHasFreeTavern(t)) - return t; - - return nullptr; -} - void AIGateway::endTurn() { logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr()); diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index a3115e3e6..d42afd31f 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -198,11 +198,6 @@ public: void retrieveVisitableObjs(); virtual std::vector getFlaggedObjects() const; - HeroPtr getHeroWithGrail() const; - - const CGTownInstance * findTownWithTavern() const; - bool canRecruitAnyHero(const CGTownInstance * t = NULL) const; - 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 diff --git a/AI/Nullkiller/Analyzers/HeroManager.cpp b/AI/Nullkiller/Analyzers/HeroManager.cpp index 572a88694..116411633 100644 --- a/AI/Nullkiller/Analyzers/HeroManager.cpp +++ b/AI/Nullkiller/Analyzers/HeroManager.cpp @@ -12,6 +12,8 @@ #include "../Engine/Nullkiller.h" #include "../../../lib/mapObjects/MapObjects.h" #include "../../../lib/CHeroHandler.h" +#include "../../../lib/GameSettings.h" +#include "../../../lib/CGameState.h" namespace NKAI { @@ -179,6 +181,51 @@ float HeroManager::evaluateHero(const CGHeroInstance * hero) const return evaluateFightingStrength(hero); } +bool HeroManager::canRecruitHero(const CGTownInstance * town) const +{ + if(!town) + town = findTownWithTavern(); + + if(!town || !townHasFreeTavern(town)) + return false; + + if(cb->getResourceAmount(EGameResID::GOLD) < GameConstants::HERO_GOLD_COST) + return false; + + const bool includeGarnisoned = true; + int heroCount = cb->getHeroCount(ai->playerID, includeGarnisoned); + + if(heroCount >= ALLOWED_ROAMING_HEROES) + return false; + + if(heroCount >= VLC->settings()->getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP)) + return false; + + if(!cb->getAvailableHeroes(town).size()) + return false; + + return true; +} + +const CGTownInstance * HeroManager::findTownWithTavern() const +{ + for(const CGTownInstance * t : cb->getTownsInfo()) + if(townHasFreeTavern(t)) + return t; + + return nullptr; +} + +const CGHeroInstance * HeroManager::findHeroWithGrail() const +{ + for(const CGHeroInstance * h : cb->getHeroesInfo()) + { + if(h->hasArt(ArtifactID::GRAIL)) + return h; + } + return nullptr; +} + SecondarySkillScoreMap::SecondarySkillScoreMap(std::map scoreMap) :scoreMap(scoreMap) { diff --git a/AI/Nullkiller/Analyzers/HeroManager.h b/AI/Nullkiller/Analyzers/HeroManager.h index 459604bef..9c98443f3 100644 --- a/AI/Nullkiller/Analyzers/HeroManager.h +++ b/AI/Nullkiller/Analyzers/HeroManager.h @@ -30,6 +30,8 @@ public: virtual void update() = 0; virtual float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const = 0; virtual float evaluateHero(const CGHeroInstance * hero) const = 0; + virtual bool canRecruitHero(const CGTownInstance * t = nullptr) const = 0; + virtual const CGHeroInstance * findHeroWithGrail() const = 0; }; class DLL_EXPORT ISecondarySkillRule @@ -57,20 +59,24 @@ private: static SecondarySkillEvaluator scountSkillsScores; CCallback * cb; //this is enough, but we downcast from CCallback + const Nullkiller * ai; std::map heroRoles; public: - HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB) {} + HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {} const std::map & getHeroRoles() const override; HeroRole getHeroRole(const HeroPtr & hero) const override; int selectBestSkill(const HeroPtr & hero, const std::vector & skills) const override; void update() override; float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override; float evaluateHero(const CGHeroInstance * hero) const override; + bool canRecruitHero(const CGTownInstance * t = nullptr) const override; + const CGHeroInstance * findHeroWithGrail() const override; private: float evaluateFightingStrength(const CGHeroInstance * hero) const; float evaluateSpeciality(const CGHeroInstance * hero) const; + const CGTownInstance * findTownWithTavern() const; }; // basic skill scores. missing skills will have score of 0 diff --git a/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp b/AI/Nullkiller/Behaviors/RecruitHeroBehavior.cpp index ab37ce028..950ce9cd0 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(ai->canRecruitAnyHero(town)) + if(ai->nullkiller->heroManager->canRecruitHero(town)) { auto availableHeroes = cb->getAvailableHeroes(town); diff --git a/AI/Nullkiller/Behaviors/StartupBehavior.cpp b/AI/Nullkiller/Behaviors/StartupBehavior.cpp index 91ef62344..448b2386e 100644 --- a/AI/Nullkiller/Behaviors/StartupBehavior.cpp +++ b/AI/Nullkiller/Behaviors/StartupBehavior.cpp @@ -66,7 +66,7 @@ const CGHeroInstance * getNearestHero(const CGTownInstance * town) bool needToRecruitHero(const CGTownInstance * startupTown) { - if(!ai->canRecruitAnyHero(startupTown)) + if(!ai->nullkiller->heroManager->canRecruitHero(startupTown)) return false; if(!startupTown->garrisonHero && !startupTown->visitingHero) diff --git a/AI/Nullkiller/Goals/RecruitHero.cpp b/AI/Nullkiller/Goals/RecruitHero.cpp index d6cfe2908..ab596f801 100644 --- a/AI/Nullkiller/Goals/RecruitHero.cpp +++ b/AI/Nullkiller/Goals/RecruitHero.cpp @@ -33,8 +33,6 @@ void RecruitHero::accept(AIGateway * ai) { auto t = town; - if(!t) t = ai->findTownWithTavern(); - if(!t) { throw cannotFulfillGoalException("No town to recruit hero!"); diff --git a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp index 0f80c0949..1f03dbb0b 100644 --- a/AI/Nullkiller/Pathfinding/AINodeStorage.cpp +++ b/AI/Nullkiller/Pathfinding/AINodeStorage.cpp @@ -1093,7 +1093,7 @@ void AINodeStorage::calculateTownPortal( if(nodeOptional) { #if NKAI_PATHFINDER_TRACE_LEVEL >= 1 - logAi->trace("Adding town portal node at %s", targetTown->name); + logAi->trace("Adding town portal node at %s", targetTown->getObjectName()); #endif output.push_back(nodeOptional.value()); }