1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Nullkiller: remove AIhelper + refactoring

This commit is contained in:
Andrii Danylchenko 2020-05-04 18:58:43 +03:00 committed by Andrii Danylchenko
parent f832a8b3cd
commit 1806dd8447
67 changed files with 598 additions and 1721 deletions

View File

@ -10,20 +10,16 @@
#include "StdInc.h"
#include "AIUtility.h"
#include "VCAI.h"
#include "FuzzyHelper.h"
#include "Goals/Goals.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/CHeroHandler.h"
#include "../../lib/mapObjects/CBank.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/CQuest.h"
#include "../../lib/mapObjects/MapObjects.h"
#include "../../lib/mapping/CMapDefines.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
//extern static const int3 dirs[8];
@ -194,11 +190,6 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns
return ln->cost < rn->cost;
}
bool isSafeToVisit(HeroPtr h, crint3 tile)
{
return isSafeToVisit(h, fh->evaluateDanger(tile, h.get()));
}
bool isSafeToVisit(HeroPtr h, const CCreatureSet * heroArmy, uint64_t dangerStrength)
{
const ui64 heroStrength = h->getFightingStrength() * heroArmy->getArmyStrength();
@ -338,3 +329,26 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
else
return art1->price > art2->price;
}
bool isWeeklyRevisitable(const CGObjectInstance * obj)
{
//TODO: allow polling of remaining creatures in dwelling
if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
return true;
if(dynamic_cast<const CGDwelling *>(obj))
return true;
if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
return true;
switch(obj->ID)
{
case Obj::STABLES:
case Obj::MAGIC_WELL:
case Obj::HILL_FORT:
return true;
case Obj::BORDER_GATE:
case Obj::BORDERGUARD:
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
}
return false;
}

View File

@ -173,12 +173,10 @@ bool isObjectPassable(const CGObjectInstance * obj, PlayerColor playerColor, Pla
bool isBlockVisitObj(const int3 & pos);
bool isWeeklyRevisitable(const CGObjectInstance * obj);
bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic to object property!
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
bool isSafeToVisit(HeroPtr h, const CCreatureSet *, uint64_t dangerStrength);
bool isSafeToVisit(HeroPtr h, crint3 tile);
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);

View File

