TGoalVec VisitTile::getAllPossibleSubgoals() { assert(cb->isInTheMap(tile)); TGoalVec ret; if (!cb->isVisible(tile)) ret.push_back (sptr(Goals::Explore())); //what sense does it make? else { std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); //use assigned hero if any else heroes = cb->getHeroesInfo(); //use most convenient hero for (auto h : heroes) { if (ai->isAccessibleForHero(tile, h)) ret.push_back (sptr(Goals::VisitTile(tile).sethero(h))); } if (ai->canRecruitAnyHero()) ret.push_back (sptr(Goals::RecruitHero())); } if (ret.empty()) { auto obj = frontOrNull(cb->getVisitableObjs(tile)); if (obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile ret.push_back (sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true))); else ret.push_back (sptr(Goals::ClearWayTo(tile))); } //important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile) return ret; }
ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor) { const TerrainTile *t = cb->getTile(tile, false); if(!t) //we can know about guard but can't check its tile (the edge of fow) return 190000000; //MUCH ui64 objectDanger = 0, guardDanger = 0; auto visitableObjects = cb->getVisitableObjs(tile); // in some scenarios hero happens to be "under" the object (eg town). Then we consider ONLY the hero. if(vstd::contains_if(visitableObjects, objWithID<Obj::HERO>)) vstd::erase_if(visitableObjects, [](const CGObjectInstance * obj) { return !objWithID<Obj::HERO>(obj); }); if(const CGObjectInstance * dangerousObject = vstd::backOrNull(visitableObjects)) { objectDanger = evaluateDanger(dangerousObject); //unguarded objects can also be dangerous or unhandled if (objectDanger) { //TODO: don't downcast objects AI shouldn't know about! auto armedObj = dynamic_cast<const CArmedInstance*>(dangerousObject); if (armedObj) { float tacticalAdvantage = fh->getTacticalAdvantage(visitor, armedObj); objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?) } } if (dangerousObject->ID == Obj::SUBTERRANEAN_GATE) { //check guard on the other side of the gate auto it = ai->knownSubterraneanGates.find(dangerousObject); if (it != ai->knownSubterraneanGates.end()) { auto guards = cb->getGuardingCreatures(it->second->visitablePos()); for (auto cre : guards) { vstd::amax (guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance*>(cre))); } } } } auto guards = cb->getGuardingCreatures(tile); for (auto cre : guards) { vstd::amax (guardDanger, evaluateDanger(cre) * fh->getTacticalAdvantage(visitor, dynamic_cast<const CArmedInstance*>(cre))); //we are interested in strongest monster around } //TODO mozna odwiedzic blockvis nie ruszajac straznika return std::max(objectDanger, guardDanger); }
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)); }
ui64 evaluateDanger(crint3 tile) { const TerrainTile *t = cb->getTile(tile, false); if(!t) //we can know about guard but can't check its tile (the edge of fow) return 190000000; //MUCH ui64 objectDanger = 0, guardDanger = 0; auto visObjs = cb->getVisitableObjs(tile); if(visObjs.size()) objectDanger = evaluateDanger(visObjs.back()); int3 guardPos = cb->guardingCreaturePosition(tile); if(guardPos.x >= 0 && guardPos != tile) guardDanger = evaluateDanger(guardPos); //TODO mozna odwiedzic blockvis nie ruszajac straznika return std::max(objectDanger, guardDanger); }
int3 whereToExplore(HeroPtr h) { TimeCheck tc ("where to explore"); int radius = h->getSightRadious(); int3 hpos = h->visitablePos(); SectorMap sm(h); //look for nearby objs -> visit them if they're close enouh const int DIST_LIMIT = 3; std::vector<const CGObjectInstance *> nearbyVisitableObjs; for (int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map { for (int y = hpos.y - DIST_LIMIT; y <= hpos.y + DIST_LIMIT; ++y) { for (auto obj : cb->getVisitableObjs (int3(x,y,hpos.z), false)) { int3 op = obj->visitablePos(); CGPath p; ai->myCb->getPathsInfo(h.get())->getPath(op, p); if (p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT) if (ai->isGoodForVisit(obj, h, sm)) nearbyVisitableObjs.push_back(obj); } } } vstd::removeDuplicates (nearbyVisitableObjs); //one object may occupy multiple tiles boost::sort(nearbyVisitableObjs, CDistanceSorter(h.get())); if(nearbyVisitableObjs.size()) return nearbyVisitableObjs.back()->visitablePos(); try //check if nearby tiles allow us to reveal anything - this is quick { return ai->explorationBestNeighbour(hpos, radius, h); } catch(cannotFulfillGoalException &e) { //perform exhaustive search return ai->explorationNewPoint(h); } }
TGoalVec VisitTile::getAllPossibleSubgoals() { assert(cb->isInTheMap(tile)); TGoalVec ret; if (!cb->isVisible(tile)) ret.push_back (sptr(Goals::Explore())); //what sense does it make? else { std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); //use assigned hero if any else heroes = cb->getHeroesInfo(); //use most convenient hero for (auto h : heroes) { if (ai->isAccessibleForHero(tile, h)) ret.push_back (sptr(Goals::VisitTile(tile).sethero(h))); } if (ai->canRecruitAnyHero()) ret.push_back (sptr(Goals::RecruitHero())); } if(ret.empty()) { auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile)); if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile { if (hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now ret.push_back(sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true))); else throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier } else ret.push_back (sptr(Goals::ClearWayTo(tile))); } //important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile) return ret; }