TGoalVec Explore::getAllPossibleSubgoals() { TGoalVec ret; std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); else { //heroes = ai->getUnblockedHeroes(); heroes = cb->getHeroesInfo(); erase_if (heroes, [](const HeroPtr h) { if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer return true; return !h->movement; //saves time, immobile heroes are useless anyway }); } //try to use buildings that uncover map std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (!vstd::contains(ai->alreadyVisited, obj)) { switch (obj->ID.num) { case Obj::REDWOOD_OBSERVATORY: case Obj::PILLAR_OF_FIRE: case Obj::CARTOGRAPHER: case Obj::SUBTERRANEAN_GATE: //TODO: check ai->knownSubterraneanGates objs.push_back (obj); } } } for (auto h : heroes) { SectorMap sm(h); for (auto obj : objs) //double loop, performance risk? { auto t = sm.firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded if (ai->canReachTile(h, t)) ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true))); } int3 t = whereToExplore(h); if (t.valid()) { ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); } else if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost { t = ai->explorationDesperate(h); if (t.valid()) //don't waste time if we are completely blocked ret.push_back (sptr(Goals::ClearWayTo(t, h).setisAbstract(true))); } } //we either don't have hero yet or none of heroes can explore if ((!hero || ret.empty()) && ai->canRecruitAnyHero()) ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) { throw goalFulfilledException (sptr(Goals::Explore().sethero(hero))); } //throw cannotFulfillGoalException("Cannot explore - no possible ways found!"); return ret; }
TGoalVec Explore::getAllPossibleSubgoals() { TGoalVec ret; std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); else { //heroes = ai->getUnblockedHeroes(); heroes = cb->getHeroesInfo(); vstd::erase_if(heroes, [](const HeroPtr h) { if (ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer return true; if (!ai->isAbleToExplore(h)) return true; return !h->movement; //saves time, immobile heroes are useless anyway }); } //try to use buildings that uncover map std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (!vstd::contains(ai->alreadyVisited, obj)) { switch (obj->ID.num) { case Obj::REDWOOD_OBSERVATORY: case Obj::PILLAR_OF_FIRE: case Obj::CARTOGRAPHER: objs.push_back (obj); break; case Obj::MONOLITH_ONE_WAY_ENTRANCE: case Obj::MONOLITH_TWO_WAY: case Obj::SUBTERRANEAN_GATE: auto tObj = dynamic_cast<const CGTeleport *>(obj); assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end()); if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability) objs.push_back (obj); break; } } else { switch (obj->ID.num) { case Obj::MONOLITH_TWO_WAY: case Obj::SUBTERRANEAN_GATE: auto tObj = dynamic_cast<const CGTeleport *>(obj); if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability) break; for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits) { if(!cb->getObj(exit)) { // Always attempt to visit two-way teleports if one of channel exits is not visible objs.push_back(obj); break; } } break; } } } for (auto h : heroes) { auto sm = ai->getCachedSectorMap(h); for (auto obj : objs) //double loop, performance risk? { auto t = sm->firstTileToGet(h, obj->visitablePos()); //we assume that no more than one tile on the way is guarded if (ai->isTileNotReserved(h, t)) ret.push_back (sptr(Goals::ClearWayTo(obj->visitablePos(), h).setisAbstract(true))); } int3 t = whereToExplore(h); if (t.valid()) { ret.push_back (sptr (Goals::VisitTile(t).sethero(h))); } else { ai->markHeroUnableToExplore (h); //there is no freely accessible tile, do not poll this hero anymore //possible issues when gathering army to break if (hero.h == h || (!hero && h == ai->primaryHero().h)) //check this only ONCE, high cost { t = ai->explorationDesperate(h); if (t.valid()) //don't waste time if we are completely blocked ret.push_back (sptr(Goals::ClearWayTo(t, h).setisAbstract(true))); } } } //we either don't have hero yet or none of heroes can explore if ((!hero || ret.empty()) && ai->canRecruitAnyHero()) ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) { throw goalFulfilledException (sptr(Goals::Explore().sethero(hero))); } //throw cannotFulfillGoalException("Cannot explore - no possible ways found!"); return ret; }