@ -1,136 +0,0 @@
/*
* AIhelper.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AIhelper.h"
AIhelper::AIhelper()
{
pathfindingManager.reset(new PathfindingManager());
armyManager.reset(new ArmyManager());
heroManager.reset(new HeroManager());
}
AIhelper::~AIhelper()
{
}
void AIhelper::init(CPlayerSpecificInfoCallback * CB)
{
pathfindingManager->init(CB);
armyManager->init(CB);
heroManager->init(CB);
}
void AIhelper::setAI(VCAI * AI)
{
pathfindingManager->setAI(AI);
armyManager->setAI(AI);
heroManager->setAI(AI);
}
void AIhelper::update()
{
armyManager->update();
heroManager->update();
}
std::vector<AIPath> AIhelper::getPathsToTile(const HeroPtr & hero, const int3 & tile) const
{
return pathfindingManager->getPathsToTile(hero, tile);
}
std::vector<AIPath> AIhelper::getPathsToTile(const int3 & tile) const
{
return pathfindingManager->getPathsToTile(tile);
}
void AIhelper::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
{
pathfindingManager->updatePaths(heroes, useHeroChain);
}
uint64_t AIhelper::evaluateStackPower(const CCreature * creature, int count) const
{
return armyManager->evaluateStackPower(creature, count);
}
SlotInfo AIhelper::getTotalCreaturesAvailable(CreatureID creatureID) const
{
return armyManager->getTotalCreaturesAvailable(creatureID);
}
bool AIhelper::canGetArmy(const CArmedInstance * army, const CArmedInstance * source) const
{
return armyManager->canGetArmy(army, source);
}
ui64 AIhelper::howManyReinforcementsCanBuy(const CCreatureSet * h, const CGDwelling * t) const
{
return armyManager->howManyReinforcementsCanBuy(h, t);
}
ui64 AIhelper::howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const
{
return armyManager->howManyReinforcementsCanGet(target, source);
}
std::vector<SlotInfo> AIhelper::getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const
{
return armyManager->getBestArmy(target, source);
}
std::vector<SlotInfo>::iterator AIhelper::getWeakestCreature(std::vector<SlotInfo> & army) const
{
return armyManager->getWeakestCreature(army);
}
std::vector<SlotInfo> AIhelper::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
{
return armyManager->getSortedSlots(target, source);
}
std::vector<creInfo> AIhelper::getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const
{
return armyManager->getArmyAvailableToBuy(hero, dwelling);
}
ArmyUpgradeInfo AIhelper::calculateCreateresUpgrade(
const CCreatureSet * army,
const CGObjectInstance * upgrader,
const TResources & availableResources) const
{
return armyManager->calculateCreateresUpgrade(army, upgrader, availableResources);
}
int AIhelper::selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const
{
return heroManager->selectBestSkill(hero, skills);
}
const std::map<HeroPtr, HeroRole> & AIhelper::getHeroRoles() const
{
return heroManager->getHeroRoles();
}
HeroRole AIhelper::getHeroRole(const HeroPtr & hero) const
{
return heroManager->getHeroRole(hero);
}
float AIhelper::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const
{
return heroManager->evaluateSecSkill(skill, hero);
}
float AIhelper::evaluateHero(const CGHeroInstance * hero) const
{
return heroManager->evaluateHero(hero);
}

View File

@ -1,73 +0,0 @@
/*
* AIhelper.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
/*
!!! Note: Include THIS file at the end of include list to avoid "undefined base class" error
*/
#include "ArmyManager.h"
#include "HeroManager.h"
#include "Pathfinding/PathfindingManager.h"
class ResourceManager;
class BuildingManager;
//TODO: remove class, put managers to engine
class DLL_EXPORT AIhelper : public IPathfindingManager, public IArmyManager, public IHeroManager
{
//std::shared_ptr<ResourceManager> resourceManager;
//std::shared_ptr<BuildingManager> buildingManager;
std::shared_ptr<PathfindingManager> pathfindingManager;
std::shared_ptr<ArmyManager> armyManager;
std::shared_ptr<HeroManager> heroManager;
//TODO: vector<IAbstractManager>
public:
AIhelper();
~AIhelper();
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
STRONG_INLINE
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
{
return pathfindingManager->isTileAccessible(hero, tile);
}
bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override;
ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override;
ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<SlotInfo> getBestArmy(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<SlotInfo>::iterator getWeakestCreature(std::vector<SlotInfo> & army) const override;
std::vector<SlotInfo> getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const override;
std::vector<creInfo> getArmyAvailableToBuy(const CCreatureSet * hero, const CGDwelling * dwelling) const override;
uint64_t evaluateStackPower(const CCreature * creature, int count) const override;
SlotInfo getTotalCreaturesAvailable(CreatureID creatureID) const override;
ArmyUpgradeInfo calculateCreateresUpgrade(
const CCreatureSet * army,
const CGObjectInstance * upgrader,
const TResources & availableResources) const override;
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;
float evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const override;
float evaluateHero(const CGHeroInstance * hero) const override;
void update() override;
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
};

View File

@ -10,7 +10,7 @@
#include "StdInc.h"
#include "ArmyManager.h"
#include "../Engine/Nullkiller.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/MapObjects.h"
@ -31,16 +31,6 @@ public:
}
};
void ArmyManager::init(CPlayerSpecificInfoCallback * CB)
{
cb = CB;
}
void ArmyManager::setAI(VCAI * AI)
{
ai = AI;
}
std::vector<SlotInfo> ArmyManager::getSortedSlots(const CCreatureSet * target, const CCreatureSet * source) const
{
const CCreatureSet * armies[] = { target, source };

View File

@ -40,8 +40,6 @@ struct ArmyUpgradeInfo
class DLL_EXPORT IArmyManager //: public: IAbstractManager
{
public:
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
virtual void setAI(VCAI * AI) = 0;
virtual void update() = 0;
virtual bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const = 0;
virtual ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const = 0;
@ -64,14 +62,12 @@ class DLL_EXPORT ArmyManager : public IArmyManager
{
private:
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
VCAI * ai;
const Nullkiller * ai;
std::map<CreatureID, SlotInfo> totalArmy;
public:
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
ArmyManager(CPlayerSpecificInfoCallback * CB, const Nullkiller * ai): cb(CB), ai(ai) {}
void update() override;
bool canGetArmy(const CArmedInstance * target, const CArmedInstance * source) const override;
ui64 howManyReinforcementsCanBuy(const CCreatureSet * target, const CGDwelling * source) const override;
ui64 howManyReinforcementsCanGet(const CCreatureSet * target, const CCreatureSet * source) const override;

View File

@ -12,7 +12,6 @@
#include "lib/mapping/CMap.h" //for victory conditions
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
{

View File

@ -10,9 +10,7 @@
#include "StdInc.h"
#include "DangerHitMapAnalyzer.h"
#include "lib/mapping/CMap.h" //for victory conditions
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
#include "../Engine/Nullkiller.h"
void DangerHitMapAnalyzer::updateHitMap()
{
@ -21,17 +19,18 @@ void DangerHitMapAnalyzer::updateHitMap()
upToDate = true;
auto mapSize = cb->getMapSize();
auto cb = ai->cb.get();
auto mapSize = ai->cb->getMapSize();
hitMap.resize(boost::extents[mapSize.x][mapSize.y][mapSize.z]);
enemyHeroAccessibleObjects.clear();
std::map<PlayerColor, std::vector<HeroPtr>> heroes;
std::map<PlayerColor, std::vector<const CGHeroInstance *>> heroes;
for(const CGObjectInstance * obj : ai->visitableObjs)
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
{
if(obj->ID == Obj::HERO)
{
HeroPtr hero = dynamic_cast<const CGHeroInstance *>(obj);
auto hero = dynamic_cast<const CGHeroInstance *>(obj);
heroes[hero->tempOwner].push_back(hero);
}
@ -43,11 +42,11 @@ void DangerHitMapAnalyzer::updateHitMap()
for(auto pair : heroes)
{
ai->ah->updatePaths(pair.second, false);
ai->pathfinder->updatePaths(pair.second, false);
foreach_tile_pos([&](const int3 & pos)
{
for(AIPath & path : ai->ah->getPathsToTile(pos))
for(AIPath & path : ai->pathfinder->getPathInfo(pos))
{
if(path.getFirstBlockedAction())
continue;

View File

@ -10,7 +10,6 @@
#pragma once
#include "../VCAI.h"
#include "../AIhelper.h"
struct HitMapInfo
{
@ -44,8 +43,11 @@ private:
boost::multi_array<HitMapNode, 3> hitMap;
std::map<const CGHeroInstance *, std::set<const CGObjectInstance *>> enemyHeroAccessibleObjects;
bool upToDate;
const Nullkiller * ai;
public:
DangerHitMapAnalyzer(const Nullkiller * ai) :ai(ai) {}
void updateHitMap();
uint64_t enemyCanKillOurHeroesAlongThePath(const AIPath & path) const;
const HitMapNode & getObjectTreat(const CGObjectInstance * obj) const;

View File

@ -10,7 +10,7 @@
#include "StdInc.h"
#include "HeroManager.h"
#include "../Engine/Nullkiller.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/MapObjects.h"
#include "../../lib/CHeroHandler.h"
@ -57,16 +57,6 @@ SecondarySkillEvaluator HeroManager::scountSkillsScores = SecondarySkillEvaluato
std::make_shared<ExistingSkillRule>()
});
void HeroManager::init(CPlayerSpecificInfoCallback * CB)
{
cb = CB;
}
void HeroManager::setAI(VCAI * AI)
{
ai = AI;
}
float HeroManager::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance * hero) const
{
auto role = getHeroRole(hero);
@ -106,11 +96,11 @@ void HeroManager::update()
logAi->trace("Start analysing our heroes");
std::map<HeroPtr, float> scores;
auto myHeroes = ai->getMyHeroes();
auto myHeroes = cb->getHeroesInfo();
for(auto & hero : myHeroes)
{
scores[hero] = evaluateFightingStrength(hero.get());
scores[hero] = evaluateFightingStrength(hero);
}
std::sort(myHeroes.begin(), myHeroes.end(), [&](const HeroPtr & h1, const HeroPtr & h2) -> bool
@ -120,10 +110,10 @@ void HeroManager::update()
int mainHeroCount = (myHeroes.size() + 2) / 3;
for(auto & hero : myHeroes)
for(auto hero : myHeroes)
{
heroRoles[hero] = (mainHeroCount--) > 0 ? HeroRole::MAIN : HeroRole::SCOUT;
logAi->trace("Hero %s has role %s", hero.name, heroRoles[hero] == HeroRole::MAIN ? "main" : "scout");
logAi->trace("Hero %s has role %s", hero->name, heroRoles[hero] == HeroRole::MAIN ? "main" : "scout");
}
}

View File

@ -21,8 +21,6 @@
class DLL_EXPORT IHeroManager //: public: IAbstractManager
{
public:
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
virtual void setAI(VCAI * AI) = 0;
virtual const std::map<HeroPtr, HeroRole> & getHeroRoles() const = 0;
virtual int selectBestSkill(const HeroPtr & hero, const std::vector<SecondarySkill> & skills) const = 0;
virtual HeroRole getHeroRole(const HeroPtr & hero) const = 0;
@ -55,12 +53,11 @@ private:
static SecondarySkillEvaluator scountSkillsScores;
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
VCAI * ai;
const Nullkiller * ai;
std::map<HeroPtr, HeroRole> heroRoles;
public:
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
HeroManager(CPlayerSpecificInfoCallback * 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;

View File

@ -14,9 +14,6 @@
#include "../Engine/Nullkiller.h"
#include "lib/mapping/CMap.h" //for victory conditions
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
void ObjectCluster::addObject(const CGObjectInstance * obj, const AIPath & path, float priority)
{
auto & info = objects[obj];
@ -93,12 +90,12 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
{
for(auto node = path.nodes.rbegin(); node != path.nodes.rend(); node++)
{
auto guardPos = cb->getGuardingCreaturePosition(node->coord);
auto blockers = cb->getVisitableObjs(node->coord);
auto guardPos = ai->cb->getGuardingCreaturePosition(node->coord);
auto blockers = ai->cb->getVisitableObjs(node->coord);
if(guardPos.valid())
{
auto guard = cb->getTopObj(cb->getGuardingCreaturePosition(node->coord));
auto guard = ai->cb->getTopObj(ai->cb->getGuardingCreaturePosition(node->coord));
if(guard)
{
@ -126,7 +123,7 @@ const CGObjectInstance * ObjectClusterizer::getBlocker(const AIPath & path) cons
return nullptr;
}
bool shouldVisitObject(const CGObjectInstance * obj)
bool ObjectClusterizer::shouldVisitObject(const CGObjectInstance * obj) const
{
if(isObjectRemovable(obj))
{
@ -135,13 +132,13 @@ bool shouldVisitObject(const CGObjectInstance * obj)
const int3 pos = obj->visitablePos();
if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->alreadyVisited, obj)
if(obj->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->memory->alreadyVisited, obj)
|| obj->wasVisited(ai->playerID))
{
return false;
}
auto playerRelations = cb->getPlayerRelations(ai->playerID, obj->tempOwner);
auto playerRelations = ai->cb->getPlayerRelations(ai->playerID, obj->tempOwner);
if(playerRelations != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
{
@ -151,12 +148,12 @@ bool shouldVisitObject(const CGObjectInstance * obj)
//it may be hero visiting this obj
//we don't try visiting object on which allied or owned hero stands
// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
const CGObjectInstance * topObj = cb->getTopObj(pos);
const CGObjectInstance * topObj = ai->cb->getTopObj(pos);
if(!topObj)
return false; // partly visible obj but its visitable pos is not visible.
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, topObj->tempOwner) != PlayerRelations::ENEMIES)
if(topObj->ID == Obj::HERO && ai->cb->getPlayerRelations(ai->playerID, topObj->tempOwner) != PlayerRelations::ENEMIES)
return false;
else
return true; //all of the following is met
@ -180,12 +177,12 @@ void ObjectClusterizer::clusterize()
logAi->debug("Begin object clusterization");
for(const CGObjectInstance * obj : ai->visitableObjs)
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
{
if(!shouldVisitObject(obj))
continue;
auto paths = ai->ah->getPathsToTile(obj->visitablePos());
auto paths = ai->pathfinder->getPathInfo(obj->visitablePos());
if(paths.empty())
continue;
@ -223,7 +220,7 @@ void ObjectClusterizer::clusterize()
if(!vstd::contains(cluster->objects, obj))
{
float priority = ai->nullkiller->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
float priority = ai->priorityEvaluator->evaluate(Goals::sptr(Goals::ExecuteHeroChain(path, obj)));
cluster->addObject(obj, path, priority);

View File

@ -51,6 +51,7 @@ private:
ObjectCluster nearObjects;
ObjectCluster farObjects;
std::map<const CGObjectInstance *, std::shared_ptr<ObjectCluster>> blockedObjects;
const Nullkiller * ai;
public:
void clusterize();
@ -59,8 +60,11 @@ public:
std::vector<std::shared_ptr<ObjectCluster>> getLockedClusters() const;
const CGObjectInstance * getBlocker(const AIPath & path) const;
ObjectClusterizer()
:nearObjects(), farObjects(), blockedObjects()
ObjectClusterizer(const Nullkiller * ai)
:nearObjects(), farObjects(), blockedObjects(), ai(ai)
{
}
private:
bool shouldVisitObject(const CGObjectInstance * obj) const;
};

View File

@ -10,16 +10,15 @@
#include "StdInc.h"
#include "BuildingBehavior.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/BuyArmy.h"
#include "../Goals/BuildThis.h"
#include "lib/mapping/CMap.h" //for victory conditions
#include "lib/CPathfinder.h"
#include "../Engine/Nullkiller.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -10,7 +10,6 @@
#include "StdInc.h"
#include "BuyArmyBehavior.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/BuyArmy.h"
#include "../Engine/Nullkiller.h"
@ -19,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -55,7 +53,7 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
{
targetHero = town->visitingHero.get();
if(ai->ah->howManyReinforcementsCanGet(targetHero, town->getUpperArmy()))
if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(targetHero, town->getUpperArmy()))
{
tasks.push_back(sptr(VisitTile(town->visitablePos()).sethero(targetHero).setpriority(5)));
@ -63,10 +61,10 @@ Goals::TGoalVec BuyArmyBehavior::decompose() const
}
}*/
auto reinforcement = ai->ah->howManyReinforcementsCanBuy(targetHero, town);
auto reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(targetHero, town);
if(reinforcement)
reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
if(reinforcement)
{

View File

@ -10,17 +10,16 @@
#include "StdInc.h"
#include "../VCAI.h"
#include "../Engine/Nullkiller.h"
#include "../AIhelper.h"
#include "../Goals/Composition.h"
#include "../Goals/ExecuteHeroChain.h"
#include "CaptureObjectsBehavior.h"
#include "../AIUtility.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/CModHandler.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -81,7 +80,7 @@ Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath>
auto hero = path.targetHero;
auto danger = path.getTotalDanger();
if(ai->ah->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::SCOUT && danger == 0 && path.exchangeCount > 1)
continue;
auto firstBlockedAction = path.getFirstBlockedAction();
@ -169,7 +168,7 @@ Goals::TGoalVec CaptureObjectsBehavior::decompose() const
const int3 pos = objToVisit->visitablePos();
auto paths = ai->ah->getPathsToTile(pos);
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
std::shared_ptr<ExecuteHeroChain> closestWay;
@ -214,3 +213,122 @@ bool CaptureObjectsBehavior::shouldVisitObject(const CGObjectInstance * obj) con
return true;
}
bool CaptureObjectsBehavior::shouldVisit(HeroPtr h, const CGObjectInstance * obj)
{
switch(obj->ID)
{
case Obj::TOWN:
case Obj::HERO: //never visit our heroes at random
return obj->tempOwner != h->tempOwner; //do not visit our towns at random
case Obj::BORDER_GATE:
{
for(auto q : ai->myCb->getMyQuests())
{
if(q.obj == obj)
{
return false; // do not visit guards or gates when wandering
}
}
return true; //we don't have this quest yet
}
case Obj::BORDERGUARD: //open borderguard if possible
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
case Obj::SEER_HUT:
case Obj::QUEST_GUARD:
{
for(auto q : ai->myCb->getMyQuests())
{
if(q.obj == obj)
{
if(q.quest->checkQuest(h.h))
return true; //we completed the quest
else
return false; //we can't complete this quest
}
}
return true; //we don't have this quest yet
}
case Obj::CREATURE_GENERATOR1:
{
if(obj->tempOwner != h->tempOwner)
return true; //flag just in case
const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
for(auto level : d->creatures)
{
for(auto c : level.second)
{
if(level.first
&& h->getSlotFor(CreatureID(c)) != SlotID()
&& cb->getResourceAmount().canAfford(c.toCreature()->cost))
{
return true;
}
}
}
return false;
}
case Obj::HILL_FORT:
{
for(auto slot : h->Slots())
{
if(slot.second->type->upgrades.size())
return true; //TODO: check price?
}
return false;
}
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
case Obj::MONOLITH_ONE_WAY_EXIT:
case Obj::MONOLITH_TWO_WAY:
case Obj::WHIRLPOOL:
return false;
case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR:
{
if(cb->getResourceAmount(Res::GOLD) < 1000)
return false;
break;
}
case Obj::LIBRARY_OF_ENLIGHTENMENT:
if(h->level < 12)
return false;
break;
case Obj::TREE_OF_KNOWLEDGE:
{
if(ai->nullkiller->heroManager->getHeroRole(h) == HeroRole::SCOUT)
return false;
TResources myRes = cb->getResourceAmount();
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
return false;
break;
}
case Obj::MAGIC_WELL:
return h->mana < h->manaLimit();
case Obj::PRISON:
return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
case Obj::TAVERN:
{
//TODO: make AI actually recruit heroes
//TODO: only on request
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
return false;
else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
return false;
break;
}
case Obj::BOAT:
return false;
//Boats are handled by pathfinder
case Obj::EYE_OF_MAGI:
return false; //this object is useless to visit, but could be visited indefinitely
}
if(obj->wasVisited(*h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
return false;
return true;
}

View File

@ -68,6 +68,7 @@ namespace Goals
private:
bool shouldVisitObject(const CGObjectInstance * obj) const;
static bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
};
}

View File

@ -11,7 +11,6 @@
#include "ClusterBehavior.h"
#include "../VCAI.h"
#include "../Engine/Nullkiller.h"
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/UnlockCluster.h"
#include "../Goals/Composition.h"
@ -19,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -44,7 +42,7 @@ Goals::TGoalVec ClusterBehavior::decompose() const
Goals::TGoalVec ClusterBehavior::decomposeCluster(std::shared_ptr<ObjectCluster> cluster) const
{
auto center = cluster->calculateCenter();
auto paths = ai->ah->getPathsToTile(center->visitablePos());
auto paths = ai->nullkiller->pathfinder->getPathInfo(center->visitablePos());
auto blockerPos = cluster->blocker->visitablePos();
std::vector<AIPath> blockerPaths;

View File

@ -11,13 +11,11 @@
#include "CompleteQuestBehavior.h"
#include "CaptureObjectsBehavior.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -11,10 +11,10 @@
#include "DefenceBehavior.h"
#include "../VCAI.h"
#include "../Engine/Nullkiller.h"
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/BuyArmy.h"
#include "../Goals/ExecuteHeroChain.h"
#include "../Goals/RecruitHero.h"
#include "../Goals/DismissHero.h"
#include "../Goals/ExchangeSwapTownHeroes.h"
#include "lib/mapping/CMap.h" //for victory conditions
@ -22,7 +22,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -98,7 +97,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
return;
}
uint64_t reinforcement = ai->ah->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
uint64_t reinforcement = ai->nullkiller->armyManager->howManyReinforcementsCanBuy(town->getUpperArmy(), town);
if(reinforcement)
{
@ -106,7 +105,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
}
auto paths = ai->ah->getPathsToTile(town->visitablePos());
auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
for(auto & treat : treats)
{
@ -168,7 +167,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
{
if(ai->nullkiller->isHeroLocked(existingHero)
|| existingHero->getArmyStrength() > hero->getArmyStrength()
|| ai->ah->getHeroRole(existingHero) == HeroRole::MAIN
|| ai->nullkiller->heroManager->getHeroRole(existingHero) == HeroRole::MAIN
|| existingHero->movement
|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
continue;
@ -262,7 +261,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
/*for(auto & treat : treats)
{
auto paths = ai->ah->getPathsToTile(treat.hero->visitablePos());
auto paths = ai->nullkiller->pathfinder->getPathInfo(treat.hero->visitablePos());
for(AIPath & path : paths)
{

View File

@ -10,7 +10,6 @@
#include "StdInc.h"
#include "../VCAI.h"
#include "../Engine/Nullkiller.h"
#include "../AIhelper.h"
#include "../Goals/ExecuteHeroChain.h"
#include "GatherArmyBehavior.h"
#include "../AIUtility.h"
@ -19,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -41,7 +39,7 @@ Goals::TGoalVec GatherArmyBehavior::decompose() const
for(const CGHeroInstance * hero : heroes)
{
if(ai->ah->getHeroRole(hero) == HeroRole::MAIN
if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::MAIN
&& hero->getArmyStrength() >= 300)
{
vstd::concatenate(tasks, deliverArmyToHero(hero));
@ -74,7 +72,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
return tasks;
}
auto paths = ai->ah->getPathsToTile(pos);
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
#if AI_TRACE_LEVEL >= 1
@ -106,7 +104,7 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her
continue;
}
float armyValue = (float)ai->ah->howManyReinforcementsCanGet(hero, path.heroArmy) / hero->getArmyStrength();
float armyValue = (float)ai->nullkiller->armyManager->howManyReinforcementsCanGet(hero, path.heroArmy) / hero->getArmyStrength();
// avoid transferring very small amount of army
if(armyValue < 0.1f)
@ -166,7 +164,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
logAi->trace("Checking ways to upgrade army in town %s, %s", upgrader->getObjectName(), pos.toString());
#endif
auto paths = ai->ah->getPathsToTile(pos);
auto paths = ai->nullkiller->pathfinder->getPathInfo(pos);
std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;
#if AI_TRACE_LEVEL >= 1
@ -203,7 +201,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader)
continue;
}
auto upgrade = ai->ah->calculateCreateresUpgrade(path.heroArmy, upgrader, availableResources);
auto upgrade = ai->nullkiller->armyManager->calculateCreateresUpgrade(path.heroArmy, upgrader, availableResources);
auto armyValue = (float)upgrade.upgradeValue / path.getHeroStrength();
if(armyValue < 0.1f || upgrade.upgradeValue < 300) // avoid small upgrades

View File

@ -10,7 +10,6 @@
#include "StdInc.h"
#include "RecruitHeroBehavior.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/RecruitHero.h"
#include "../Goals/ExecuteHeroChain.h"
@ -19,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -10,7 +10,6 @@
#include "StdInc.h"
#include "StartupBehavior.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../AIUtility.h"
#include "../Goals/RecruitHero.h"
#include "../Goals/ExecuteHeroChain.h"
@ -22,7 +21,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -46,7 +44,7 @@ const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPa
const CGHeroInstance * getNearestHero(const CGTownInstance * town)
{
auto paths = ai->ah->getPathsToTile(town->visitablePos());
auto paths = ai->nullkiller->pathfinder->getPathInfo(town->visitablePos());
if(paths.empty())
return nullptr;
@ -114,12 +112,12 @@ Goals::TGoalVec StartupBehavior::decompose() const
startupTown = *vstd::maxElementByFun(towns, [](const CGTownInstance * town) -> float
{
if(town->garrisonHero)
return ai->ah->evaluateHero(town->garrisonHero.get());
return ai->nullkiller->heroManager->evaluateHero(town->garrisonHero.get());
auto closestHero = getNearestHero(town);
if(closestHero)
return ai->ah->evaluateHero(closestHero);
return ai->nullkiller->heroManager->evaluateHero(closestHero);
return 0;
});
@ -132,9 +130,9 @@ Goals::TGoalVec StartupBehavior::decompose() const
{
if(!startupTown->visitingHero)
{
if(ai->ah->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), closestHero) > 200)
{
auto paths = ai->ah->getPathsToTile(startupTown->visitablePos());
auto paths = ai->nullkiller->pathfinder->getPathInfo(startupTown->visitablePos());
if(paths.size())
{
@ -147,22 +145,22 @@ Goals::TGoalVec StartupBehavior::decompose() const
else
{
auto visitingHero = startupTown->visitingHero.get();
auto visitingHeroScore = ai->ah->evaluateHero(visitingHero);
auto visitingHeroScore = ai->nullkiller->heroManager->evaluateHero(visitingHero);
if(startupTown->garrisonHero)
{
auto garrisonHero = startupTown->garrisonHero.get();
auto garrisonHeroScore = ai->ah->evaluateHero(garrisonHero);
auto garrisonHeroScore = ai->nullkiller->heroManager->evaluateHero(garrisonHero);
if(visitingHeroScore > garrisonHeroScore
|| ai->ah->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->ah->getHeroRole(visitingHero) == HeroRole::MAIN)
|| ai->nullkiller->heroManager->getHeroRole(garrisonHero) == HeroRole::SCOUT && ai->nullkiller->heroManager->getHeroRole(visitingHero) == HeroRole::MAIN)
{
if(canRecruitHero || ai->ah->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
if(canRecruitHero || ai->nullkiller->armyManager->howManyReinforcementsCanGet(visitingHero, garrisonHero) > 200)
{
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, visitingHero, HeroLockedReason::STARTUP).setpriority(100)));
}
}
else if(ai->ah->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
else if(ai->nullkiller->armyManager->howManyReinforcementsCanGet(garrisonHero, visitingHero) > 200)
{
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(startupTown, garrisonHero, HeroLockedReason::STARTUP).setpriority(100)));
}

View File

@ -4,7 +4,6 @@ set(VCAI_SRCS
Pathfinding/AIPathfinderConfig.cpp
Pathfinding/AIPathfinder.cpp
Pathfinding/AINodeStorage.cpp
Pathfinding/PathfindingManager.cpp
Pathfinding/Actors.cpp
Pathfinding/Actions/SpecialAction.cpp
Pathfinding/Actions/BattleAction.cpp
@ -15,12 +14,11 @@ set(VCAI_SRCS
Pathfinding/Rules/AIMovementToDestinationRule.cpp
Pathfinding/Rules/AIPreviousNodeRule.cpp
AIUtility.cpp
AIhelper.cpp
ArmyManager.cpp
HeroManager.cpp
MapObjectsEvaluator.cpp
FuzzyEngines.cpp
FuzzyHelper.cpp
Analyzers/ArmyManager.cpp
Analyzers/HeroManager.cpp
Engine/FuzzyEngines.cpp
Engine/FuzzyHelper.cpp
Engine/AIMemory.cpp
Goals/AbstractGoal.cpp
Goals/Composition.cpp
Goals/BuildBoat.cpp
@ -61,7 +59,6 @@ set(VCAI_HEADERS
Pathfinding/AIPathfinderConfig.h
Pathfinding/AIPathfinder.h
Pathfinding/AINodeStorage.h
Pathfinding/PathfindingManager.h
Pathfinding/Actors.h
Pathfinding/Actions/SpecialAction.h
Pathfinding/Actions/BattleAction.h
@ -72,12 +69,11 @@ set(VCAI_HEADERS
Pathfinding/Rules/AIMovementToDestinationRule.h
Pathfinding/Rules/AIPreviousNodeRule.h
AIUtility.h
AIhelper.h
ArmyManager.h
HeroManager.h
MapObjectsEvaluator.h
FuzzyEngines.h
FuzzyHelper.h
Analyzers/ArmyManager.h
Analyzers/HeroManager.h
Engine/FuzzyEngines.h
Engine/FuzzyHelper.h
Engine/AIMemory.h
Goals/AbstractGoal.h
Goals/CGoal.h
Goals/Composition.h

View File

@ -0,0 +1,104 @@
/*
* AIMemory.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "AIMemory.h"
#include "../../../lib/mapObjects/MapObjects.h"
void AIMemory::removeFromMemory(const CGObjectInstance * obj)
{
vstd::erase_if_present(visitableObjs, obj);
vstd::erase_if_present(alreadyVisited, obj);
//TODO: Find better way to handle hero boat removal
if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
{
if(hero->boat)
{
vstd::erase_if_present(visitableObjs, hero->boat);
vstd::erase_if_present(alreadyVisited, hero->boat);
}
}
}
void AIMemory::removeFromMemory(ObjectIdRef obj)
{
auto matchesId = [&](const CGObjectInstance * hlpObj) -> bool
{
return hlpObj->id == obj.id;
};
vstd::erase_if(visitableObjs, matchesId);
vstd::erase_if(alreadyVisited, matchesId);
}
void AIMemory::addSubterraneanGate(const CGObjectInstance * entrance, const CGObjectInstance * exit)
{
knownSubterraneanGates[entrance] = exit;
knownSubterraneanGates[exit] = entrance;
logAi->trace(
"Found a pair of subterranean gates between %s and %s!",
entrance->visitablePos().toString(),
exit->visitablePos().toString());
}
void AIMemory::addVisitableObject(const CGObjectInstance * obj)
{
visitableObjs.insert(obj);
// All teleport objects seen automatically assigned to appropriate channels
auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
if(teleportObj)
{
CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
}
}
void AIMemory::markObjectVisited(const CGObjectInstance * obj)
{
if(!obj)
return;
// TODO: maybe this logic belongs to CaptureObjects::shouldVisit
if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
return;
if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
return;
if(obj->ID == Obj::MONSTER)
return;
alreadyVisited.insert(obj);
}
void AIMemory::markObjectUnvisited(const CGObjectInstance * obj)
{
vstd::erase_if_present(alreadyVisited, obj);
}
bool AIMemory::wasVisited(const CGObjectInstance * obj) const
{
return vstd::contains(alreadyVisited, obj);
}
void AIMemory::removeInvisibleObjects(CCallback * cb)
{
auto shouldBeErased = [&](const CGObjectInstance * obj) -> bool
{
if(obj)
return !cb->getObj(obj->id, false); // no verbose output needed as we check object visibility
else
return true;
};
vstd::erase_if(visitableObjs, shouldBeErased);
vstd::erase_if(alreadyVisited, shouldBeErased);
}

View File

@ -0,0 +1,36 @@
/*
* AIMemory.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "PriorityEvaluator.h"
#include "../Analyzers/DangerHitMapAnalyzer.h"
#include "../Analyzers/BuildAnalyzer.h"
#include "../Analyzers/ArmyManager.h"
#include "../Analyzers/HeroManager.h"
#include "../Analyzers/ObjectClusterizer.h"
#include "../Goals/AbstractGoal.h"
class AIMemory
{
public:
std::set<const CGObjectInstance *> visitableObjs;
std::set<const CGObjectInstance *> alreadyVisited;
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
void removeFromMemory(const CGObjectInstance * obj);
void removeFromMemory(ObjectIdRef obj);
void addSubterraneanGate(const CGObjectInstance * entrance, const CGObjectInstance * exit);
void addVisitableObject(const CGObjectInstance * obj);
void markObjectVisited(const CGObjectInstance * obj);
void markObjectUnvisited(const CGObjectInstance * obj);
bool wasVisited(const CGObjectInstance * obj) const;
void removeInvisibleObjects(CCallback * cb);
};

View File

@ -9,17 +9,15 @@
*/
#include "StdInc.h"
#include "FuzzyEngines.h"
#include "Goals/Goals.h"
#include "../Goals/Goals.h"
#include "../../lib/mapObjects/MapObjects.h"
#include "VCAI.h"
#include "MapObjectsEvaluator.h"
#define MIN_AI_STRENGTH (0.5f) //lower when combat AI gets smarter
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
engineBase::engineBase()
{

View File

@ -12,13 +12,7 @@
#include "../../lib/mapObjects/CommonConstructors.h"
#include "Goals/Goals.h"
#include "VCAI.h"
FuzzyHelper * fh;
extern boost::thread_specific_ptr<VCAI> ai;
extern boost::thread_specific_ptr<CCallback> cb;
#include "Nullkiller.h"
ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
{
@ -39,14 +33,9 @@ ui64 FuzzyHelper::estimateBankDanger(const CBank * bank)
}
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor)
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, bool checkGuards)
{
return evaluateDanger(tile, visitor, ai.get());
}
ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards)
{
auto cb = ai->myCb;
auto cb = ai->cb.get();
const TerrainTile * t = cb->getTile(tile, false);
if(!t) //we can know about guard but can't check its tile (the edge of fow)
return 190000000; //MUCH
@ -66,7 +55,7 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
{
objectDanger = evaluateDanger(dangerousObject, ai); //unguarded objects can also be dangerous or unhandled
objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled
if(objectDanger)
{
//TODO: don't downcast objects AI shouldn't know about!
@ -80,15 +69,18 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
{
//check guard on the other side of the gate
auto it = ai->knownSubterraneanGates.find(dangerousObject);
if(it != ai->knownSubterraneanGates.end())
auto it = ai->memory->knownSubterraneanGates.find(dangerousObject);
if(it != ai->memory->knownSubterraneanGates.end())
{
auto guards = cb->getGuardingCreatures(it->second->visitablePos());
for(auto cre : guards)
{
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(
visitor,
dynamic_cast<const CArmedInstance *>(cre));
vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage);
vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantage);
}
}
}
@ -101,7 +93,7 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
{
float tacticalAdvantage = tacticalAdvantageEngine.getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre));
vstd::amax(guardDanger, evaluateDanger(cre, ai) * tacticalAdvantage); //we are interested in strongest monster around
vstd::amax(guardDanger, evaluateDanger(cre) * tacticalAdvantage); //we are interested in strongest monster around
}
}
@ -109,9 +101,9 @@ ui64 FuzzyHelper::evaluateDanger(crint3 tile, const CGHeroInstance * visitor, co
return std::max(objectDanger, guardDanger);
}
ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
{
auto cb = ai->myCb;
auto cb = ai->cb.get();
if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat
return 0;
@ -126,7 +118,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai)
case Obj::ARTIFACT:
case Obj::RESOURCE:
{
if(!vstd::contains(ai->alreadyVisited, obj))
if(!vstd::contains(ai->memory->alreadyVisited, obj))
{
return 0;
}

View File

@ -11,15 +11,19 @@
#include "FuzzyEngines.h"
class CBank;
class Nullkiller;
class DLL_EXPORT FuzzyHelper
{
public:
private:
const Nullkiller * ai;
TacticalAdvantageEngine tacticalAdvantageEngine;
public:
FuzzyHelper(const Nullkiller * ai) : ai(ai), tacticalAdvantageEngine() {}
ui64 estimateBankDanger(const CBank * bank); //TODO: move to another class?
ui64 evaluateDanger(const CGObjectInstance * obj, const VCAI * ai);
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, const VCAI * ai, bool checkGuards = true);
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor);
ui64 evaluateDanger(const CGObjectInstance * obj);
ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor, bool checkGuards = true);
};

View File

@ -10,7 +10,6 @@
#include "StdInc.h"
#include "Nullkiller.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../Behaviors/CaptureObjectsBehavior.h"
#include "../Behaviors/RecruitHeroBehavior.h"
#include "../Behaviors/BuyArmyBehavior.h"
@ -28,10 +27,22 @@ using namespace Goals;
Nullkiller::Nullkiller()
{
priorityEvaluator.reset(new PriorityEvaluator());
dangerHitMap.reset(new DangerHitMapAnalyzer());
memory.reset(new AIMemory());
}
void Nullkiller::init(std::shared_ptr<CCallback> cb, PlayerColor playerID)
{
this->cb = cb;
this->playerID = playerID;
priorityEvaluator.reset(new PriorityEvaluator(this));
dangerHitMap.reset(new DangerHitMapAnalyzer(this));
buildAnalyzer.reset(new BuildAnalyzer());
objectClusterizer.reset(new ObjectClusterizer());
objectClusterizer.reset(new ObjectClusterizer(this));
dangerEvaluator.reset(new FuzzyHelper(this));
pathfinder.reset(new AIPathfinder(cb.get(), this));
armyManager.reset(new ArmyManager(cb.get(), this));
heroManager.reset(new HeroManager(cb.get(), this));
}
Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const
@ -130,6 +141,7 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior) const
void Nullkiller::resetAiState()
{
playerID = ai->playerID;
lockedHeroes.clear();
dangerHitMap->reset();
}
@ -138,11 +150,11 @@ void Nullkiller::updateAiState()
{
activeHero = nullptr;
ai->validateVisitableObjs();
memory->removeInvisibleObjects(cb.get());
dangerHitMap->updateHitMap();
// TODO: move to hero manager
auto activeHeroes = ai->getMyHeroes();
auto activeHeroes = cb->getHeroesInfo();
vstd::erase_if(activeHeroes, [this](const HeroPtr & hero) -> bool
{
@ -151,8 +163,9 @@ void Nullkiller::updateAiState()
return lockedReason == HeroLockedReason::DEFENCE;
});
ai->ah->updatePaths(activeHeroes, true);
ai->ah->update();
pathfinder->updatePaths(activeHeroes, true);
heroManager->update();
armyManager->update();
objectClusterizer->clusterize();
buildAnalyzer->update();

View File

@ -10,8 +10,12 @@
#pragma once
#include "PriorityEvaluator.h"
#include "FuzzyHelper.h"
#include "AIMemory.h"
#include "../Analyzers/DangerHitMapAnalyzer.h"
#include "../Analyzers/BuildAnalyzer.h"
#include "../Analyzers/ArmyManager.h"
#include "../Analyzers/HeroManager.h"
#include "../Analyzers/ObjectClusterizer.h"
#include "../Goals/AbstractGoal.h"
@ -41,8 +45,16 @@ public:
std::unique_ptr<BuildAnalyzer> buildAnalyzer;
std::unique_ptr<ObjectClusterizer> objectClusterizer;
std::unique_ptr<PriorityEvaluator> priorityEvaluator;
std::unique_ptr<AIPathfinder> pathfinder;
std::unique_ptr<HeroManager> heroManager;
std::unique_ptr<ArmyManager> armyManager;
std::unique_ptr<AIMemory> memory;
std::unique_ptr<FuzzyHelper> dangerEvaluator;
PlayerColor playerID;
std::shared_ptr<CCallback> cb;
Nullkiller();
void init(std::shared_ptr<CCallback> cb, PlayerColor playerID);
void makeTurn();
bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; }
bool isHeroLocked(const CGHeroInstance * hero) const;

View File

@ -19,25 +19,17 @@
#include "../../../lib/VCMI_Lib.h"
#include "../../../CCallback.h"
#include "../../../lib/filesystem/Filesystem.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../Engine/Nullkiller.h"
#include "../Goals/ExecuteHeroChain.h"
#include "../Goals/UnlockCluster.h"
#include "../Goals/BuildThis.h"
#define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us
struct BankConfig;
class CBankInfo;
class Engine;
class InputVariable;
class CGTownInstance;
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
EvaluationContext::EvaluationContext()
EvaluationContext::EvaluationContext(const Nullkiller * ai)
: movementCost(0.0),
manaCost(0),
danger(0),
@ -50,7 +42,8 @@ EvaluationContext::EvaluationContext()
armyLossPersentage(0),
heroRole(HeroRole::SCOUT),
turn(0),
strategicalValue(0)
strategicalValue(0),
evaluator(ai)
{
}
@ -194,7 +187,11 @@ uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
return statsValue > classValue ? statsValue : classValue;
}
uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army, bool checkGold)
uint64_t RewardEvaluator::getArmyReward(
const CGObjectInstance * target,
const CGHeroInstance * hero,
const CCreatureSet * army,
bool checkGold) const
{
const float enemyArmyEliminationRewardRatio = 0.5f;
@ -206,7 +203,7 @@ uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * h
case Obj::TOWN:
return target->tempOwner == PlayerColor::NEUTRAL ? 1000 : 10000;
case Obj::HILL_FORT:
return ai->ah->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeValue;
return ai->armyManager->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeValue;
case Obj::CREATURE_BANK:
return getCreatureBankArmyReward(target, hero);
case Obj::CREATURE_GENERATOR1:
@ -232,7 +229,7 @@ uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * h
}
}
int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army)
int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const
{
if(!target)
return 0;
@ -240,7 +237,7 @@ int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, co
switch(target->ID)
{
case Obj::HILL_FORT:
return ai->ah->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeCost[Res::GOLD];
return ai->armyManager->calculateCreateresUpgrade(army, target, cb->getResourceAmount()).upgradeCost[Res::GOLD];
case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR:
return 1000;
@ -256,11 +253,9 @@ int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, co
}
}
float getStrategicalValue(const CGObjectInstance * target);
float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
float RewardEvaluator::getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const
{
auto objectsUnderTreat = ai->nullkiller->dangerHitMap->getOneTurnAccessibleObjects(enemy);
auto objectsUnderTreat = ai->dangerHitMap->getOneTurnAccessibleObjects(enemy);
float objectValue = 0;
for(auto obj : objectsUnderTreat)
@ -271,10 +266,10 @@ float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy)
return objectValue / 2.0f + enemy->level / 15.0f;
}
float getResourceRequirementStrength(int resType)
float RewardEvaluator::getResourceRequirementStrength(int resType) const
{
TResources requiredResources = ai->nullkiller->buildAnalyzer->getResourcesRequiredNow();
TResources dailyIncome = ai->nullkiller->buildAnalyzer->getDailyIncome();
TResources requiredResources = ai->buildAnalyzer->getResourcesRequiredNow();
TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
if(requiredResources[resType] == 0)
return 0;
@ -287,10 +282,10 @@ float getResourceRequirementStrength(int resType)
return std::min(ratio, 1.0f);
}
float getTotalResourceRequirementStrength(int resType)
float RewardEvaluator::getTotalResourceRequirementStrength(int resType) const
{
TResources requiredResources = ai->nullkiller->buildAnalyzer->getTotalResourcesRequired();
TResources dailyIncome = ai->nullkiller->buildAnalyzer->getDailyIncome();
TResources requiredResources = ai->buildAnalyzer->getTotalResourcesRequired();
TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
if(requiredResources[resType] == 0)
return 0;
@ -302,7 +297,7 @@ float getTotalResourceRequirementStrength(int resType)
return std::min(ratio, 1.0f);
}
float getStrategicalValue(const CGObjectInstance * target)
float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) const
{
if(!target)
return 0;
@ -310,7 +305,9 @@ float getStrategicalValue(const CGObjectInstance * target)
switch(target->ID)
{
case Obj::MINE:
return target->subID == Res::GOLD ? 0.5f : 0.02f * getTotalResourceRequirementStrength(target->subID) + 0.02f * getResourceRequirementStrength(target->subID);
return target->subID == Res::GOLD
? 0.5f
: 0.02f * getTotalResourceRequirementStrength(target->subID) + 0.02f * getResourceRequirementStrength(target->subID);
case Obj::RESOURCE:
return target->subID == Res::GOLD ? 0 : 0.1f * getResourceRequirementStrength(target->subID);
@ -330,7 +327,7 @@ float getStrategicalValue(const CGObjectInstance * target)
}
}
float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role)
float RewardEvaluator::evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role) const
{
if(!hut->wasVisited(hero->tempOwner))
return role == HeroRole::SCOUT ? 2 : 0;
@ -341,12 +338,12 @@ float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance *
|| hero->secSkills.size() >= GameConstants::SKILL_PER_HERO)
return 0;
auto score = ai->ah->evaluateSecSkill(skill, hero);
auto score = ai->heroManager->evaluateSecSkill(skill, hero);
return score >= 2 ? (role == HeroRole::MAIN ? 10 : 4) : score;
}
float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role)
float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const
{
const float enemyHeroEliminationSkillRewardRatio = 0.5f;
@ -397,7 +394,7 @@ int32_t getArmyCost(const CArmedInstance * army)
}
/// Gets aproximated reward in gold. Daily income is multiplied by 5
int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero)
int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const
{
if(!target)
return 0;
@ -469,7 +466,7 @@ public:
for(auto pair : costsPerHero)
{
auto role = ai->ah->getHeroRole(pair.first);
auto role = evaluationContext.evaluator.ai->heroManager->getHeroRole(pair.first);
evaluationContext.movementCostByRole[role] += pair.second;
}
@ -482,12 +479,12 @@ public:
auto army = path.heroArmy;
vstd::amax(evaluationContext.armyLossPersentage, path.getTotalArmyLoss() / (double)path.getHeroStrength());
evaluationContext.heroRole = ai->ah->getHeroRole(heroPtr);
evaluationContext.goldReward += getGoldReward(target, hero);
evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold);
evaluationContext.skillReward += getSkillReward(target, hero, evaluationContext.heroRole);
evaluationContext.strategicalValue += getStrategicalValue(target);
evaluationContext.goldCost += getGoldCost(target, hero, army);
evaluationContext.heroRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(heroPtr);
evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero);
evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold);
evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, evaluationContext.heroRole);
evaluationContext.strategicalValue += evaluationContext.evaluator.getStrategicalValue(target);
evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
vstd::amax(evaluationContext.turn, path.turn());
}
};
@ -504,7 +501,7 @@ public:
std::shared_ptr<ObjectCluster> cluster = clusterGoal.getCluster();
auto hero = clusterGoal.hero.get();
auto role = ai->ah->getHeroRole(clusterGoal.hero);
auto role = evaluationContext.evaluator.ai->heroManager->getHeroRole(clusterGoal.hero);
std::vector<std::pair<const CGObjectInstance *, ObjectInfo>> objects(cluster->objects.begin(), cluster->objects.end());
@ -522,11 +519,11 @@ public:
bool checkGold = objInfo.second.danger == 0;
auto army = hero;
evaluationContext.goldReward += getGoldReward(target, hero) / boost;
evaluationContext.armyReward += getArmyReward(target, hero, army, checkGold) / boost;
evaluationContext.skillReward += getSkillReward(target, hero, role) / boost;
evaluationContext.strategicalValue += getStrategicalValue(target) / boost;
evaluationContext.goldCost += getGoldCost(target, hero, army) / boost;
evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero) / boost;
evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold) / boost;
evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, role) / boost;
evaluationContext.strategicalValue += evaluationContext.evaluator.getStrategicalValue(target) / boost;
evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army) / boost;
evaluationContext.movementCostByRole[role] += objInfo.second.movementCost / boost;
evaluationContext.movementCost += objInfo.second.movementCost / boost;
@ -565,27 +562,34 @@ public:
if(bi.baseCreatureID == bi.creatureID)
{
evaluationContext.strategicalValue += 0.5f + 0.1f * bi.creatureLevel / (float)bi.prerequisitesCount;
evaluationContext.armyReward += ai->ah->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
evaluationContext.armyReward += evaluationContext.evaluator.ai->armyManager->evaluateStackPower(bi.creatureID.toCreature(), bi.creatureGrows);
}
else
{
auto creaturesToUpgrade = ai->ah->getTotalCreaturesAvailable(bi.baseCreatureID);
auto upgradedPower = ai->ah->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
evaluationContext.strategicalValue += 0.05f * bi.creatureLevel / (float)bi.prerequisitesCount;
if(!ai->nullkiller->buildAnalyzer->hasAnyBuilding(buildThis.town->alignment, bi.id))
evaluationContext.armyReward += upgradedPower - creaturesToUpgrade.power;
evaluationContext.armyReward += evaluationContext.evaluator.getUpgradeArmyReward(buildThis.town, bi);
}
}
else
{
evaluationContext.strategicalValue += ai->nullkiller->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
evaluationContext.strategicalValue += evaluationContext.evaluator.ai->buildAnalyzer->getGoldPreasure() * evaluationContext.goldReward / 2200.0f;
}
}
};
PriorityEvaluator::PriorityEvaluator()
uint64_t RewardEvaluator::getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const
{
if(ai->buildAnalyzer->hasAnyBuilding(town->alignment, bi.id))
return 0;
auto creaturesToUpgrade = ai->armyManager->getTotalCreaturesAvailable(bi.baseCreatureID);
auto upgradedPower = ai->armyManager->evaluateStackPower(bi.creatureID.toCreature(), creaturesToUpgrade.count);
return upgradedPower - creaturesToUpgrade.power;
}
PriorityEvaluator::PriorityEvaluator(const Nullkiller * ai)
:ai(ai)
{
initVisitTile();
evaluationContextBuilders.push_back(std::make_shared<ExecuteHeroChainEvaluationContextBuilder>());
@ -596,7 +600,7 @@ PriorityEvaluator::PriorityEvaluator()
EvaluationContext PriorityEvaluator::buildEvaluationContext(Goals::TSubgoal goal) const
{
Goals::TGoalVec parts;
EvaluationContext context;
EvaluationContext context(ai);
if(goal->goalType == Goals::COMPOSITION)
{
@ -652,8 +656,8 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
rewardTypeVariable->setValue(rewardType);
closestHeroRatioVariable->setValue(evaluationContext.closestWayRatio);
strategicalValueVariable->setValue(evaluationContext.strategicalValue);
goldPreasureVariable->setValue(ai->nullkiller->buildAnalyzer->getGoldPreasure());
goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->nullkiller->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
goldPreasureVariable->setValue(ai->buildAnalyzer->getGoldPreasure());
goldCostVariable->setValue(evaluationContext.goldCost / ((float)cb->getResourceAmount(Res::GOLD) + (float)ai->buildAnalyzer->getDailyIncome()[Res::GOLD] + 1.0f));
turnVariable->setValue(evaluationContext.turn);
engine->process();

View File

@ -9,7 +9,28 @@
*/
#pragma once
#include "fl/Headers.h"
#include "../Goals/Goals.h"
#include "../Goals/CGoal.h"
class BuildingInfo;
class RewardEvaluator
{
public:
const Nullkiller * ai;
RewardEvaluator(const Nullkiller * ai) : ai(ai) {}
uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army, bool checkGold) const;
int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const;
float getResourceRequirementStrength(int resType) const;
float getStrategicalValue(const CGObjectInstance * target) const;
float getTotalResourceRequirementStrength(int resType) const;
float evaluateWitchHutSkillScore(const CGWitchHut * hut, const CGHeroInstance * hero, HeroRole role) const;
float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;
int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;
uint64_t getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const;
};
struct DLL_EXPORT EvaluationContext
{
@ -26,8 +47,9 @@ struct DLL_EXPORT EvaluationContext
float strategicalValue;
HeroRole heroRole;
uint8_t turn;
RewardEvaluator evaluator;
EvaluationContext();
EvaluationContext(const Nullkiller * ai);
};
class IEvaluationContextBuilder
@ -36,16 +58,20 @@ public:
virtual void buildEvaluationContext(EvaluationContext & evaluationContext, Goals::TSubgoal goal) const = 0;
};
class Nullkiller;
class PriorityEvaluator
{
public:
PriorityEvaluator();
PriorityEvaluator(const Nullkiller * ai);
~PriorityEvaluator();
void initVisitTile();
float evaluate(Goals::TSubgoal task);
private:
const Nullkiller * ai;
fl::Engine * engine;
fl::InputVariable * armyLossPersentageVariable;
fl::InputVariable * heroRoleVariable;

View File

@ -10,15 +10,12 @@
#include "StdInc.h"
#include "AbstractGoal.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -10,14 +10,11 @@
#include "StdInc.h"
#include "AdventureSpellCast.h"
#include "../VCAI.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -10,15 +10,12 @@
#include "StdInc.h"
#include "BuildBoat.h"
#include "../VCAI.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../Behaviors/CaptureObjectsBehavior.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -11,8 +11,6 @@
#include "BuildThis.h"
#include "../VCAI.h"
#include "../AIUtility.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h"
@ -20,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -9,14 +9,13 @@
*/
#include "StdInc.h"
#include "BuyArmy.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.h"
#include "../../../lib/mapObjects/CGTownInstance.h"
#include "VCAI.h"
#include "Engine/Nullkiller.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;
@ -37,7 +36,7 @@ void BuyArmy::accept(VCAI * ai)
auto upgradeSuccessfull = ai->makePossibleUpgrades(town);
auto armyToBuy = ai->ah->getArmyAvailableToBuy(town->getUpperArmy(), town);
auto armyToBuy = ai->nullkiller->armyManager->getArmyAvailableToBuy(town->getUpperArmy(), town);
if(armyToBuy.empty())
{

View File

@ -10,7 +10,6 @@
#pragma once
#include "AbstractGoal.h"
#include "../FuzzyHelper.h"
#include "../VCAI.h"
struct HeroPtr;
@ -92,13 +91,6 @@ namespace Goals
{
}
///Visitor pattern
//TODO: make accept work for std::shared_ptr... somehow
virtual void accept(VCAI * ai) override //unhandled goal will report standard error
{
ai->tryRealize(*((T *)this));
}
T & setpriority(float p)
{
ITask::priority = p;
@ -134,5 +126,10 @@ namespace Goals
{
return "Invalid";
}
virtual void accept(VCAI * ai) override
{
throw cannotFulfillGoalException("Can not fulfill Invalid goal!");
}
};
}

View File

@ -11,8 +11,6 @@
#include "Goals.h"
#include "../VCAI.h"
#include "../AIUtility.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h"
@ -20,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -11,8 +11,6 @@
#include "Composition.h"
#include "../VCAI.h"
#include "../AIUtility.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h"
@ -20,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -15,7 +15,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -10,14 +10,11 @@
#include "StdInc.h"
#include "DismissHero.h"
#include "../VCAI.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -11,15 +11,12 @@
#include "ExchangeSwapTownHeroes.h"
#include "ExecuteHeroChain.h"
#include "../VCAI.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../Engine/Nullkiller.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -10,15 +10,12 @@
#include "StdInc.h"
#include "ExecuteHeroChain.h"
#include "../VCAI.h"
#include "../FuzzyHelper.h"
#include "../AIhelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../Engine/Nullkiller.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -11,8 +11,6 @@
#include "Goals.h"
#include "../VCAI.h"
#include "../AIUtility.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h"
@ -20,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -15,7 +15,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -11,8 +11,6 @@
#include "Goals.h"
#include "../VCAI.h"
#include "../AIUtility.h"
#include "../AIhelper.h"
#include "../FuzzyHelper.h"
#include "../../../lib/mapping/CMap.h" //for victory conditions
#include "../../../lib/CPathfinder.h"
#include "../../../lib/StringConstants.h"
@ -20,7 +18,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -15,7 +15,6 @@
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
extern FuzzyHelper * fh;
using namespace Goals;

View File

@ -1,140 +0,0 @@
#include "StdInc.h"
#include "MapObjectsEvaluator.h"
#include "../../lib/GameConstants.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/CCreatureHandler.h"
#include "../../lib/CHeroHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/MiscObjects.h"
#include "../../lib/CRandomGenerator.h"
#include "../../lib/spells/CSpellHandler.h"
MapObjectsEvaluator & MapObjectsEvaluator::getInstance()
{
static std::unique_ptr<MapObjectsEvaluator> singletonInstance;
if(singletonInstance == nullptr)
singletonInstance.reset(new MapObjectsEvaluator());
return *(singletonInstance.get());
}
MapObjectsEvaluator::MapObjectsEvaluator()
{
for(auto primaryID : VLC->objtypeh->knownObjects())
{
for(auto secondaryID : VLC->objtypeh->knownSubObjects(primaryID))
{
auto handler = VLC->objtypeh->getHandlerFor(primaryID, secondaryID);
if(!handler->isStaticObject())
{
if(handler->getAiValue() != boost::none)
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = handler->getAiValue().get();
}
else if(VLC->objtypeh->getObjGroupAiValue(primaryID) != boost::none) //if value is not initialized - fallback to default value for this object family if it exists
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = VLC->objtypeh->getObjGroupAiValue(primaryID).get();
}
else //some default handling when aiValue not found, objects that require advanced properties (unavailable from handler) get their value calculated in getObjectValue
{
objectDatabase[CompoundMapObjectID(primaryID, secondaryID)] = 0;
}
}
}
}
}
boost::optional<int> MapObjectsEvaluator::getObjectValue(int primaryID, int secondaryID) const
{
CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
auto object = objectDatabase.find(internalIdentifier);
if(object != objectDatabase.end())
return object->second;
logGlobal->trace("Unknown object for AI, ID: " + std::to_string(primaryID) + ", SubID: " + std::to_string(secondaryID));
return boost::optional<int>();
}
boost::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance * obj) const
{
if(obj->ID == Obj::HERO)
{
//special case handling: in-game heroes have hero ID as object subID, but when reading configs available hero object subID's are hero classes
auto hero = dynamic_cast<const CGHeroInstance*>(obj);
return getObjectValue(obj->ID, hero->type->heroClass->id);
}
else if(obj->ID == Obj::PRISON)
{
//special case: in-game prison subID is captured hero ID, but config has one subID with index 0 for normal prison - use that one
return getObjectValue(obj->ID, 0);
}
else if(obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4)
{
auto dwelling = dynamic_cast<const CGDwelling *>(obj);
int aiValue = 0;
for(auto & creLevel : dwelling->creatures)
{
for(auto & creatureID : creLevel.second)
{
auto creature = VLC->creh->creatures[creatureID];
aiValue += (creature->AIValue * creature->growth);
}
}
return aiValue;
}
else if(obj->ID == Obj::ARTIFACT)
{
auto artifactObject = dynamic_cast<const CGArtifact *>(obj);
switch(artifactObject->storedArtifact->artType->aClass)
{
case CArtifact::EartClass::ART_TREASURE:
return 2000;
case CArtifact::EartClass::ART_MINOR:
return 5000;
case CArtifact::EartClass::ART_MAJOR:
return 10000;
case CArtifact::EartClass::ART_RELIC:
return 20000;
case CArtifact::EartClass::ART_SPECIAL:
return 20000;
default:
return 0; //invalid artifact class
}
}
else if(obj->ID == Obj::SPELL_SCROLL)
{
auto scrollObject = dynamic_cast<const CGArtifact *>(obj);
auto spell = scrollObject->storedArtifact->getGivenSpellID().toSpell();
if(spell)
{
switch(spell->getLevel())
{
case 0: return 0; //scroll with creature ability? Let's assume it is useless
case 1: return 1000;
case 2: return 2000;
case 3: return 5000;
case 4: return 10000;
case 5: return 20000;
default: logAi->warn("AI detected spell scroll with spell level %s", spell->getLevel());
}
}
else
logAi->warn("AI found spell scroll with invalid spell ID: %s", scrollObject->storedArtifact->getGivenSpellID());
}
return getObjectValue(obj->ID, obj->subID);
}
void MapObjectsEvaluator::addObjectData(int primaryID, int secondaryID, int value) //by current design it updates value if already in AI database
{
CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
objectDatabase[internalIdentifier] = value;
}
void MapObjectsEvaluator::removeObjectData(int primaryID, int secondaryID)
{
CompoundMapObjectID internalIdentifier = CompoundMapObjectID(primaryID, secondaryID);
vstd::erase_if_present(objectDatabase, internalIdentifier);
}

