ui64 howManyReinforcementsCanGet(HeroPtr h, const CGTownInstance *t) { ui64 ret = 0; int freeHeroSlots = GameConstants::ARMY_SIZE - h->stacksCount(); std::vector<const CStackInstance *> toMove; for(auto const slot : t->Slots()) { //can be merged woth another stack? SlotID dst = h->getSlotFor(slot.second->getCreatureID()); if(h->hasStackAtSlot(dst)) ret += t->getPower(slot.first); else toMove.push_back(slot.second); } boost::sort(toMove, [](const CStackInstance *lhs, const CStackInstance *rhs) { return lhs->getPower() < rhs->getPower(); }); for (auto & stack : boost::adaptors::reverse(toMove)) { if(freeHeroSlots) { ret += stack->getPower(); freeHeroSlots--; } else break; } return ret; }
int3 whereToExplore(HeroPtr h) { //TODO it's stupid and ineffective, write sth better cb->setSelection(*h); int radius = h->getSightRadious(); int3 hpos = h->visitablePos(); //look for nearby objs -> visit them if they're close enouh const int DIST_LIMIT = 3; std::vector<const CGObjectInstance *> nearbyVisitableObjs; for(const CGObjectInstance *obj : ai->getPossibleDestinations(h)) { int3 op = obj->visitablePos(); CGPath p; cb->getPath2(op, p); if(p.nodes.size() && p.endPos() == op && p.nodes.size() <= DIST_LIMIT) nearbyVisitableObjs.push_back(obj); } boost::sort(nearbyVisitableObjs, isCloser); if(nearbyVisitableObjs.size()) return nearbyVisitableObjs.back()->visitablePos(); try { return ai->explorationBestNeighbour(hpos, radius, h); } catch(cannotFulfillGoalException &e) { std::vector<std::vector<int3> > tiles; //tiles[distance_to_fow] try { return ai->explorationNewPoint(radius, h, tiles); } catch(cannotFulfillGoalException &e) { std::map<int, std::vector<int3> > profits; { TimeCheck tc("Evaluating exploration possibilities"); tiles[0].clear(); //we can't reach FoW anyway for(auto &vt : tiles) for(auto &tile : vt) profits[howManyTilesWillBeDiscovered(tile, radius)].push_back(tile); } if(profits.empty()) return int3 (-1,-1,-1); auto bestDest = profits.end(); bestDest--; return bestDest->second.front(); //TODO which is the real best tile? } } }
/** * \brief Creates a boomerang. * \param hero the hero * \param max_distance maximum distance of the movement in pixels * \param speed speed of the movement in pixels per second * \param angle the angle of the boomerang trajectory in radians * \param sprite_name animation set id representing the boomerang */ Boomerang::Boomerang( const HeroPtr& hero, int max_distance, int speed, double angle, const std::string& sprite_name ): Entity("", 0, hero->get_layer(), Point(0, 0), Size(0, 0)), hero(hero), has_to_go_back(false), going_back(false), speed(speed) { // initialize the entity create_sprite(sprite_name); set_size(16, 16); set_origin(8, 8); int hero_x = hero->get_top_left_x(); int hero_y = hero->get_top_left_y(); switch (hero->get_animation_direction()) { case 0: set_xy(hero_x + 24, hero_y + 8); break; case 1: set_xy(hero_x + 8, hero_y - 8); break; case 2: set_xy(hero_x - 8, hero_y + 8); break; case 3: set_xy(hero_x + 8, hero_y + 24); break; } std::shared_ptr<StraightMovement> movement = std::make_shared<StraightMovement>(false, false); movement->set_speed(speed); movement->set_angle(angle); movement->set_max_distance(max_distance); set_movement(movement); next_sound_date = System::now(); }
void SkeletonAI::act(MonsterPtr monster) { HeroPtr hero = find_hero(monster); TilePtr tile = monster->get_pos(); Coord coord = tile->get_coord(); if (hero) { TilePtr hero_tile = hero->get_pos(); Coord hero_coord = hero_tile->get_coord(); if (can_atack(coord, hero_coord)) { atack(monster, hero); } else { Coord coord_to = small_path_search(coord, hero_coord); TilePtr tile_to = main_core->get_tile(coord_to); MovePtr move = Move::make_Ptr(monster, tile_to); main_core->do_action(move); } } else { make_move(monster); } }
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); } }
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; }
std::vector<const CGObjectInstance *> SectorMap::getNearbyObjs(HeroPtr h, bool sectorsAround) { const Sector * heroSector = &infoOnSectors[retrieveTile(h->visitablePos())]; if (sectorsAround) { std::vector<const CGObjectInstance *> ret; for (auto embarkPoint : heroSector->embarkmentPoints) { const Sector * embarkSector = &infoOnSectors[retrieveTile(embarkPoint)]; range::copy(embarkSector->visitableObjs, std::back_inserter(ret)); } return ret; } return heroSector->visitableObjs; }
bool isSafeToVisit(HeroPtr h, crint3 tile) { const ui64 heroStrength = h->getTotalStrength(), dangerStrength = evaluateDanger(tile, *h); if(dangerStrength) { if(heroStrength / SAFE_ATTACK_CONSTANT > dangerStrength) { logAi->debugStream() << boost::format("It's, safe for %s to visit tile %s") % h->name % tile; return true; } else return false; } return true; //there's no danger }
SectorMap::SectorMap(HeroPtr h) { update(); makeParentBFS(h->visitablePos()); }
/* 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); } }
bool compareHeroStrength(HeroPtr h1, HeroPtr h2) { return h1->getTotalStrength() < h2->getTotalStrength(); }