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

View File

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

View File

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

View File

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

View File

@@ -78,7 +78,8 @@ public:
const CGTownInstance *town; VSETTER(CGTownInstance *, town) const CGTownInstance *town; VSETTER(CGTownInstance *, town)
int bid; VSETTER(int, bid) int bid; VSETTER(int, bid)
AbstractGoal (EGoals goal = INVALID) : goalType (goal) AbstractGoal(EGoals goal = INVALID)
: goalType (goal)
{ {
priority = 0; priority = 0;
isElementar = false; isElementar = false;
@@ -91,16 +92,28 @@ public:
town = nullptr; town = nullptr;
bid = -1; bid = -1;
} }
virtual ~AbstractGoal(){}; virtual ~AbstractGoal(){}
//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case //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 AbstractGoal * clone() const
virtual TGoalVec getAllPossibleSubgoals() {TGoalVec vec; return vec;}; {
virtual TSubgoal whatToDoToAchieve() {return sptr(AbstractGoal());}; return const_cast<AbstractGoal *>(this);
}
virtual TGoalVec getAllPossibleSubgoals()
{
return TGoalVec();
}
virtual TSubgoal whatToDoToAchieve()
{
return sptr(AbstractGoal());
}
EGoals goalType; EGoals goalType;
std::string name() const; 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; bool invalid() const;
@@ -189,176 +202,381 @@ public:
class Invalid : public CGoal<Invalid> class Invalid : public CGoal<Invalid>
{ {
public: public:
Invalid() : CGoal (Goals::INVALID) {priority = -1e10;}; Invalid()
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::INVALID)
{
priority = -1e10;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class Win : public CGoal<Win> class Win : public CGoal<Win>
{ {
public: public:
Win() : CGoal (Goals::WIN) {priority = 100;}; Win()
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::WIN)
{
priority = 100;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class NotLose : public CGoal<NotLose> class NotLose : public CGoal<NotLose>
{ {
public: public:
NotLose() : CGoal (Goals::DO_NOT_LOSE) {priority = 100;}; NotLose()
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::DO_NOT_LOSE)
{
priority = 100;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
//TSubgoal whatToDoToAchieve() override; //TSubgoal whatToDoToAchieve() override;
}; };
class Conquer : public CGoal<Conquer> class Conquer : public CGoal<Conquer>
{ {
public: public:
Conquer() : CGoal (Goals::CONQUER) {priority = 10;}; Conquer()
: CGoal(Goals::CONQUER)
{
priority = 10;
}
TGoalVec getAllPossibleSubgoals() override; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class Build : public CGoal<Build> class Build : public CGoal<Build>
{ {
public: public:
Build() : CGoal (Goals::BUILD) {priority = 1;}; Build()
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::BUILD)
{
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class Explore : public CGoal<Explore> class Explore : public CGoal<Explore>
{ {
public: public:
Explore() : CGoal (Goals::EXPLORE){priority = 1;}; Explore()
Explore(HeroPtr h) : CGoal (Goals::EXPLORE){hero = h; priority = 1;}; : CGoal(Goals::EXPLORE)
{
priority = 1;
}
Explore(HeroPtr h)
: CGoal(Goals::EXPLORE)
{
hero = h;
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override; std::string completeMessage() const override;
bool fulfillsMe(TSubgoal goal) override; bool fulfillsMe(TSubgoal goal) override;
}; };
class GatherArmy : public CGoal<GatherArmy> class GatherArmy : public CGoal<GatherArmy>
{ {
public: public:
GatherArmy() : CGoal (Goals::GATHER_ARMY){}; GatherArmy()
: CGoal(Goals::GATHER_ARMY)
GatherArmy(int val) : CGoal (Goals::GATHER_ARMY){value = val; priority = 2.5;}; {
}
GatherArmy(int val)
: CGoal(Goals::GATHER_ARMY)
{
value = val;
priority = 2.5;
}
TGoalVec getAllPossibleSubgoals() override; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
std::string completeMessage() const override; std::string completeMessage() const override;
}; };
class BoostHero : public CGoal<BoostHero> class BoostHero : public CGoal<BoostHero>
{ {
public: public:
BoostHero() : CGoal (Goals::INVALID){priority = -1e10;}; //TODO BoostHero()
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::INVALID)
{
priority = -1e10; //TODO
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());}; //TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
}; };
class RecruitHero : public CGoal<RecruitHero> class RecruitHero : public CGoal<RecruitHero>
{ {
public: public:
RecruitHero() : CGoal (Goals::RECRUIT_HERO){priority = 1;}; RecruitHero()
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::RECRUIT_HERO)
{
priority = 1;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class BuildThis : public CGoal<BuildThis> class BuildThis : public CGoal<BuildThis>
{ {
public: public:
BuildThis() : CGoal (Goals::BUILD_STRUCTURE){}; //FIXME: should be not allowed (private) BuildThis()
: CGoal(Goals::BUILD_STRUCTURE)
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;}; //FIXME: should be not allowed (private)
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; }
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; TSubgoal whatToDoToAchieve() override;
}; };
class CollectRes : public CGoal<CollectRes> class CollectRes : public CGoal<CollectRes>
{ {
public: public:
CollectRes() : CGoal (Goals::COLLECT_RES){}; 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(int rid, int val)
: CGoal(Goals::COLLECT_RES)
{
resID = rid;
value = val;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
};
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class GatherTroops : public CGoal<GatherTroops> class GatherTroops : public CGoal<GatherTroops>
{ {
public: public:
GatherTroops() : CGoal (Goals::GATHER_TROOPS){priority = 2;}; GatherTroops()
: CGoal(Goals::GATHER_TROOPS)
GatherTroops(int type, int val) : CGoal (Goals::GATHER_TROOPS){objid = type; value = val; priority = 2;}; {
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; 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; TSubgoal whatToDoToAchieve() override;
}; };
class GetObj : public CGoal<GetObj> class GetObj : public CGoal<GetObj>
{ {
public: public:
GetObj() {}; // empty constructor not allowed GetObj() {} // empty constructor not allowed
GetObj(int Objid) : CGoal(Goals::GET_OBJ) {objid = Objid; priority = 3;}; GetObj(int Objid)
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; : CGoal(Goals::GET_OBJ)
{
objid = Objid;
priority = 3;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
bool operator== (GetObj &g) {return g.objid == objid;} bool operator==(GetObj & g)
{
return g.objid == objid;
}
bool fulfillsMe(TSubgoal goal) override; bool fulfillsMe(TSubgoal goal) override;
std::string completeMessage() const override; std::string completeMessage() const override;
}; };
class FindObj : public CGoal<FindObj> class FindObj : public CGoal<FindObj>
{ {
public: 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)
FindObj(int ID, int subID) : CGoal(Goals::FIND_OBJ) {objid = ID; resID = subID; priority = 1;}; : CGoal(Goals::FIND_OBJ)
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; {
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; TSubgoal whatToDoToAchieve() override;
}; };
class VisitHero : public CGoal<VisitHero> class VisitHero : public CGoal<VisitHero>
{ {
public: public:
VisitHero() : CGoal (Goals::VISIT_HERO){}; VisitHero()
: CGoal(Goals::VISIT_HERO)
VisitHero(int hid) : CGoal (Goals::VISIT_HERO){objid = hid; priority = 4;}; {
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; }
VisitHero(int hid)
: CGoal(Goals::VISIT_HERO)
{
objid = hid;
priority = 4;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; 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; bool fulfillsMe(TSubgoal goal) override;
std::string completeMessage() const override; std::string completeMessage() const override;
}; };
class GetArtOfType : public CGoal<GetArtOfType> class GetArtOfType : public CGoal<GetArtOfType>
{ {
public: public:
GetArtOfType() : CGoal (Goals::GET_ART_TYPE){}; GetArtOfType()
: CGoal(Goals::GET_ART_TYPE)
GetArtOfType(int type) : CGoal (Goals::GET_ART_TYPE){aid = type; priority = 2;}; {
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; }
GetArtOfType(int type)
: CGoal(Goals::GET_ART_TYPE)
{
aid = type;
priority = 2;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; TSubgoal whatToDoToAchieve() override;
}; };
class VisitTile : public CGoal<VisitTile> class VisitTile : public CGoal<VisitTile>
//tile, in conjunction with hero elementar; assumes tile is reachable //tile, in conjunction with hero elementar; assumes tile is reachable
{ {
public: 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; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() 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; std::string completeMessage() const override;
}; };
class ClearWayTo : public CGoal<ClearWayTo> class ClearWayTo : public CGoal<ClearWayTo>
{ {
public: public:
ClearWayTo() : CGoal (Goals::CLEAR_WAY_TO){}; 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(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; TGoalVec getAllPossibleSubgoals() override;
TSubgoal whatToDoToAchieve() 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> class DigAtTile : public CGoal<DigAtTile>
//elementar with hero on tile //elementar with hero on tile
{ {
public: public:
DigAtTile() : CGoal (Goals::DIG_AT_TILE){}; DigAtTile()
: CGoal(Goals::DIG_AT_TILE)
DigAtTile(int3 Tile) : CGoal (Goals::DIG_AT_TILE) {tile = Tile; priority = 20;}; {
TGoalVec getAllPossibleSubgoals() override {return TGoalVec();}; }
DigAtTile(int3 Tile)
: CGoal(Goals::DIG_AT_TILE)
{
tile = Tile;
priority = 20;
}
TGoalVec getAllPossibleSubgoals() override
{
return TGoalVec();
}
TSubgoal whatToDoToAchieve() override; 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> class CIssueCommand : public CGoal<CIssueCommand>
@@ -366,11 +584,20 @@ class CIssueCommand : public CGoal<CIssueCommand>
std::function<bool()> command; std::function<bool()> command;
public: public:
CIssueCommand(): CGoal(ISSUE_COMMAND){}; 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(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) 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 & vector : vectors)
{
for(auto j = vector.begin(); j != vector.end(); j++) for(auto j = vector.begin(); j != vector.end(); j++)
{
for(auto & elem : *j) for(auto & elem : *j)
foo(elem); foo(elem);
} }
}
}
struct ObjInfo struct ObjInfo
{ {
int3 pos; int3 pos;
std::string name; std::string name;
ObjInfo(){} ObjInfo(){}
ObjInfo(const CGObjectInstance *obj): ObjInfo(const CGObjectInstance * obj)
pos(obj->pos), : pos(obj->pos), name(obj->getObjectName())
name(obj->getObjectName())
{ {
} }
}; };
@@ -110,10 +113,10 @@ void VCAI::heroMoved(const TryMoveHero & details)
auto hero = cb->getHero(details.id); auto hero = cb->getHero(details.id);
cachedSectorMaps.clear(); cachedSectorMaps.clear();
const int3 from = CGHeroInstance::convertPosition(details.start, false), const int3 from = CGHeroInstance::convertPosition(details.start, false);
to = CGHeroInstance::convertPosition(details.end, false); const int3 to = CGHeroInstance::convertPosition(details.end, false);
const CGObjectInstance *o1 = vstd::frontOrNull(cb->getVisitableObjs(from)), const CGObjectInstance * o1 = vstd::frontOrNull(cb->getVisitableObjs(from));
*o2 = vstd::frontOrNull(cb->getVisitableObjs(to)); const CGObjectInstance * o2 = vstd::frontOrNull(cb->getVisitableObjs(to));
if(details.result == TryMoveHero::TELEPORTATION) if(details.result == TryMoveHero::TELEPORTATION)
{ {
@@ -290,8 +293,10 @@ void VCAI::tileRevealed(const std::unordered_set<int3, ShashInt3> &pos)
LOG_TRACE(logAi); LOG_TRACE(logAi);
NET_EVENT_HANDLER; NET_EVENT_HANDLER;
for(int3 tile : pos) for(int3 tile : pos)
{
for(const CGObjectInstance * obj : myCb->getVisitableObjs(tile)) for(const CGObjectInstance * obj : myCb->getVisitableObjs(tile))
addVisitableObj(obj); addVisitableObj(obj);
}
clearPathsInfo(); 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 //Do not attempt army or artifacts exchange if we visited ally player
//Visits can still be useful if hero have skills like Scholar //Visits can still be useful if hero have skills like Scholar
if(firstHero->tempOwner != secondHero->tempOwner) if(firstHero->tempOwner != secondHero->tempOwner)
{
logAi->debug("Heroes owned by different players. Do not exchange army or artifacts."); logAi->debug("Heroes owned by different players. Do not exchange army or artifacts.");
}
else if(goalpriority1 > goalpriority2) else if(goalpriority1 > goalpriority2)
{
transferFrom2to1(firstHero, secondHero); transferFrom2to1(firstHero, secondHero);
}
else if(goalpriority1 < goalpriority2) else if(goalpriority1 < goalpriority2)
{
transferFrom2to1(secondHero, firstHero); transferFrom2to1(secondHero, firstHero);
}
else //regular criteria else //regular criteria
{ {
if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && canGetArmy(firstHero, secondHero)) if(firstHero->getFightingStrength() > secondHero->getFightingStrength() && canGetArmy(firstHero, secondHero))
@@ -460,9 +471,11 @@ void VCAI::requestRealized(PackageApplied *pa)
if(status.haveTurn()) if(status.haveTurn())
{ {
if(pa->packType == typeList.getTypeID<EndTurn>()) if(pa->packType == typeList.getTypeID<EndTurn>())
{
if(pa->result) if(pa->result)
status.madeTurn(); status.madeTurn();
} }
}
if(pa->packType == typeList.getTypeID<QueryReply>()) if(pa->packType == typeList.getTypeID<QueryReply>())
{ {
@@ -615,7 +628,9 @@ void VCAI::showTeleportDialog(TeleportChannelID channel, TTeleportExitsList exit
int choosenExit = -1; int choosenExit = -1;
if(impassable) if(impassable)
{
knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE; knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
}
else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid()) else if(destinationTeleport != ObjectInstanceID() && destinationTeleportPos.valid())
{ {
auto neededExit = std::make_pair(destinationTeleport, destinationTeleportPos); 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 // TODO: Implement checking if visiting that teleport will uncovert any FoW
// So far this is the best option to handle decision about probing // So far this is the best option to handle decision about probing
auto obj = cb->getObj(exit.first, false); auto obj = cb->getObj(exit.first, false);
if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first) && if(obj == nullptr && !vstd::contains(teleportChannelProbingList, exit.first))
exit.first != destinationTeleport)
{ {
if(exit.first != destinationTeleport)
teleportChannelProbingList.push_back(exit.first); teleportChannelProbingList.push_back(exit.first);
} }
} }
@@ -746,14 +761,12 @@ void VCAI::makeTurn()
vstd::erase_if_present(alreadyVisited, obj); vstd::erase_if_present(alreadyVisited, obj);
} }
} }
}
break; break;
} }
}
markHeroAbleToExplore(primaryHero()); markHeroAbleToExplore(primaryHero());
makeTurnInternal(); makeTurnInternal();
return;
} }
void VCAI::makeTurnInternal() void VCAI::makeTurnInternal()
@@ -817,7 +830,9 @@ void VCAI::makeTurnInternal()
break; break;
} }
if(safeCopy.empty()) if(safeCopy.empty())
{
break; //all heroes exhausted their locked goals break; //all heroes exhausted their locked goals
}
else else
{ {
typedef std::pair<HeroPtr, Goals::TSubgoal> TItrType; 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 if(h->visitedTown) //we are inside, not just attacking
{ {
townVisitsThisWeek[h].insert(h->visitedTown); townVisitsThisWeek[h].insert(h->visitedTown);
if (!h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST + saving[Res::GOLD] && if(!h->hasSpellbook() && cb->getResourceAmount(Res::GOLD) >= GameConstants::SPELLBOOK_GOLD_COST + saving[Res::GOLD])
h->visitedTown->hasBuilt (BuildingID::MAGES_GUILD_1)) {
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK); cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
} }
}
break; break;
} }
completeGoal(sptr(Goals::GetObj(obj->id.getNum()).sethero(h))); 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) bool VCAI::canGetArmy(const CGHeroInstance * army, const CGHeroInstance * source)
{ //TODO: merge with pickBestCreatures {
//TODO: merge with pickBestCreatures
//if (ai->primaryHero().h == source) //if (ai->primaryHero().h == source)
if(army->tempOwner != source->tempOwner) if(army->tempOwner != source->tempOwner)
{ {
logAi->error("Why are we even considering exchange between heroes from different players?"); logAi->error("Why are we even considering exchange between heroes from different players?");
return false; return false;
} }
const CArmedInstance * armies[] = {army, source}; const CArmedInstance * armies[] = {army, source};
//we calculate total strength for each creature type available in armies //we calculate total strength for each creature type available in armies
std::map<const CCreature *, int> creToPower; std::map<const CCreature *, int> creToPower;
for(auto armyPtr : armies) for(auto armyPtr : armies)
{
for(auto & i : armyPtr->Slots()) for(auto & i : armyPtr->Slots())
{ {
//TODO: allow splitting stacks? //TODO: allow splitting stacks?
creToPower[i.second->type] += i.second->getPower(); creToPower[i.second->type] += i.second->getPower();
} }
}
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc) //TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
int armySize = creToPower.size(); int armySize = creToPower.size();
armySize = std::min((source->needsLastStack() ? armySize - 1 : armySize), GameConstants::ARMY_SIZE); //can't move away last stack 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(int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot
{ {
for(auto armyPtr : armies) for(auto armyPtr : armies)
{
for(int j = 0; j < GameConstants::ARMY_SIZE; j++) 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 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; 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 //we calculate total strength for each creature type available in armies
std::map<const CCreature *, int> creToPower; std::map<const CCreature *, int> creToPower;
for(auto armyPtr : armies) for(auto armyPtr : armies)
{
for(auto & i : armyPtr->Slots()) for(auto & i : armyPtr->Slots())
{//TODO: allow splitting stacks? {
//TODO: allow splitting stacks?
creToPower[i.second->type] += i.second->getPower(); creToPower[i.second->type] += i.second->getPower();
} }
}
//TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc) //TODO - consider more than just power (ie morale penalty, hero specialty in certain stacks, etc)
int armySize = creToPower.size(); 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(int i = 0; i < bestArmy.size(); i++) //i-th strongest creature type will go to i-th slot
{ {
for(auto armyPtr : armies) for(auto armyPtr : armies)
{
for(int j = 0; j < GameConstants::ARMY_SIZE; j++) 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->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 if(!(armyPtr->needsLastStack() && armyPtr->stacksCount() == 1)) //can't take away last creature
cb->mergeOrSwapStacks(armyPtr, army, SlotID(j), SlotID(i)); cb->mergeOrSwapStacks(armyPtr, army, SlotID(j), SlotID(i));
} }
} }
}
}
//TODO - having now strongest possible army, we may want to think about arranging stacks //TODO - having now strongest possible army, we may want to think about arranging stacks
auto hero = dynamic_cast<const CGHeroInstance *>(army); auto hero = dynamic_cast<const CGHeroInstance *>(army);
if(hero) if(hero)
{
checkHeroArmy(hero); checkHeroArmy(hero);
} }
}
void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other) void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other)
{ {
auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void auto equipBest = [](const CGHeroInstance * h, const CGHeroInstance * otherh, bool giveStuffToFirstHero) -> void
{ {
bool changeMade = false; bool changeMade = false;
do do
{ {
changeMade = false; changeMade = false;
@@ -1100,18 +1124,16 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
if(changeMade) if(changeMade)
break; //start evaluating artifacts from scratch break; //start evaluating artifacts from scratch
} }
} while (changeMade); }
while(changeMade);
}; };
equipBest(h, other, true); equipBest(h, other, true);
if(other) if(other)
{
equipBest(h, other, false); equipBest(h, other, false);
} }
}
void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter) void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter)
{ {
for(int i = 0; i < d->creatures.size(); i++) 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) for(BuildingID buildID : toBuild)
{ {
EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID); EBuildingState::EBuildingState canBuild = cb->canBuildStructure(t, buildID);
if (canBuild == EBuildingState::HAVE_CAPITAL if(canBuild == EBuildingState::HAVE_CAPITAL || canBuild == EBuildingState::FORBIDDEN || canBuild == EBuildingState::NO_WATER)
|| canBuild == EBuildingState::FORBIDDEN
|| canBuild == EBuildingState::NO_WATER)
return false; //we won't be able to build this return false; //we won't be able to build this
} }
@@ -1359,15 +1379,22 @@ void VCAI::buildStructure(const CGTownInstance * t)
return; return;
//workaround for mantis #2696 - build fort and citadel - building castle will be handled without bug //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 && if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL)
cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN) {
if(cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN)
{
if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements, capitolRequirements + ARRAY_COUNT(capitolRequirements)))) if(tryBuildNextStructure(t, std::vector<BuildingID>(capitolRequirements, capitolRequirements + ARRAY_COUNT(capitolRequirements))))
return; return;
}
}
if((!vstd::contains(t->builtBuildings, BuildingID::CAPITOL) && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CAPITOL) != EBuildingState::FORBIDDEN) //save money for capitol or city hall if capitol unavailable, do not build other things (unless gold source buildings are disabled in map editor)
|| (!vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && cb->canBuildStructure(t, BuildingID::CAPITOL) == EBuildingState::HAVE_CAPITAL && cb->canBuildStructure(t, BuildingID::CITY_HALL) != EBuildingState::FORBIDDEN) 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::TOWN_HALL) && cb->canBuildStructure(t, BuildingID::TOWN_HALL) != EBuildingState::FORBIDDEN)) return;
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) 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 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 // 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(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))) if(tryBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)))
return; return;
}
//try to upgrade dwelling //try to upgrade dwelling
for(int i = 0; i < ARRAY_COUNT(unitsUpgrade); i++) 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) //at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
std::vector<BuildingID> extraBuildings; std::vector<BuildingID> extraBuildings;
for(auto buildingInfo : t->town->buildings) for(auto buildingInfo : t->town->buildings)
{
if(buildingInfo.first > 43) if(buildingInfo.first > 43)
extraBuildings.push_back(buildingInfo.first); extraBuildings.push_back(buildingInfo.first);
}
if(tryBuildAnyStructure(t, extraBuildings)) if(tryBuildAnyStructure(t, extraBuildings))
return; return;
} }
@@ -1413,15 +1442,23 @@ bool VCAI::isGoodForVisit(const CGObjectInstance *obj, HeroPtr h, SectorMap &sm)
const int3 targetPos = sm.firstTileToGet(h, pos); const int3 targetPos = sm.firstTileToGet(h, pos);
if(!targetPos.valid()) if(!targetPos.valid())
return false; return false;
if (isTileNotReserved(h.get(), targetPos) && if(!isTileNotReserved(h.get(), targetPos))
!obj->wasVisited(playerID) && return false;
(cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES || isWeeklyRevisitable(obj)) && //flag or get weekly resources / creatures if(obj->wasVisited(playerID))
isSafeToVisit(h, pos) && return false;
shouldVisit(h, obj) && if(cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES && !isWeeklyRevisitable(obj))
!vstd::contains(alreadyVisited, obj) && return false; // Otherwise we flag or get weekly resources / creatures
!vstd::contains(reservedObjs, obj) && if(!isSafeToVisit(h, pos))
isAccessibleForHero(targetPos, h)) 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 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 //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 // -> 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; return false;
else else
return true; //all of the following is met return true; //all of the following is met
}
return false;
} }
bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t) bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t)
@@ -1445,20 +1480,26 @@ bool VCAI::isTileNotReserved(const CGHeroInstance * h, int3 t)
return true; return true;
} }
else else
{
return false; return false;
} }
}
bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const bool VCAI::canRecruitAnyHero(const CGTownInstance * t) const
{ {
//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals //TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
if(!t) if(!t)
t = findTownWithTavern(); t = findTownWithTavern();
if (t) if(!t)
return cb->getResourceAmount(Res::GOLD) >= GameConstants::HERO_GOLD_COST &&
cb->getHeroesInfo().size() < ALLOWED_ROAMING_HEROES &&
cb->getAvailableHeroes(t).size();
else
return false; 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) void VCAI::wander(HeroPtr h)
@@ -1566,8 +1607,10 @@ void VCAI::wander(HeroPtr h)
vstd::erase_if(towns, [](const CGTownInstance * t) -> bool vstd::erase_if(towns, [](const CGTownInstance * t) -> bool
{ {
for(const CGHeroInstance * h : cb->getHeroesInfo()) for(const CGHeroInstance * h : cb->getHeroesInfo())
{
if(!t->getArmyStrength() || howManyReinforcementsCanGet(h, t)) if(!t->getArmyStrength() || howManyReinforcementsCanGet(h, t))
return true; return true;
}
return false; return false;
}); });
boost::sort(towns, compareArmyStrength); boost::sort(towns, compareArmyStrength);
@@ -1615,7 +1658,9 @@ void VCAI::wander(HeroPtr h)
void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal) void VCAI::setGoal(HeroPtr h, Goals::TSubgoal goal)
{ {
if(goal->invalid()) if(goal->invalid())
{
vstd::erase_if_present(lockedHeroes, h); vstd::erase_if_present(lockedHeroes, h);
}
else else
{ {
lockedHeroes[h] = goal; lockedHeroes[h] = goal;
@@ -1635,12 +1680,14 @@ void VCAI::completeGoal (Goals::TSubgoal goal)
{ {
auto it = lockedHeroes.find(h); auto it = lockedHeroes.find(h);
if(it != lockedHeroes.end()) if(it != lockedHeroes.end())
{
if(it->second == goal) if(it->second == goal)
{ {
logAi->debug(goal->completeMessage()); logAi->debug(goal->completeMessage());
lockedHeroes.erase(it); //goal fulfilled, free hero lockedHeroes.erase(it); //goal fulfilled, free hero
} }
} }
}
else //complete goal for all heroes maybe? else //complete goal for all heroes maybe?
{ {
vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p) vstd::erase_if(lockedHeroes, [goal](std::pair<HeroPtr, Goals::TSubgoal> p)
@@ -1687,9 +1734,11 @@ void VCAI::markObjectVisited (const CGObjectInstance *obj)
{ {
if(!obj) if(!obj)
return; return;
if(dynamic_cast<const CGVisitableOPH *>(obj) || //we may want to visit it with another hero if(dynamic_cast<const CGVisitableOPH *>(obj)) //we may want to visit it with another hero
dynamic_cast<const CGBonusingObject *>(obj) || //or another time return;
(obj->ID == Obj::MONSTER)) if(dynamic_cast<const CGBonusingObject *>(obj)) //or another time
return;
if(obj->ID == Obj::MONSTER)
return; return;
alreadyVisited.insert(obj); alreadyVisited.insert(obj);
} }
@@ -1834,9 +1883,10 @@ bool VCAI::isAccessible(const int3 &pos)
HeroPtr VCAI::getHeroWithGrail() const HeroPtr VCAI::getHeroWithGrail() const
{ {
for(const CGHeroInstance * h : cb->getHeroesInfo()) for(const CGHeroInstance * h : cb->getHeroesInfo())
{
if(h->hasArt(2)) //grail if(h->hasArt(2)) //grail
return h; return h;
}
return nullptr; return nullptr;
} }
@@ -1844,24 +1894,27 @@ const CGObjectInstance * VCAI::getUnvisitedObj(const std::function<bool(const CG
{ {
//TODO smarter definition of unvisited //TODO smarter definition of unvisited
for(const CGObjectInstance * obj : visitableObjs) for(const CGObjectInstance * obj : visitableObjs)
{
if(predicate(obj) && !vstd::contains(alreadyVisited, obj)) if(predicate(obj) && !vstd::contains(alreadyVisited, obj))
return obj; return obj;
}
return nullptr; return nullptr;
} }
bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies) const bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies) const
{ {
// Don't visit tile occupied by allied hero
if(!includeAllies) if(!includeAllies)
{ //don't visit tile occupied by allied hero {
for(auto obj : cb->getVisitableObjs(pos)) for(auto obj : cb->getVisitableObjs(pos))
{ {
if (obj->ID == Obj::HERO && if(obj->ID == Obj::HERO && cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES)
cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES && {
obj != h.get()) if(obj != h.get())
return false; return false;
} }
} }
}
return cb->getPathsInfo(h.get())->getPathInfo(pos)->reachable(); 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 auto isTeleportAction = [&](CGPathNode::ENodeAction action) -> bool
{ {
if(action != CGPathNode::TELEPORT_NORMAL && if(action != CGPathNode::TELEPORT_NORMAL && action != CGPathNode::TELEPORT_BLOCKING_VISIT)
action != CGPathNode::TELEPORT_BLOCKING_VISIT && {
action != CGPathNode::TELEPORT_BATTLE) if(action != CGPathNode::TELEPORT_BATTLE)
{ {
return false; return false;
} }
}
return true; return true;
}; };
@@ -1929,9 +1983,9 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
{ {
if(CGTeleport::isConnected(currentObject, nextObjectTop)) if(CGTeleport::isConnected(currentObject, nextObjectTop))
return nextObjectTop; return nextObjectTop;
if(nextObjectTop && nextObjectTop->ID == Obj::HERO && if(nextObjectTop && nextObjectTop->ID == Obj::HERO)
CGTeleport::isConnected(currentObject, nextObject))
{ {
if(CGTeleport::isConnected(currentObject, nextObject))
return nextObject; return nextObject;
} }
@@ -1978,7 +2032,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
auto nextObject = getObj(nextCoord, true); auto nextObject = getObj(nextCoord, true);
auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject); auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
if(isTeleportAction(path.nodes[i - 1].action) && destTeleportObj != nullptr) 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); doTeleportMovement(destTeleportObj->id, nextCoord);
if(teleportChannelProbingList.size()) if(teleportChannelProbingList.size())
doChannelProbing(); doChannelProbing();
@@ -1998,16 +2053,27 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
if(endpos == h->visitablePos()) if(endpos == h->visitablePos())
continue; continue;
if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless bool isConnected = false;
&& (CGTeleport::isConnected(nextObjectTop, getObj(path.nodes[i-2].coord, false)) bool isNextObjectTeleport = false;
|| CGTeleport::isTeleport(nextObjectTop))) // Check there is node after next one; otherwise transit is pointless
{ // Hero should be able to go through object if it's allow transit 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); doMovement(endpos, true);
} }
else if(path.nodes[i - 1].layer == EPathfindingLayer::AIR) else if(path.nodes[i - 1].layer == EPathfindingLayer::AIR)
{
doMovement(endpos, true); doMovement(endpos, true);
}
else else
{
doMovement(endpos, false); doMovement(endpos, false);
}
afterMovementCheck(); afterMovementCheck();
@@ -2091,8 +2157,10 @@ void VCAI::tryRealize(Goals::VisitHero & g)
} }
} }
else else
{
throw cannotFulfillGoalException("Cannot visit hero: object not found!"); throw cannotFulfillGoalException("Cannot visit hero: object not found!");
} }
}
void VCAI::tryRealize(Goals::BuildThis & g) 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)) 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; int toGive, toGet;
m->getOffer(i, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE); m->getOffer(i, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
toGive = toGive * (cb->getResourceAmount(i) / toGive); toGive = toGive * (cb->getResourceAmount(i) / toGive);
@@ -2265,11 +2334,11 @@ void VCAI::endTurn()
logAi->error("Not having turn at the end of turn???"); logAi->error("Not having turn at the end of turn???");
} }
logAi->debug("Resources at the end of turn: %s", cb->getResourceAmount().toString()); logAi->debug("Resources at the end of turn: %s", cb->getResourceAmount().toString());
do do
{ {
cb->endTurn(); 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()); 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; int3 tile = hpos + dir;
if(cb->isInTheMap(tile)) if(cb->isInTheMap(tile))
{
if(ourPos != dir) //don't stand in place if(ourPos != dir) //don't stand in place
{
if(isSafeToVisit(h, tile) && isAccessibleForHero(tile, h)) if(isSafeToVisit(h, tile) && isAccessibleForHero(tile, h))
{ {
if(isBlockVisitObj(tile)) if(isBlockVisitObj(tile))
@@ -2572,6 +2643,8 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h)
dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(radius, hpos, dir); dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(radius, hpos, dir);
} }
} }
}
}
if(dstToRevealedTiles.empty()) //yes, it DID happen! if(dstToRevealedTiles.empty()) //yes, it DID happen!
throw cannotFulfillGoalException("No neighbour will bring new discoveries!"); throw cannotFulfillGoalException("No neighbour will bring new discoveries!");
@@ -2701,8 +2774,6 @@ TResources VCAI::estimateIncome() const
ret += t->dailyIncome(); ret += t->dailyIncome();
} }
for(const CGObjectInstance * obj : getFlaggedObjects()) for(const CGObjectInstance * obj : getFlaggedObjects())
{ {
if(obj->ID == Obj::MINE) if(obj->ID == Obj::MINE)
@@ -2764,8 +2835,10 @@ void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
cb->recruitHero(t, hero); cb->recruitHero(t, hero);
} }
else if(throwing) else if(throwing)
{
throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName()); throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
} }
}
void VCAI::finish() void VCAI::finish()
{ {
@@ -2839,7 +2912,10 @@ void VCAI::validateObject(const CGObjectInstance *obj)
void VCAI::validateObject(ObjectIdRef 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) if(!obj)
{ {
vstd::erase_if(visitableObjs, matchesId); vstd::erase_if(visitableObjs, matchesId);
@@ -2869,7 +2945,9 @@ std::shared_ptr<SectorMap> VCAI::getCachedSectorMap(HeroPtr h)
{ {
auto it = cachedSectorMaps.find(h); auto it = cachedSectorMaps.find(h);
if(it != cachedSectorMaps.end()) if(it != cachedSectorMaps.end())
{
return it->second; return it->second;
}
else else
{ {
cachedSectorMaps[h] = std::make_shared<SectorMap>(h); 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); boost::unique_lock<boost::mutex> lock(mx);
if(started) if(started)
{
objectsBeingVisited.push_back(obj); objectsBeingVisited.push_back(obj);
}
else else
{ {
// There can be more than one object visited at the time (eg. hero visits Subterranean Gate // 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 height = fow.front().size();
auto depth = fow.front().front().size(); auto depth = fow.front().front().size();
for(size_t x = 0; x < width; x++) for(size_t x = 0; x < width; x++)
{
for(size_t y = 0; y < height; y++) for(size_t y = 0; y < height; y++)
{
for(size_t z = 0; z < depth; z++) for(size_t z = 0; z < depth; z++)
sector[x][y][z] = fow[x][y][z]; sector[x][y][z] = fow[x][y][z];
}
}
valid = false; valid = false;
} }
@@ -3175,11 +3259,15 @@ void SectorMap::write(crstring fname)
} }
bool isWeeklyRevisitable(const CGObjectInstance * obj) bool isWeeklyRevisitable(const CGObjectInstance * obj)
{ //TODO: allow polling of remaining creatures in dwelling {
if (dynamic_cast<const CGVisitableOPW *>(obj) || //ensures future compatibility, unlike IDs //TODO: allow polling of remaining creatures in dwelling
dynamic_cast<const CGDwelling *>(obj) || if(dynamic_cast<const CGVisitableOPW *>(obj)) // ensures future compatibility, unlike IDs
dynamic_cast<const CBank *>(obj)) //banks tend to respawn often in mods
return true; 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) switch(obj->ID)
{ {
case Obj::STABLES: case Obj::STABLES:
@@ -3200,7 +3288,6 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
case Obj::TOWN: case Obj::TOWN:
case Obj::HERO: //never visit our heroes at random case Obj::HERO: //never visit our heroes at random
return obj->tempOwner != h->tempOwner; //do not visit our towns at random return obj->tempOwner != h->tempOwner; //do not visit our towns at random
break;
case Obj::BORDER_GATE: case Obj::BORDER_GATE:
{ {
for(auto q : ai->myCb->getMyQuests()) 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 return true; //we don't have this quest yet
} }
break;
case Obj::BORDERGUARD: //open borderguard if possible case Obj::BORDERGUARD: //open borderguard if possible
return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID); return (dynamic_cast<const CGKeys *>(obj))->wasMyColorVisited(ai->playerID);
case Obj::SEER_HUT: case Obj::SEER_HUT:
@@ -3230,7 +3316,6 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
} }
return true; //we don't have this quest yet return true; //we don't have this quest yet
} }
break;
case Obj::CREATURE_GENERATOR1: case Obj::CREATURE_GENERATOR1:
{ {
if(obj->tempOwner != h->tempOwner) if(obj->tempOwner != h->tempOwner)
@@ -3267,8 +3352,8 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
TResources myRes = ai->myCb->getResourceAmount(); TResources myRes = ai->myCb->getResourceAmount();
if(myRes[Res::GOLD] - GOLD_RESERVE < 1000) if(myRes[Res::GOLD] - GOLD_RESERVE < 1000)
return false; return false;
}
break; break;
}
case Obj::LIBRARY_OF_ENLIGHTENMENT: case Obj::LIBRARY_OF_ENLIGHTENMENT:
if(h->level < 12) if(h->level < 12)
return false; return false;
@@ -3278,26 +3363,27 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
TResources myRes = ai->myCb->getResourceAmount(); TResources myRes = ai->myCb->getResourceAmount();
if(myRes[Res::GOLD] - GOLD_RESERVE < 2000 || myRes[Res::GEMS] < 10) if(myRes[Res::GOLD] - GOLD_RESERVE < 2000 || myRes[Res::GEMS] < 10)
return false; return false;
}
break; break;
}
case Obj::MAGIC_WELL: case Obj::MAGIC_WELL:
return h->mana < h->manaLimit(); return h->mana < h->manaLimit();
case Obj::PRISON: case Obj::PRISON:
return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER; return ai->myCb->getHeroesInfo().size() < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER;
case Obj::TAVERN: case Obj::TAVERN:
{//TODO: make AI actually recruit heroes {
//TODO: make AI actually recruit heroes
//TODO: only on request //TODO: only on request
if((ai->myCb->getHeroesInfo().size() >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER) || 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)) return false;
else if(ai->myCb->getResourceAmount()[Res::GOLD] - GOLD_RESERVE < GameConstants::HERO_GOLD_COST)
return false; return false;
}
break; break;
}
case Obj::BOAT: case Obj::BOAT:
return false; return false;
//Boats are handled by pathfinder //Boats are handled by pathfinder
case Obj::EYE_OF_MAGI: case Obj::EYE_OF_MAGI:
return false; //this object is useless to visit, but could be visited indefinitely 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); 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; 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 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 For ship construction etc, another function (goal?) is needed
*/ */
int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst)
{ {
int3 ret(-1, -1, -1); int3 ret(-1, -1, -1);
int sourceSector = retrieveTile(h->visitablePos()), int sourceSector = retrieveTile(h->visitablePos());
destinationSector = retrieveTile(dst); int destinationSector = retrieveTile(dst);
const Sector *src = &infoOnSectors[sourceSector], const Sector * src = &infoOnSectors[sourceSector];
*dest = &infoOnSectors[destinationSector]; const Sector * dest = &infoOnSectors[destinationSector];
if(sourceSector != destinationSector) //use ships, shipyards etc.. 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 auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool
{ {
const TerrainTile * t = getTile(pos); const TerrainTile * t = getTile(pos);
return t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT if(t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT)
&& retrieveTile(pos) == sectorToReach->id; {
if(retrieveTile(pos) == sectorToReach->id)
return true;
}
return false;
}); });
if(firstEP != src->embarkmentPoints.end()) if(firstEP != src->embarkmentPoints.end())
@@ -3393,9 +3483,11 @@ For ship construction etc, another function (goal?) is needed
for(const CGObjectInstance * obj : ai->getFlaggedObjects()) for(const CGObjectInstance * obj : ai->getFlaggedObjects())
{ {
if(obj->ID != Obj::TOWN) //towns were handled in the previous loop if(obj->ID != Obj::TOWN) //towns were handled in the previous loop
{
if(const IShipyard * shipyard = IShipyard::castFrom(obj)) if(const IShipyard * shipyard = IShipyard::castFrom(obj))
shipyards.push_back(shipyard); shipyards.push_back(shipyard);
} }
}
shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool 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()) while(curtile != h->visitablePos())
{ {
auto topObj = cb->getTopObj(curtile); auto topObj = cb->getTopObj(curtile);
if(topObj && topObj->ID == Obj::HERO && topObj != h.h && if(topObj && topObj->ID == Obj::HERO && topObj != h.h)
cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) {
if(cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
{ {
logAi->warn("Another allied hero stands in our way"); logAi->warn("Another allied hero stands in our way");
return ret; return ret;
} }
}
if(ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable()) if(ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable())
{ {
return curtile; 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 struct SectorMap
{ {
@@ -413,8 +418,10 @@ public:
class cannotFulfillGoalException : public std::exception class cannotFulfillGoalException : public std::exception
{ {
std::string msg; std::string msg;
public: public:
explicit cannotFulfillGoalException(crstring _Message) : msg(_Message) explicit cannotFulfillGoalException(crstring _Message)
: msg(_Message)
{ {
} }
@@ -427,13 +434,16 @@ public:
return msg.c_str(); return msg.c_str();
} }
}; };
class goalFulfilledException : public std::exception class goalFulfilledException : public std::exception
{ {
std::string msg; std::string msg;
public: public:
Goals::TSubgoal goal; Goals::TSubgoal goal;
explicit goalFulfilledException(Goals::TSubgoal Goal) : goal(Goal) explicit goalFulfilledException(Goals::TSubgoal Goal)
: goal(Goal)
{ {
msg = goal->name(); msg = goal->name();
} }
@@ -449,4 +459,3 @@ public:
}; };
void makePossibleUpgrades(const CArmedInstance * obj); void makePossibleUpgrades(const CArmedInstance * obj);