diff --git a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp index bb18fc070..b1728ccc4 100644 --- a/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp +++ b/AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp @@ -123,9 +123,6 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() if(ai->nullkiller->arePathHeroesLocked(way->getPath())) continue; - if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP) - continue; - way->evaluationContext.closestWayRatio = closestWay->evaluationContext.movementCost / way->evaluationContext.movementCost; @@ -149,7 +146,6 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks() bool CaptureObjectsBehavior::shouldVisitObject(ObjectIdRef obj) const { const CGObjectInstance* objInstance = obj; - if(!objInstance) return false; @@ -162,6 +158,11 @@ bool CaptureObjectsBehavior::shouldVisitObject(ObjectIdRef obj) const { return false; } + + if(isObjectRemovable(obj)) + { + return true; + } const int3 pos = objInstance->visitablePos(); diff --git a/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp b/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp index 1debfb4ff..28024e023 100644 --- a/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp +++ b/AI/Nullkiller/Behaviors/GatherArmyBehavior.cpp @@ -148,9 +148,6 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const CGHeroInstance * her if(ai->nullkiller->arePathHeroesLocked(way->getPath())) continue; - if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP) - continue; - way->evaluationContext.closestWayRatio = 1; tasks.push_back(sptr(*way)); @@ -246,9 +243,6 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const CGTownInstance * upgrader) if(ai->nullkiller->arePathHeroesLocked(way->getPath())) continue; - if(ai->nullkiller->getHeroLockedReason(way->hero.get()) == HeroLockedReason::STARTUP) - continue; - way->evaluationContext.closestWayRatio = 1; tasks.push_back(sptr(*way)); diff --git a/AI/Nullkiller/Engine/Nullkiller.cpp b/AI/Nullkiller/Engine/Nullkiller.cpp index 4dda05be8..a83047428 100644 --- a/AI/Nullkiller/Engine/Nullkiller.cpp +++ b/AI/Nullkiller/Engine/Nullkiller.cpp @@ -69,12 +69,13 @@ Goals::TSubgoal Nullkiller::choseBestTask(std::shared_ptr behavior) co void Nullkiller::resetAiState() { lockedHeroes.clear(); - dangerHitMap->reset(); } void Nullkiller::updateAiState() { + activeHero = nullptr; + ai->validateVisitableObjs(); dangerHitMap->updateHitMap(); @@ -94,17 +95,44 @@ void Nullkiller::updateAiState() buildAnalyzer->update(); } +bool Nullkiller::isHeroLocked(const CGHeroInstance * hero) const +{ + return getHeroLockedReason(hero) != HeroLockedReason::NOT_LOCKED; +} + bool Nullkiller::arePathHeroesLocked(const AIPath & path) const { + if(getHeroLockedReason(path.targetHero) == HeroLockedReason::STARTUP) + { +#if AI_TRACE_LEVEL >= 1 + logAi->trace("Hero %s is locked by STARTUP. Discarding %s", path.targetHero->name, path.toString()); +#endif + return true; + } + for(auto & node : path.nodes) { - if(isHeroLocked(node.targetHero)) + auto lockReason = getHeroLockedReason(node.targetHero); + + if(lockReason != HeroLockedReason::NOT_LOCKED) + { +#if AI_TRACE_LEVEL >= 1 + logAi->trace("Hero %s is locked by STARTUP. Discarding %s", path.targetHero->name, path.toString()); +#endif return true; + } } return false; } +HeroLockedReason Nullkiller::getHeroLockedReason(const CGHeroInstance * hero) const +{ + auto found = lockedHeroes.find(hero); + + return found != lockedHeroes.end() ? found->second : HeroLockedReason::NOT_LOCKED; +} + void Nullkiller::makeTurn() { resetAiState(); @@ -149,7 +177,7 @@ void Nullkiller::makeTurn() { if(bestTask->hero) { - activeHero = bestTask->hero.get(); + setActive(bestTask->hero.get(), bestTask->tile); } bestTask->accept(ai.get()); diff --git a/AI/Nullkiller/Engine/Nullkiller.h b/AI/Nullkiller/Engine/Nullkiller.h index 51e4b5797..ff9d1bacb 100644 --- a/AI/Nullkiller/Engine/Nullkiller.h +++ b/AI/Nullkiller/Engine/Nullkiller.h @@ -34,6 +34,7 @@ class Nullkiller private: std::unique_ptr priorityEvaluator; const CGHeroInstance * activeHero; + int3 targetTile; std::map lockedHeroes; public: @@ -43,9 +44,11 @@ public: Nullkiller(); void makeTurn(); bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; } - bool isHeroLocked(const CGHeroInstance * hero) const { return vstd::contains(lockedHeroes, hero); } - HeroLockedReason getHeroLockedReason(const CGHeroInstance * hero) const { return isHeroLocked(hero) ? lockedHeroes.at(hero) : HeroLockedReason::NOT_LOCKED; } - void setActive(const CGHeroInstance * hero) { activeHero = hero; } + bool isHeroLocked(const CGHeroInstance * hero) const; + HeroPtr getActiveHero() { return activeHero; } + HeroLockedReason getHeroLockedReason(const CGHeroInstance * hero) const; + int3 getTargetTile() const { return targetTile; } + void setActive(const CGHeroInstance * hero, int3 tile) { activeHero = hero; targetTile = tile; } void lockHero(const CGHeroInstance * hero, HeroLockedReason lockReason) { lockedHeroes[hero] = lockReason; } void unlockHero(const CGHeroInstance * hero) { lockedHeroes.erase(hero); } bool arePathHeroesLocked(const AIPath & path) const; diff --git a/AI/Nullkiller/FuzzyHelper.cpp b/AI/Nullkiller/FuzzyHelper.cpp index 03a763a07..751c09beb 100644 --- a/AI/Nullkiller/FuzzyHelper.cpp +++ b/AI/Nullkiller/FuzzyHelper.cpp @@ -289,6 +289,15 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj, const VCAI * ai) const CGTownInstance * cre = dynamic_cast(obj); return cre->getUpperArmy()->getArmyStrength(); } + case Obj::ARTIFACT: + case Obj::RESOURCE: + { + if(!vstd::contains(ai->alreadyVisited, obj)) + { + return 0; + } + // passthrough + } case Obj::MONSTER: case Obj::HERO: case Obj::GARRISON: diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp index 394a9c48a..6b119919b 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp @@ -88,7 +88,7 @@ void ExecuteHeroChain::accept(VCAI * ai) { if(hero->movement) { - ai->nullkiller->setActive(hero.get()); + ai->nullkiller->setActive(hero.get(), node.coord); if(node.specialAction) { diff --git a/AI/Nullkiller/VCAI.cpp b/AI/Nullkiller/VCAI.cpp index 1d634ead4..24f3dacef 100644 --- a/AI/Nullkiller/VCAI.cpp +++ b/AI/Nullkiller/VCAI.cpp @@ -652,8 +652,34 @@ void VCAI::showBlockingDialog(const std::string & text, const std::vector take the last one (they're indexed [1-size]) sel = components.size(); - if(!selection && cancel) //yes&no -> always answer yes, we are a brave AI :) - sel = 1; + if(!selection && cancel) + { + requestActionASAP([=]() + { + //yes&no -> always answer yes, we are a brave AI :) + auto answer = 1; + if(nullkiller) + { + auto hero = nullkiller->getActiveHero(); + auto target = nullkiller->getTargetTile(); + + if(hero.validAndSet() && target.valid()) + { + auto ratio = (float)fh->evaluateDanger(target, hero.get()) / (float)hero->getTotalStrength(); + bool dangerUnknown = ratio == 0; + bool dangerTooHigh = ratio > (1 / SAFE_ATTACK_CONSTANT); + + logAi->trace("Guarded object query hook: %s by %s danger ratio %f", target.toString(), hero.name, ratio); + + if(text.find("guarded") >= 0 && (dangerUnknown || dangerTooHigh)) + answer = 0; // no + } + } + + answerQuery(askID, answer); + }); + return; + } // TODO: Find better way to understand it is Chest of Treasures if(components.size() == 2 && components.front().id == Component::RESOURCE)