| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 | /** CaptureObjectsBehavior.cpp, part of VCMI engine** Authors: listed in file AUTHORS in main folder** License: GNU General Public License v2.0 or later* Full text of license available in license.txt file, in main folder**/#include "StdInc.h"#include "../AIGateway.h"#include "../Engine/Nullkiller.h"#include "../Goals/Composition.h"#include "../Goals/ExecuteHeroChain.h"#include "../Goals/Invalid.h"#include "CaptureObjectsBehavior.h"#include "../AIUtility.h"namespace NKAI{using namespace Goals;template <typename T>bool vectorEquals(const std::vector<T> & v1, const std::vector<T> & v2){	return vstd::contains_if(v1, [&](T o) -> bool	{		return vstd::contains(v2, o);	});}std::string CaptureObjectsBehavior::toString() const{	return "Capture objects";}bool CaptureObjectsBehavior::operator==(const CaptureObjectsBehavior & other) const{	if(specificObjects != other.specificObjects)		return false;	if(specificObjects)		return vectorEquals(objectsToCapture, other.objectsToCapture);	return vectorEquals(objectTypes, other.objectTypes)		&& vectorEquals(objectSubTypes, other.objectSubTypes);}Goals::TGoalVec CaptureObjectsBehavior::getVisitGoals(const std::vector<AIPath> & paths, const CGObjectInstance * objToVisit){	Goals::TGoalVec tasks;	tasks.reserve(paths.size());	std::unordered_map<HeroRole, const AIPath *> closestWaysByRole;	std::vector<ExecuteHeroChain *> waysToVisitObj;	for(auto & path : paths)	{		tasks.push_back(sptr(Goals::Invalid()));#if NKAI_TRACE_LEVEL >= 2		logAi->trace("Path found %s", path.toString());#endif		if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))		{#if NKAI_TRACE_LEVEL >= 2			logAi->trace("Ignore path. Target hero can be killed by enemy. Our power %lld", path.getHeroStrength());#endif			continue;		}		if(objToVisit && !shouldVisit(ai->nullkiller.get(), path.targetHero, objToVisit))		{#if NKAI_TRACE_LEVEL >= 2			logAi->trace("Ignore path. Hero %s should not visit obj %s", path.targetHero->getNameTranslated(), objToVisit->getObjectName());#endif			continue;		}		auto hero = path.targetHero;		auto danger = path.getTotalDanger();		if(ai->nullkiller->heroManager->getHeroRole(hero) == HeroRole::SCOUT			&& (path.getTotalDanger() == 0 || path.turn() > 0)			&& path.exchangeCount > 1)		{#if NKAI_TRACE_LEVEL >= 2			logAi->trace("Ignore path. Hero %s is SCOUT, chain used and no danger", path.targetHero->getNameTranslated());#endif			continue;		}		auto firstBlockedAction = path.getFirstBlockedAction();		if(firstBlockedAction)		{			auto subGoal = firstBlockedAction->decompose(path.targetHero);#if NKAI_TRACE_LEVEL >= 2			logAi->trace("Decomposing special action %s returns %s", firstBlockedAction->toString(), subGoal->toString());#endif			if(!subGoal->invalid())			{				Composition composition;				composition.addNext(ExecuteHeroChain(path, objToVisit));				composition.addNext(subGoal);				tasks[tasks.size() - 1] = sptr(composition);			}			continue;		}		auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);#if NKAI_TRACE_LEVEL >= 2		logAi->trace(			"It is %s to visit %s by %s with army %lld, danger %lld and army loss %lld",			isSafe ? "safe" : "not safe",			objToVisit ? objToVisit->getObjectName() : path.targetTile().toString(),			hero->getObjectName(),			path.getHeroStrength(),			danger,			path.getTotalArmyLoss());#endif		if(isSafe)		{			auto newWay = new ExecuteHeroChain(path, objToVisit);			TSubgoal sharedPtr;			sharedPtr.reset(newWay);			auto heroRole = ai->nullkiller->heroManager->getHeroRole(path.targetHero);			auto & closestWay = closestWaysByRole[heroRole];			if(!closestWay || closestWay->movementCost() > path.movementCost())			{				closestWay = &path;			}			if(!ai->nullkiller->arePathHeroesLocked(path))			{				waysToVisitObj.push_back(newWay);				tasks[tasks.size() - 1] = sharedPtr;			}		}	}	for(auto way : waysToVisitObj)	{		auto heroRole = ai->nullkiller->heroManager->getHeroRole(way->getPath().targetHero);		auto closestWay = closestWaysByRole[heroRole];		if(closestWay)		{			way->closestWayRatio				= closestWay->movementCost() / way->getPath().movementCost();		}	}	return tasks;}Goals::TGoalVec CaptureObjectsBehavior::decompose() const{	Goals::TGoalVec tasks;	auto captureObjects = [&](const std::vector<const CGObjectInstance*> & objs) -> void	{		if(objs.empty())		{			return;		}		logAi->debug("Scanning objects, count %d", objs.size());		for(auto objToVisit : objs)		{			if(!objectMatchesFilter(objToVisit))				continue;	#if NKAI_TRACE_LEVEL >= 1			logAi->trace("Checking object %s, %s", objToVisit->getObjectName(), objToVisit->visitablePos().toString());#endif			const int3 pos = objToVisit->visitablePos();			bool useObjectGraph = ai->nullkiller->settings->isObjectGraphAllowed()				&& ai->nullkiller->getScanDepth() != ScanDepth::SMALL;			auto paths = ai->nullkiller->pathfinder->getPathInfo(pos, useObjectGraph);			std::vector<std::shared_ptr<ExecuteHeroChain>> waysToVisitObj;			std::shared_ptr<ExecuteHeroChain> closestWay;					#if NKAI_TRACE_LEVEL >= 1			logAi->trace("Found %d paths", paths.size());#endif			vstd::concatenate(tasks, getVisitGoals(paths, objToVisit));		}		vstd::erase_if(tasks, [](TSubgoal task) -> bool		{			return task->invalid();		});	};	if(specificObjects)	{		captureObjects(objectsToCapture);	}	else if(objectTypes.size())	{		captureObjects(			std::vector<const CGObjectInstance *>(				ai->nullkiller->memory->visitableObjs.begin(),				ai->nullkiller->memory->visitableObjs.end()));	}	else	{		captureObjects(ai->nullkiller->objectClusterizer->getNearbyObjects());		if(tasks.empty() || ai->nullkiller->getScanDepth() != ScanDepth::SMALL)			captureObjects(ai->nullkiller->objectClusterizer->getFarObjects());	}	return tasks;}bool CaptureObjectsBehavior::objectMatchesFilter(const CGObjectInstance * obj) const{	if(objectTypes.size() && !vstd::contains(objectTypes, obj->ID.num))	{		return false;	}	if(objectSubTypes.size() && !vstd::contains(objectSubTypes, obj->subID))	{		return false;	}	return true;}}
 |