Pārlūkot izejas kodu

CPathfinder: improve support for visits and battles in teleports

Related movement code for client and AI is plumbed as well. Though at moment code still not finished because it's not teleport hero to the exit tile if he won battle.
ArseniyShestakov 10 gadi atpakaļ
vecāks
revīzija
ab92123da3

+ 33 - 6
AI/VCAI/VCAI.cpp

@@ -1879,6 +1879,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
             logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
             logAi->errorStream() << "Hero " << h->name << " cannot reach " << dst;
 			throw goalFulfilledException (sptr(Goals::VisitTile(dst).sethero(h)));
 			throw goalFulfilledException (sptr(Goals::VisitTile(dst).sethero(h)));
 		}
 		}
+		int i = path.nodes.size()-1;
 
 
 		auto getObj = [&](int3 coord, bool ignoreHero)
 		auto getObj = [&](int3 coord, bool ignoreHero)
 		{
 		{
@@ -1888,6 +1889,31 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			//return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
 			//return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
 		};
 		};
 
 
+		auto isTeleportAction = [&](CGPathNode::ENodeAction action) -> bool
+		{
+			if(action != CGPathNode::TELEPORT_NORMAL &&
+				action != CGPathNode::TELEPORT_BLOCKING_VISIT &&
+				action != CGPathNode::TELEPORT_BATTLE)
+			{
+				return false;
+			}
+
+			return true;
+		};
+
+		auto getDestTeleportObj = [&](const CGObjectInstance * currentObject, const CGObjectInstance * nextObjectTop, const CGObjectInstance * nextObject) -> const CGObjectInstance *
+		{
+			if(CGTeleport::isConnected(currentObject, nextObjectTop))
+				return nextObjectTop;
+			if(nextObjectTop && nextObjectTop->ID == Obj::HERO &&
+				CGTeleport::isConnected(currentObject, nextObject))
+			{
+				return nextObject;
+			}
+
+			return nullptr;
+		};
+
 		auto doMovement = [&](int3 dst, bool transit)
 		auto doMovement = [&](int3 dst, bool transit)
 		{
 		{
 			cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit);
 			cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit);
@@ -1918,17 +1944,18 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			doTeleportMovement(currentExit, currentPos);
 			doTeleportMovement(currentExit, currentPos);
 		};
 		};
 
 
-		int i=path.nodes.size()-1;
 		for(; i>0; i--)
 		for(; i>0; i--)
 		{
 		{
 			int3 currentCoord = path.nodes[i].coord;
 			int3 currentCoord = path.nodes[i].coord;
 			int3 nextCoord = path.nodes[i-1].coord;
 			int3 nextCoord = path.nodes[i-1].coord;
 
 
 			auto currentObject = getObj(currentCoord, currentCoord == CGHeroInstance::convertPosition(h->pos,false));
 			auto currentObject = getObj(currentCoord, currentCoord == CGHeroInstance::convertPosition(h->pos,false));
-			auto nextObject = getObj(nextCoord, false);
-			if(CGTeleport::isConnected(currentObject, nextObject))
+			auto nextObjectTop = getObj(nextCoord, false);
+			auto nextObject = getObj(nextCoord, true);
+			auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
+			if(isTeleportAction(path.nodes[i-1].action) && destTeleportObj != nullptr)
 			{ //we use special login if hero standing on teleporter it's mean we need
 			{ //we use special login if hero standing on teleporter it's mean we need
-				doTeleportMovement(nextObject->id, nextCoord);
+				doTeleportMovement(destTeleportObj->id, nextCoord);
 				if(teleportChannelProbingList.size())
 				if(teleportChannelProbingList.size())
 					doChannelProbing();
 					doChannelProbing();
 
 
@@ -1947,8 +1974,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 				continue;
 				continue;
 
 
 			if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
 			if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
-				&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord, false))
-					|| CGTeleport::isTeleport(nextObject)))
+				&& (CGTeleport::isConnected(nextObjectTop, getObj(path.nodes[i-2].coord, false))
+					|| CGTeleport::isTeleport(nextObjectTop)))
 			{ // Hero should be able to go through object if it's allow transit
 			{ // Hero should be able to go through object if it's allow transit
 				doMovement(endpos, true);
 				doMovement(endpos, true);
 			}
 			}

