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:
committed by
ArseniyShestakov
parent
2ede3783dd
commit
25dea1a599
@@ -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++)
|
||||
{
|
||||
|
@@ -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);
|
||||
};
|
||||
|
@@ -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);
|
||||
|
@@ -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)));
|
||||
|
371
AI/VCAI/Goals.h
371
AI/VCAI/Goals.h
@@ -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());}
|
||||
};
|
||||
|
||||
}
|
||||
|
294
AI/VCAI/VCAI.cpp
294
AI/VCAI/VCAI.cpp
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
Reference in New Issue
Block a user