mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Merge pull request #2051 from vcmi/nkai-fix-recruiting-hero
Nkai fix recruiting hero
This commit is contained in:
@@ -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)
|
void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side)
|
||||||
{
|
{
|
||||||
NET_EVENT_HANDLER;
|
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)
|
bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
|
||||||
{
|
{
|
||||||
if(h->inTownGarrison && h->visitedTown)
|
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()
|
void AIGateway::endTurn()
|
||||||
{
|
{
|
||||||
logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr());
|
logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr());
|
||||||
|
@@ -198,11 +198,6 @@ public:
|
|||||||
void retrieveVisitableObjs();
|
void retrieveVisitableObjs();
|
||||||
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
|
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 requestSent(const CPackForServer * pack, int requestID) override;
|
||||||
void answerQuery(QueryID queryID, int selection);
|
void answerQuery(QueryID queryID, int selection);
|
||||||
//special function that can be called ONLY from game events handling thread and will send request ASAP
|
//special function that can be called ONLY from game events handling thread and will send request ASAP
|
||||||
|
@@ -12,6 +12,8 @@
|
|||||||
#include "../Engine/Nullkiller.h"
|
#include "../Engine/Nullkiller.h"
|
||||||
#include "../../../lib/mapObjects/MapObjects.h"
|
#include "../../../lib/mapObjects/MapObjects.h"
|
||||||
#include "../../../lib/CHeroHandler.h"
|
#include "../../../lib/CHeroHandler.h"
|
||||||
|
#include "../../../lib/GameSettings.h"
|
||||||
|
#include "../../../lib/CGameState.h"
|
||||||
|
|
||||||
namespace NKAI
|
namespace NKAI
|
||||||
{
|
{
|
||||||
@@ -179,6 +181,51 @@ float HeroManager::evaluateHero(const CGHeroInstance * hero) const
|
|||||||
return evaluateFightingStrength(hero);
|
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)
|
SecondarySkillScoreMap::SecondarySkillScoreMap(std::map<SecondarySkill, float> scoreMap)
|
||||||
:scoreMap(scoreMap)
|
:scoreMap(scoreMap)
|
||||||
{
|
{
|
||||||
|
@@ -30,6 +30,8 @@ public:
|
|||||||
virtual void update() = 0;
|
virtual void update() = 0;
|
||||||
virtual float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const = 0;
|
virtual float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const = 0;
|
||||||
virtual float evaluateHero(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
|
class DLL_EXPORT ISecondarySkillRule
|
||||||
@@ -57,20 +59,24 @@ private:
|
|||||||
static SecondarySkillEvaluator scountSkillsScores;
|
static SecondarySkillEvaluator scountSkillsScores;
|
||||||
|
|
||||||
CCallback * cb; //this is enough, but we downcast from CCallback
|
CCallback * cb; //this is enough, but we downcast from CCallback
|
||||||
|
const Nullkiller * ai;
|
||||||
std::map<HeroPtr, HeroRole> heroRoles;
|
std::map<HeroPtr, HeroRole> heroRoles;
|
||||||
|
|
||||||
public:
|
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;
|
const std::map<HeroPtr, HeroRole> & getHeroRoles() const override;
|
||||||
HeroRole getHeroRole(const HeroPtr & hero) const override;
|
HeroRole getHeroRole(const HeroPtr & hero) const override;
|
||||||
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
|
int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
|
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
|
||||||
float evaluateHero(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:
|
private:
|
||||||
float evaluateFightingStrength(const CGHeroInstance * hero) const;
|
float evaluateFightingStrength(const CGHeroInstance * hero) const;
|
||||||
float evaluateSpeciality(const CGHeroInstance * hero) const;
|
float evaluateSpeciality(const CGHeroInstance * hero) const;
|
||||||
|
const CGTownInstance * findTownWithTavern() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// basic skill scores. missing skills will have score of 0
|
// basic skill scores. missing skills will have score of 0
|
||||||
|
@@ -209,7 +209,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
|
|||||||
{
|
{
|
||||||
captureObjects(ai->nullkiller->objectClusterizer->getNearbyObjects());
|
captureObjects(ai->nullkiller->objectClusterizer->getNearbyObjects());
|
||||||
|
|
||||||
if(tasks.empty())
|
if(tasks.empty() || ai->nullkiller->getScanDepth() == ScanDepth::FULL)
|
||||||
captureObjects(ai->nullkiller->objectClusterizer->getFarObjects());
|
captureObjects(ai->nullkiller->objectClusterizer->getFarObjects());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -58,13 +58,6 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
|
||||||
auto treats = { treatNode.maximumDanger, treatNode.fastestDanger };
|
auto treats = { treatNode.maximumDanger, treatNode.fastestDanger };
|
||||||
|
|
||||||
if(!treatNode.fastestDanger.hero)
|
|
||||||
{
|
|
||||||
logAi->trace("No treat found for town %s", town->getNameTranslated());
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
int dayOfWeek = cb->getDate(Date::DAY_OF_WEEK);
|
||||||
|
|
||||||
if(town->garrisonHero)
|
if(town->garrisonHero)
|
||||||
@@ -91,6 +84,13 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!treatNode.fastestDanger.hero)
|
||||||
|
{
|
||||||
|
logAi->trace("No treat found for town %s", town->getNameTranslated());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
|
||||||
|
|
||||||
|
@@ -53,7 +53,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
|||||||
|
|
||||||
for(auto town : towns)
|
for(auto town : towns)
|
||||||
{
|
{
|
||||||
if(ai->canRecruitAnyHero(town))
|
if(ai->nullkiller->heroManager->canRecruitHero(town))
|
||||||
{
|
{
|
||||||
auto availableHeroes = cb->getAvailableHeroes(town);
|
auto availableHeroes = cb->getAvailableHeroes(town);
|
||||||
|
|
||||||
|
@@ -66,7 +66,7 @@ const CGHeroInstance * getNearestHero(const CGTownInstance * town)
|
|||||||
|
|
||||||
bool needToRecruitHero(const CGTownInstance * startupTown)
|
bool needToRecruitHero(const CGTownInstance * startupTown)
|
||||||
{
|
{
|
||||||
if(!ai->canRecruitAnyHero(startupTown))
|
if(!ai->nullkiller->heroManager->canRecruitHero(startupTown))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(!startupTown->garrisonHero && !startupTown->visitingHero)
|
if(!startupTown->garrisonHero && !startupTown->visitingHero)
|
||||||
|
@@ -117,7 +117,7 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior, int decompositi
|
|||||||
void Nullkiller::resetAiState()
|
void Nullkiller::resetAiState()
|
||||||
{
|
{
|
||||||
lockedResources = TResources();
|
lockedResources = TResources();
|
||||||
scanDepth = ScanDepth::SMALL;
|
scanDepth = ScanDepth::FULL;
|
||||||
playerID = ai->playerID;
|
playerID = ai->playerID;
|
||||||
lockedHeroes.clear();
|
lockedHeroes.clear();
|
||||||
dangerHitMap->reset();
|
dangerHitMap->reset();
|
||||||
|
@@ -88,6 +88,7 @@ public:
|
|||||||
int32_t getFreeGold() const { return getFreeResources()[EGameResID::GOLD]; }
|
int32_t getFreeGold() const { return getFreeResources()[EGameResID::GOLD]; }
|
||||||
void lockResources(const TResources & res);
|
void lockResources(const TResources & res);
|
||||||
const TResources & getLockedResources() const { return lockedResources; }
|
const TResources & getLockedResources() const { return lockedResources; }
|
||||||
|
ScanDepth getScanDepth() const { return scanDepth; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resetAiState();
|
void resetAiState();
|
||||||
|
@@ -33,8 +33,6 @@ void RecruitHero::accept(AIGateway * ai)
|
|||||||
{
|
{
|
||||||
auto t = town;
|
auto t = town;
|
||||||
|
|
||||||
if(!t) t = ai->findTownWithTavern();
|
|
||||||
|
|
||||||
if(!t)
|
if(!t)
|
||||||
{
|
{
|
||||||
throw cannotFulfillGoalException("No town to recruit hero!");
|
throw cannotFulfillGoalException("No town to recruit hero!");
|
||||||
|
@@ -1093,7 +1093,7 @@ void AINodeStorage::calculateTownPortal(
|
|||||||
if(nodeOptional)
|
if(nodeOptional)
|
||||||
{
|
{
|
||||||
#if NKAI_PATHFINDER_TRACE_LEVEL >= 1
|
#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
|
#endif
|
||||||
output.push_back(nodeOptional.value());
|
output.push_back(nodeOptional.value());
|
||||||
}
|
}
|
||||||
|
@@ -53,15 +53,13 @@ namespace AIPathfinding
|
|||||||
|
|
||||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||||
{
|
{
|
||||||
// do not allow ally shipyards because of bug
|
if(t->hasBuilt(BuildingID::SHIPYARD))
|
||||||
if(t->hasBuilt(BuildingID::SHIPYARD) && t->getOwner() == ai->playerID)
|
|
||||||
shipyards.push_back(t);
|
shipyards.push_back(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
|
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
|
||||||
{
|
{
|
||||||
// do not allow ally shipyards because of bug
|
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
|
||||||
if(obj->ID != Obj::TOWN && obj->getOwner() == ai->playerID) //towns were handled in the previous loop
|
|
||||||
{
|
{
|
||||||
if(const IShipyard * shipyard = IShipyard::castFrom(obj))
|
if(const IShipyard * shipyard = IShipyard::castFrom(obj))
|
||||||
shipyards.push_back(shipyard);
|
shipyards.push_back(shipyard);
|
||||||
|
Reference in New Issue
Block a user