+ 38 - 5
client/CPlayerInterface.cpp

@@ -2629,6 +2629,31 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 		return cb->getTile(CGHeroInstance::convertPosition(coord,false))->topVisitableObj(ignoreHero);
 		return cb->getTile(CGHeroInstance::convertPosition(coord,false))->topVisitableObj(ignoreHero);
 	};
 	};
 
 
+	auto isTeleportAction = [&](CGPathNode::ENodeAction action) -> bool
+	{
+		if(action != CGPathNode::TELEPORT_NORMAL &&
+			action != CGPathNode::TELEPORT_BLOCKING_VISIT &&
+			action != CGPathNode::TELEPORT_BATTLE)
+		{
+			return false;
+		}
+
+		return true;
+	};
+
+	auto getDestTeleportObj = [&](const CGObjectInstance * currentObject, const CGObjectInstance * nextObjectTop, const CGObjectInstance * nextObject) -> const CGObjectInstance *
+	{
+		if(CGTeleport::isConnected(currentObject, nextObjectTop))
+			return nextObjectTop;
+		if(nextObjectTop && nextObjectTop->ID == Obj::HERO &&
+			CGTeleport::isConnected(currentObject, nextObject))
+		{
+			return nextObject;
+		}
+
+		return nullptr;
+	};
+
 	boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
 	boost::unique_lock<boost::mutex> un(stillMoveHero.mx);
 	stillMoveHero.data = CONTINUE_MOVE;
 	stillMoveHero.data = CONTINUE_MOVE;
 	auto doMovement = [&](int3 dst, bool transit)
 	auto doMovement = [&](int3 dst, bool transit)
@@ -2661,13 +2686,21 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 			int3 currentCoord = path.nodes[i].coord;
 			int3 currentCoord = path.nodes[i].coord;
 			int3 nextCoord = path.nodes[i-1].coord;
 			int3 nextCoord = path.nodes[i-1].coord;
 
 
-			auto nextObject = getObj(nextCoord, nextCoord == h->pos);
-			if(CGTeleport::isConnected(getObj(currentCoord, currentCoord == h->pos), nextObject))
+			auto currentObject = getObj(currentCoord, currentCoord == h->pos);
+			auto nextObjectTop = getObj(nextCoord, false);
+			auto nextObject = getObj(nextCoord, true);
+			auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
+			if(isTeleportAction(path.nodes[i-1].action) && destTeleportObj != nullptr)
 			{
 			{
 				CCS->soundh->stopSound(sh);
 				CCS->soundh->stopSound(sh);
-				destinationTeleport = nextObject->id;
+				destinationTeleport = destTeleportObj->id;
 				destinationTeleportPos = nextCoord;
 				destinationTeleportPos = nextCoord;
 				doMovement(h->pos, false);
 				doMovement(h->pos, false);
+				if(path.nodes[i-1].action == CGPathNode::TELEPORT_BLOCKING_VISIT)
+				{
+					destinationTeleport = ObjectInstanceID();
+					destinationTeleportPos = int3(-1);
+				}
 				sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
 				sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
 				continue;
 				continue;
 			}
 			}
@@ -2700,8 +2733,8 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 
 
 			bool useTransit = false;
 			bool useTransit = false;
 			if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
 			if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
-				&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord, false))
-					|| CGTeleport::isTeleport(nextObject)))
+				&& (CGTeleport::isConnected(nextObjectTop, getObj(path.nodes[i-2].coord, false))
+					|| CGTeleport::isTeleport(nextObjectTop)))
 			{ // Hero should be able to go through object if it's allow transit
 			{ // Hero should be able to go through object if it's allow transit
 				useTransit = true;
 				useTransit = true;
 			}
 			}

+ 3 - 0
client/windows/CAdvmapInterface.cpp