View File

@ -1,26 +0,0 @@
/*
* MapObjectsEvaluator.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../../lib/mapObjects/CObjectClassesHandler.h"
class MapObjectsEvaluator
{
private:
std::map<CompoundMapObjectID, int> objectDatabase; //value for each object type
public:
MapObjectsEvaluator();
static MapObjectsEvaluator & getInstance();
boost::optional<int> getObjectValue(int primaryID, int secondaryID) const;
boost::optional<int> getObjectValue(const CGObjectInstance * obj) const;
void addObjectData(int primaryID, int secondaryID, int value);
void removeObjectData(int primaryID, int secondaryID);
};

View File

@ -22,11 +22,11 @@
/// 1-3 - position on map, 4 - layer (air, water, land), 5 - chain (normal, battle, spellcast and combinations)
boost::multi_array<AIPathNode, 5> nodes;
AINodeStorage::AINodeStorage(const int3 & Sizes)
: sizes(Sizes)
AINodeStorage::AINodeStorage(const Nullkiller * ai, const int3 & Sizes)
: sizes(Sizes), ai(ai), cb(ai->cb.get())
{
nodes.resize(boost::extents[sizes.x][sizes.y][sizes.z][EPathfindingLayer::NUM_LAYERS][NUM_CHAINS]);
dangerEvaluator.reset(new FuzzyHelper());
dangerEvaluator.reset(new FuzzyHelper(ai));
}
AINodeStorage::~AINodeStorage() = default;
@ -614,17 +614,14 @@ const std::set<const CGHeroInstance *> AINodeStorage::getAllHeroes() const
return heroes;
}
void AINodeStorage::setHeroes(std::vector<HeroPtr> heroes, const VCAI * _ai)
void AINodeStorage::setHeroes(std::vector<const CGHeroInstance *> heroes)
{
cb = _ai->myCb.get();
ai = _ai;
playerID = ai->playerID;
for(auto & hero : heroes)
{
uint64_t mask = 1 << actors.size();
auto actor = std::make_shared<HeroActor>(hero.get(), mask, ai);
auto actor = std::make_shared<HeroActor>(hero, mask, ai);
if(hero->tempOwner != ai->playerID)
{

View File

@ -16,7 +16,7 @@
#include "../../../lib/CPathfinder.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../AIUtility.h"
#include "../FuzzyHelper.h"
#include "../Engine/FuzzyHelper.h"
#include "../Goals/AbstractGoal.h"
#include "Actions/SpecialAction.h"
#include "Actors.h"
@ -107,7 +107,7 @@ private:
int3 sizes;
const CPlayerSpecificInfoCallback * cb;
const VCAI * ai;
const Nullkiller * ai;
std::unique_ptr<FuzzyHelper> dangerEvaluator;
std::vector<std::shared_ptr<ChainActor>> actors;
std::vector<CGPathNode *> heroChain;
@ -121,7 +121,7 @@ public:
/// more than 1 chain layer for each hero allows us to have more than 1 path to each tile so we can chose more optimal one.
static const int NUM_CHAINS = 10 * GameConstants::MAX_HEROES_PER_PLAYER;
AINodeStorage(const int3 & sizes);
AINodeStorage(const Nullkiller * ai, const int3 & sizes);
~AINodeStorage();
void initialize(const PathfinderOptions & options, const CGameState * gs) override;
@ -176,7 +176,7 @@ public:
boost::optional<AIPathNode *> getOrCreateNode(const int3 & coord, const EPathfindingLayer layer, const ChainActor * actor);
std::vector<AIPath> getChainInfo(const int3 & pos, bool isOnLand) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & pos, const EPathfindingLayer layer) const;
void setHeroes(std::vector<HeroPtr> heroes, const VCAI * ai);
void setHeroes(std::vector<const CGHeroInstance *> heroes);
void setTownsAndDwellings(
const std::vector<const CGTownInstance *> & towns,
const std::set<const CGObjectInstance *> & visitableObjs);
@ -188,7 +188,7 @@ public:
uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero, bool checkGuards) const
{
return dangerEvaluator->evaluateDanger(tile, hero, ai, checkGuards);
return dangerEvaluator->evaluateDanger(tile, hero, checkGuards);
}
uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const

View File

@ -12,10 +12,11 @@
#include "AIPathfinderConfig.h"
#include "../../../CCallback.h"
#include "../../../lib/mapping/CMap.h"
#include "../Engine/Nullkiller.h"
std::shared_ptr<AINodeStorage> AIPathfinder::storage;
AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai)
AIPathfinder::AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai)
:cb(cb), ai(ai)
{
}
@ -43,22 +44,22 @@ std::vector<AIPath> AIPathfinder::getPathInfo(const int3 & tile) const
return storage->getChainInfo(tile, !tileInfo->isWater());
}
void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain)
void AIPathfinder::updatePaths(std::vector<const CGHeroInstance *> heroes, bool useHeroChain)
{
if(!storage)
{
storage = std::make_shared<AINodeStorage>(cb->getMapSize());
storage.reset(new AINodeStorage(ai, cb->getMapSize()));
}
logAi->debug("Recalculate all paths");
int pass = 0;
storage->clear();
storage->setHeroes(heroes, ai);
storage->setHeroes(heroes);
if(useHeroChain)
{
storage->setTownsAndDwellings(cb->getTownsInfo(), ai->visitableObjs);
storage->setTownsAndDwellings(cb->getTownsInfo(), ai->memory->visitableObjs);
}
auto config = std::make_shared<AIPathfinding::AIPathfinderConfig>(cb, ai, storage);

View File

@ -12,19 +12,20 @@
#include "AINodeStorage.h"
#include "../AIUtility.h"
#include "../VCAI.h"
class Nullkiller;
class AIPathfinder
{
private:
static std::shared_ptr<AINodeStorage> storage;
CPlayerSpecificInfoCallback * cb;
VCAI * ai;
Nullkiller * ai;
public:
AIPathfinder(CPlayerSpecificInfoCallback * cb, VCAI * ai);
AIPathfinder(CPlayerSpecificInfoCallback * cb, Nullkiller * ai);
std::vector<AIPath> getPathInfo(const int3 & tile) const;
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const;
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false);
void updatePaths(std::vector<const CGHeroInstance *> heroes, bool useHeroChain = false);
void init();
};

View File

@ -13,12 +13,13 @@
#include "Rules/AIMovementAfterDestinationRule.h"
#include "Rules/AIMovementToDestinationRule.h"
#include "Rules/AIPreviousNodeRule.h"
#include "../Engine//Nullkiller.h"
namespace AIPathfinding
{
std::vector<std::shared_ptr<IPathfindingRule>> makeRuleset(
CPlayerSpecificInfoCallback * cb,
VCAI * ai,
Nullkiller * ai,
std::shared_ptr<AINodeStorage> nodeStorage)
{
std::vector<std::shared_ptr<IPathfindingRule>> rules = {
@ -35,7 +36,7 @@ namespace AIPathfinding
AIPathfinderConfig::AIPathfinderConfig(
CPlayerSpecificInfoCallback * cb,
VCAI * ai,
Nullkiller * ai,
std::shared_ptr<AINodeStorage> nodeStorage)
:PathfinderConfig(nodeStorage, makeRuleset(cb, ai, nodeStorage)), aiNodeStorage(nodeStorage)
{

View File

@ -11,7 +11,8 @@
#pragma once
#include "AINodeStorage.h"
#include "../VCAI.h"
class Nullkiller;
namespace AIPathfinding
{
@ -24,7 +25,7 @@ namespace AIPathfinding
public:
AIPathfinderConfig(
CPlayerSpecificInfoCallback * cb,
VCAI * ai,
Nullkiller * ai,
std::shared_ptr<AINodeStorage> nodeStorage);
virtual CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override;

View File

@ -10,7 +10,7 @@
#include "StdInc.h"
#include "Actors.h"
#include "../VCAI.h"
#include "../AIhelper.h"
#include "../Engine/Nullkiller.h"
#include "../../../CCallback.h"
#include "../../../lib/mapping/CMap.h"
#include "../../../lib/mapObjects/MapObjects.h"
@ -72,7 +72,7 @@ std::string ObjectActor::toString() const
return object->getObjectName() + " at " + object->visitablePos().toString();
}
HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai)
HeroActor::HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const Nullkiller * ai)
:ChainActor(hero, chainMask)
{
exchangeMap = new HeroExchangeMap(this, ai);
@ -83,7 +83,7 @@ HeroActor::HeroActor(
const ChainActor * carrier,
const ChainActor * other,
const CCreatureSet * army,
const VCAI * ai)
const Nullkiller * ai)
:ChainActor(carrier, other, army)
{
exchangeMap = new HeroExchangeMap(this, ai);
@ -167,7 +167,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
if(result)
{
TResources resources = ai->myCb->getResourceAmount();
TResources resources = ai->cb->getResourceAmount();
if(!resources.canAfford(actor->armyCost + other->armyCost))
{
@ -181,7 +181,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
return;
}
auto upgradeInfo = ai->ah->calculateCreateresUpgrade(
auto upgradeInfo = ai->armyManager->calculateCreateresUpgrade(
actor->creatureSet,
other->getActorObject(),
resources - actor->armyCost - other->armyCost);
@ -189,7 +189,7 @@ bool HeroExchangeMap::canExchange(const ChainActor * other)
uint64_t reinforcment = upgradeInfo.upgradeValue;
if(other->creatureSet->Slots().size())
reinforcment += ai->ah->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
reinforcment += ai->armyManager->howManyReinforcementsCanGet(actor->creatureSet, other->creatureSet);
#if PATHFINDER_TRACE_LEVEL >= 2
logAi->trace(
@ -240,7 +240,7 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
result = exchangeMap.at(other);
else
{
TResources availableResources = ai->myCb->getResourceAmount() - actor->armyCost - other->armyCost;
TResources availableResources = ai->cb->getResourceAmount() - actor->armyCost - other->armyCost;
CCreatureSet * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);
CCreatureSet * newArmy;
@ -270,7 +270,7 @@ HeroActor * HeroExchangeMap::exchange(const ChainActor * other)
CCreatureSet * HeroExchangeMap::tryUpgrade(const CCreatureSet * army, const CGObjectInstance * upgrader, TResources resources) const
{
auto upgradeInfo = ai->ah->calculateCreateresUpgrade(army, upgrader, resources);
auto upgradeInfo = ai->armyManager->calculateCreateresUpgrade(army, upgrader, resources);
if(!upgradeInfo.upgradeValue)
return nullptr;
@ -290,7 +290,7 @@ CCreatureSet * HeroExchangeMap::tryUpgrade(const CCreatureSet * army, const CGOb
CCreatureSet * HeroExchangeMap::pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const
{
CCreatureSet * target = new HeroExchangeArmy();
auto bestArmy = ai->ah->getBestArmy(army1, army2);
auto bestArmy = ai->armyManager->getBestArmy(army1, army2);
for(auto & slotInfo : bestArmy)
{

View File

@ -16,7 +16,7 @@
#include "Actions/SpecialAction.h"
class HeroActor;
class VCAI;
class Nullkiller;
class HeroExchangeArmy : public CCreatureSet
{
@ -72,10 +72,10 @@ private:
const HeroActor * actor;
std::map<const ChainActor *, HeroActor *> exchangeMap;
std::map<const ChainActor *, bool> canExchangeCache;
const VCAI * ai;
const Nullkiller * ai;
public:
HeroExchangeMap(const HeroActor * actor, const VCAI * ai)
HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai)
:actor(actor), ai(ai)
{
}
@ -105,8 +105,8 @@ public:
std::shared_ptr<SpecialAction> exchangeAction;
// chain flags, can be combined meaning hero exchange and so on
HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const VCAI * ai);
HeroActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * army, const VCAI * ai);
HeroActor(const CGHeroInstance * hero, uint64_t chainMask, const Nullkiller * ai);
HeroActor(const ChainActor * carrier, const ChainActor * other, const CCreatureSet * army, const Nullkiller * ai);
virtual bool canExchange(const ChainActor * other) const override;

View File

@ -1,62 +0,0 @@
/*
* PathfindingManager.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "../VCAI.h"
#include "AINodeStorage.h"
class DLL_EXPORT IPathfindingManager
{
public:
virtual ~IPathfindingManager() = default;
virtual void init(CPlayerSpecificInfoCallback * CB) = 0;
virtual void setAI(VCAI * AI) = 0;
virtual void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) = 0;
virtual std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const = 0;
virtual std::vector<AIPath> getPathsToTile(const int3 & tile) const = 0;
};
class DLL_EXPORT PathfindingManager : public IPathfindingManager
{
friend class AIhelper;
private:
CPlayerSpecificInfoCallback * cb; //this is enough, but we downcast from CCallback
VCAI * ai;
std::unique_ptr<AIPathfinder> pathfinder;
public:
PathfindingManager() = default;
PathfindingManager(CPlayerSpecificInfoCallback * CB, VCAI * AI = nullptr); //for tests only
std::vector<AIPath> getPathsToTile(const HeroPtr & hero, const int3 & tile) const override;
std::vector<AIPath> getPathsToTile(const int3 & tile) const override;
void updatePaths(std::vector<HeroPtr> heroes, bool useHeroChain = false) override;
STRONG_INLINE
bool isTileAccessible(const HeroPtr & hero, const int3 & tile) const
{
return pathfinder->isTileAccessible(hero, tile);
}
private:
void init(CPlayerSpecificInfoCallback * CB) override;
void setAI(VCAI * AI) override;
Goals::TGoalVec findPaths(
crint3 dest,
bool allowGatherArmy,
HeroPtr hero,
const std::function<Goals::TSubgoal(int3)> goalFactory) const;
Goals::TSubgoal clearWayTo(HeroPtr hero, int3 firstTileToGet) const;
};

View File

@ -9,10 +9,11 @@
*/
#include "StdInc.h"
#include "AILayerTransitionRule.h"
#include "../../Engine/Nullkiller.h"
namespace AIPathfinding
{
AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage)
AILayerTransitionRule::AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage)
:cb(cb), ai(ai), nodeStorage(nodeStorage)
{
setup();
@ -54,7 +55,7 @@ namespace AIPathfinding
shipyards.push_back(t);
}
for(const CGObjectInstance * obj : ai->visitableObjs)
for(const CGObjectInstance * obj : ai->memory->visitableObjs)
{
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
{

View File

@ -23,13 +23,13 @@ namespace AIPathfinding
{
private:
CPlayerSpecificInfoCallback * cb;
VCAI * ai;
Nullkiller * ai;
std::map<int3, std::shared_ptr<const BuildBoatAction>> virtualBoats;
std::shared_ptr<AINodeStorage> nodeStorage;
std::map<const CGHeroInstance *, std::shared_ptr<const SummonBoatAction>> summonableVirtualBoats;
public:
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, VCAI * ai, std::shared_ptr<AINodeStorage> nodeStorage);
AILayerTransitionRule(CPlayerSpecificInfoCallback * cb, Nullkiller * ai, std::shared_ptr<AINodeStorage> nodeStorage);
virtual void process(
const PathNodeInfo & source,

View File

@ -1,431 +0,0 @@
/*
* SectorMap.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "SectorMap.h"
#include "VCAI.h"
#include "../../CCallback.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/mapObjects/MapObjects.h"
#include "../../lib/CPathfinder.h"
#include "../../lib/CGameState.h"
extern boost::thread_specific_ptr<CCallback> cb;
extern boost::thread_specific_ptr<VCAI> ai;
SectorMap::SectorMap()
{
update();
}
SectorMap::SectorMap(HeroPtr h)
{
update();
makeParentBFS(h->visitablePos());
}
bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t)
{
if (t->blocked && !t->visitable)
{
sec = NOT_AVAILABLE;
return true;
}
return false;
}
bool SectorMap::markIfBlocked(TSectorID & sec, crint3 pos)
{
return markIfBlocked(sec, pos, getTile(pos));
}
void SectorMap::update()
{
visibleTiles = cb->getAllVisibleTiles();
auto shape = visibleTiles->shape();
sector.resize(boost::extents[shape[0]][shape[1]][shape[2]]);
clear();
int curSector = 3; //0 is invisible, 1 is not explored
CCallback * cbp = cb.get(); //optimization
foreach_tile_pos([&](crint3 pos)
{
if (retrieveTile(pos) == NOT_CHECKED)
{
if (!markIfBlocked(retrieveTile(pos), pos))
exploreNewSector(pos, curSector++, cbp);
}
});
valid = true;
}
SectorMap::TSectorID & SectorMap::retrieveTileN(SectorMap::TSectorArray & a, const int3 & pos)
{
return a[pos.x][pos.y][pos.z];
}
const SectorMap::TSectorID & SectorMap::retrieveTileN(const SectorMap::TSectorArray & a, const int3 & pos)
{
return a[pos.x][pos.y][pos.z];
}
void SectorMap::clear()
{
//TODO: rotate to [z][x][y]
auto fow = cb->getVisibilityMap();
//TODO: any magic to automate this? will need array->array conversion
//std::transform(fow.begin(), fow.end(), sector.begin(), [](const ui8 &f) -> unsigned short
//{
// return f; //type conversion
//});
auto width = fow.size();
auto height = fow.front().size();
auto depth = fow.front().front().size();
for (size_t x = 0; x < width; x++)
{
for (size_t y = 0; y < height; y++)
{
for (size_t z = 0; z < depth; z++)
sector[x][y][z] = fow[x][y][z];
}
}
valid = false;
}
void SectorMap::exploreNewSector(crint3 pos, int num, CCallback * cbp)
{
Sector & s = infoOnSectors[num];
s.id = num;
s.water = getTile(pos)->isWater();
std::queue<int3> toVisit;
toVisit.push(pos);
while (!toVisit.empty())
{
int3 curPos = toVisit.front();
toVisit.pop();
TSectorID & sec = retrieveTile(curPos);
if (sec == NOT_CHECKED)
{
const TerrainTile * t = getTile(curPos);
if (!markIfBlocked(sec, curPos, t))
{
if (t->isWater() == s.water) //sector is only-water or only-land
{
sec = num;
s.tiles.push_back(curPos);
foreach_neighbour(cbp, curPos, [&](CCallback * cbp, crint3 neighPos)
{
if (retrieveTile(neighPos) == NOT_CHECKED)
{
toVisit.push(neighPos);
//parent[neighPos] = curPos;
}
const TerrainTile * nt = getTile(neighPos);
if (nt && nt->isWater() != s.water && canBeEmbarkmentPoint(nt, s.water))
{
s.embarkmentPoints.push_back(neighPos);
}
});
if (t->visitable)
{
auto obj = t->visitableObjects.front();
if (cb->getObj(obj->id, false)) // FIXME: we have to filter invisible objcts like events, but probably TerrainTile shouldn't be used in SectorMap at all
s.visitableObjs.push_back(obj);
}
}
}
}
}
vstd::removeDuplicates(s.embarkmentPoints);
}
void SectorMap::write(crstring fname)
{
std::ofstream out(fname);
for (int k = 0; k < cb->getMapSize().z; k++)
{
for (int j = 0; j < cb->getMapSize().y; j++)
{
for (int i = 0; i < cb->getMapSize().x; i++)
{
out << (int)sector[i][j][k] << '\t';
}
out << std::endl;
}
out << std::endl;
}
}
/*
this functions returns one target tile or invalid tile. We will use it to poll possible destinations
For ship construction etc, another function (goal?) is needed
*/
int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
{
int3 ret(-1, -1, -1);
int sourceSector = retrieveTile(h->visitablePos());
int destinationSector = retrieveTile(dst);
const Sector * src = &infoOnSectors[sourceSector];
const Sector * dest = &infoOnSectors[destinationSector];
if (sourceSector != destinationSector) //use ships, shipyards etc..
{
if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects
return dst;
std::map<const Sector *, const Sector *> preds;
std::queue<const Sector *> sectorQueue;
sectorQueue.push(src);
while (!sectorQueue.empty())
{
const Sector * s = sectorQueue.front();
sectorQueue.pop();
for (int3 ep : s->embarkmentPoints)
{
Sector * neigh = &infoOnSectors[retrieveTile(ep)];
//preds[s].push_back(neigh);
if (!preds[neigh])
{
preds[neigh] = s;
sectorQueue.push(neigh);
}
}
}
if (!preds[dest])
{
//write("test.txt");
return ret;
//throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id));
}
std::vector<const Sector *> toTraverse;
toTraverse.push_back(dest);
while (toTraverse.back() != src)
{
toTraverse.push_back(preds[toTraverse.back()]);
}
if (preds[dest])
{
//TODO: would be nice to find sectors in loop
const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2);
if (!src->water && sectorToReach->water) //embark
{
//embark on ship -> look for an EP with a boat
auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
{
const TerrainTile * t = getTile(pos);
if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
{
if (retrieveTile(pos) == sectorToReach->id)
return true;
}
return false;
});
if (firstEP != src->embarkmentPoints.end())
{
return *firstEP;
}
else
{
//we need to find a shipyard with an access to the desired sector's EP
//TODO what about Summon Boat spell?
std::vector<const IShipyard *> shipyards;
for (const CGTownInstance * t : cb->getTownsInfo())
{
if (t->hasBuilt(BuildingID::SHIPYARD))
shipyards.push_back(t);
}
for (const CGObjectInstance * obj : ai->getFlaggedObjects())
{
if (obj->ID != Obj::TOWN) //towns were handled in the previous loop
{
if (const IShipyard * shipyard = IShipyard::castFrom(obj))
shipyards.push_back(shipyard);
}
}
shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool
{
return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id;
}), shipyards.end());
if (!shipyards.size())
{
//TODO consider possibility of building shipyard in a town
return ret;
//throw cannotFulfillGoalException("There is no known shipyard!");
}
//we have only shipyards that possibly can build ships onto the appropriate EP
auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool
{
return s->o->tempOwner == ai->playerID;
});
if (ownedGoodShipyard != shipyards.end())
{
const IShipyard * s = *ownedGoodShipyard;
TResources shipCost;
s->getBoatCost(shipCost);
if (cb->getResourceAmount().canAfford(shipCost))
{
int3 ret = s->bestLocation();
cb->buildBoat(s); //TODO: move actions elsewhere
return ret;
}
else
{
//TODO gather res
return ret;
//throw cannotFulfillGoalException("Not enough resources to build a boat");
}
}
else
{
//TODO pick best shipyard to take over
return shipyards.front()->o->visitablePos();
}
}
}
else if (src->water && !sectorToReach->water)
{
//TODO
//disembark
return ret;
}
else //use subterranean gates - not needed since gates are now handled via Pathfinder
{
return ret;
//throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!");
}
}
else
{
return ret;
//throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?");
}
}
else //tiles are in same sector
{
return findFirstVisitableTile(h, dst);
}
}
int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst)
{
int3 ret(-1, -1, -1);
int3 curtile = dst;
while (curtile != h->visitablePos())
{
auto topObj = cb->getTopObj(curtile);
if (topObj && topObj->ID == Obj::HERO && topObj != h.h)
{
if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
{
logAi->warn("Another allied hero stands in our way");
return ret;
}
}
if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable())
{
return curtile;
}
else
{
auto i = parent.find(curtile);
if (i != parent.end())
{
assert(curtile != i->second);
curtile = i->second;
}
else
{
return ret;
//throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!");
}
}
}
return ret;
}
void SectorMap::makeParentBFS(crint3 source)
{
parent.clear();
int mySector = retrieveTile(source);
std::queue<int3> toVisit;
toVisit.push(source);
while (!toVisit.empty())
{
int3 curPos = toVisit.front();
toVisit.pop();
TSectorID & sec = retrieveTile(curPos);
assert(sec == mySector); //consider only tiles from the same sector
UNUSED(sec);
foreach_neighbour(curPos, [&](crint3 neighPos)
{
if (retrieveTile(neighPos) == mySector && !vstd::contains(parent, neighPos))
{
if (cb->canMoveBetween(curPos, neighPos))
{
toVisit.push(neighPos);
parent[neighPos] = curPos;
}
}
});
}
}
SectorMap::TSectorID & SectorMap::retrieveTile(crint3 pos)
{
return retrieveTileN(sector, pos);
}
TerrainTile * SectorMap::getTile(crint3 pos) const
{
//out of bounds access should be handled by boost::multi_array
//still we cached this array to avoid any checks
return visibleTiles->operator[](pos.x)[pos.y][pos.z];
}
std::vector<const CGObjectInstance *> SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround)
{
const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())];
if (sectorsAround)
{
std::vector<const CGObjectInstance *> ret;
for (auto embarkPoint : heroSector->embarkmentPoints)
{
const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)];
range::copy(embarkSector->visitableObjs, std::back_inserter(ret));
}
return ret;
}
return heroSector->visitableObjs;
}

