1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Code style: formatting and refactoring of VCAI code

This commit is contained in:
Arseniy Shestakov
2018-04-07 15:44:14 +07:00
committed by ArseniyShestakov
parent 2ede3783dd
commit 25dea1a599
9 changed files with 1864 additions and 1487 deletions

View File

@@ -37,12 +37,14 @@ ObjectIdRef::operator const CGObjectInstance*() const
return cb->getObj(id, false);
}
ObjectIdRef::ObjectIdRef(ObjectInstanceID _id) : id(_id)
ObjectIdRef::ObjectIdRef(ObjectInstanceID _id)
: id(_id)
{
}
ObjectIdRef::ObjectIdRef(const CGObjectInstance *obj) : id(obj->id)
ObjectIdRef::ObjectIdRef(const CGObjectInstance * obj)
: id(obj->id)
{
}
@@ -63,7 +65,6 @@ HeroPtr::HeroPtr(const CGHeroInstance *H)
h = H;
name = h->name;
hid = H->id;
// infosCount[ai->playerID][hid]++;
}
@@ -131,28 +132,32 @@ void foreach_tile_pos(std::function<void(const int3& pos)> foo)
// some micro-optimizations since this function gets called a LOT
// callback pointer is thread-specific and slow to retrieve -> read map size only once
int3 mapSize = cb->getMapSize();
for(int i = 0; i < mapSize.x; i++)
{
for(int j = 0; j < mapSize.y; j++)
{
for(int k = 0; k < mapSize.z; k++)
foo(int3(i, j, k));
}
}
}
void foreach_tile_pos(CCallback * cbp, std::function<void(CCallback * cbp, const int3 & pos)> foo)
{
int3 mapSize = cbp->getMapSize();
for(int i = 0; i < mapSize.x; i++)
{
for(int j = 0; j < mapSize.y; j++)
{
for(int k = 0; k < mapSize.z; k++)
foo(cbp, int3(i, j, k));
}
}
}
void foreach_neighbour(const int3 & pos, std::function<void(const int3 & pos)> foo)
{
CCallback * cbp = cb.get(); // avoid costly retrieval of thread-specific pointer
for(const int3 & dir : int3::getDirs())
{
const int3 n = pos + dir;
@@ -173,8 +178,8 @@ void foreach_neighbour(CCallback * cbp, const int3 &pos, std::function<void(CCal
bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs)
{
const CGPathNode *ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()),
*rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
if(ln->turns != rn->turns)
return ln->turns < rn->turns;
@@ -193,7 +198,8 @@ ui64 evaluateDanger(crint3 tile)
if(!t) //we can know about guard but can't check its tile (the edge of fow)
return 190000000; //MUCH
ui64 objectDanger = 0, guardDanger = 0;
ui64 objectDanger = 0;
ui64 guardDanger = 0;
auto visObjs = cb->getVisitableObjs(tile);
if(visObjs.size())
@@ -213,15 +219,18 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor)
if(!t) //we can know about guard but can't check its tile (the edge of fow)
return 190000000; //MUCH
ui64 objectDanger = 0, guardDanger = 0;
ui64 objectDanger = 0;
ui64 guardDanger = 0;
auto visitableObjects = cb->getVisitableObjs(tile);
// in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero.
if(vstd::contains_if(visitableObjects, objWithID<Obj::HERO>))
{
vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj)
{
return !objWithID<Obj::HERO>(obj);
});
}
if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects))
{
@@ -237,15 +246,15 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor)
}
}
if(dangerousObject->ID == Obj::SUBTERRANEAN_GATE)
{ //check guard on the other side of the gate
{
//check guard on the other side of the gate
auto it = ai->knownSubterraneanGates.find(dangerousObject);
if(it != ai->knownSubterraneanGates.end())
{
auto guards = cb->getGuardingCreatures(it->second->visitablePos());
for(auto cre : guards)
{
vstd::amax (guardDanger, evaluateDanger(cre) *
fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance*>(cre)));
vstd::amax(guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre)));
}
}
}
@@ -257,7 +266,6 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor)
vstd::amax(guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance *>(cre))); //we are interested in strongest monster around
}
//TODO mozna odwiedzic blockvis nie ruszajac straznika
return std::max(objectDanger, guardDanger);
}
@@ -276,7 +284,8 @@ ui64 evaluateDanger(const CGObjectInstance *obj)
return iah.army.getStrength();
}
case Obj::TOWN:
case Obj::GARRISON: case Obj::GARRISON2: //garrison
case Obj::GARRISON:
case Obj::GARRISON2:
{
InfoAboutTown iat;
cb->getTownInfo(obj, iat);
@@ -326,8 +335,8 @@ bool compareDanger(const CGObjectInstance *lhs, const CGObjectInstance *rhs)
bool isSafeToVisit(HeroPtr h, crint3 tile)
{
const ui64 heroStrength = h->getTotalStrength(),
dangerStrength = evaluateDanger(tile, *h);
const ui64 heroStrength = h->getTotalStrength();
const ui64 dangerStrength = evaluateDanger(tile, *h);
if(dangerStrength)
{
if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength)
@@ -336,18 +345,28 @@ bool isSafeToVisit(HeroPtr h, crint3 tile)
return true;
}
else
{
return false;
}
}
return true; //there's no danger
}
bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
{
//tile must be free of with unoccupied boat
return !t->blocked
|| (!fromWater && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT);
//do not try to board when in water sector
// TODO: Such information should be provided by pathfinder
// Tile must be free or with unoccupied boat
if(!t->blocked)
{
return true;
}
else if(!fromWater) // do not try to board when in water sector
{
if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
return true;
}
return false;
}
int3 whereToExplore(HeroPtr h)
@@ -371,11 +390,13 @@ int3 whereToExplore(HeroPtr h)
CGPath p;
ai->myCb->getPathsInfo(h.get())->getPath(p, op);
if(p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT)
{
if(ai->isGoodForVisit(obj, h, *sm))
nearbyVisitableObjs.push_back(obj);
}
}
}
}
vstd::removeDuplicates(nearbyVisitableObjs); //one object may occupy multiple tiles
boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get()));
if(nearbyVisitableObjs.size())
@@ -394,20 +415,25 @@ int3 whereToExplore(HeroPtr h)
bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
{
return cb->getTile(tileToHit)->topVisitableId() == Obj::BORDER_GATE &&
(dynamic_cast <const CGKeys *>(cb->getTile(tileToHit)->visitableObjects.back()))->wasMyColorVisited (ai->playerID);
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE)
return false;
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
return !gate->wasMyColorVisited(ai->playerID);
}
bool isBlockVisitObj(const int3 & pos)
{
if(auto obj = cb->getTopObj(pos))
{
if(obj->blockVisit) //we can't stand on that object
return true;
}
return false;
}
int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp)
{ //TODO: do not explore dead-end boundaries
{
//TODO: do not explore dead-end boundaries
int ret = 0;
for(int x = pos.x - radious; x <= pos.x + radious; x++)
{

View File

@@ -101,7 +101,8 @@ struct TimeCheck
{
CStopWatch time;
std::string txt;
TimeCheck(crstring TXT) : txt(TXT)
TimeCheck(crstring TXT)
: txt(TXT)
{
}
@@ -115,7 +116,8 @@ struct TimeCheck
struct AtScopeExit
{
std::function<void()> foo;
AtScopeExit(const std::function<void()> &FOO) : foo(FOO)
AtScopeExit(const std::function<void()> & FOO)
: foo(FOO)
{}
~AtScopeExit()
{
@@ -126,7 +128,6 @@ struct AtScopeExit
class ObjsVector : public std::vector<ObjectIdRef>
{
private:
};
template<int id>
@@ -166,8 +167,11 @@ int3 whereToExplore(HeroPtr h);
class CDistanceSorter
{
const CGHeroInstance * hero;
public:
CDistanceSorter(const CGHeroInstance * hero): hero(hero) {}
public:
CDistanceSorter(const CGHeroInstance * hero)
: hero(hero)
{
}
bool operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs);
};

View File

@@ -29,8 +29,6 @@ class Engine;
class InputVariable;
class CGTownInstance;
//using namespace Goals;
FuzzyHelper * fh;
extern boost::thread_specific_ptr<CCallback> cb;
@@ -106,7 +104,6 @@ void FuzzyHelper::initTacticalAdvantage()
{
try
{
ta.ourShooters = new fl::InputVariable("OurShooters");
ta.ourWalkers = new fl::InputVariable("OurWalkers");
ta.ourFlyers = new fl::InputVariable("OurFlyers");
@@ -159,7 +156,6 @@ void FuzzyHelper::initTacticalAdvantage()
}
ta.bankPresent = new fl::InputVariable("Bank");
ta.engine.addInputVariable(ta.bankPresent);
{
@@ -248,9 +244,7 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI
const CGTownInstance * fort = dynamic_cast<const CGTownInstance *>(enemy);
if(fort)
{
ta.castleWalls->setValue(fort->fortLevel());
}
else
ta.castleWalls->setValue(0);
@@ -431,7 +425,9 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
float turns = 0;
float distance = CPathfinderHelper::getMovementCost(g.hero.h, g.tile);
if(!distance) //we stand on that tile
{
turns = 0;
}
else
{
if(distance < g.hero->movement) //we can move there within one turn
@@ -451,7 +447,9 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g)
float tilePriority = 0;
if(g.objid == -1)
{
vt.estimatedReward->setEnabled(false);
}
else if(g.objid == Obj::TOWN) //TODO: move to getObj eventually and add appropiate logic there
{
vt.estimatedReward->setEnabled(true);

View File

@@ -479,13 +479,10 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
TGoalVec ret;
std::vector<const CGHeroInstance *> heroes;
if(hero)
heroes.push_back(hero.h);
else
{
heroes = cb->getHeroesInfo();
}
for(auto h : heroes)
{
@@ -503,7 +500,8 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
continue;
if(isBlockedBorderGate(tileToHit))
{ //FIXME: this way we'll not visit gate and activate quest :?
{
//FIXME: this way we'll not visit gate and activate quest :?
ret.push_back(sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID)));
}
@@ -517,8 +515,10 @@ TGoalVec ClearWayTo::getAllPossibleSubgoals()
}
if(topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
{
if(topObj != hero.get(true)) //the hero we want to free
logAi->error("%s stands in the way of %s", topObj->getObjectName(), h->getObjectName());
}
if(topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD)
{
if(shouldVisit(h, topObj))
@@ -566,7 +566,9 @@ TSubgoal Explore::whatToDoToAchieve()
{
auto ret = fh->chooseSolution(getAllPossibleSubgoals());
if(hero) //use best step for this hero
{
return ret;
}
else
{
if(ret->hero.get(true))
@@ -582,7 +584,9 @@ TGoalVec Explore::getAllPossibleSubgoals()
std::vector<const CGHeroInstance *> heroes;
if(hero)
{
heroes.push_back(hero.h);
}
else
{
//heroes = ai->getUnblockedHeroes();
@@ -824,8 +828,12 @@ TSubgoal CollectRes::whatToDoToAchieve()
markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
{
return !(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID)
&& !ai->isAccessible(market->o->visitablePos());
if(!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
{
if(!ai->isAccessible(market->o->visitablePos()))
return true;
}
return false;
}), markets.end());
if(!markets.size())
@@ -843,7 +851,8 @@ TSubgoal CollectRes::whatToDoToAchieve()
int howManyCanWeBuy = 0;
for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
{
if(i == resID) continue;
if(i == resID)
continue;
int toGive = -1, toReceive = -1;
m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
assert(toGive > 0 && toReceive > 0);
@@ -919,8 +928,8 @@ TSubgoal GatherTroops::whatToDoToAchieve()
// sorted helper
auto comparator = [](const TDwellMap::value_type & a, const TDwellMap::value_type & b) -> bool
{
const CGPathNode *ln = ai->myCb->getPathsInfo(a.first)->getPathInfo(a.second->visitablePos()),
*rn = ai->myCb->getPathsInfo(b.first)->getPathInfo(b.second->visitablePos());
const CGPathNode * ln = ai->myCb->getPathsInfo(a.first)->getPathInfo(a.second->visitablePos());
const CGPathNode * rn = ai->myCb->getPathsInfo(b.first)->getPathInfo(b.second->visitablePos());
if(ln->turns != rn->turns)
return ln->turns < rn->turns;
@@ -947,7 +956,9 @@ TSubgoal GatherTroops::whatToDoToAchieve()
return sptr(Goals::Explore());
}
else
{
return sptr(Goals::Explore());
}
//TODO: exchange troops between heroes
}
@@ -1076,8 +1087,7 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
ret.push_back(sptr(Goals::VisitTile(pos).sethero(hero)));
}
auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID>
(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
if(bid != BuildingID::NONE)
ret.push_back(sptr(BuildThis(bid, t)));
}
@@ -1087,15 +1097,23 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
auto heroDummy = hero;
vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
{
return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->visitablePos(), h, true)
|| !ai->canGetArmy(heroDummy.h, h) || ai->getGoal(h)->goalType == Goals::GATHER_ARMY);
if(h == heroDummy.h)
return true;
else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
return true;
else if(!ai->canGetArmy(heroDummy.h, h))
return true;
else if(ai->getGoal(h)->goalType == Goals::GATHER_ARMY)
return true;
else
return false;
});
for(auto h : otherHeroes)
{
// Go to the other hero if we are faster
ret.push_back(sptr(Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero)));
//go to the other hero if we are faster
// Let the other hero come to us
ret.push_back(sptr(Goals::VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h)));
//let the other hero come to us
}
std::vector<const CGObjectInstance *> objs;
@@ -1128,7 +1146,8 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
{
auto sm = ai->getCachedSectorMap(h);
for(auto obj : objs)
{ //find safe dwelling
{
//find safe dwelling
auto pos = obj->visitablePos();
if(ai->isGoodForVisit(obj, h, *sm))
ret.push_back(sptr(Goals::VisitTile(pos).sethero(h)));

View File

@@ -78,7 +78,8 @@ public:
const CGTownInstance *town; VSETTER(CGTownInstance *, town)
int bid; VSETTER(int, bid)
AbstractGoal (EGoals goal = INVALID) : goalType (goal)
AbstractGoal(EGoals goal = INVALID)
: goalType (goal)
{
priority = 0;
isElementar = false;
@@ -91,16 +92,28 @@ public:
town = nullptr;
bid = -1;
}
virtual ~AbstractGoal(){};
virtual ~AbstractGoal(){}
//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
virtual AbstractGoal * clone() const {return const_cast<AbstractGoal*>(this);};
virtual TGoalVec getAllPossibleSubgoals() {TGoalVec vec; return vec;};
virtual TSubgoal whatToDoToAchieve() {return sptr(AbstractGoal());};
virtual AbstractGoal * clone() const
{
return const_cast<AbstractGoal *>(this);
}
virtual TGoalVec getAllPossibleSubgoals()
{
return TGoalVec();
}
virtual TSubgoal whatToDoToAchieve()
{
return sptr(AbstractGoal());
}
EGoals goalType;
std::string name() const;
virtual std::string completeMessage() const {return "This goal is unspecified!";};
virtual std::string completeMessage() const
{
return "This goal is unspecified!";
}
bool invalid() const;
@@ -189,176 +202,381 @@ public:
class Invalid : public CGoal<Invalid>
{
public:
Invalid() : CGoal (Goals::INVALID) {priority = -1e10;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
Invalid()
: CGoal(Goals::INVALID)
{
priority = -1e10;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class Win : public CGoal<Win>
{
public:
Win() : CGoal (Goals::WIN) {priority = 100;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
Win()
: CGoal(Goals::WIN)
{
priority = 100;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class NotLose : public CGoal<NotLose>
{
public:
NotLose() : CGoal (Goals::DO_NOT_LOSE) {priority = 100;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
NotLose()
: CGoal(Goals::DO_NOT_LOSE)
{
priority = 100;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
//TSubgoal whatToDoToAchieve() override;
};
class Conquer : public CGoal<Conquer>
{
public:
Conquer() : CGoal (Goals::CONQUER) {priority = 10;};
Conquer()
: CGoal(Goals::CONQUER)
{
priority = 10;
}
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
};
class Build : public CGoal<Build>
{
public:
Build() : CGoal (Goals::BUILD) {priority = 1;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
Build()
: CGoal(Goals::BUILD)
{
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class Explore : public CGoal<Explore>
{
public:
Explore() : CGoal (Goals::EXPLORE){priority = 1;};
Explore(HeroPtr h) : CGoal (Goals::EXPLORE){hero = h; priority = 1;};
Explore()
: CGoal(Goals::EXPLORE)
{
priority = 1;
}
Explore(HeroPtr h)
: CGoal(Goals::EXPLORE)
{
hero = h;
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override;
bool fulfillsMe(TSubgoal goal) override;
};
class GatherArmy : public CGoal<GatherArmy>
{
public:
GatherArmy() : CGoal (Goals::GATHER_ARMY){};
GatherArmy(int val) : CGoal (Goals::GATHER_ARMY){value = val; priority = 2.5;};
GatherArmy()
: CGoal(Goals::GATHER_ARMY)
{
}
GatherArmy(int val)
: CGoal(Goals::GATHER_ARMY)
{
value = val;
priority = 2.5;
}
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override;
};
class BoostHero : public CGoal<BoostHero>
{
public:
BoostHero() : CGoal (Goals::INVALID){priority = -1e10;}; //TODO
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
BoostHero()
: CGoal(Goals::INVALID)
{
priority = -1e10; //TODO
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
};
class RecruitHero : public CGoal<RecruitHero>
{
public:
RecruitHero() : CGoal (Goals::RECRUIT_HERO){priority = 1;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
RecruitHero()
: CGoal(Goals::RECRUIT_HERO)
{
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class BuildThis : public CGoal<BuildThis>
{
public:
BuildThis() : CGoal (Goals::BUILD_STRUCTURE){}; //FIXME: should be not allowed (private)
BuildThis(BuildingID Bid, const CGTownInstance *tid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid; town = tid; priority = 5;};
BuildThis(BuildingID Bid) : CGoal (Goals::BUILD_STRUCTURE) {bid = Bid; priority = 5;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
BuildThis()
: CGoal(Goals::BUILD_STRUCTURE)
{
//FIXME: should be not allowed (private)
}
BuildThis(BuildingID Bid, const CGTownInstance * tid)
: CGoal(Goals::BUILD_STRUCTURE)
{
bid = Bid;
town = tid;
priority = 5;
}
BuildThis(BuildingID Bid)
: CGoal(Goals::BUILD_STRUCTURE)
{
bid = Bid;
priority = 5;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class CollectRes : public CGoal<CollectRes>
{
public:
CollectRes() : CGoal (Goals::COLLECT_RES){};
CollectRes(int rid, int val) : CGoal (Goals::COLLECT_RES) {resID = rid; value = val; priority = 2;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
CollectRes()
: CGoal(Goals::COLLECT_RES)
{
}
CollectRes(int rid, int val)
: CGoal(Goals::COLLECT_RES)
{
resID = rid;
value = val;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
};
TSubgoal whatToDoToAchieve() override;
};
class GatherTroops : public CGoal<GatherTroops>
{
public:
GatherTroops() : CGoal (Goals::GATHER_TROOPS){priority = 2;};
GatherTroops(int type, int val) : CGoal (Goals::GATHER_TROOPS){objid = type; value = val; priority = 2;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
GatherTroops()
: CGoal(Goals::GATHER_TROOPS)
{
priority = 2;
}
GatherTroops(int type, int val)
: CGoal(Goals::GATHER_TROOPS)
{
objid = type;
value = val;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class GetObj : public CGoal<GetObj>
{
public:
GetObj() {}; // empty constructor not allowed
GetObj() {} // empty constructor not allowed
GetObj(int Objid) : CGoal(Goals::GET_OBJ) {objid = Objid; priority = 3;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
GetObj(int Objid)
: CGoal(Goals::GET_OBJ)
{
objid = Objid;
priority = 3;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool operator== (GetObj &g) {return g.objid == objid;}
bool operator==(GetObj & g)
{
return g.objid == objid;
}
bool fulfillsMe(TSubgoal goal) override;
std::string completeMessage() const override;
};
class FindObj : public CGoal<FindObj>
{
public:
FindObj() {}; // empty constructor not allowed
FindObj() {} // empty constructor not allowed
FindObj(int ID) : CGoal(Goals::FIND_OBJ) {objid = ID; priority = 1;};
FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {objid = ID; resID = subID; priority = 1;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
FindObj(int ID)
: CGoal(Goals::FIND_OBJ)
{
objid = ID;
priority = 1;
}
FindObj(int ID, int subID)
: CGoal(Goals::FIND_OBJ)
{
objid = ID;
resID = subID;
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class VisitHero : public CGoal<VisitHero>
{
public:
VisitHero() : CGoal (Goals::VISIT_HERO){};
VisitHero(int hid) : CGoal (Goals::VISIT_HERO){objid = hid; priority = 4;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
VisitHero()
: CGoal(Goals::VISIT_HERO)
{
}
VisitHero(int hid)
: CGoal(Goals::VISIT_HERO)
{
objid = hid;
priority = 4;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool operator== (VisitHero &g) { return g.goalType == goalType && g.objid == objid; }
bool operator==(VisitHero & g)
{
return g.goalType == goalType && g.objid == objid;
}
bool fulfillsMe(TSubgoal goal) override;
std::string completeMessage() const override;
};
class GetArtOfType : public CGoal<GetArtOfType>
{
public:
GetArtOfType() : CGoal (Goals::GET_ART_TYPE){};
GetArtOfType(int type) : CGoal (Goals::GET_ART_TYPE){aid = type; priority = 2;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
GetArtOfType()
: CGoal(Goals::GET_ART_TYPE)
{
}
GetArtOfType(int type)
: CGoal(Goals::GET_ART_TYPE)
{
aid = type;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
};
class VisitTile : public CGoal<VisitTile>
//tile, in conjunction with hero elementar; assumes tile is reachable
{
public:
VisitTile() {}; // empty constructor not allowed
VisitTile() {} // empty constructor not allowed
VisitTile(int3 Tile) : CGoal (Goals::VISIT_TILE) {tile = Tile; priority = 5;};
VisitTile(int3 Tile)
: CGoal(Goals::VISIT_TILE)
{
tile = Tile;
priority = 5;
}
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
bool operator== (VisitTile &g) { return g.goalType == goalType && g.tile == tile; }
bool operator==(VisitTile & g)
{
return g.goalType == goalType && g.tile == tile;
}
std::string completeMessage() const override;
};
class ClearWayTo : public CGoal<ClearWayTo>
{
public:
ClearWayTo() : CGoal (Goals::CLEAR_WAY_TO){};
ClearWayTo(int3 Tile) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile; priority = 5;};
ClearWayTo(int3 Tile, HeroPtr h) : CGoal (Goals::CLEAR_WAY_TO) {tile = Tile; hero = h; priority = 5;};
ClearWayTo()
: CGoal(Goals::CLEAR_WAY_TO)
{
}
ClearWayTo(int3 Tile)
: CGoal(Goals::CLEAR_WAY_TO)
{
tile = Tile;
priority = 5;
}
ClearWayTo(int3 Tile, HeroPtr h)
: CGoal(Goals::CLEAR_WAY_TO)
{
tile = Tile;
hero = h;
priority = 5;
}
TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override;
bool operator== (ClearWayTo &g) { return g.goalType == goalType && g.tile == tile; }
bool operator==(ClearWayTo & g)
{
return g.goalType == goalType && g.tile == tile;
}
};
class DigAtTile : public CGoal<DigAtTile>
//elementar with hero on tile
{
public:
DigAtTile() : CGoal (Goals::DIG_AT_TILE){};
DigAtTile(int3 Tile) : CGoal (Goals::DIG_AT_TILE) {tile = Tile; priority = 20;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
DigAtTile()
: CGoal(Goals::DIG_AT_TILE)
{
}
DigAtTile(int3 Tile)
: CGoal(Goals::DIG_AT_TILE)
{
tile = Tile;
priority = 20;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override;
bool operator== (DigAtTile &g) { return g.goalType == goalType && g.tile == tile; }
bool operator==(DigAtTile & g)
{
return g.goalType == goalType && g.tile == tile;
}
};
class CIssueCommand : public CGoal<CIssueCommand>
@@ -366,11 +584,20 @@ class CIssueCommand : public CGoal<CIssueCommand>
std::function<bool()> command;
public:
CIssueCommand(): CGoal(ISSUE_COMMAND){};
CIssueCommand(std::function<bool()> _command): CGoal(ISSUE_COMMAND), command(_command) {priority = 1e10;};
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();};
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
CIssueCommand()
: CGoal(ISSUE_COMMAND)
{
}
CIssueCommand(std::function<bool()> _command)
: CGoal(ISSUE_COMMAND), command(_command)
{
priority = 1e10;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());}
};
}

View File

@@ -62,19 +62,22 @@ struct SetGlobalState
void foreach_tile(std::vector<std::vector<std::vector<unsigned char>>> & vectors, std::function<void(unsigned char & in)> foo)
{
for(auto & vector : vectors)
{
for(auto j = vector.begin(); j != vector.end(); j++)
{
for(auto & elem : *j)
foo(elem);
}
}
}
struct ObjInfo
{
int3 pos;
std::string name;
ObjInfo(){}
ObjInfo(const CGObjectInstance *obj):
pos(obj->pos),
name(obj->getObjectName())
ObjInfo(const CGObjectInstance * obj)
: pos(obj->pos), name(obj->getObjectName())
{
}
};
@@ -110,10 +113,10 @@ void VCAI::heroMoved(const TryMoveHero & details)
auto hero = cb->getHero(details.id);
cachedSectorMaps.clear();
const int3 from = CGHeroInstance::convertPosition(details.start, false),
to = CGHeroInstance::convertPosition(details.end, false);
const CGObjectInstance *o1 = vstd::frontOrNull(cb->getVisitableObjs(from)),
*o2 = vstd::frontOrNull(cb->getVisitableObjs(to));
const int3 from = CGHeroInstance::convertPosition(details.start, false);
const int3 to = CGHeroInstance::convertPosition(details.end, false);
const CGObjectInstance * o1 = vstd::frontOrNull(cb->getVisitableObjs(from));
const CGObjectInstance * o2 = vstd::frontOrNull(cb->getVisitableObjs(to));
if(details.result == TryMoveHero::TELEPORTATION)
{
@@ -290,8 +293,10 @@ void VCAI::tileRevealed(const std::unordered_set<int3, ShashInt3> &pos)
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
for(int3 tile : pos)
{
for(const CGObjectInstance * obj : myCb->getVisitableObjs(tile))
addVisitableObj(obj);
}
clearPathsInfo();
}
@@ -326,11 +331,17 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
//Do not attempt army or artifacts exchange if we visited ally player
//Visits can still be useful if hero have skills like Scholar
if(firstHero->tempOwner != secondHero->tempOwner)
{
logAi->debug("Heroes owned by different players. Do not exchange army or artifacts.");
}
else if(goalpriority1 > goalpriority2)
{
transferFrom2to1(firstHero, secondHero);
}
else if(goalpriority1 < goalpriority2)
{
transferFrom2to1(secondHero, firstHero);
}
else //regular criteria
{
if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && canGetArmy(firstHero, secondHero))
@@ -460,9 +471,11 @@ void VCAI::requestRealized(PackageApplied *pa)
if(status.haveTurn())
{
if(pa->packType == typeList.getTypeID<EndTurn>())
{
if(pa->result)
status.madeTurn();
}
}
if(pa->packType == typeList.getTypeID<QueryReply>())
{
@@ -615,7 +628,9 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit
int choosenExit = -1;
if(impassable)
{
knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
}
else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid())
{
auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos);
@@ -635,9 +650,9 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit
// TODO: Implement checking if visiting that teleport will uncovert any FoW
// So far this is the best option to handle decision about probing
auto obj = cb->getObj(exit.first, false);
if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first) &&
exit.first != destinationTeleport)
if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first))
{
if(exit.first != destinationTeleport)
teleportChannelProbingList.push_back(exit.first);
}
}
@@ -746,14 +761,12 @@ void VCAI::makeTurn()
vstd::erase_if_present(alreadyVisited, obj);
}
}
}
break;
}
}
markHeroAbleToExplore(primaryHero());
makeTurnInternal();
return;
}
void VCAI::makeTurnInternal()
@@ -817,7 +830,9 @@ void VCAI::makeTurnInternal()
break;
}
if(safeCopy.empty())
{
break; //all heroes exhausted their locked goals
}
else
{
typedef std::pair<HeroPtr, Goals::TSubgoal> TItrType;
@@ -885,10 +900,12 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
if(h->visitedTown) //we are inside, not just attacking
{
townVisitsThisWeek[h].insert(h->visitedTown);
if (!h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST + saving[Res::GOLD] &&
h->visitedTown->hasBuilt (BuildingID::MAGES_GUILD_1))
if(!h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST + saving[Res::GOLD])
{
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
}
}
break;
}
completeGoal(sptr(Goals::GetObj(obj->id.getNum()).sethero(h)));
@@ -903,26 +920,27 @@ void VCAI::moveCreaturesToHero(const CGTownInstance * t)
}
bool VCAI::canGetArmy(const CGHeroInstance * army, const CGHeroInstance * source)
{ //TODO: merge with pickBestCreatures
{
//TODO: merge with pickBestCreatures
//if (ai->primaryHero().h == source)
if(army->tempOwner != source->tempOwner)
{
logAi->error("Why are we even considering exchange between heroes from different players?");
return false;
}
const CArmedInstance * armies[] = {army, source};
//we calculate total strength for each creature type available in armies
std::map<const CCreature *, int> creToPower;
for(auto armyPtr : armies)
{
for(auto & i : armyPtr->Slots())
{
//TODO: allow splitting stacks?
creToPower[i.second->type] += i.second->getPower();
}
}
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
int armySize = creToPower.size();
armySize = std::min((source->needsLastStack() ? armySize - 1 : armySize), GameConstants::ARMY_SIZE); //can't move away last stack
@@ -944,6 +962,7 @@ bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * sourc
for(int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot
{
for(auto armyPtr : armies)
{
for(int j = 0; j < GameConstants::ARMY_SIZE; j++)
{
if(armyPtr->getCreature(SlotID(j)) == bestArmy[i] && armyPtr != army) //it's a searched creature not in dst ARMY
@@ -956,6 +975,7 @@ bool VCAI::canGetArmy (const CGHeroInstance * army, const CGHeroInstance * sourc
}
}
}
}
return false;
}
@@ -967,10 +987,13 @@ void VCAI::pickBestCreatures(const CArmedInstance * army, const CArmedInstance *
//we calculate total strength for each creature type available in armies
std::map<const CCreature *, int> creToPower;
for(auto armyPtr : armies)
{
for(auto & i : armyPtr->Slots())
{//TODO: allow splitting stacks?
{
//TODO: allow splitting stacks?
creToPower[i.second->type] += i.second->getPower();
}
}
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
int armySize = creToPower.size();
@@ -993,29 +1016,30 @@ void VCAI::pickBestCreatures(const CArmedInstance * army, const CArmedInstance *
for(int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot
{
for(auto armyPtr : armies)
{
for(int j = 0; j < GameConstants::ARMY_SIZE; j++)
{
if(armyPtr->getCreature(SlotID(j)) == bestArmy[i] && (i != j || armyPtr != army)) //it's a searched creature not in dst SLOT
{
if(!(armyPtr->needsLastStack() && armyPtr->stacksCount() == 1)) //can't take away last creature
cb->mergeOrSwapStacks(armyPtr, army, SlotID(j), SlotID(i));
}
}
}
}
//TODO - having now strongest possible army, we may want to think about arranging stacks
auto hero = dynamic_cast<const CGHeroInstance *>(army);
if(hero)
{
checkHeroArmy(hero);
}
}
void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
{
auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
{
bool changeMade = false;
do
{
changeMade = false;
@@ -1100,18 +1124,16 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
if(changeMade)
break; //start evaluating artifacts from scratch
}
} while (changeMade);
}
while(changeMade);
};
equipBest(h, other, true);
if(other)
{
equipBest(h, other, false);
}
}
void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter)
{
for(int i = 0; i < d->creatures.size(); i++)
@@ -1156,9 +1178,7 @@ bool VCAI::tryBuildStructure(const CGTownInstance * t, BuildingID building, unsi
for(BuildingID buildID : toBuild)
{
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
if (canBuild == EBuildingState::HAVE_CAPITAL
|| canBuild == EBuildingState::FORBIDDEN
|| canBuild == EBuildingState::NO_WATER)
if(canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER)
return false; //we won't be able to build this
}
@@ -1359,15 +1379,22 @@ void VCAI::buildStructure(const CGTownInstance * t)
return;
//workaround for mantis #2696 - build fort and citadel - building castle will be handled without bug
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL &&
cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
{
if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
{
if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements, capitolRequirements + ARRAY_COUNT(capitolRequirements))))
return;
}
}
if((!vstd::contains(t->builtBuildings, BuildingID::CAPITOL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
|| (!vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) == EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
|| (!vstd::contains(t->builtBuildings, BuildingID::TOWN_HALL) && cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN))
return; //save money for capitol or city hall if capitol unavailable, do not build other things (unless gold source buildings are disabled in map editor)
//save money for capitol or city hall if capitol unavailable, do not build other things (unless gold source buildings are disabled in map editor)
if(!vstd::contains(t->builtBuildings, BuildingID::CAPITOL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
return;
else if(!vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) == EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN)
return;
else if(!vstd::contains(t->builtBuildings, BuildingID::TOWN_HALL) && cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)
return;
if(cb->getDate(Date::DAY_OF_WEEK) > 6) // last 2 days of week - try to focus on growth
{
@@ -1377,8 +1404,10 @@ void VCAI::buildStructure(const CGTownInstance * t)
// first in-game week or second half of any week: try build dwellings
if(cb->getDate(Date::DAY) < 7 || cb->getDate(Date::DAY_OF_WEEK) > 3)
{
if(tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
return;
}
//try to upgrade dwelling
for(int i = 0; i < ARRAY_COUNT(unitsUpgrade); i++)
@@ -1398,11 +1427,11 @@ void VCAI::buildStructure(const CGTownInstance * t)
//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
std::vector<BuildingID> extraBuildings;
for(auto buildingInfo : t->town->buildings)
{
if(buildingInfo.first > 43)
extraBuildings.push_back(buildingInfo.first);
}
if(tryBuildAnyStructure(t, extraBuildings))
return;
}
@@ -1413,15 +1442,23 @@ bool VCAI::isGoodForVisit(const CGObjectInstance *obj, HeroPtr h, SectorMap &sm)
const int3 targetPos = sm.firstTileToGet(h, pos);
if(!targetPos.valid())
return false;
if (isTileNotReserved(h.get(), targetPos) &&
!obj->wasVisited(playerID) &&
(cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES || isWeeklyRevisitable(obj)) && //flag or get weekly resources / creatures
isSafeToVisit(h, pos) &&
shouldVisit(h, obj) &&
!vstd::contains(alreadyVisited, obj) &&
!vstd::contains(reservedObjs, obj) &&
isAccessibleForHero(targetPos, h))
{
if(!isTileNotReserved(h.get(), targetPos))
return false;
if(obj->wasVisited(playerID))
return false;
if(cb->getPlayerRelations(ai->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;
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
@@ -1429,9 +1466,7 @@ bool VCAI::isGoodForVisit(const CGObjectInstance *obj, HeroPtr h, SectorMap &sm)
return false;
else
return true; //all of the following is met
}
return false;
}
bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t)
@@ -1445,20 +1480,26 @@ bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t)
return true;
}
else
{
return false;
}
}
bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
{
//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
if(!t)
t = findTownWithTavern();
if (t)
return cb->getResourceAmount(Res::GOLD) >= GameConstants::HERO_GOLD_COST &&
cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES &&
cb->getAvailableHeroes(t).size();
else
if(!t)
return false;
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST)
return false;
if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
return false;
if(!cb->getAvailableHeroes(t).size())
return false;
return true;
}
void VCAI::wander(HeroPtr h)
@@ -1566,8 +1607,10 @@ void VCAI::wander(HeroPtr h)
vstd::erase_if(towns, [](const CGTownInstance * t) -> bool
{
for(const CGHeroInstance * h : cb->getHeroesInfo())
{
if(!t->getArmyStrength() || howManyReinforcementsCanGet(h, t))
return true;
}
return false;
});
boost::sort(towns, compareArmyStrength);
@@ -1615,7 +1658,9 @@ void VCAI::wander(HeroPtr h)
void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
{
if(goal->invalid())
{
vstd::erase_if_present(lockedHeroes, h);
}
else
{
lockedHeroes[h] = goal;
@@ -1635,12 +1680,14 @@ void VCAI::completeGoal (Goals::TSubgoal goal)
{
auto it = lockedHeroes.find(h);
if(it != lockedHeroes.end())
{
if(it->second == goal)
{
logAi->debug(goal->completeMessage());
lockedHeroes.erase(it); //goal fulfilled, free hero
}
}
}
else //complete goal for all heroes maybe?
{
vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p)
@@ -1687,9 +1734,11 @@ void VCAI::markObjectVisited (const CGObjectInstance *obj)
{
if(!obj)
return;
if(dynamic_cast<const CGVisitableOPH *>(obj) || //we may want to visit it with another hero
dynamic_cast<const CGBonusingObject *>(obj) || //or another time
(obj->ID == Obj::MONSTER))
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);
}
@@ -1834,9 +1883,10 @@ bool VCAI::isAccessible(const int3 &pos)
HeroPtr VCAI::getHeroWithGrail() const
{
for(const CGHeroInstance * h : cb->getHeroesInfo())
{
if(h->hasArt(2)) //grail
return h;
}
return nullptr;
}
@@ -1844,24 +1894,27 @@ const CGObjectInstance * VCAI::getUnvisitedObj(const std::function<bool(const CG
{
//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)
{ //don't visit tile occupied by allied hero
{
for(auto obj : cb->getVisitableObjs(pos))
{
if (obj->ID == Obj::HERO &&
cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES &&
obj != h.get())
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();
}
@@ -1915,12 +1968,13 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
auto isTeleportAction = [&](CGPathNode::ENodeAction action) -> bool
{
if(action != CGPathNode::TELEPORT_NORMAL &&
action != CGPathNode::TELEPORT_BLOCKING_VISIT &&
action != CGPathNode::TELEPORT_BATTLE)
if(action != CGPathNode::TELEPORT_NORMAL && action != CGPathNode::TELEPORT_BLOCKING_VISIT)
{
if(action != CGPathNode::TELEPORT_BATTLE)
{
return false;
}
}
return true;
};
@@ -1929,9 +1983,9 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
{
if(CGTeleport::isConnected(currentObject, nextObjectTop))
return nextObjectTop;
if(nextObjectTop && nextObjectTop->ID == Obj::HERO &&
CGTeleport::isConnected(currentObject, nextObject))
if(nextObjectTop && nextObjectTop->ID == Obj::HERO)
{
if(CGTeleport::isConnected(currentObject, nextObject))
return nextObject;
}
@@ -1978,7 +2032,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
auto nextObject = getObj(nextCoord, true);
auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
if(isTeleportAction(path.nodes[i - 1].action) && destTeleportObj != nullptr)
{ //we use special login if hero standing on teleporter it's mean we need
{
//we use special login if hero standing on teleporter it's mean we need
doTeleportMovement(destTeleportObj->id, nextCoord);
if(teleportChannelProbingList.size())
doChannelProbing();
@@ -1998,16 +2053,27 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
if(endpos == h->visitablePos())
continue;
if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
&& (CGTeleport::isConnected(nextObjectTop, getObj(path.nodes[i-2].coord, false))
|| CGTeleport::isTeleport(nextObjectTop)))
{ // Hero should be able to go through object if it's allow transit
bool isConnected = false;
bool isNextObjectTeleport = false;
// Check there is node after next one; otherwise transit is pointless
if(i - 2 >= 0)
{
isConnected = CGTeleport::isConnected(nextObjectTop, getObj(path.nodes[i - 2].coord, false));
isNextObjectTeleport = CGTeleport::isTeleport(nextObjectTop);
}
if(isConnected || isNextObjectTeleport)
{
// Hero should be able to go through object if it's allow transit
doMovement(endpos, true);
}
else if(path.nodes[i - 1].layer == EPathfindingLayer::AIR)
{
doMovement(endpos, true);
}
else
{
doMovement(endpos, false);
}
afterMovementCheck();
@@ -2091,8 +2157,10 @@ void VCAI::tryRealize(Goals::VisitHero & g)
}
}
else
{
throw cannotFulfillGoalException("Cannot visit hero: object not found!");
}
}
void VCAI::tryRealize(Goals::BuildThis & g)
{
@@ -2149,7 +2217,8 @@ void VCAI::tryRealize(Goals::CollectRes & g)
{
for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
{
if(i == g.resID) continue;
if(i == g.resID)
continue;
int toGive, toGet;
m->getOffer(i, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
toGive = toGive * (cb->getResourceAmount(i) / toGive);
@@ -2265,11 +2334,11 @@ void VCAI::endTurn()
logAi->error("Not having turn at the end of turn???");
}
logAi->debug("Resources at the end of turn: %s", cb->getResourceAmount().toString());
do
{
cb->endTurn();
} while(status.haveTurn()); //for some reasons, our request may fail -> stop requesting end of turn only after we've received a confirmation that it's over
}
while(status.haveTurn()); //for some reasons, our request may fail -> stop requesting end of turn only after we've received a confirmation that it's over
logGlobal->info("Player %d (%s) ended turn", playerID, playerID.getStr());
}
@@ -2563,7 +2632,9 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
{
int3 tile = hpos + dir;
if(cb->isInTheMap(tile))
{
if(ourPos != dir) //don't stand in place
{
if(isSafeToVisit(h, tile) && isAccessibleForHero(tile, h))
{
if(isBlockVisitObj(tile))
@@ -2572,6 +2643,8 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(radius, hpos, dir);
}
}
}
}
if(dstToRevealedTiles.empty()) //yes, it DID happen!
throw cannotFulfillGoalException("No neighbour will bring new discoveries!");
@@ -2701,8 +2774,6 @@ TResources VCAI::estimateIncome() const
ret += t->dailyIncome();
}
for(const CGObjectInstance * obj : getFlaggedObjects())
{
if(obj->ID == Obj::MINE)
@@ -2764,8 +2835,10 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
cb->recruitHero(t, hero);
}
else if(throwing)
{
throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
}
}
void VCAI::finish()
{
@@ -2839,7 +2912,10 @@ void VCAI::validateObject(const CGObjectInstance *obj)
void VCAI::validateObject(ObjectIdRef obj)
{
auto matchesId = [&](const CGObjectInstance *hlpObj) -> bool { return hlpObj->id == obj.id; };
auto matchesId = [&](const CGObjectInstance * hlpObj) -> bool
{
return hlpObj->id == obj.id;
};
if(!obj)
{
vstd::erase_if(visitableObjs, matchesId);
@@ -2869,7 +2945,9 @@ std::shared_ptr<SectorMap> VCAI::getCachedSectorMap(HeroPtr h)
{
auto it = cachedSectorMaps.find(h);
if(it != cachedSectorMaps.end())
{
return it->second;
}
else
{
cachedSectorMaps[h] = std::make_shared<SectorMap>(h);
@@ -2999,7 +3077,9 @@ void AIStatus::heroVisit(const CGObjectInstance *obj, bool started)
{
boost::unique_lock<boost::mutex> lock(mx);
if(started)
{
objectsBeingVisited.push_back(obj);
}
else
{
// There can be more than one object visited at the time (eg. hero visits Subterranean Gate
@@ -3101,9 +3181,13 @@ void SectorMap::clear()
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;
}
@@ -3175,11 +3259,15 @@ void SectorMap::write(crstring fname)
}
bool isWeeklyRevisitable(const CGObjectInstance * obj)
{ //TODO: allow polling of remaining creatures in dwelling
if (dynamic_cast<const CGVisitableOPW *>(obj) || //ensures future compatibility, unlike IDs
dynamic_cast<const CGDwelling *>(obj) ||
dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
{
//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:
@@ -3200,7 +3288,6 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
case Obj::TOWN:
case Obj::HERO: //never visit our heroes at random
return obj->tempOwner != h->tempOwner; //do not visit our towns at random
break;
case Obj::BORDER_GATE:
{
for(auto q : ai->myCb->getMyQuests())
@@ -3212,7 +3299,6 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
}
return true; //we don't have this quest yet
}
break;
case Obj::BORDERGUARD: //open borderguard if possible
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
case Obj::SEER_HUT:
@@ -3230,7 +3316,6 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
}
return true; //we don't have this quest yet
}
break;
case Obj::CREATURE_GENERATOR1:
{
if(obj->tempOwner != h->tempOwner)
@@ -3267,8 +3352,8 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
TResources myRes = ai->myCb->getResourceAmount();
if(myRes[Res::GOLD] - GOLD_RESERVE < 1000)
return false;
}
break;
}
case Obj::LIBRARY_OF_ENLIGHTENMENT:
if(h->level < 12)
return false;
@@ -3278,26 +3363,27 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
TResources myRes = ai->myCb->getResourceAmount();
if(myRes[Res::GOLD] - GOLD_RESERVE < 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: make AI actually recruit heroes
//TODO: only on request
if((ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER) ||
(ai->myCb->getResourceAmount()[Res::GOLD] - GOLD_RESERVE < GameConstants::HERO_GOLD_COST))
if(ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)
return false;
else if(ai->myCb->getResourceAmount()[Res::GOLD] - GOLD_RESERVE < 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);
@@ -3306,19 +3392,19 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
return true;
}
int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
/*
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()),
destinationSector = retrieveTile(dst);
int sourceSector = retrieveTile(h->visitablePos());
int destinationSector = retrieveTile(dst);
const Sector *src = &infoOnSectors[sourceSector],
*dest = &infoOnSectors[destinationSector];
const Sector * src = &infoOnSectors[sourceSector];
const Sector * dest = &infoOnSectors[destinationSector];
if(sourceSector != destinationSector) //use ships, shipyards etc..
{
@@ -3371,8 +3457,12 @@ For ship construction etc, another function (goal?) is needed
auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
{
const TerrainTile * t = getTile(pos);
return t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT
&& retrieveTile(pos) == sectorToReach->id;
if(t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
{
if(retrieveTile(pos) == sectorToReach->id)
return true;
}
return false;
});
if(firstEP != src->embarkmentPoints.end())
@@ -3393,9 +3483,11 @@ For ship construction etc, another function (goal?) is needed
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
{
@@ -3474,12 +3566,14 @@ int3 SectorMap::findFirstVisitableTile (HeroPtr h, crint3 dst)
while(curtile != h->visitablePos())
{
auto topObj = cb->getTopObj(curtile);
if(topObj && topObj->ID == Obj::HERO && topObj != h.h &&
cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
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;

View File

@@ -70,7 +70,12 @@ public:
}
};
enum {NOT_VISIBLE = 0, NOT_CHECKED = 1, NOT_AVAILABLE};
enum
{
NOT_VISIBLE = 0,
NOT_CHECKED = 1,
NOT_AVAILABLE
};
struct SectorMap
{
@@ -413,8 +418,10 @@ public:
class cannotFulfillGoalException : public std::exception
{
std::string msg;
public:
explicit cannotFulfillGoalException(crstring _Message) : msg(_Message)
explicit cannotFulfillGoalException(crstring _Message)
: msg(_Message)
{
}
@@ -427,13 +434,16 @@ public:
return msg.c_str();
}
};
class goalFulfilledException : public std::exception
{
std::string msg;
public:
Goals::TSubgoal goal;
explicit goalFulfilledException(Goals::TSubgoal Goal) : goal(Goal)
explicit goalFulfilledException(Goals::TSubgoal Goal)
: goal(Goal)
{
msg = goal->name();
}
@@ -449,4 +459,3 @@ public:
};
void makePossibleUpgrades(const CArmedInstance * obj);