TGoalVec ClearWayTo::getAllPossibleSubgoals() { TGoalVec ret; for (auto h : cb->getHeroesInfo()) { if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves h->visitablePos() == tile) //we are already on that tile! what does it mean? continue; SectorMap sm(h); int3 tileToHit = sm.firstTileToGet(hero ? hero : h, tile); //if our hero is trapped, make sure we request clearing the way from OUR perspective if (!tileToHit.valid()) continue; if (isBlockedBorderGate(tileToHit)) { //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))); } auto topObj = cb->getTopObj(tileToHit); if(topObj) { if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) if (topObj != hero.get(true)) //the hero we wnat to free logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getHoverText() % h->getHoverText(); if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD) { if (shouldVisit(h, topObj)) { //do NOT use VISIT_TILE, as tile with quets guard can't be visited ret.push_back (sptr (Goals::GetObj(topObj->id.getNum()).sethero(h))); } else { //TODO: we should be able to return apriopriate quest here (VCAI::striveToQuest) logAi->debugStream() << "Quest guard blocks the way to " + tile(); } } } else ret.push_back (sptr (Goals::VisitTile(tileToHit).sethero(h))); } if (ai->canRecruitAnyHero()) ret.push_back (sptr (Goals::RecruitHero())); if (ret.empty()) { logAi->warnStream() << "There is no known way to clear the way to tile " + tile(); throw goalFulfilledException (sptr(*this)); //make sure asigned hero gets unlocked } 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); }
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); }
bool boundaryBetweenTwoPoints (int3 pos1, int3 pos2) //determines if two points are separated by known barrier { int xMin = std::min (pos1.x, pos2.x); int xMax = std::max (pos1.x, pos2.x); int yMin = std::min (pos1.y, pos2.y); int yMax = std::max (pos1.y, pos2.y); for (int x = xMin; x <= xMax; ++x) { for (int y = yMin; y <= yMax; ++y) { int3 tile = int3(x, y, pos1.z); //use only on same level, ofc if (abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5) { if (!(cb->isVisible(tile) && cb->getTile(tile)->blocked)) //if there's invisible or unblocked tile inbetween, it's good return false; } } } return true; //if all are visible and blocked, we're at dead end }
TGoalVec ClearWayTo::getAllPossibleSubgoals() { TGoalVec ret; std::vector<const CGHeroInstance *> heroes; if (hero) heroes.push_back(hero.h); else { heroes = cb->getHeroesInfo(); } for (auto h : heroes) { //TODO: handle clearing way to allied heroes that are blocked //if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves // h->visitablePos() == tile) //we are already on that tile! what does it mean? // continue; //if our hero is trapped, make sure we request clearing the way from OUR perspective SectorMap sm(h); int3 tileToHit = sm.firstTileToGet(h, tile); if (!tileToHit.valid()) continue; if (isBlockedBorderGate(tileToHit)) { //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))); } auto topObj = cb->getTopObj(tileToHit); if (topObj) { if (vstd::contains(ai->reservedObjs, topObj) && !vstd::contains(ai->reservedHeroesMap[h], topObj)) { throw goalFulfilledException (sptr(Goals::ClearWayTo(tile, h))); continue; //do not capure object reserved by other hero } if (topObj->ID == Obj::HERO && cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) if (topObj != hero.get(true)) //the hero we want to free logAi->errorStream() << boost::format("%s stands in the way of %s") % topObj->getHoverText() % h->getHoverText(); if (topObj->ID == Obj::QUEST_GUARD || topObj->ID == Obj::BORDERGUARD) { if (shouldVisit(h, topObj)) { //do NOT use VISIT_TILE, as tile with quets guard can't be visited ret.push_back (sptr (Goals::GetObj(topObj->id.getNum()).sethero(h))); continue; //do not try to visit tile or gather army } else { //TODO: we should be able to return apriopriate quest here (VCAI::striveToQuest) logAi->debugStream() << "Quest guard blocks the way to " + tile(); } } } if (isSafeToVisit(h, tileToHit)) //this makes sense only if tile is guarded, but there i no quest object { ret.push_back (sptr (Goals::VisitTile(tileToHit).sethero(h))); } else { ret.push_back (sptr (Goals::GatherArmy(evaluateDanger(tileToHit, h)*SAFE_ATTACK_CONSTANT). sethero(h).setisAbstract(true))); } } if (ai->canRecruitAnyHero()) ret.push_back (sptr (Goals::RecruitHero())); if (ret.empty()) { logAi->warnStream() << "There is no known way to clear the way to tile " + tile(); throw goalFulfilledException (sptr(Goals::ClearWayTo(tile))); //make sure asigned hero gets unlocked } return ret; }
bool isBlockedBorderGate(int3 tileToHit) { return cb->getTile(tileToHit)->topVisitableId() == Obj::BORDER_GATE && cb->getPathInfo(tileToHit)->accessible != CGPathNode::ACCESSIBLE; }
bool isBlockedBorderGate(int3 tileToHit) { return cb->getTile(tileToHit)->topVisitableId() == Obj::BORDER_GATE && (dynamic_cast <const CGKeys *>(cb->getTile(tileToHit)->visitableObjects.back()))->wasMyColorVisited (ai->playerID); }