View File

@ -1,70 +0,0 @@
/*
* SectorMap.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#include "AIUtility.h"
enum
{
NOT_VISIBLE = 0,
NOT_CHECKED = 1,
NOT_AVAILABLE
};
struct SectorMap
{
//a sector is set of tiles that would be mutually reachable if all visitable objs would be passable (incl monsters)
struct Sector
{
int id;
std::vector<int3> tiles;
std::vector<int3> embarkmentPoints; //tiles of other sectors onto which we can (dis)embark
std::vector<const CGObjectInstance *> visitableObjs;
bool water; //all tiles of sector are land or water
Sector()
{
id = -1;
water = false;
}
};
typedef unsigned short TSectorID; //smaller than int to allow -1 value. Max number of sectors 65K should be enough for any proper map.
typedef boost::multi_array<TSectorID, 3> TSectorArray;
bool valid; //some kind of lazy eval
std::map<int3, int3> parent;
TSectorArray sector;
//std::vector<std::vector<std::vector<unsigned char>>> pathfinderSector;
std::map<int, Sector> infoOnSectors;
std::shared_ptr<boost::multi_array<TerrainTile *, 3>> visibleTiles;
SectorMap();
SectorMap(HeroPtr h);
void update();
void clear();
void exploreNewSector(crint3 pos, int num, CCallback * cbp);
void write(crstring fname);
bool markIfBlocked(TSectorID & sec, crint3 pos, const TerrainTile * t);
bool markIfBlocked(TSectorID & sec, crint3 pos);
TSectorID & retrieveTile(crint3 pos);
TSectorID & retrieveTileN(TSectorArray & vectors, const int3 & pos);
const TSectorID & retrieveTileN(const TSectorArray & vectors, const int3 & pos);
TerrainTile * getTile(crint3 pos) const;
std::vector<const CGObjectInstance *> getNearbyObjs(HeroPtr h, bool sectorsAround);
void makeParentBFS(crint3 source);
int3 firstTileToGet(HeroPtr h, crint3 dst); //if h wants to reach tile dst, which tile he should visit to clear the way?
int3 findFirstVisitableTile(HeroPtr h, crint3 dst);
};

View File

@ -9,7 +9,6 @@
*/
#include "StdInc.h"
#include "VCAI.h"
#include "FuzzyHelper.h"
#include "Goals/Goals.h"
#include "../../lib/UnlockGuard.h"
@ -23,10 +22,8 @@
#include "../../lib/serializer/BinarySerializer.h"
#include "../../lib/serializer/BinaryDeserializer.h"
#include "AIhelper.h"
#include "Engine/Nullkiller.h"
extern FuzzyHelper * fh;
class CGVisitableOPW;
@ -68,14 +65,11 @@ VCAI::VCAI()
makingTurn = nullptr;
destinationTeleport = ObjectInstanceID();
destinationTeleportPos = int3(-1);
ah = new AIhelper();
ah->setAI(this);
nullkiller.reset(new Nullkiller());
}
VCAI::~VCAI()
{
delete ah;
LOG_TRACE(logAi);
finish();
}
@ -109,9 +103,7 @@ void VCAI::heroMoved(const TryMoveHero & details)
{
if(o1->ID == Obj::SUBTERRANEAN_GATE && o1->ID == o2->ID) // We need to only add subterranean gates in knownSubterraneanGates. Used for features not yet ported to use teleport channels
{
knownSubterraneanGates[o1] = o2;
knownSubterraneanGates[o2] = o1;
logAi->debug("Found a pair of subterranean gates between %s and %s!", from.toString(), to.toString());
nullkiller->memory->addSubterraneanGate(o1, o2);
}
}
}
@ -238,12 +230,7 @@ void VCAI::heroVisit(const CGHeroInstance * visitor, const CGObjectInstance * vi
if(start && visitedObj) //we can end visit with null object, anyway
{
markObjectVisited(visitedObj);
//TODO: what if we visited one-time visitable object that was reserved by another hero (shouldn't, but..)
if (visitedObj->ID == Obj::HERO)
{
visitedHeroes[visitor].insert(HeroPtr(dynamic_cast<const CGHeroInstance *>(visitedObj)));
}
nullkiller->memory->markObjectVisited(visitedObj);
}
status.heroVisit(visitedObj, start);
@ -259,8 +246,6 @@ void VCAI::heroVisitsTown(const CGHeroInstance * hero, const CGTownInstance * to
{
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
//buildArmyIn(town);
//moveCreaturesToHero(town);
}
void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
@ -268,7 +253,7 @@ void VCAI::tileHidden(const std::unordered_set<int3, ShashInt3> & pos)
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
validateVisitableObjs();
nullkiller->memory->removeInvisibleObjects(myCb.get());
clearPathsInfo();
}
@ -360,22 +345,7 @@ void VCAI::objectRemoved(const CGObjectInstance * obj)
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
vstd::erase_if_present(visitableObjs, obj);
vstd::erase_if_present(alreadyVisited, obj);
//TODO: Find better way to handle hero boat removal
if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
{
if(hero->boat)
{
vstd::erase_if_present(visitableObjs, hero->boat);
vstd::erase_if_present(alreadyVisited, hero->boat);
}
}
//TODO
//there are other places where CGObjectinstance ptrs are stored...
//
nullkiller->memory->removeFromMemory(obj);
if(obj->ID == Obj::HERO && obj->tempOwner == playerID)
{
@ -398,8 +368,6 @@ void VCAI::playerBonusChanged(const Bonus & bonus, bool gain)
void VCAI::heroCreated(const CGHeroInstance * h)
{
LOG_TRACE(logAi);
if(h->visitedTown)
townVisitsThisWeek[HeroPtr(h)].insert(h->visitedTown);
NET_EVENT_HANDLER;
}
@ -480,10 +448,10 @@ void VCAI::objectPropertyChanged(const SetObjectProperty * sop)
if(relations == PlayerRelations::ENEMIES)
{
//we want to visit objects owned by oppponents
addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
vstd::erase_if_present(alreadyVisited, obj);
//addVisitableObj(obj); // TODO: Remove once save compatability broken. In past owned objects were removed from this set
nullkiller->memory->markObjectUnvisited(obj);
}
else if(relations == PlayerRelations::SAME_PLAYER && obj->ID == Obj::TOWN && nullkiller)
else if(relations == PlayerRelations::SAME_PLAYER && obj->ID == Obj::TOWN)
{
// reevaluate defence for a new town
nullkiller->dangerHitMap->reset();
@ -523,20 +491,12 @@ void VCAI::init(std::shared_ptr<CCallback> CB)
myCb = CB;
cbc = CB;
ah->init(CB.get());
NET_EVENT_HANDLER; //sets ah->rm->cb
NET_EVENT_HANDLER;
playerID = *myCb->getMyColor();
myCb->waitTillRealize = true;
myCb->unlockGsWhenWaiting = true;
if(!fh)
fh = new FuzzyHelper();
//if(playerID.getStr(false) == "blue")
{
nullkiller.reset(new Nullkiller());
}
nullkiller->init(CB, playerID);
retrieveVisitableObjs();
}
@ -554,7 +514,7 @@ void VCAI::heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill
LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
NET_EVENT_HANDLER;
status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->name % hero->level));
requestActionASAP([=](){ answerQuery(queryID, ah->selectBestSkill(hero, skills)); });
requestActionASAP([=](){ answerQuery(queryID, nullkiller->heroManager->selectBestSkill(hero, skills)); });
}
void VCAI::commanderGotLevel(const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)
@ -589,7 +549,7 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector<Compon
if(objType == Obj::ARTIFACT || objType == Obj::RESOURCE)
{
auto ratio = (float)fh->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength();
auto ratio = (float)nullkiller->dangerEvaluator->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength();
bool dangerUnknown = ratio == 0;
bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT);
@ -615,7 +575,7 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector<Compon
// TODO: Find better way to understand it is Chest of Treasures
if(components.size() == 2
&& components.front().id == Component::RESOURCE
&& ah->getHeroRole(hero) != HeroRole::MAIN)
&& nullkiller->heroManager->getHeroRole(hero) != HeroRole::MAIN)
{
sel = 1; // for now lets pick gold from a chest.
}
@ -626,15 +586,13 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector<Compon
void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID)
{
// LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
NET_EVENT_HANDLER;
status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits")
% exits.size()));
status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits") % exits.size()));
int choosenExit = -1;
if(impassable)
{
knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
nullkiller->memory->knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
}
else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid())
{
@ -700,12 +658,8 @@ void VCAI::saveGame(BinarySerializer & h, const int version)
{
LOG_TRACE_PARAMS(logAi, "version '%i'", version);
NET_EVENT_HANDLER;
validateVisitableObjs();
nullkiller->memory->removeInvisibleObjects(myCb.get());
#if 0
//disabled due to issue 2890
registerGoals(h);
#endif // 0
CAdventureAI::saveGame(h, version);
serializeInternal(h, version);
}
@ -760,7 +714,6 @@ void VCAI::makeTurn()
if(cb->getDate(Date::DAY_OF_WEEK) == 1)
{
townVisitsThisWeek.clear();
std::vector<const CGObjectInstance *> objs;
retrieveVisitableObjs(objs, true);
@ -769,14 +722,11 @@ void VCAI::makeTurn()
if(isWeeklyRevisitable(obj))
{
addVisitableObj(obj);
vstd::erase_if_present(alreadyVisited, obj);
nullkiller->memory->markObjectUnvisited(obj);
}
}
}
markHeroAbleToExplore(primaryHero());
visitedHeroes.clear();
if(cb->getDate(Date::DAY) == 1)
{
retrieveVisitableObjs();
@ -807,18 +757,6 @@ void VCAI::makeTurn()
endTurn();
}
std::vector<HeroPtr> VCAI::getMyHeroes() const
{
std::vector<HeroPtr> ret;
for(auto h : cb->getHeroesInfo())
{
ret.push_back(h);
}
return ret;
}
void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString());
@ -832,17 +770,8 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{
makePossibleUpgrades(h.get());
if(!nullkiller || !h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero))
moveCreaturesToHero(h->visitedTown);
townVisitsThisWeek[h].insert(h->visitedTown);
if(!ai->nullkiller)
{
ah->update();
}
if(ah->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST)
if(ai->nullkiller->heroManager->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook()
&& cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST)
{
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
@ -867,7 +796,7 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme
{
const CArmedInstance * armies[] = {destinationArmy, source};
auto bestArmy = ah->getSortedSlots(destinationArmy, source);
auto bestArmy = nullkiller->armyManager->getSortedSlots(destinationArmy, source);
//foreach best type -> iterate over slots in both armies and if it's the appropriate type, send it to the slot where it belongs
for(SlotID i = SlotID(0); i.getNum() < bestArmy.size() && i.validSlot(); i.advance(1)) //i-th strongest creature type will go to i-th slot
@ -886,7 +815,7 @@ void VCAI::pickBestCreatures(const CArmedInstance * destinationArmy, const CArme
&& source->stacksCount() == 1
&& (!destinationArmy->hasStackAtSlot(i) || destinationArmy->getCreature(i) == targetCreature))
{
auto weakest = ah->getWeakestCreature(bestArmy);
auto weakest = nullkiller->armyManager->getWeakestCreature(bestArmy);
if(weakest->creature == targetCreature)
{
@ -1040,54 +969,6 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
}
}
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit)
{
int3 op = obj->visitablePos();
auto paths = ah->getPathsToTile(h, op);
for(const auto & path : paths)
{
if(movementCostLimit && movementCostLimit.get() < path.movementCost())
return false;
if(isGoodForVisit(obj, h, path))
return true;
}
return false;
}
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const
{
const int3 pos = obj->visitablePos();
const int3 targetPos = path.firstTileToGet();
if (!targetPos.valid())
return false;
if (!isTileNotReserved(h.get(), targetPos))
return false;
if (obj->wasVisited(playerID))
return false;
if (cb->getPlayerRelations(playerID, obj->tempOwner) != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
return false; // Otherwise we flag or get weekly resources / creatures
if (!isSafeToVisit(h, pos))
return false;
if (!shouldVisit(h, obj))
return false;
if (vstd::contains(alreadyVisited, obj))
return false;
if (vstd::contains(reservedObjs, obj))
return false;
// TODO: looks extra if we already have AIPath
//if (!isAccessibleForHero(targetPos, h))
// return false;
const CGObjectInstance * topObj = cb->getVisitableObjs(obj->visitablePos()).back(); //it may be hero visiting this obj
//we don't try visiting object on which allied or owned hero stands
// -> it will just trigger exchange windows and AI will be confused that obj behind doesn't get visited
return !(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES); //all of the following is met
}
bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t) const
{
if(t.valid())
@ -1150,19 +1031,6 @@ void VCAI::waitTillFree()
status.waitTillFree();
}
void VCAI::markObjectVisited(const CGObjectInstance * obj)
{
if(!obj)
return;
if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
return;
if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
return;
if(obj->ID == Obj::MONSTER)
return;
alreadyVisited.insert(obj);
}
void VCAI::markHeroUnableToExplore(HeroPtr h)
{
heroesUnableToExplore.insert(h);
@ -1180,40 +1048,6 @@ void VCAI::clearPathsInfo()
heroesUnableToExplore.clear();
}
void VCAI::validateVisitableObjs()
{
std::string errorMsg;
auto shouldBeErased = [&](const CGObjectInstance * obj) -> bool
{
if(obj)
return !cb->getObj(obj->id, false); // no verbose output needed as we check object visibility
else
return true;
};
//errorMsg is captured by ref so lambda will take the new text
errorMsg = " shouldn't be on the visitable objects list!";
vstd::erase_if(visitableObjs, shouldBeErased);
//FIXME: how comes our own heroes become inaccessible?
vstd::erase_if(reservedHeroesMap, [](std::pair<HeroPtr, std::set<const CGObjectInstance *>> hp) -> bool
{
return !hp.first.get(true);
});
for(auto & p : reservedHeroesMap)
{
errorMsg = " shouldn't be on list for hero " + p.first->name + "!";
vstd::erase_if(p.second, shouldBeErased);
}
errorMsg = " shouldn't be on the reserved objs list!";
vstd::erase_if(reservedObjs, shouldBeErased);
//TODO overkill, hidden object should not be removed. However, we can't know if hidden object is erased from game.
errorMsg = " shouldn't be on the already visited objs list!";
vstd::erase_if(alreadyVisited, shouldBeErased);
}
void VCAI::retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned) const
{
foreach_tile_pos([&](const int3 & pos)
@ -1241,7 +1075,7 @@ void VCAI::retrieveVisitableObjs()
std::vector<const CGObjectInstance *> VCAI::getFlaggedObjects() const
{
std::vector<const CGObjectInstance *> ret;
for(const CGObjectInstance * obj : visitableObjs)
for(const CGObjectInstance * obj : nullkiller->memory->visitableObjs)
{
if(obj->tempOwner == playerID)
ret.push_back(obj);
@ -1254,45 +1088,14 @@ void VCAI::addVisitableObj(const CGObjectInstance * obj)
if(obj->ID == Obj::EVENT)
return;
visitableObjs.insert(obj);
// All teleport objects seen automatically assigned to appropriate channels
auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
if(teleportObj)
CGTeleport::addToChannel(knownTeleportChannels, teleportObj);
nullkiller->memory->addVisitableObject(obj);
if(obj->ID == Obj::HERO && cb->getPlayerRelations(obj->tempOwner, playerID) == PlayerRelations::ENEMIES)
{
if(nullkiller) nullkiller->dangerHitMap->reset();
nullkiller->dangerHitMap->reset();
}
}
const CGObjectInstance * VCAI::lookForArt(int aid) const
{
for(const CGObjectInstance * obj : ai->visitableObjs)
{
if(obj->ID == Obj::ARTIFACT && obj->subID == aid)
return obj;
}
return nullptr;
//TODO what if more than one artifact is available? return them all or some slection criteria
}
bool VCAI::isAccessible(const int3 & pos) const
{
//TODO precalculate for speed
for(const CGHeroInstance * h : cb->getHeroesInfo())
{
if(isAccessibleForHero(pos, h))
return true;
}
return false;
}
HeroPtr VCAI::getHeroWithGrail() const
{
for(const CGHeroInstance * h : cb->getHeroesInfo())
@ -1303,34 +1106,6 @@ HeroPtr VCAI::getHeroWithGrail() const
return nullptr;
}
const CGObjectInstance * VCAI::getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate)
{
//TODO smarter definition of unvisited
for(const CGObjectInstance * obj : visitableObjs)
{
if(predicate(obj) && !vstd::contains(alreadyVisited, obj))
return obj;
}
return nullptr;
}
bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies) const
{
// Don't visit tile occupied by allied hero
if(!includeAllies)
{
for(auto obj : cb->getVisitableObjs(pos))
{
if(obj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES)
{
if(obj != h.get())
return false;
}
}
}
return cb->getPathsInfo(h.get())->getPathInfo(pos)->reachable();
}
bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
{
if(h->inTownGarrison && h->visitedTown)
@ -1363,6 +1138,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
afterMovementCheck(); // TODO: is it feasible to hero get killed there if game work properly?
// If revisiting, teleport probing is never done, and so the entries into the list would remain unused and uncleared
teleportChannelProbingList.clear();
// not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
ret = true;
}
@ -1456,7 +1233,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
doTeleportMovement(destTeleportObj->id, nextCoord);
if(teleportChannelProbingList.size())
doChannelProbing();
markObjectVisited(destTeleportObj); //FIXME: Monoliths are not correctly visited
nullkiller->memory->markObjectVisited(destTeleportObj); //FIXME: Monoliths are not correctly visited
continue;
}
@ -1596,17 +1373,6 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
}
}
void VCAI::tryRealize(Goals::Invalid & g)
{
throw cannotFulfillGoalException("I don't know how to fulfill this!");
}
void VCAI::tryRealize(Goals::AbstractGoal & g)
{
logAi->debug("Attempting realizing goal with code %s", g.toString());
throw cannotFulfillGoalException("Unknown type of goal !");
}
const CGTownInstance * VCAI::findTownWithTavern() const
{
for(const CGTownInstance * t : cb->getTownsInfo())
@ -1616,15 +1382,6 @@ const CGTownInstance * VCAI::findTownWithTavern() const
return nullptr;
}
HeroPtr VCAI::primaryHero() const
{
auto hs = cb->getHeroesInfo();
if (hs.empty())
return nullptr;
else
return *boost::max_element(hs, compareHeroStrength);
}
void VCAI::endTurn()
{
logAi->info("Player %d (%s) ends turn", playerID, playerID.getStr());
@ -1663,9 +1420,9 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
if(heroes[1]->getTotalStrength() > hero->getTotalStrength())
hero = heroes[1];
}
cb->recruitHero(t, hero);
ai->ah->update();
cb->recruitHero(t, hero);
nullkiller->heroManager->update();
if(t->visitingHero)
moveHeroToTile(t->visitablePos(), t->visitingHero.get());
@ -1750,18 +1507,9 @@ void VCAI::validateObject(const CGObjectInstance * obj)
void VCAI::validateObject(ObjectIdRef obj)
{
auto matchesId = [&](const CGObjectInstance * hlpObj) -> bool
{
return hlpObj->id == obj.id;
};
if(!obj)
{
vstd::erase_if(visitableObjs, matchesId);
for(auto & p : reservedHeroesMap)
vstd::erase_if(p.second, matchesId);
vstd::erase_if(reservedObjs, matchesId);
nullkiller->memory->removeFromMemory(obj);
}
}
@ -1918,150 +1666,4 @@ void AIStatus::setChannelProbing(bool ongoing)
bool AIStatus::channelProbing()
{
return ongoingChannelProbing;
}
bool isWeeklyRevisitable(const CGObjectInstance * obj)
{
//TODO: allow polling of remaining creatures in dwelling
if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
return true;
if(dynamic_cast<const CGDwelling *>(obj))
return true;
if(dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
return true;
switch(obj->ID)
{
case Obj::STABLES:
case Obj::MAGIC_WELL:
case Obj::HILL_FORT:
return true;
case Obj::BORDER_GATE:
case Obj::BORDERGUARD:
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); //FIXME: they could be revisited sooner than in a week
}
return false;
}
bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
{
switch(obj->ID)
{
case Obj::TOWN:
case Obj::HERO: //never visit our heroes at random
return obj->tempOwner != h->tempOwner; //do not visit our towns at random
case Obj::BORDER_GATE:
{
for(auto q : ai->myCb->getMyQuests())
{
if(q.obj == obj)
{
return false; // do not visit guards or gates when wandering
}
}
return true; //we don't have this quest yet
}
case Obj::BORDERGUARD: //open borderguard if possible
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
case Obj::SEER_HUT:
case Obj::QUEST_GUARD:
{
for(auto q : ai->myCb->getMyQuests())
{
if(q.obj == obj)
{
if(q.quest->checkQuest(h.h))
return true; //we completed the quest
else
return false; //we can't complete this quest
}
}
return true; //we don't have this quest yet
}
case Obj::CREATURE_GENERATOR1:
{
if(obj->tempOwner != h->tempOwner)
return true; //flag just in case
const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
for(auto level : d->creatures)
{
for(auto c : level.second)
{
if(level.first
&& h->getSlotFor(CreatureID(c)) != SlotID()
&& cb->getResourceAmount().canAfford(c.toCreature()->cost))
{
return true;
}
}
}
return false;
}
case Obj::HILL_FORT:
{
for(auto slot : h->Slots())
{
if(slot.second->type->upgrades.size())
return true; //TODO: check price?
}
return false;
}
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
case Obj::MONOLITH_ONE_WAY_EXIT:
case Obj::MONOLITH_TWO_WAY:
case Obj::WHIRLPOOL:
return false;
case Obj::SCHOOL_OF_MAGIC:
case Obj::SCHOOL_OF_WAR:
{
if (cb->getResourceAmount(Res::GOLD) < 1000)
return false;
break;
}
case Obj::LIBRARY_OF_ENLIGHTENMENT:
if(h->level < 12)
return false;
break;
case Obj::TREE_OF_KNOWLEDGE:
{
if(ai->ah->getHeroRole(h) == HeroRole::SCOUT)
return false;
TResources myRes = cb->getResourceAmount();
if(myRes[Res::GOLD] < 2000 || myRes[Res::GEMS] < 10)
return false;
break;
}
case Obj::MAGIC_WELL:
return h->mana < h->manaLimit();
case Obj::PRISON:
return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
case Obj::TAVERN:
{
//TODO: make AI actually recruit heroes
//TODO: only on request
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
return false;
else if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
return false;
break;
}
case Obj::BOAT:
return false;
//Boats are handled by pathfinder
case Obj::EYE_OF_MAGI:
return false; //this object is useless to visit, but could be visited indefinitely
}
if(obj->wasVisited(*h)) //it must pointer to hero instance, heroPtr calls function wasVisited(ui8 player);
return false;
return true;
}
}

