mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Nullkiller: remove AIhelper + refactoring
This commit is contained in:
parent
f832a8b3cd
commit
1806dd8447
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
};
|
||||
|
@ -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 };
|
@ -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;
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ namespace Goals
|
||||
|
||||
private:
|
||||
bool shouldVisitObject(const CGObjectInstance * obj) const;
|
||||
static bool shouldVisit(HeroPtr h, const CGObjectInstance * obj);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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
|
||||
|
104
AI/Nullkiller/Engine/AIMemory.cpp
Normal file
104
AI/Nullkiller/Engine/AIMemory.cpp
Normal 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);
|
||||
}
|
36
AI/Nullkiller/Engine/AIMemory.h
Normal file
36
AI/Nullkiller/Engine/AIMemory.h
Normal 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);
|
||||
};
|
@ -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()
|
||||
{
|
@ -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;
|
||||
}
|
@ -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);
|
||||
};
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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!");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
extern boost::thread_specific_ptr<CCallback> cb;
|
||||
extern boost::thread_specific_ptr<VCAI> ai;
|
||||
extern FuzzyHelper * fh;
|
||||
|
||||
using namespace Goals;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
@ -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
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user