Procházet zdrojové kódy

VCAI: add all new movement code include teleports and transit support

ArseniyShestakov před 10 roky
rodič
revize
12cf883740
2 změnil soubory, kde provedl 116 přidání a 18 odebrání
  1. 114 18
      AI/VCAI/VCAI.cpp
  2. 2 0
      AI/VCAI/VCAI.h

+ 114 - 18
AI/VCAI/VCAI.cpp

@@ -95,6 +95,7 @@ VCAI::VCAI(void)
 {
 	LOG_TRACE(logAi);
 	makingTurn = nullptr;
+	destinationTeleport = ObjectInstanceID();
 }
 
 VCAI::~VCAI(void)
@@ -122,11 +123,19 @@ void VCAI::heroMoved(const TryMoveHero & details)
 		const CGObjectInstance *o1 = frontOrNull(cb->getVisitableObjs(from)),
 			*o2 = frontOrNull(cb->getVisitableObjs(to));
 
-		if(o1 && o2 && o1->ID == Obj::SUBTERRANEAN_GATE && o2->ID == Obj::SUBTERRANEAN_GATE)
+		auto t1 = dynamic_cast<const CGTeleport *>(o1);
+		auto t2 = dynamic_cast<const CGTeleport *>(o2);
+		if(t1 && t2)
 		{
-			knownSubterraneanGates[o1] = o2;
-			knownSubterraneanGates[o2] = o1;
-            logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
+			if(ETeleportChannelType::BIDIRECTIONAL == cb->getTeleportChannelType(t1->channel))
+			{
+				if(o1->ID == Obj::SUBTERRANEAN_GATE)
+				{
+					knownSubterraneanGates[o1] = o2;
+					knownSubterraneanGates[o2] = o1;
+					logAi->debugStream() << boost::format("Found a pair of subterranean gates between %s and %s!") % from % to;
+				}
+			}
 		}
 	}
 }
@@ -590,6 +599,37 @@ void VCAI::showBlockingDialog(const std::string &text, const std::vector<Compone
 	});
 }
 
+void VCAI::showTeleportDialog(TeleportChannelID channel, std::vector<ObjectInstanceID> exits, bool impassable, QueryID askID)
+{
+	LOG_TRACE_PARAMS(logAi, "askID '%i', exits '%s'", askID % exits);
+	NET_EVENT_HANDLER;
+	status.addQuery(askID, boost::str(boost::format("Teleport dialog query with %d exits")
+																			% exits.size()));
+
+	ObjectInstanceID choosenExit;
+	if(impassable)
+		knownTeleportChannels[channel]->passability = TeleportChannel::IMPASSABLE;
+	else
+	{
+		if(destinationTeleport != ObjectInstanceID() && vstd::contains(exits, destinationTeleport))
+			choosenExit = destinationTeleport;
+
+		if(!status.channelProbing())
+		{
+			vstd::erase_if(exits, [&](ObjectInstanceID id) -> bool
+			{
+				return vstd::contains(visitableObjs, cb->getObj(id)) || id == choosenExit;
+			});
+			teleportChannelProbingList = exits;
+		}
+	}
+
+	requestActionASAP([=]()
+	{
+		answerQuery(askID, choosenExit.getNum());
+	});
+}
+
 void VCAI::showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID)
 {
 	LOG_TRACE_PARAMS(logAi, "removableUnits '%i', queryID '%i'", removableUnits % queryID);
@@ -1649,6 +1689,18 @@ bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies /
 
 bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 {
+	auto afterMovementCheck = [&]() -> void
+	{
+		waitTillFree(); //movement may cause battle or blocking dialog
+		if(!h)
+		{
+			lostHero(h);
+			if (status.channelProbing()) // if hero lost during channel probing we need to switch this mode off
+				status.setChannelProbing(false);
+			throw std::runtime_error("Hero was lost!");
+		}
+	};
+
 	logAi->debugStream() << boost::format("Moving hero %s to tile %s") % h->name % dst;
 	int3 startHpos = h->visitablePos();
 	bool ret = false;
@@ -1657,12 +1709,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 		//FIXME: this assertion fails also if AI moves onto defeated guarded object
 		assert(cb->getVisitableObjs(dst).size() > 1); //there's no point in revisiting tile where there is no visitable object
 		cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
-		waitTillFree(); //movement may cause battle or blocking dialog
-		if(!h) // TODO is it feasible to hero get killed there if game work properly?
-		{ // not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
-			lostHero(h);
-			throw std::runtime_error("Hero was lost!");
-		}
+		afterMovementCheck();// TODO: is it feasible to hero get killed there if game work properly?
+							 // not sure if AI can currently reconsider to attack bank while staying on it. Check issue 2084 on mantis for more information.
 		ret = true;
 	}
 	else
@@ -1675,9 +1723,54 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			throw goalFulfilledException (sptr(Goals::VisitTile(dst).sethero(h)));
 		}
 