View File

@ -26,7 +26,6 @@
struct QuestInfo;
class AIhelper;
class Nullkiller;
class AIStatus
@ -75,18 +74,10 @@ public:
class DLL_EXPORT VCAI : public CAdventureAI
{
public:
friend class FuzzyHelper;
friend class ResourceManager;
friend class BuildingManager;
std::map<TeleportChannelID, std::shared_ptr<TeleportChannel>> knownTeleportChannels;
std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
ObjectInstanceID destinationTeleport;
int3 destinationTeleportPos;
std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
std::map<HeroPtr, std::set<const CGTownInstance *>> townVisitsThisWeek;
std::set<HeroPtr> invalidPathHeroes; //FIXME, just a workaround
std::map<HeroPtr, Goals::TSubgoal> lockedHeroes; //TODO: allow non-elementar objectives
@ -94,8 +85,6 @@ public:
std::set<HeroPtr> heroesUnableToExplore; //these heroes will not be polled for exploration in current state of game
//sets are faster to search, also do not contain duplicates
std::set<const CGObjectInstance *> visitableObjs;
std::set<const CGObjectInstance *> alreadyVisited;
std::set<const CGObjectInstance *> reservedObjs; //to be visited by specific hero
std::map<HeroPtr, std::set<HeroPtr>> visitedHeroes; //visited this turn //FIXME: this is just bug workaround
@ -110,7 +99,6 @@ private:
public:
ObjectInstanceID selectedObject;
AIhelper * ah;
std::unique_ptr<Nullkiller> nullkiller;
VCAI();
@ -119,8 +107,6 @@ public:
//TODO: use only smart pointers?
void tryRealize(Goals::DigAtTile & g);
void tryRealize(Goals::Trade & g);
void tryRealize(Goals::Invalid & g);
void tryRealize(Goals::AbstractGoal & g);
bool isTileNotReserved(const CGHeroInstance * h, int3 t) const; //the tile is not occupied by allied hero and the object is not reserved
@ -192,8 +178,6 @@ public:
void endTurn();
void recruitHero(const CGTownInstance * t, bool throwing = false);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
//void recruitCreatures(const CGTownInstance * t);
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
@ -209,7 +193,6 @@ public:
void waitTillFree();
void addVisitableObj(const CGObjectInstance * obj);
void markObjectVisited(const CGObjectInstance * obj);
void markHeroUnableToExplore(HeroPtr h);
void markHeroAbleToExplore(HeroPtr h);
@ -218,25 +201,15 @@ public:
void validateObject(const CGObjectInstance * obj); //checks if object is still visible and if not, removes references to it
void validateObject(ObjectIdRef obj); //checks if object is still visible and if not, removes references to it
void validateVisitableObjs();
void retrieveVisitableObjs(std::vector<const CGObjectInstance *> & out, bool includeOwned = false) const;
void retrieveVisitableObjs();
virtual std::vector<const CGObjectInstance *> getFlaggedObjects() const;
const CGObjectInstance * lookForArt(int aid) const;
bool isAccessible(const int3 & pos) const;
HeroPtr getHeroWithGrail() const;
const CGObjectInstance * getUnvisitedObj(const std::function<bool(const CGObjectInstance *)> & predicate);
bool isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies = false) const;
//optimization - use one SM for every hero call
const CGTownInstance * findTownWithTavern() const;
bool canRecruitAnyHero(const CGTownInstance * t = NULL) const;
std::vector<HeroPtr> getMyHeroes() const;
HeroPtr primaryHero() 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
@ -271,10 +244,12 @@ public:
template<typename Handler> void serializeInternal(Handler & h, const int version)
{
h & knownTeleportChannels;
h & knownSubterraneanGates;
std::map<HeroPtr, std::set<const CGTownInstance *>> ignore;
h & nullkiller->memory->knownTeleportChannels;
h & nullkiller->memory->knownSubterraneanGates;
h & destinationTeleport;
h & townVisitsThisWeek;
h & ignore;
#if 0
//disabled due to issue 2890
@ -318,8 +293,8 @@ public:
#endif
h & reservedHeroesMap; //FIXME: cannot instantiate abstract class
h & visitableObjs;
h & alreadyVisited;
h & nullkiller->memory->visitableObjs;
h & nullkiller->memory->alreadyVisited;
h & reservedObjs;
if (version < 788 && !h.saving)
{