TGoalVec CompleteQuest::missionDestroyObj() const { TGoalVec solutions; auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val); if(!obj) return ai->ah->howToVisitObj(q.obj); if(obj->ID == Obj::HERO) { auto relations = cb->getPlayerRelations(ai->playerID, obj->tempOwner); if(relations == PlayerRelations::SAME_PLAYER) { auto heroToProtect = cb->getHero(obj->id); solutions.push_back(sptr(GatherArmy().sethero(heroToProtect))); } else if(relations == PlayerRelations::ENEMIES) { solutions = ai->ah->howToVisitObj(obj); } } return solutions; }
void BuildBoat::accept(VCAI * ai) { TResources boatCost; shipyard->getBoatCost(boatCost); if(!cb->getResourceAmount().canAfford(boatCost)) { throw cannotFulfillGoalException("Can not afford boat"); } if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) { throw cannotFulfillGoalException("Can not build boat in enemy shipyard"); } if(shipyard->shipyardStatus() != IShipyard::GOOD) { throw cannotFulfillGoalException("Shipyard is busy."); } logAi->trace( "Building boat at shipyard %s located at %s, estimated boat position %s", shipyard->o->getObjectName(), shipyard->o->visitablePos().toString(), shipyard->bestLocation().toString()); cb->buildBoat(shipyard); throw goalFulfilledException(sptr(*this)); }
ui64 evaluateDanger(const CGObjectInstance *obj) { if(obj->tempOwner < PlayerColor::PLAYER_LIMIT && cb->getPlayerRelations(obj->tempOwner, ai->playerID) != PlayerRelations::ENEMIES) //owned or allied objects don't pose any threat return 0; switch(obj->ID) { case Obj::HERO: { InfoAboutHero iah; cb->getHeroInfo(obj, iah); return iah.army.getStrength(); } case Obj::TOWN: case Obj::GARRISON: case Obj::GARRISON2: //garrison { InfoAboutTown iat; cb->getTownInfo(obj, iat); return iat.army.getStrength(); } case Obj::MONSTER: { //TODO!!!!!!!! const CGCreature *cre = dynamic_cast<const CGCreature*>(obj); return cre->getArmyStrength(); } case Obj::CREATURE_GENERATOR1: { const CGDwelling *d = dynamic_cast<const CGDwelling*>(obj); return d->getArmyStrength(); } case Obj::MINE: case Obj::ABANDONED_MINE: { const CArmedInstance * a = dynamic_cast<const CArmedInstance*>(obj); return a->getArmyStrength(); } case Obj::CRYPT: //crypt case Obj::CREATURE_BANK: //crebank case Obj::DRAGON_UTOPIA: case Obj::SHIPWRECK: //shipwreck case Obj::DERELICT_SHIP: //derelict ship // case Obj::PYRAMID: return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj)); case Obj::PYRAMID: { if(obj->subID == 0) return fh->estimateBankDanger (VLC->objh->bankObjToIndex(obj)); else return 0; } default: return 0; } }
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; }
TGoalVec Conquer::getAllPossibleSubgoals() { TGoalVec ret; auto conquerable = [](const CGObjectInstance * obj) -> bool { if (cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES) { switch (obj->ID.num) { case Obj::TOWN: case Obj::HERO: case Obj::CREATURE_GENERATOR1: case Obj::MINE: //TODO: check ai->knownSubterraneanGates return true; } } return false; }; std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (conquerable(obj)) objs.push_back (obj); } for (auto h : cb->getHeroesInfo()) { SectorMap sm(h); std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero { if (conquerable(obj)) ourObjs.push_back(obj); } for (auto obj : ourObjs) //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))); } } if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) ret.push_back (sptr(Goals::Explore())); //we need to find an enemy return ret; }
TSubgoal BuildBoat::whatToDoToAchieve() { if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES) { return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o)); } if(shipyard->shipyardStatus() != IShipyard::GOOD) { throw cannotFulfillGoalException("Shipyard is busy."); } TResources boatCost; shipyard->getBoatCost(boatCost); return ai->ah->whatToDo(boatCost, this->iAmElementar()); }
int3 SectorMap::findFirstVisitableTile(HeroPtr h, crint3 dst) { int3 ret(-1, -1, -1); int3 curtile = dst; while (curtile != h->visitablePos()) { auto topObj = cb->getTopObj(curtile); if (topObj && topObj->ID == Obj::HERO && topObj != h.h) { if (cb->getPlayerRelations(h->tempOwner, topObj->tempOwner) != PlayerRelations::ENEMIES) { logAi->warn("Another allied hero stands in our way"); return ret; } } if (ai->myCb->getPathsInfo(h.get())->getPathInfo(curtile)->reachable()) { return curtile; } else { auto i = parent.find(curtile); if (i != parent.end()) { assert(curtile != i->second); curtile = i->second; } else { return ret; //throw cannotFulfillGoalException("Unreachable tile in sector? Should not happen!"); } } } return ret; }
TGoalVec GatherArmy::getAllPossibleSubgoals() { //get all possible towns, heroes and dwellings we may use TGoalVec ret; //TODO: include evaluation of monsters gather in calculation for (auto t : cb->getTownsInfo()) { auto pos = t->visitablePos(); if (ai->isAccessibleForHero(pos, hero)) { if(!t->visitingHero && howManyReinforcementsCanGet(hero,t)) { if (!vstd::contains (ai->townVisitsThisWeek[hero], t)) ret.push_back (sptr (Goals::VisitTile(pos).sethero(hero))); } auto bid = ai->canBuildAnyStructure(t, std::vector<BuildingID> (unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)); if (bid != BuildingID::NONE) ret.push_back (sptr(BuildThis(bid, t))); } } 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) || ai->getGoal(h)->goalType == Goals::GATHER_ARMY); }); for (auto h : otherHeroes) { ret.push_back (sptr (Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero))); //go to the other hero if we are faster 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; for (auto obj : ai->visitableObjs) { if(obj->ID == Obj::CREATURE_GENERATOR1) { auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID); //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)) objs.push_back(obj); } } } } } } for(auto h : cb->getHeroesInfo()) { for (auto obj : objs) { //find safe dwelling auto pos = obj->visitablePos(); if (ai->isGoodForVisit(obj, h)) ret.push_back (sptr (Goals::VisitTile(pos).sethero(h))); } } if (ai->canRecruitAnyHero()) //this is not stupid in early phase of game ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) { if (hero == ai->primaryHero() || value >= 1.1f) ret.push_back (sptr(Goals::Explore())); else //workaround to break loop - seemingly there are no ways to explore left throw goalFulfilledException (sptr(Goals::GatherArmy(0).sethero(hero))); } return ret; }
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; }
TGoalVec Conquer::getAllPossibleSubgoals() { TGoalVec ret; auto conquerable = [](const CGObjectInstance * obj) -> bool { if (cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES) { switch (obj->ID.num) { case Obj::TOWN: case Obj::HERO: case Obj::CREATURE_GENERATOR1: case Obj::MINE: //TODO: check ai->knownSubterraneanGates return true; } } return false; }; std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (conquerable(obj)) objs.push_back (obj); } for (auto h : cb->getHeroesInfo()) { auto sm = ai->getCachedSectorMap(h); std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero { if (conquerable(obj)) ourObjs.push_back(obj); } for (auto obj : ourObjs) { int3 dest = obj->visitablePos(); auto t = sm->firstTileToGet(h, dest); //we assume that no more than one tile on the way is guarded if (t.valid()) //we know any path at all { if (ai->isTileNotReserved(h, t)) //no other hero wants to conquer that tile { if (isSafeToVisit(h, dest)) { if (dest != t) //there is something blocking our way ret.push_back(sptr(Goals::ClearWayTo(dest, h).setisAbstract(true))); else { if (obj->ID.num == Obj::HERO) //enemy hero may move to other position ret.push_back(sptr(Goals::VisitHero(obj->id.getNum()).sethero(h).setisAbstract(true))); else //just visit that tile ret.push_back(sptr(Goals::VisitTile(dest).sethero(h).setisAbstract(true))); } } else //we need to get army in order to conquer that place ret.push_back(sptr(Goals::GatherArmy(evaluateDanger(dest, h) * SAFE_ATTACK_CONSTANT).sethero(h).setisAbstract(true))); } } } } if (!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture ret.push_back (sptr(Goals::RecruitHero())); if (ret.empty()) ret.push_back (sptr(Goals::Explore())); //we need to find an enemy return ret; }
TGoalVec CollectRes::getAllPossibleSubgoals() { TGoalVec ret; auto givesResource = [this](const CGObjectInstance * obj) -> bool { //TODO: move this logic to object side //TODO: remember mithril exists //TODO: water objects //TODO: Creature banks //return false first from once-visitable, before checking if they were even visited switch (obj->ID.num) { case Obj::TREASURE_CHEST: return resID == Res::GOLD; break; case Obj::RESOURCE: return obj->subID == resID; break; case Obj::MINE: return (obj->subID == resID && (cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines break; case Obj::CAMPFIRE: return true; //contains all resources break; case Obj::WINDMILL: switch (resID) { case Res::GOLD: case Res::WOOD: return false; } break; case Obj::WATER_WHEEL: if (resID != Res::GOLD) return false; break; case Obj::MYSTICAL_GARDEN: if ((resID != Res::GOLD) && (resID != Res::GEMS)) return false; break; case Obj::LEAN_TO: case Obj::WAGON: if (resID != Res::GOLD) return false; break; default: return false; break; } return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable }; std::vector<const CGObjectInstance *> objs; for (auto obj : ai->visitableObjs) { if (givesResource(obj)) objs.push_back(obj); } for (auto h : cb->getHeroesInfo()) { std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero { if (givesResource(obj)) ourObjs.push_back(obj); } for (auto obj : ourObjs) { auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj)); vstd::concatenate(ret, waysToGo); } } return ret; }
TGoalVec GatherArmy::getAllPossibleSubgoals() { //get all possible towns, heroes and dwellings we may use TGoalVec ret; if(!hero.validAndSet()) { return ret; } //TODO: include evaluation of monsters gather in calculation for(auto t : cb->getTownsInfo()) { auto waysToVisit = ai->ah->howToVisitObj(hero, t); if(waysToVisit.size()) { //grab army from town if(!t->visitingHero && howManyReinforcementsCanGet(hero.get(), t)) { if(!vstd::contains(ai->townVisitsThisWeek[hero], t)) vstd::concatenate(ret, waysToVisit); } //buy army in town if (!t->visitingHero || t->visitingHero == hero.get(true)) { std::vector<int> values = { value, (int)howManyReinforcementsCanBuy(t->getUpperArmy(), t), (int)howManyReinforcementsCanBuy(hero.get(), t) }; int val = *std::min_element(values.begin(), values.end()); if (val) { auto goal = sptr(BuyArmy(t, val).sethero(hero)); if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice ret.push_back(goal); else logAi->debug("Can not buy army, because of ai->ah->containsObjective"); } } //build dwelling //TODO: plan building over multiple turns? //auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK)); //Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm /*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1); if (bid.is_initialized()) { auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority)); if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice ret.push_back(goal); else logAi->debug("Can not build a structure, because of ai->ah->containsObjective"); }*/ } } auto otherHeroes = cb->getHeroesInfo(); auto heroDummy = hero; vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h) { if(h == heroDummy.h) return true; else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true)) return true; else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue return true; else if(ai->getGoal(h)->goalType == GATHER_ARMY) return true; else return false; }); for(auto h : otherHeroes) { // Go to the other hero if we are faster if(!vstd::contains(ai->visitedHeroes[hero], h)) { vstd::concatenate(ret, ai->ah->howToVisitObj(hero, h)); } // Go to the other hero if we are faster if(!vstd::contains(ai->visitedHeroes[h], hero)) { vstd::concatenate(ret, ai->ah->howToVisitObj(h, hero.get())); } } std::vector<const CGObjectInstance *> objs; for(auto obj : ai->visitableObjs) { if(obj->ID == Obj::CREATURE_GENERATOR1) { auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID); //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); ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero.get(), dwelling)); if(val) { for(auto & creLevel : dwelling->creatures) { if(creLevel.first) { for(auto & creatureID : creLevel.second) { auto creature = VLC->creh->creatures[creatureID]; if(ai->ah->freeResources().canAfford(creature->cost)) objs.push_back(obj); //TODO: reserve resources? } } } } } } } for(auto h : cb->getHeroesInfo()) { for(auto obj : objs) { //find safe dwelling if(ai->isGoodForVisit(obj, h)) { vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj)); } } } if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game { if(auto t = ai->findTownWithTavern()) { for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes { if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy { ret.push_back(sptr(RecruitHero())); break; } } } } if(ret.empty()) { const bool allowGatherArmy = false; if(hero == ai->primaryHero()) ret.push_back(sptr(Explore(allowGatherArmy))); else throw cannotFulfillGoalException("No ways to gather army"); } return ret; }