diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 087d1a997..414c81a79 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -641,6 +641,21 @@ void VCAI::heroExchangeStarted(si32 hero1, si32 hero2) { NET_EVENT_HANDLER; LOG_ENTRY; + + auto firstHero = cb->getHero(hero1); + auto secondHero = cb->getHero(hero2); + + requestActionASAP([=]() + { + if (firstHero->getHeroStrength() > secondHero->getHeroStrength()) + pickBestCreatures (firstHero, secondHero); + else + pickBestCreatures (secondHero, firstHero); + + completeGoal(CGoal(VISIT_HERO).sethero(firstHero)); //TODO: what if we were visited by other hero in the meantime? + completeGoal(CGoal(VISIT_HERO).sethero(secondHero)); + //TODO: exchange artifacts + }); } void VCAI::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) @@ -1815,6 +1830,16 @@ void VCAI::tryRealize(CGoal g) // throw cannotFulfillGoalException("There's a blocked gate!, we should never be here"); //CLEAR_WAY_TO should get keymaster tent } break; + case VISIT_HERO: + { + if(!g.hero->movement) + throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); + if (ai->moveHeroToTile(g.tile, g.hero.get())) + { + throw goalFulfilledException(""); + } + } + break; case BUILD_STRUCTURE: { const CGTownInstance *t = g.town; @@ -1844,7 +1869,7 @@ void VCAI::tryRealize(CGoal g) throw cannotFulfillGoalException("Cannot build a given structure!"); } break; - case DIG_AT_TILE: + case DIG_AT_TILE: { assert(g.hero->visitablePos() == g.tile); if (g.hero->diggingStatus() == CGHeroInstance::CAN_DIG) @@ -2742,6 +2767,17 @@ TSubgoal CGoal::whatToDoToAchieve() return CGoal(VISIT_TILE).settile(pos); } break; + case VISIT_HERO: + { + const CGObjectInstance * obj = cb->getObj(objid); + if(!obj) + return CGoal(EXPLORE); + int3 pos = cb->getObj(objid)->visitablePos(); + + if (hero && ai->isAccessibleForHero(obj->pos, hero, true)) + return CGoal(*this).settile(pos).setisElementar(true); + } + break; case GET_ART_TYPE: { TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use @@ -3120,22 +3156,61 @@ TSubgoal CGoal::whatToDoToAchieve() } else { + if (hero == ai->primaryHero()) //we can get army from other heroes + { + auto otherHeroes = cb->getHeroesInfo(); + auto heroDummy = hero; + erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h) + { + return (h == heroDummy.h || !ai->isAccessibleForHero(heroDummy->pos, h, true)); + }); + if (otherHeroes.size()) + { + boost::sort(otherHeroes, compareArmyStrength); //TODO: check if hero has at least one stack more powerful than ours? not likely to fail + int primaryPath, secondaryPath; + auto h = otherHeroes.back(); + cb->setSelection(hero.h); + primaryPath = cb->getPathInfo(h->pos)->turns; + cb->setSelection(h); + secondaryPath = cb->getPathInfo(hero->pos)->turns; + + if (primaryPath < secondaryPath) + return CGoal(VISIT_HERO).setisAbstract(true).setobjid(h->id).sethero(hero); //go to the other hero if we are faster + else + return CGoal(VISIT_HERO).setisAbstract(true).setobjid(hero->id).sethero(h); //let the other hero come to us + } + } + std::vector objs; //here we'll gather all dwellings ai->retreiveVisitableObjs(objs); erase_if(objs, [&](const CGObjectInstance *obj) { - return (obj->ID != Obj::CREATURE_GENERATOR1); //not town/ dwelling + return (obj->ID != Obj::CREATURE_GENERATOR1); }); if(objs.empty()) //no possible objects, we did eveyrthing already return CGoal(EXPLORE).sethero(hero); //TODO: check if we can recruit any creatures there, evaluate army - boost::sort(objs, isCloser); - BOOST_FOREACH(const CGObjectInstance *obj, objs) - { //find safe dwelling - auto pos = obj->visitablePos(); - if (isSafeToVisit(hero, pos) && ai->isAccessibleForHero(pos, hero)) //TODO: make use of multiple heroes - return CGoal(VISIT_TILE).sethero(hero).settile(pos); + if (objs.size()) + { + boost::sort(objs, isCloser); + HeroPtr h = NULL; + BOOST_FOREACH(const CGObjectInstance *obj, objs) + { //find safe dwelling + auto pos = obj->visitablePos(); + if (shouldVisit (hero, obj)) //creatures fit in army + h = hero; + else + { + BOOST_FOREACH(auto ourHero, cb->getHeroesInfo()) //make use of multiple heroes + { + if (shouldVisit(ourHero, obj)) + h = ourHero; + } + } + if (h && isSafeToVisit(h, pos) && ai->isAccessibleForHero(pos, h)) + return CGoal(VISIT_TILE).sethero(h).settile(pos); + } } } @@ -3319,6 +3394,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj) case Obj::SEER_HUT: case Obj::QUEST_GUARD: { + return false; //fixme: avoid crash BOOST_FOREACH (auto q, ai->myCb->getMyQuests()) { if (q.obj == obj) diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 01bc9ebfe..7789cb27f 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -78,15 +78,13 @@ enum EGoals OBJECT_GOALS_BEGIN, GET_OBJ, //visit or defeat or collect the object FIND_OBJ, //find and visit any obj with objid + resid //TODO: consider universal subid for various types (aid, bid) + VISIT_HERO, //heroes can move around - set goal abstract and track hero every turn GET_ART_TYPE, //BUILD_STRUCTURE, ISSUE_COMMAND, - //hero - //VISIT_OBJ, //hero + tile - VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable CLEAR_WAY_TO, DIG_AT_TILE //elementar with hero on tile