浏览代码

Fix hero count calculation for resourceful ai mod

Andrii Danylchenko 2 年之前
父节点
当前提交
d347db4c16

+ 0 - 40
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());

+ 0 - 5
AI/Nullkiller/AIGateway.h

@@ -198,11 +198,6 @@ public:
 	void retrieveVisitableObjs();
 	virtual std::vector<const CGObjectInstance *> 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

+ 47 - 0
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<SecondarySkill, float> scoreMap)
 	:scoreMap(scoreMap)
 {

+ 7 - 1
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<HeroPtr, HeroRole> heroRoles;
 
 public:
-	HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB) {}
+	HeroManager(CCallback * CB, const Nullkiller * ai) : cb(CB), ai(ai) {}
 	const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
 	HeroRole getHeroRole(const HeroPtr & hero) const override;
 	int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & 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

+ 1 - 1
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);
 

+ 1 - 1
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)

+ 0 - 2
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!");

+ 1 - 1
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());
 			}