+		auto getObj = [&](int3 coord, bool ignoreHero = false)
+		{
+			return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
+		};
+
+		auto doMovement = [&](int3 dst, bool transit = false)
+		{
+			cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true), transit);
+		};
+
+		auto doTeleportMovement = [&](int3 dst, ObjectInstanceID exitId)
+		{
+			destinationTeleport = exitId;
+			cb->moveHero(*h, CGHeroInstance::convertPosition(dst, true));
+			destinationTeleport = ObjectInstanceID();
+			afterMovementCheck();
+		};
+
+		auto doChannelProbing = [&]() -> void
+		{
+			auto currentExit = getObj(CGHeroInstance::convertPosition(h->pos,false));
+			assert(currentExit);
+
+			status.setChannelProbing(true);
+			for(auto exit : teleportChannelProbingList)
+				doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), exit);
+			teleportChannelProbingList.clear();
+			doTeleportMovement(CGHeroInstance::convertPosition(h->pos,false), currentExit->id);
+			status.setChannelProbing(false);
+		};
+
 		int i=path.nodes.size()-1;
 		for(; i>0; i--)
 		{
+			int3 currentCoord = path.nodes[i].coord;
+			int3 nextCoord = path.nodes[i-1].coord;
+
+			auto currentObject = getObj(currentCoord, currentCoord == CGHeroInstance::convertPosition(h->pos,false));
+			auto nextObject = getObj(nextCoord);
+			if(CGTeleport::isConnected(currentObject, nextObject))
+			{ //we use special login if hero standing on teleporter it's mean we need
+				doTeleportMovement(currentCoord, nextObject->id);
+				if(teleportChannelProbingList.size())
+					doChannelProbing();
+
+				continue;
+			}
+
 			//stop sending move requests if the next node can't be reached at the current turn (hero exhausted his move points)
 			if(path.nodes[i-1].turns)
 			{
@@ -1689,16 +1782,19 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			if(endpos == h->visitablePos())
 				continue;
 
-			cb->moveHero(*h, CGHeroInstance::convertPosition(endpos, true));
-			waitTillFree(); //movement may cause battle or blocking dialog
-			boost::this_thread::interruption_point();
-			if(!h) //we lost hero - remove all tasks assigned to him/her
-			{
-				lostHero(h);
-				//we need to throw, otherwise hero will be assigned to sth again
-				throw std::runtime_error("Hero was lost!");
+			if((i-2 >= 0) // Check there is node after next one; otherwise transit is pointless
+				&& (CGTeleport::isConnected(nextObject, getObj(path.nodes[i-2].coord))
+					|| CGTeleport::isTeleport(nextObject)))
+			{ // Hero should be able to go through object if it's allow transit
+				doMovement(endpos, true);
 			}
+			else
+				doMovement(endpos);
+
+			afterMovementCheck();
 
+			if(teleportChannelProbingList.size())
+				doChannelProbing();
 		}
 		ret = !i;
 	}

+ 2 - 0
AI/VCAI/VCAI.h

@@ -143,6 +143,8 @@ public:
 
 	std::map<TeleportChannelID, shared_ptr<TeleportChannel> > knownTeleportChannels;
 	std::map<const CGObjectInstance *, const CGObjectInstance *> knownSubterraneanGates;
+	ObjectInstanceID destinationTeleport;
+	std::vector<ObjectInstanceID> teleportChannelProbingList; //list of teleport channel exits that not visible and need to be (re-)explored
 	//std::vector<const CGObjectInstance *> visitedThisWeek; //only OPWs
 	std::map<HeroPtr, std::set<const CGTownInstance *> > townVisitsThisWeek;