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)); }
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(); }
/* this functions returns one target tile or invalid tile. We will use it to poll possible destinations For ship construction etc, another function (goal?) is needed */ int3 SectorMap::firstTileToGet(HeroPtr h, crint3 dst) { int3 ret(-1, -1, -1); int sourceSector = retrieveTile(h->visitablePos()); int destinationSector = retrieveTile(dst); const Sector * src = &infoOnSectors[sourceSector]; const Sector * dest = &infoOnSectors[destinationSector]; if (sourceSector != destinationSector) //use ships, shipyards etc.. { if (ai->isAccessibleForHero(dst, h)) //pathfinder can find a way using ships and gates if tile is not blocked by objects return dst; std::map<const Sector *, const Sector *> preds; std::queue<const Sector *> sectorQueue; sectorQueue.push(src); while (!sectorQueue.empty()) { const Sector * s = sectorQueue.front(); sectorQueue.pop(); for (int3 ep : s->embarkmentPoints) { Sector * neigh = &infoOnSectors[retrieveTile(ep)]; //preds[s].push_back(neigh); if (!preds[neigh]) { preds[neigh] = s; sectorQueue.push(neigh); } } } if (!preds[dest]) { //write("test.txt"); return ret; //throw cannotFulfillGoalException(boost::str(boost::format("Cannot find connection between sectors %d and %d") % src->id % dst->id)); } std::vector<const Sector *> toTraverse; toTraverse.push_back(dest); while (toTraverse.back() != src) { toTraverse.push_back(preds[toTraverse.back()]); } if (preds[dest]) { //TODO: would be nice to find sectors in loop const Sector * sectorToReach = toTraverse.at(toTraverse.size() - 2); if (!src->water && sectorToReach->water) //embark { //embark on ship -> look for an EP with a boat auto firstEP = boost::find_if(src->embarkmentPoints, [=](crint3 pos) -> bool { const TerrainTile * t = getTile(pos); if (t && t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT) { if (retrieveTile(pos) == sectorToReach->id) return true; } return false; }); if (firstEP != src->embarkmentPoints.end()) { return *firstEP; } else { //we need to find a shipyard with an access to the desired sector's EP //TODO what about Summon Boat spell? std::vector<const IShipyard *> shipyards; for (const CGTownInstance * t : cb->getTownsInfo()) { if (t->hasBuilt(BuildingID::SHIPYARD)) shipyards.push_back(t); } for (const CGObjectInstance * obj : ai->getFlaggedObjects()) { if (obj->ID != Obj::TOWN) //towns were handled in the previous loop { if (const IShipyard * shipyard = IShipyard::castFrom(obj)) shipyards.push_back(shipyard); } } shipyards.erase(boost::remove_if(shipyards, [=](const IShipyard * shipyard) -> bool { return shipyard->shipyardStatus() != 0 || retrieveTile(shipyard->bestLocation()) != sectorToReach->id; }), shipyards.end()); if (!shipyards.size()) { //TODO consider possibility of building shipyard in a town return ret; //throw cannotFulfillGoalException("There is no known shipyard!"); } //we have only shipyards that possibly can build ships onto the appropriate EP auto ownedGoodShipyard = boost::find_if(shipyards, [](const IShipyard * s) -> bool { return s->o->tempOwner == ai->playerID; }); if (ownedGoodShipyard != shipyards.end()) { const IShipyard * s = *ownedGoodShipyard; TResources shipCost; s->getBoatCost(shipCost); if (cb->getResourceAmount().canAfford(shipCost)) { int3 ret = s->bestLocation(); cb->buildBoat(s); //TODO: move actions elsewhere return ret; } else { //TODO gather res return ret; //throw cannotFulfillGoalException("Not enough resources to build a boat"); } } else { //TODO pick best shipyard to take over return shipyards.front()->o->visitablePos(); } } } else if (src->water && !sectorToReach->water) { //TODO //disembark return ret; } else //use subterranean gates - not needed since gates are now handled via Pathfinder { return ret; //throw cannotFulfillGoalException("Land-land and water-water inter-sector transitions are not implemented!"); } } else { return ret; //throw cannotFulfillGoalException("Inter-sector route detection failed: not connected sectors?"); } } else //tiles are in same sector { return findFirstVisitableTile(h, dst); } }
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 = cb->getTopObj(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 (setisElementar(true)); //all the conditions for trade are met }