diff --git a/AI/VCAI/AIhelper.cpp b/AI/VCAI/AIhelper.cpp index bf9149bf1..87e5ae5f1 100644 --- a/AI/VCAI/AIhelper.cpp +++ b/AI/VCAI/AIhelper.cpp @@ -88,6 +88,11 @@ bool AIhelper::hasTasksLeft() const return resourceManager->hasTasksLeft(); } +bool AIhelper::removeOutdatedObjectives(std::function predicate) +{ + return resourceManager->removeOutdatedObjectives(predicate); +} + bool AIhelper::canAfford(const TResources & cost) const { return resourceManager->canAfford(cost); diff --git a/AI/VCAI/AIhelper.h b/AI/VCAI/AIhelper.h index 28bf82dad..688081055 100644 --- a/AI/VCAI/AIhelper.h +++ b/AI/VCAI/AIhelper.h @@ -47,6 +47,7 @@ public: Goals::TSubgoal whatToDo() const override; bool containsObjective(Goals::TSubgoal goal) const; bool hasTasksLeft() const override; + bool removeOutdatedObjectives(std::function predicate) override; bool getBuildingOptions(const CGTownInstance * t) override; BuildingID getMaxPossibleGoldBuilding(const CGTownInstance * t); diff --git a/AI/VCAI/ResourceManager.cpp b/AI/VCAI/ResourceManager.cpp index 918956ef3..e466805da 100644 --- a/AI/VCAI/ResourceManager.cpp +++ b/AI/VCAI/ResourceManager.cpp @@ -232,22 +232,12 @@ bool ResourceManager::notifyGoalCompleted(Goals::TSubgoal goal) if (goal->invalid()) logAi->warn("Attempt to complete Invalid goal"); - bool removedGoal = false; - while (true) - { //unfortunatelly we can't use remove_if on heap - auto it = boost::find_if(queue, [goal](const ResourceObjective & ro) -> bool - { - return ro.goal == goal || ro.goal->fulfillsMe (goal); - }); - if(it != queue.end()) //removed at least one - { - logAi->debug("Removing goal %s from ResourceManager.", it->goal->name()); - queue.erase(queue.s_handle_from_iterator(it)); - removedGoal = true; - } - else //found nothing more to remove - break; - } + std::function equivalentGoalsCheck = [goal](const Goals::TSubgoal & x) -> bool + { + return x == goal || x->fulfillsMe(goal); + }; + + bool removedGoal = removeOutdatedObjectives(equivalentGoalsCheck); dumpToLog(); @@ -315,6 +305,27 @@ bool ResourceManager::hasTasksLeft() const return !queue.empty(); } +bool ResourceManager::removeOutdatedObjectives(std::function predicate) +{ + bool removedAnything = false; + while(true) + { //unfortunately we can't use remove_if on heap + auto it = boost::find_if(queue, [&](const ResourceObjective & ro) -> bool + { + predicate(ro.goal); + }); + if(it != queue.end()) //removed at least one + { + logAi->debug("Removing goal %s from ResourceManager.", it->goal->name()); + queue.erase(queue.s_handle_from_iterator(it)); + removedAnything = true; + } + else //found nothing more to remove + break; + } + return removedAnything; +} + TResources ResourceManager::reservedResources() const { TResources res; diff --git a/AI/VCAI/ResourceManager.h b/AI/VCAI/ResourceManager.h index 5ec08433c..61f1c98e5 100644 --- a/AI/VCAI/ResourceManager.h +++ b/AI/VCAI/ResourceManager.h @@ -52,7 +52,7 @@ public: virtual Goals::TSubgoal whatToDo(TResources &res, Goals::TSubgoal goal) = 0; virtual bool containsObjective(Goals::TSubgoal goal) const = 0; virtual bool hasTasksLeft() const = 0; -private: + virtual bool removeOutdatedObjectives(std::function predicate) = 0; //remove ResourceObjectives from queue if ResourceObjective->goal meets specific criteria virtual bool notifyGoalCompleted(Goals::TSubgoal goal) = 0; }; @@ -82,6 +82,7 @@ public: Goals::TSubgoal whatToDo(TResources & res, Goals::TSubgoal goal); //can we afford this goal or need to CollectRes? bool containsObjective(Goals::TSubgoal goal) const; bool hasTasksLeft() const override; + bool removeOutdatedObjectives(std::function predicate) override; protected: //not-const actions only for AI virtual void reserveResoures(const TResources & res, Goals::TSubgoal goal = Goals::TSubgoal()); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 5a46b72df..29e0fada0 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -418,37 +418,36 @@ void VCAI::objectRemoved(const CGObjectInstance * obj) for(auto h : cb->getHeroesInfo()) unreserveObject(h, obj); - - vstd::erase_if(lockedHeroes, [&](const std::pair & x) -> bool - { - if((x.second->goalType == Goals::VISIT_OBJ) && (x.second->objid == obj->id.getNum())) - return true; - else - return false; - }); - - vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair & x) -> bool - { - if((x.first->goalType == Goals::VISIT_OBJ) && (x.first->objid == obj->id.getNum())) - return true; - else - return false; - }); - - auto goalErasePredicate = [&](const Goals::TSubgoal & x) ->bool + std::function checkRemovalValidity = [&](const Goals::TSubgoal & x) -> bool { if((x->goalType == Goals::VISIT_OBJ) && (x->objid == obj->id.getNum())) return true; + else if(x->parent && checkRemovalValidity(x->parent)) //repeat this lambda check recursively on parent goal + return true; else return false; }; - vstd::erase_if(basicGoals, goalErasePredicate); - vstd::erase_if(goalsToAdd, goalErasePredicate); - vstd::erase_if(goalsToRemove, goalErasePredicate); + //clear VCAI / main loop caches + vstd::erase_if(lockedHeroes, [&](const std::pair & x) -> bool + { + return checkRemovalValidity(x.second); + }); + + vstd::erase_if(ultimateGoalsFromBasic, [&](const std::pair & x) -> bool + { + return checkRemovalValidity(x.first); + }); + + vstd::erase_if(basicGoals, checkRemovalValidity); + vstd::erase_if(goalsToAdd, checkRemovalValidity); + vstd::erase_if(goalsToRemove, checkRemovalValidity); for(auto goal : ultimateGoalsFromBasic) - vstd::erase_if(goal.second, goalErasePredicate); + vstd::erase_if(goal.second, checkRemovalValidity); + + //clear resource manager goal cache + ah->removeOutdatedObjectives(checkRemovalValidity); //TODO: Find better way to handle hero boat removal if(auto hero = dynamic_cast(obj))