| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 |
- #include "StdInc.h"
- #include "Goals.h"
- #include "VCAI.h"
- /*
- * Goals.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
- extern boost::thread_specific_ptr<CCallback> cb;
- extern boost::thread_specific_ptr<VCAI> ai;
- using namespace vstd;
- using namespace Goals;
- TSubgoal Goals::sptr(const AbstractGoal & tmp)
- {
- shared_ptr<AbstractGoal> ptr;
- ptr.reset(tmp.clone());
- return ptr;
- }
- std::string Goals::AbstractGoal::name() const //TODO: virtualize
- {
- switch (goalType)
- {
- case INVALID:
- return "INVALID";
- case WIN:
- return "WIN";
- case DO_NOT_LOSE:
- return "DO NOT LOOSE";
- case CONQUER:
- return "CONQUER";
- case BUILD:
- return "BUILD";
- case EXPLORE:
- return "EXPLORE";
- case GATHER_ARMY:
- return "GATHER ARMY";
- case BOOST_HERO:
- return "BOOST_HERO (unsupported)";
- case RECRUIT_HERO:
- return "RECRUIT HERO";
- case BUILD_STRUCTURE:
- return "BUILD STRUCTURE";
- case COLLECT_RES:
- return "COLLECT RESOURCE";
- case GATHER_TROOPS:
- return "GATHER TROOPS";
- case GET_OBJ:
- return "GET OBJECT " + boost::lexical_cast<std::string>(objid);
- case FIND_OBJ:
- return "FIND OBJECT " + boost::lexical_cast<std::string>(objid);
- case VISIT_HERO:
- return "VISIT HERO " + boost::lexical_cast<std::string>(objid);
- case GET_ART_TYPE:
- return "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
- case ISSUE_COMMAND:
- return "ISSUE COMMAND (unsupported)";
- case VISIT_TILE:
- return "VISIT TILE " + tile();
- case CLEAR_WAY_TO:
- return "CLEAR WAY TO " + tile();
- case DIG_AT_TILE:
- return "DIG AT TILE " + tile();
- default:
- return boost::lexical_cast<std::string>(goalType);
- }
- }
- //TSubgoal AbstractGoal::whatToDoToAchieve()
- //{
- // logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
- // return sptr (Goals::Explore());
- //}
- TSubgoal Win::whatToDoToAchieve()
- {
- const VictoryCondition &vc = cb->getMapHeader()->victoryCondition;
- EVictoryConditionType::EVictoryConditionType cond = vc.condition;
- if(!vc.appliesToAI)
- {
- //TODO deduce victory from human loss condition
- cond = EVictoryConditionType::WINSTANDARD;
- }
- switch(cond)
- {
- case EVictoryConditionType::ARTIFACT:
- return sptr (Goals::GetArtOfType(vc.objectId));
- case EVictoryConditionType::BEATHERO:
- return sptr (Goals::GetObj(vc.obj->id.getNum()));
- case EVictoryConditionType::BEATMONSTER:
- return sptr (Goals::GetObj(vc.obj->id.getNum()));
- case EVictoryConditionType::BUILDCITY:
- //TODO build castle/capitol
- break;
- case EVictoryConditionType::BUILDGRAIL:
- {
- if(auto h = ai->getHeroWithGrail())
- {
- //hero is in a town that can host Grail
- if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
- {
- const CGTownInstance *t = h->visitedTown;
- return sptr (Goals::BuildThis(BuildingID::GRAIL, t));
- }
- else
- {
- auto towns = cb->getTownsInfo();
- towns.erase(boost::remove_if(towns,
- [](const CGTownInstance *t) -> bool
- {
- return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
- }),
- towns.end());
- boost::sort(towns, isCloser);
- if(towns.size())
- {
- return sptr (Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
- }
- }
- }
- double ratio = 0;
- int3 grailPos = cb->getGrailPos(ratio);
- if(ratio > 0.99)
- {
- return sptr (Goals::DigAtTile(grailPos));
- } //TODO: use FIND_OBJ
- else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
- {
- return sptr (Goals::GetObj(obj->id.getNum()));
- }
- else
- return sptr (Goals::Explore());
- }
- break;
- case EVictoryConditionType::CAPTURECITY:
- return sptr (Goals::GetObj(vc.obj->id.getNum()));
- case EVictoryConditionType::GATHERRESOURCE:
- return sptr (Goals::CollectRes(static_cast<Res::ERes>(vc.objectId), vc.count));
- //TODO mines? piles? marketplace?
- //save?
- break;
- case EVictoryConditionType::GATHERTROOP:
- return sptr (Goals::GatherTroops(vc.objectId, vc.count));
- break;
- case EVictoryConditionType::TAKEDWELLINGS:
- break;
- case EVictoryConditionType::TAKEMINES:
- break;
- case EVictoryConditionType::TRANSPORTITEM:
- break;
- case EVictoryConditionType::WINSTANDARD:
- return sptr (Goals::Conquer());
- default:
- assert(0);
- }
- return sptr (Goals::Invalid());
- }
- TSubgoal FindObj::whatToDoToAchieve()
- {
- const CGObjectInstance * o = nullptr;
- if (resID > -1) //specified
- {
- for(const CGObjectInstance *obj : ai->visitableObjs)
- {
- if(obj->ID == objid && obj->subID == resID)
- {
- o = obj;
- break; //TODO: consider multiple objects and choose best
- }
- }
- }
- else
- {
- for(const CGObjectInstance *obj : ai->visitableObjs)
- {
- if(obj->ID == objid)
- {
- o = obj;
- break; //TODO: consider multiple objects and choose best
- }
- }
- }
- if (o && isReachable(o))
- return sptr (Goals::GetObj(o->id.getNum()));
- else
- return sptr (Goals::Explore());
- }
- TSubgoal GetObj::whatToDoToAchieve()
- {
- const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
- if(!obj)
- return sptr (Goals::Explore());
- int3 pos = obj->visitablePos();
- return sptr (Goals::VisitTile(pos));
- }
- TSubgoal VisitHero::whatToDoToAchieve()
- {
- const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
- if(!obj)
- return sptr (Goals::Explore());
- int3 pos = obj->visitablePos();
- if (hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
- {
- settile(pos).setisElementar(true);
- return sptr (*this);
- }
- return sptr (Goals::Invalid());
- }
- TSubgoal GetArtOfType::whatToDoToAchieve()
- {
- TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
- if(alternativeWay->invalid())
- return sptr (Goals::FindObj(Obj::ARTIFACT, aid));
- return sptr (Goals::Invalid());
- }
- TSubgoal ClearWayTo::whatToDoToAchieve()
- {
- assert(tile.x >= 0); //set tile
- if(!cb->isVisible(tile))
- {
- logAi->errorStream() << "Clear way should be used with visible tiles!";
- return sptr (Goals::Explore());
- }
- HeroPtr h = hero ? hero : ai->primaryHero();
- if(!h)
- return sptr (Goals::RecruitHero());
- cb->setSelection(*h);
- SectorMap sm;
- bool dropToFile = false;
- if(dropToFile) //for debug purposes
- sm.write("test.txt");
- int3 tileToHit = sm.firstTileToGet(h, tile);
- //if(isSafeToVisit(h, tileToHit))
- if(isBlockedBorderGate(tileToHit))
- { //FIXME: this way we'll not visit gate and activate quest :?
- return sptr (Goals::FindObj (Obj::KEYMASTER, cb->getTile(tileToHit)->visitableObjects.back()->subID));
- }
- //FIXME: this code shouldn't be necessary
- if(tileToHit == tile)
- {
- logAi->errorStream() << boost::format("Very strange, tile to hit is %s and tile is also %s, while hero %s is at %s\n")
- % tileToHit % tile % h->name % h->visitablePos();
- throw cannotFulfillGoalException("Retrieving first tile to hit failed (probably)!");
- }
- auto topObj = backOrNull(cb->getVisitableObjs(tileToHit));
- if(topObj && topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES)
- {
- std::string problem = boost::str(boost::format("%s stands in the way of %s.\n") % topObj->getHoverText() % h->getHoverText());
- throw cannotFulfillGoalException(problem);
- }
- return sptr (Goals::VisitTile(tileToHit).sethero(h));
- //FIXME:: attempts to visit completely unreachable tile with hero results in stall
- //TODO czy istnieje lepsza droga?
- throw cannotFulfillGoalException("Cannot reach given tile!"); //how and when could this be used?
- }
- TSubgoal Explore::whatToDoToAchieve()
- {
- auto objs = ai->visitableObjs; //try to use buildings that uncover map
- erase_if(objs, [&](const CGObjectInstance *obj) -> bool
- {
- if (vstd::contains(ai->alreadyVisited, obj))
- return true;
- switch (obj->ID.num)
- {
- case Obj::REDWOOD_OBSERVATORY:
- case Obj::PILLAR_OF_FIRE:
- case Obj::CARTOGRAPHER:
- case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates
- //case Obj::MONOLITH1:
- //case obj::MONOLITH2:
- //case obj::MONOLITH3:
- //case Obj::WHIRLPOOL:
- return false; //do not erase
- break;
- default:
- return true;
- }
- });
- if (objs.size())
- {
- if (hero.get(true))
- {
- for (auto obj : objs)
- {
- auto pos = obj->visitablePos();
- //FIXME: this confition fails if everything but guarded subterranen gate was explored. in this case we should gather army for hero
- if (isSafeToVisit(hero, pos) && ai->isAccessibleForHero(pos, hero))
- return sptr (Goals::VisitTile(pos).sethero(hero));
- }
- }
- else
- {
- for (auto obj : objs)
- {
- auto pos = obj->visitablePos();
- if (ai->isAccessible (pos)) //TODO: check safety?
- return sptr (Goals::VisitTile(pos).sethero(hero));
- }
- }
- }
- if (hero)
- {
- int3 t = whereToExplore(hero);
- if (t.z == -1) //no safe tile to explore - we need to break!
- {
- erase_if (objs, [&](const CGObjectInstance *obj) -> bool
- {
- switch (obj->ID.num)
- {
- case Obj::CARTOGRAPHER:
- case Obj::SUBTERRANEAN_GATE:
- //case Obj::MONOLITH1:
- //case obj::MONOLITH2:
- //case obj::MONOLITH3:
- //case Obj::WHIRLPOOL:
- return false; //do not erase
- break;
- default:
- return true;
- }
- });
- if (objs.size())
- {
- return sptr (Goals::VisitTile(objs.front()->visitablePos()).sethero(hero).setisAbstract(true));
- }
- else
- throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
- }
- return sptr (Goals::VisitTile(t).sethero(hero));
- }
- auto hs = cb->getHeroesInfo();
- int howManyHeroes = hs.size();
- erase(hs, [](const CGHeroInstance *h)
- {
- return contains(ai->lockedHeroes, h);
- });
- if(hs.empty()) //all heroes are busy. buy new one
- {
- if (howManyHeroes < 3 && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally
- return sptr (Goals::RecruitHero());
- else //find mobile hero with weakest army
- {
- hs = cb->getHeroesInfo();
- erase_if(hs, [](const CGHeroInstance *h)
- {
- return !h->movement; //only hero with movement are of interest for us
- });
- if (hs.empty())
- {
- if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
- return sptr (Goals::RecruitHero());
- else
- throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
- }
- boost::sort(hs, compareMovement); //closer to what?
- }
- }
- const CGHeroInstance *h = hs.front();
- return sptr (sethero(h).setisAbstract(true));
- return iAmElementar(); //FIXME: how can this be called?
- };
- TSubgoal RecruitHero::whatToDoToAchieve()
- {
- const CGTownInstance *t = ai->findTownWithTavern();
- if(!t)
- return sptr (Goals::BuildThis(BuildingID::TAVERN));
- if(cb->getResourceAmount(Res::GOLD) < HERO_GOLD_COST)
- return sptr (Goals::CollectRes(Res::GOLD, HERO_GOLD_COST));
- return iAmElementar();
- }
- TSubgoal VisitTile::whatToDoToAchieve()
- {
- if(!cb->isVisible(tile))
- return sptr (Goals::Explore());
- if(hero && !ai->isAccessibleForHero(tile, hero))
- hero = nullptr;
- if(!hero)
- {
- if(cb->getHeroesInfo().empty())
- {
- return sptr (Goals::RecruitHero());
- }
- for(const CGHeroInstance *h : cb->getHeroesInfo())
- {
- if(ai->isAccessibleForHero(tile, h))
- {
- hero = h;
- break;
- }
- }
- }
- if(hero)
- {
- if(isSafeToVisit(hero, tile))
- return sptr (setisElementar(true));
- else
- {
- return sptr (Goals::GatherArmy(evaluateDanger(tile, *hero) * SAFE_ATTACK_CONSTANT).sethero(hero));
- }
- }
- else //inaccessible for all heroes
- {
- return sptr (Goals::ClearWayTo(tile));
- }
- }
- TSubgoal DigAtTile::whatToDoToAchieve()
- {
- const CGObjectInstance *firstObj = frontOrNull(cb->getVisitableObjs(tile));
- if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
- {
- const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(firstObj);
- sethero(h).setisElementar(true);
- return sptr (*this);
- }
- return sptr (Goals::VisitTile(tile));
- }
- TSubgoal BuildThis::whatToDoToAchieve()
- {
- //TODO check res
- //look for town
- //prerequisites?
- return iAmElementar();
- }
- TSubgoal CollectRes::whatToDoToAchieve()
- {
- std::vector<const IMarket*> markets;
- std::vector<const CGObjectInstance*> visObjs;
- ai->retreiveVisitableObjs(visObjs, true);
- for(const CGObjectInstance *obj : visObjs)
- {
- if(const IMarket *m = IMarket::castFrom(obj, false))
- {
- if(obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
- markets.push_back(m);
- else if(obj->ID == Obj::TRADING_POST) //TODO a moze po prostu test na pozwalanie handlu?
- markets.push_back(m);
- }
- }
- boost::sort(markets, [](const IMarket *m1, const IMarket *m2) -> bool
- {
- return m1->getMarketEfficiency() < m2->getMarketEfficiency();
- });
- 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());
- }),markets.end());
- if(!markets.size())
- {
- for(const CGTownInstance *t : cb->getTownsInfo())
- {
- if(cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
- return sptr (Goals::BuildThis(BuildingID::MARKETPLACE, t));
- }
- }
- else
- {
- const IMarket *m = markets.back();
- //attempt trade at back (best prices)
- int howManyCanWeBuy = 0;
- for(Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
- {
- if(i == resID) continue;
- int toGive = -1, toReceive = -1;
- m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
- assert(toGive > 0 && toReceive > 0);
- howManyCanWeBuy += toReceive * (cb->getResourceAmount(i) / toGive);
- }
- if(howManyCanWeBuy + cb->getResourceAmount(static_cast<Res::ERes>(resID)) >= value)
- {
- auto backObj = backOrNull(cb->getVisitableObjs(m->o->visitablePos())); //it'll be a hero if we have one there; otherwise marketplace
- assert(backObj);
- if (backObj->tempOwner != ai->playerID)
- {
- return sptr (Goals::GetObj(m->o->id.getNum()));
- }
- else
- {
- return sptr (Goals::GetObj(m->o->id.getNum()).setisElementar(true));
- }
- }
- }
- return sptr (Goals::Invalid()); //FIXME: unused?
- }
- TSubgoal GatherTroops::whatToDoToAchieve()
- {
- std::vector<const CGDwelling *> dwellings;
- for(const CGTownInstance *t : cb->getTownsInfo())
- {
- auto creature = VLC->creh->creatures[objid];
- if (t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
- {
- auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
- if(!creatures)
- continue;
- int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
- if(upgradeNumber < 0)
- continue;
- BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
- if (t->hasBuilt(bid)) //this assumes only creatures with dwellings are assigned to faction
- {
- dwellings.push_back(t);
- }
- else
- {
- return sptr (Goals::BuildThis(bid, t));
- }
- }
- }
- for (auto obj : ai->visitableObjs)
- {
- if (obj->ID != Obj::CREATURE_GENERATOR1) //TODO: what with other creature generators?
- continue;
- auto d = dynamic_cast<const CGDwelling *>(obj);
- for (auto creature : d->creatures)
- {
- if (creature.first) //there are more than 0 creatures avaliabe
- {
- for (auto type : creature.second)
- {
- if (type == objid && ai->freeResources().canAfford(VLC->creh->creatures[type]->cost))
- dwellings.push_back(d);
- }
- }
- }
- }
- if (dwellings.size())
- {
- boost::sort(dwellings, isCloser);
- return sptr (Goals::GetObj(dwellings.front()->id.getNum()));
- }
- else
- return sptr (Goals::Explore());
- //TODO: exchange troops between heroes
- }
- TSubgoal Conquer::whatToDoToAchieve()
- {
- auto hs = cb->getHeroesInfo();
- int howManyHeroes = hs.size();
- erase(hs, [](const CGHeroInstance *h)
- {
- return contains(ai->lockedHeroes, h);
- });
- if(hs.empty()) //all heroes are busy. buy new one
- {
- if (howManyHeroes < 3 && ai->findTownWithTavern()) //we may want to recruit second hero. TODO: make it smart finally
- return sptr (Goals::RecruitHero());
- else //find mobile hero with weakest army
- {
- hs = cb->getHeroesInfo();
- erase_if(hs, [](const CGHeroInstance *h)
- {
- return !h->movement; //only hero with movement are of interest for us
- });
- if (hs.empty())
- {
- if (howManyHeroes < GameConstants::MAX_HEROES_PER_PLAYER)
- return sptr (Goals::RecruitHero());
- else
- throw cannotFulfillGoalException("No heroes with remaining MPs for exploring!\n");
- }
- boost::sort(hs, compareHeroStrength);
- }
- }
- const CGHeroInstance *h = hs.back();
- cb->setSelection(h);
- std::vector<const CGObjectInstance *> objs; //here we'll gather enemy towns and heroes
- ai->retreiveVisitableObjs(objs);
- erase_if(objs, [&](const CGObjectInstance *obj)
- {
- return (obj->ID != Obj::TOWN && obj->ID != Obj::HERO) //not town/hero
- || cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES;
- });
-
- if (objs.empty()) //experiment - try to conquer dwellings and mines, it should pay off
- {
- ai->retreiveVisitableObjs(objs);
- erase_if(objs, [&](const CGObjectInstance *obj)
- {
- return (obj->ID != Obj::CREATURE_GENERATOR1 && obj->ID != Obj::MINE) //not dwelling or mine
- || cb->getPlayerRelations(ai->playerID, obj->tempOwner) != PlayerRelations::ENEMIES;
- });
- }
- if(objs.empty())
- return sptr (Goals::Explore()); //we need to find an enemy
- erase_if(objs, [&](const CGObjectInstance *obj)
- {
- return !isSafeToVisit(h, obj->visitablePos()) || vstd::contains (ai->reservedObjs, obj); //no need to capture same object twice
- });
- if(objs.empty())
- return iAmElementar();
- boost::sort(objs, isCloser);
- for(const CGObjectInstance *obj : objs)
- {
- if (ai->isAccessibleForHero(obj->visitablePos(), h))
- {
- ai->reserveObject(h, obj); //no one else will capture same object until we fail
- if (obj->ID == Obj::HERO)
- return sptr (Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true));
- //track enemy hero
- else
- return sptr (Goals::VisitTile(obj->visitablePos()).sethero(h));
- }
- }
- return sptr (Goals::Explore()); //enemy is inaccessible
- }
- TSubgoal Build::whatToDoToAchieve()
- {
- return iAmElementar();
- }
- TSubgoal Invalid::whatToDoToAchieve()
- {
- return iAmElementar();
- }
- TSubgoal GatherArmy::whatToDoToAchieve()
- {
- //TODO: find hero if none set
- assert(hero);
- cb->setSelection(*hero);
- auto compareReinforcements = [this](const CGTownInstance *lhs, const CGTownInstance *rhs) -> bool
- {
- return howManyReinforcementsCanGet(hero, lhs) < howManyReinforcementsCanGet(hero, rhs);
- };
- std::vector<const CGTownInstance *> townsReachable;
- for(const CGTownInstance *t : cb->getTownsInfo())
- {
- if(!t->visitingHero && howManyReinforcementsCanGet(hero,t))
- {
- if(isReachable(t) && !vstd::contains (ai->townVisitsThisWeek[hero], t))
- townsReachable.push_back(t);
- }
- }
- if(townsReachable.size()) //try towns first
- {
- boost::sort(townsReachable, compareReinforcements);
- return sptr (Goals::VisitTile(townsReachable.back()->visitablePos()).sethero(hero));
- }
- else
- {
- if (hero == ai->primaryHero()) //we can get army from other heroes
- {
- auto otherHeroes = cb->getHeroesInfo();
- auto heroDummy = hero;
- erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
- {
- return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->visitablePos(), h, true) || !ai->canGetArmy(heroDummy.h, h));
- });
- if (otherHeroes.size())
- {
- boost::sort(otherHeroes, compareArmyStrength); //TODO: check if hero has at least one stack more powerful than ours? not likely to fail
- int primaryPath, secondaryPath;
- auto h = otherHeroes.back();
- cb->setSelection(hero.h);
- primaryPath = cb->getPathInfo(h->visitablePos())->turns;
- cb->setSelection(h);
- secondaryPath = cb->getPathInfo(hero->visitablePos())->turns;
- if (primaryPath < secondaryPath)
- return sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero));
- //go to the other hero if we are faster
- else
- return sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(h));
- //let the other hero come to us
- }
- }
- std::vector<const CGObjectInstance *> objs; //here we'll gather all dwellings
- ai->retreiveVisitableObjs(objs, true);
- erase_if(objs, [&](const CGObjectInstance *obj)
- {
- if(obj->ID != Obj::CREATURE_GENERATOR1)
- return true;
- auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
- if(relationToOwner == PlayerRelations::ALLIES)
- return true;
- //Use flagged dwellings only when there are available creatures that we can afford
- if(relationToOwner == PlayerRelations::SAME_PLAYER)
- {
- auto dwelling = dynamic_cast<const CGDwelling*>(obj);
- for(auto & creLevel : dwelling->creatures)
- {
- if(creLevel.first)
- {
- for(auto & creatureID : creLevel.second)
- {
- auto creature = VLC->creh->creatures[creatureID];
- if(ai->freeResources().canAfford(creature->cost))
- return false;
- }
- }
- }
- }
- return true;
- });
- if(objs.empty()) //no possible objects, we did eveyrthing already
- return sptr (Goals::Explore(hero));
- //TODO: check if we can recruit any creatures there, evaluate army
- else
- {
- boost::sort(objs, isCloser);
- HeroPtr h = nullptr;
- for(const CGObjectInstance *obj : objs)
- { //find safe dwelling
- auto pos = obj->visitablePos();
- if (shouldVisit (hero, obj)) //creatures fit in army
- h = hero;
- else
- {
- for(auto ourHero : cb->getHeroesInfo()) //make use of multiple heroes
- {
- if (shouldVisit(ourHero, obj))
- h = ourHero;
- }
- }
- if (h && isSafeToVisit(h, pos) && ai->isAccessibleForHero(pos, h))
- return sptr (Goals::VisitTile(pos).sethero(h));
- }
- }
- }
- return sptr (Goals::Explore(hero)); //find dwelling. use current hero to prevent him from doing nothing.
- }
- //TSubgoal AbstractGoal::whatToDoToAchieve()
- //{
- // logAi->debugStream() << boost::format("Decomposing goal of type %s") % name();
- // return sptr (Goals::Explore());
- //}
- TSubgoal AbstractGoal::goVisitOrLookFor(const CGObjectInstance *obj)
- {
- if(obj)
- return sptr (Goals::GetObj(obj->id.getNum()));
- else
- return sptr (Goals::Explore());
- }
- TSubgoal AbstractGoal::lookForArtSmart(int aid)
- {
- return sptr (Goals::Invalid());
- }
- bool AbstractGoal::invalid() const
- {
- return goalType == INVALID;
- }
- void AbstractGoal::accept (VCAI * ai)
- {
- ai->tryRealize(*this);
- };
|