@@ -1544,6 +1544,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 		switch(pnode->action)
 		switch(pnode->action)
 		{
 		{
 		case CGPathNode::NORMAL:
 		case CGPathNode::NORMAL:
+		case CGPathNode::TELEPORT_NORMAL:
 			if(pnode->layer == EPathfindingLayer::LAND)
 			if(pnode->layer == EPathfindingLayer::LAND)
 				CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns*6);
 				CCS->curh->changeGraphic(ECursor::ADVENTURE, 4 + turns*6);
 			else
 			else
@@ -1552,6 +1553,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 
 
 		case CGPathNode::VISIT:
 		case CGPathNode::VISIT:
 		case CGPathNode::BLOCKING_VISIT:
 		case CGPathNode::BLOCKING_VISIT:
+		case CGPathNode::TELEPORT_BLOCKING_VISIT:
 			if(objAtTile && objAtTile->ID == Obj::HERO)
 			if(objAtTile && objAtTile->ID == Obj::HERO)
 			{
 			{
 				if(selection == objAtTile)
 				if(selection == objAtTile)
@@ -1566,6 +1568,7 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
 			break;
 			break;
 
 
 		case CGPathNode::BATTLE:
 		case CGPathNode::BATTLE:
+		case CGPathNode::TELEPORT_BATTLE:
 			CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
 			CCS->curh->changeGraphic(ECursor::ADVENTURE, 5 + turns*6);
 			break;
 			break;
 
 

+ 19 - 8
lib/CPathfinder.cpp

@@ -199,18 +199,14 @@ void CPathfinder::calculatePaths()
 
 
 			if(isBetterWay(movement, turn))
 			if(isBetterWay(movement, turn))
 			{
 			{
+				dtObj = gs->map->getTile(neighbour).topVisitableObj();
+
 				dp->moveRemains = movement;
 				dp->moveRemains = movement;
 				dp->turns = turn;
 				dp->turns = turn;
 				dp->theNodeBefore = cp;
 				dp->theNodeBefore = cp;
-
-				dtObj = gs->map->getTile(neighbour).topVisitableObj();
-				if(CGTeleport::isTeleport(dtObj))
-				{
-					dp->action = CGPathNode::NORMAL;
+				dp->action = getTeleportDestAction();
+				if(dp->action == CGPathNode::TELEPORT_NORMAL)
 					pq.push(dp);
 					pq.push(dp);
-				}
-				else
-					dp->action = getDestAction(); // TODO: We only need to check for hero on other side, but not for guards.
 			}
 			}
 		}
 		}
 	} //queue loop
 	} //queue loop
@@ -555,6 +551,21 @@ CGPathNode::ENodeAction CPathfinder::getDestAction() const
 	return action;
 	return action;
 }
 }
 
 
+CGPathNode::ENodeAction CPathfinder::getTeleportDestAction() const
+{
+	CGPathNode::ENodeAction action = CGPathNode::TELEPORT_NORMAL;
+	if(isDestVisitableObj() && dtObj->ID == Obj::HERO)
+	{
+		auto objRel = getPlayerRelations(dtObj->tempOwner, hero->tempOwner);
+		if(objRel == PlayerRelations::ENEMIES)
+			action = CGPathNode::TELEPORT_BATTLE;
+		else
+			action = CGPathNode::TELEPORT_BLOCKING_VISIT;
+	}
+
+	return action;
+}
+
 bool CPathfinder::isSourceInitialPosition() const
 bool CPathfinder::isSourceInitialPosition() const
 {
 {
 	return cp->coord == out.hpos;
 	return cp->coord == out.hpos;

+ 5 - 1
lib/CPathfinder.h

@@ -36,7 +36,10 @@ struct DLL_LINKAGE CGPathNode
 		NORMAL,
 		NORMAL,
 		BATTLE,
 		BATTLE,
 		VISIT,
 		VISIT,
-		BLOCKING_VISIT
+		BLOCKING_VISIT,
+		TELEPORT_NORMAL,
+		TELEPORT_BLOCKING_VISIT,
+		TELEPORT_BATTLE
 	};
 	};
 
 
 	enum EAccessibility : ui8
 	enum EAccessibility : ui8
@@ -202,6 +205,7 @@ private:
 	bool isMovementToDestPossible() const;
 	bool isMovementToDestPossible() const;
 	bool isMovementAfterDestPossible() const;
 	bool isMovementAfterDestPossible() const;
 	CGPathNode::ENodeAction getDestAction() const;
 	CGPathNode::ENodeAction getDestAction() const;
+	CGPathNode::ENodeAction getTeleportDestAction() const;
 
 
 	bool isSourceInitialPosition() const;
 	bool isSourceInitialPosition() const;
 	bool isSourceVisitableObj() const;
 	bool isSourceVisitableObj() const;