فهرست منبع

Nullkiller: fix a few freezes

Andrii Danylchenko 4 سال پیش
والد
کامیت
642f3a3fa4

+ 26 - 1
AI/Nullkiller/Behaviors/CaptureObjectsBehavior.cpp

@@ -28,6 +28,17 @@ std::string CaptureObjectsBehavior::toString() const
 	return "Capture objects";
 }
 
+std::shared_ptr<const ISpecialAction> getFirstBlockedAction(const AIPath & path)
+{
+	for(auto node : path.nodes)
+	{
+		if(node.specialAction && !node.specialAction->canAct(node.targetHero))
+			return node.specialAction;
+	}
+
+	return std::shared_ptr<const ISpecialAction>();
+}
+
 Goals::TGoalVec CaptureObjectsBehavior::getTasks()
 {
 	Goals::TGoalVec tasks;
@@ -65,6 +76,15 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
 				logAi->trace("Path found %s", path.toString());
 #endif
 
+				if(getFirstBlockedAction(path))
+				{
+#ifdef VCMI_TRACE_PATHFINDER
+					// TODO: decomposition?
+					logAi->trace("Ignore path. Action is blocked.");
+#endif
+					continue;
+				}
+
 				if(ai->nullkiller->dangerHitMap->enemyCanKillOurHeroesAlongThePath(path))
 				{
 #ifdef VCMI_TRACE_PATHFINDER
@@ -78,6 +98,10 @@ Goals::TGoalVec CaptureObjectsBehavior::getTasks()
 
 				auto hero = path.targetHero;
 				auto danger = path.getTotalDanger(hero);
+
+				if(danger == 0 && path.exchangeCount > 1)
+					continue;
+
 				auto isSafe = isSafeToVisit(hero, path.heroArmy, danger);
 				
 #ifdef VCMI_TRACE_PATHFINDER
@@ -146,7 +170,8 @@ bool CaptureObjectsBehavior::shouldVisitObject(ObjectIdRef obj) const
 
 	const int3 pos = objInstance->visitablePos();
 
-	if(vstd::contains(ai->alreadyVisited, objInstance))
+	if(objInstance->ID != Obj::CREATURE_GENERATOR1 && vstd::contains(ai->alreadyVisited, objInstance)
+		|| obj->wasVisited(ai->playerID))
 	{
 		return false;
 	}

+ 3 - 1
AI/Nullkiller/Behaviors/StartupBehavior.cpp

@@ -16,6 +16,7 @@
 #include "../Goals/ExecuteHeroChain.h"
 #include "../Goals/ExchangeSwapTownHeroes.h"
 #include "lib/mapping/CMap.h" //for victory conditions
+#include "lib/mapObjects/MapObjects.h" //for victory conditions
 #include "lib/CPathfinder.h"
 
 extern boost::thread_specific_ptr<CCallback> cb;
@@ -78,7 +79,8 @@ bool needToRecruitHero(const CGTownInstance * startupTown)
 			|| obj->ID == Obj::WATER_WHEEL)
 		{
 			auto path = paths->getPathInfo(obj->visitablePos());
-			if(path->accessible == CGPathNode::BLOCKVIS && path->turns != 255)
+			if((path->accessible == CGPathNode::BLOCKVIS || path->accessible == CGPathNode::VISIT) 
+				&& path->reachable())
 			{
 				return true;
 			}

+ 2 - 0
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -331,6 +331,7 @@ void AINodeStorage::calculateHeroChain(
 {	
 	if(carrier->armyLoss < carrier->actor->armyValue
 		&& (carrier->action != CGPathNode::BATTLE || (carrier->actor->allowBattle && carrier->specialAction))
+		&& carrier->action != CGPathNode::BLOCKING_VISIT
 		&& other->armyLoss < other->actor->armyValue
 		&& carrier->actor->canExchange(other->actor))
 	{
@@ -745,6 +746,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand)
 		path.armyLoss = node.armyLoss;
 		path.targetObjectDanger = evaluateDanger(pos, path.targetHero);
 		path.chainMask = node.actor->chainMask;
+		path.exchangeCount = node.actor->actorExchangeCount;
 		
 		fillChainInfo(&node, path, -1);
 

+ 2 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -10,7 +10,7 @@
 
 #pragma once
 
-#undef VCMI_TRACE_PATHFINDER
+#define VCMI_TRACE_PATHFINDER 1
 
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
@@ -50,6 +50,7 @@ struct AIPath
 	const CGHeroInstance * targetHero;
 	const CCreatureSet * heroArmy;
 	uint64_t chainMask;
+	uint8_t exchangeCount;
 
 	AIPath();
 

+ 5 - 0
AI/Nullkiller/Pathfinding/Actions/ISpecialAction.h

@@ -18,6 +18,11 @@ struct AIPathNode;
 class ISpecialAction
 {
 public:
+	virtual bool canAct(const CGHeroInstance * hero) const
+	{
+		return true;
+	}
+
 	virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const = 0;
 
 	virtual void applyOnDestination(

+ 42 - 0
AI/Nullkiller/Pathfinding/Rules/AIMovementAfterDestinationRule.cpp

@@ -10,9 +10,28 @@
 #include "StdInc.h"
 #include "AIMovementAfterDestinationRule.h"
 #include "../Actions/BattleAction.h"
+#include "../../Goals/Invalid.h"
 
 namespace AIPathfinding
 {
+	class QuestAction : public ISpecialAction
+	{
+	public:
+		QuestAction(QuestInfo questInfo)
+		{
+		}
+
+		virtual bool canAct(const CGHeroInstance * hero) const override
+		{
+			return false;
+		}
+
+		virtual Goals::TSubgoal whatToDo(const HeroPtr & hero) const override
+		{
+			return Goals::sptr(Goals::Invalid());
+		}
+	};
+
 	AIMovementAfterDestinationRule::AIMovementAfterDestinationRule(
 		CPlayerSpecificInfoCallback * cb, 
 		std::shared_ptr<AINodeStorage> nodeStorage)
@@ -50,6 +69,23 @@ namespace AIPathfinding
 				destination.blocked = true;
 			}
 
+			if(destination.nodeObject->ID == Obj::QUEST_GUARD || destination.nodeObject->ID == Obj::BORDERGUARD)
+			{
+				auto questObj = dynamic_cast<const IQuestObject *>(destination.nodeObject);
+				auto nodeHero = pathfinderHelper->hero;
+				
+				if(!destination.nodeObject->wasVisited(nodeHero->tempOwner)
+					|| !questObj->checkQuest(nodeHero))
+				{
+					nodeStorage->updateAINode(destination.node, [&](AIPathNode * node)
+					{
+						auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord);
+
+						node->specialAction.reset(new QuestAction(questInfo));
+					});
+				}
+			}
+
 			return;
 		}
 
@@ -141,6 +177,12 @@ namespace AIPathfinding
 				vstd::amax(battleNode->danger, danger);
 
 				battleNode->specialAction = std::make_shared<BattleAction>(destination.coord);
+
+				if(source.nodeObject && isObjectRemovable(source.nodeObject))
+				{
+					battleNode->theNodeBefore = source.node;
+				}
+
 #ifdef VCMI_TRACE_PATHFINDER
 				logAi->trace(
 					"Begin bypass guard at destination with danger %s while moving %s -> %s",

+ 11 - 5
AI/Nullkiller/VCAI.cpp

@@ -603,7 +603,7 @@ void VCAI::init(std::shared_ptr<CCallback> CB)
 	if(!fh)
 		fh = new FuzzyHelper();
 
-	if(playerID.getStr(false) == "blue")
+	if(playerID.getStr(false) == "red")
 	{
 		nullkiller.reset(new Nullkiller());
 	}
@@ -2823,17 +2823,23 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
 	{
 		if(obj->tempOwner != h->tempOwner)
 			return true; //flag just in case
-		bool canRecruitCreatures = false;
+
 		const CGDwelling * d = dynamic_cast<const CGDwelling *>(obj);
+
 		for(auto level : d->creatures)
 		{
 			for(auto c : level.second)
 			{
-				if(h->getSlotFor(CreatureID(c)) != SlotID())
-					canRecruitCreatures = true;
+				if(level.first
+					&& h->getSlotFor(CreatureID(c)) != SlotID()
+					&& cb->getResourceAmount().canAfford(c.toCreature()->cost))
+				{
+					return true;
+				}
 			}
 		}
-		return canRecruitCreatures;
+
+		return false;
 	}
 	case Obj::HILL_FORT:
 	{