Sfoglia il codice sorgente

Merge pull request #3786 from IvanSavenko/instant_movement

Instant movement
Ivan Savenko 1 anno fa
parent
commit
21dff9413e

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -1230,7 +1230,7 @@ bool AIGateway::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, h->convertFromVisitablePos(dst));
+		cb->moveHero(*h, h->convertFromVisitablePos(dst), false);
 		afterMovementCheck(); // TODO: is it feasible to hero get killed there if game work properly?
 		// If revisiting, teleport probing is never done, and so the entries into the list would remain unused and uncleared
 		teleportChannelProbingList.clear();
@@ -1292,7 +1292,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 			destinationTeleport = exitId;
 			if(exitPos.valid())
 				destinationTeleportPos = h->convertFromVisitablePos(exitPos);
-			cb->moveHero(*h, h->pos);
+			cb->moveHero(*h, h->pos, false);
 			destinationTeleport = ObjectInstanceID();
 			destinationTeleportPos = int3(-1);
 			afterMovementCheck();

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -1837,7 +1837,7 @@ 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, h->convertFromVisitablePos(dst));
+		cb->moveHero(*h, h->convertFromVisitablePos(dst), false);
 		afterMovementCheck(); // TODO: is it feasible to hero get killed there if game work properly?
 		// If revisiting, teleport probing is never done, and so the entries into the list would remain unused and uncleared
 		teleportChannelProbingList.clear();
@@ -1899,7 +1899,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 			destinationTeleport = exitId;
 			if(exitPos.valid())
 				destinationTeleportPos = h->convertFromVisitablePos(exitPos);
-			cb->moveHero(*h, h->pos);
+			cb->moveHero(*h, h->pos, false);
 			destinationTeleport = ObjectInstanceID();
 			destinationTeleportPos = int3(-1);
 			afterMovementCheck();

+ 8 - 3
CCallback.cpp

@@ -34,11 +34,16 @@ bool CCallback::teleportHero(const CGHeroInstance *who, const CGTownInstance *wh
 	return true;
 }
 
-bool CCallback::moveHero(const CGHeroInstance *h, int3 dst, bool transit)
+void CCallback::moveHero(const CGHeroInstance *h, const int3 & destination, bool transit)
 {
-	MoveHero pack(dst,h->id,transit);
+	MoveHero pack({destination}, h->id, transit);
+	sendRequest(&pack);
+}
+
+void CCallback::moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit)
+{
+	MoveHero pack(path, h->id, transit);
 	sendRequest(&pack);
-	return true;
 }
 
 int CCallback::selectionMade(int selection, QueryID queryID)

+ 4 - 2
CCallback.h

@@ -67,7 +67,8 @@ class IGameActionCallback
 {
 public:
 	//hero
-	virtual bool moveHero(const CGHeroInstance *h, int3 dst, bool transit) =0; //dst must be free, neighbouring tile (this function can move hero only by one tile)
+	virtual void moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit) =0; //moves hero alongside provided path
+	virtual void moveHero(const CGHeroInstance *h, const int3 & destination, bool transit) =0; //moves hero alongside provided path
 	virtual bool dismissHero(const CGHeroInstance * hero)=0; //dismisses given hero; true - successfuly, false - not successfuly
 	virtual void dig(const CGObjectInstance *hero)=0;
 	virtual void castSpell(const CGHeroInstance *hero, SpellID spellID, const int3 &pos = int3(-1, -1, -1))=0; //cast adventure map spell
@@ -159,7 +160,8 @@ public:
 	void unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver> battleEvents);
 
 //commands
-	bool moveHero(const CGHeroInstance *h, int3 dst, bool transit = false) override; //dst must be free, neighbouring tile (this function can move hero only by one tile)
+	void moveHero(const CGHeroInstance *h, const std::vector<int3> & path, bool transit) override;
+	void moveHero(const CGHeroInstance *h, const int3 & destination, bool transit) override;
 	bool teleportHero(const CGHeroInstance *who, const CGTownInstance *where);
 	int selectionMade(int selection, QueryID queryID) override;
 	int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) override;

+ 4 - 4
client/Client.cpp

@@ -518,15 +518,15 @@ void CClient::handlePack(CPack * pack)
 	if(apply)
 	{
 		apply->applyOnClBefore(this, pack);
-		logNetwork->trace("\tMade first apply on cl: %s", typeid(pack).name());
+		logNetwork->trace("\tMade first apply on cl: %s", typeid(*pack).name());
 		gs->apply(pack);
-		logNetwork->trace("\tApplied on gs: %s", typeid(pack).name());
+		logNetwork->trace("\tApplied on gs: %s", typeid(*pack).name());
 		apply->applyOnClAfter(this, pack);
-		logNetwork->trace("\tMade second apply on cl: %s", typeid(pack).name());
+		logNetwork->trace("\tMade second apply on cl: %s", typeid(*pack).name());
 	}
 	else
 	{
-		logNetwork->error("Message %s cannot be applied, cannot find applier!", typeid(pack).name());
+		logNetwork->error("Message %s cannot be applied, cannot find applier!", typeid(*pack).name());
 	}
 	delete pack;
 }

+ 46 - 9
client/HeroMovementController.cpp

@@ -244,7 +244,7 @@ void HeroMovementController::onMoveHeroApplied()
 	}
 	else
 	{
-		moveOnce(hero, LOCPLINT->localState->getPath(hero));
+		sendMovementRequest(hero, LOCPLINT->localState->getPath(hero));
 	}
 }
 
@@ -335,28 +335,27 @@ bool HeroMovementController::canHeroStopAtNode(const CGPathNode & node) const
 void HeroMovementController::requestMovementStart(const CGHeroInstance * h, const CGPath & path)
 {
 	assert(duringMovement == false);
+
 	duringMovement = true;
 	currentlyMovingHero = h;
 
 	CCS->curh->hide();
-	moveOnce(h, path);
+	sendMovementRequest(h, path);
 }
 
-void HeroMovementController::moveOnce(const CGHeroInstance * h, const CGPath & path)
+void HeroMovementController::sendMovementRequest(const CGHeroInstance * h, const CGPath & path)
 {
-	// Moves hero once, sends request to server and immediately returns
-	// movement alongside paths will be done on receiving response from server
-
 	assert(duringMovement == true);
 
+	int heroMovementSpeed = settings["adventure"]["heroMoveTime"].Integer();
+	bool useMovementBatching = heroMovementSpeed == 0;
+
 	const auto & currNode = path.currNode();
 	const auto & nextNode = path.nextNode();
 
 	assert(nextNode.turns == 0);
 	assert(currNode.coord == h->visitablePos());
 
-	int3 nextCoord = h->convertFromVisitablePos(nextNode.coord);
-
 	if(nextNode.isTeleportAction())
 	{
 		stopMovementSound();
@@ -364,7 +363,8 @@ void HeroMovementController::moveOnce(const CGHeroInstance * h, const CGPath & p
 		LOCPLINT->cb->moveHero(h, h->pos, false);
 		return;
 	}
-	else
+
+	if (!useMovementBatching)
 	{
 		updateMovementSound(h, currNode.coord, nextNode.coord, nextNode.action);
 
@@ -373,7 +373,44 @@ void HeroMovementController::moveOnce(const CGHeroInstance * h, const CGPath & p
 		logGlobal->trace("Requesting hero movement to %s", nextNode.coord.toString());
 
 		bool useTransit = nextNode.layer == EPathfindingLayer::AIR || nextNode.layer == EPathfindingLayer::WATER;
+		int3 nextCoord = h->convertFromVisitablePos(nextNode.coord);
+
 		LOCPLINT->cb->moveHero(h, nextCoord, useTransit);
 		return;
 	}
+
+	bool useTransitAtStart = path.nextNode().layer == EPathfindingLayer::AIR || path.nextNode().layer == EPathfindingLayer::WATER;
+	std::vector<int3> pathToMove;
+
+	for (auto const & node : boost::adaptors::reverse(path.nodes))
+	{
+		if (node.coord == h->visitablePos())
+			continue; // first node, ignore - this is hero current position
+
+		if(node.isTeleportAction())
+			break; // pause after monolith / subterra gates
+
+		if (node.turns != 0)
+			break; // ran out of move points
+
+		bool useTransitHere = node.layer == EPathfindingLayer::AIR || node.layer == EPathfindingLayer::WATER;
+		if (useTransitHere != useTransitAtStart)
+			break;
+
+		int3 coord = h->convertFromVisitablePos(node.coord);
+		pathToMove.push_back(coord);
+
+		if (LOCPLINT->cb->guardingCreaturePosition(node.coord) != int3(-1, -1, -1))
+			break; // we reached zone-of-control of wandering monster
+
+		if (!LOCPLINT->cb->getVisitableObjs(node.coord).empty())
+			break; // we reached event, garrison or some other visitable object - end this movement batch
+	}
+
+	assert(!pathToMove.empty());
+	if (!pathToMove.empty())
+	{
+		updateMovementSound(h, currNode.coord, nextNode.coord, nextNode.action);
+		LOCPLINT->cb->moveHero(h, pathToMove, useTransitAtStart);
+	}
 }

+ 3 - 2
client/HeroMovementController.h

@@ -42,8 +42,9 @@ class HeroMovementController
 
 	void updatePath(const CGHeroInstance * hero, const TryMoveHero & details);
 
-	/// Moves hero 1 tile / path node
-	void moveOnce(const CGHeroInstance * h, const CGPath & path);
+	/// Sends one request to server to move selected hero alongside path.
+	/// Automatically selects between single-tile and multi-tile movement modes
+	void sendMovementRequest(const CGHeroInstance * h, const CGPath & path);
 
 	void endMove(const CGHeroInstance * h);
 

+ 1 - 1
client/adventureMap/AdventureMapShortcuts.cpp

@@ -314,7 +314,7 @@ void AdventureMapShortcuts::visitObject()
 	const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero();
 
 	if(h)
-		LOCPLINT->cb->moveHero(h, h->pos);
+		LOCPLINT->cb->moveHero(h, h->pos, false);
 }
 
 void AdventureMapShortcuts::openObject()

+ 2 - 1
client/widgets/Images.cpp

@@ -19,6 +19,7 @@
 #include "../render/CAnimation.h"
 #include "../render/Canvas.h"
 #include "../render/ColorFilter.h"
+#include "../render/Graphics.h"
 
 #include "../battle/BattleInterface.h"
 #include "../battle/BattleInterfaceClasses.h"
@@ -177,7 +178,7 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i
 {
 	pos.x += x;
 	pos.y += y;
-	anim = GH.renderHandler().loadAnimation(name);
+	anim = graphics->getAnimation(name);
 	init();
 }
 

+ 6 - 5
lib/networkPacks/PacksForServer.h

@@ -59,22 +59,23 @@ struct DLL_LINKAGE DismissHero : public CPackForServer
 struct DLL_LINKAGE MoveHero : public CPackForServer
 {
 	MoveHero() = default;
-	MoveHero(const int3 & Dest, const ObjectInstanceID & HID, bool Transit)
-		: dest(Dest)
+	MoveHero(const std::vector<int3> & path, const ObjectInstanceID & HID, bool Transit)
+		: path(path)
 		, hid(HID)
 		, transit(Transit)
 	{
 	}
-	int3 dest;
+	std::vector<int3> path;
 	ObjectInstanceID hid;
 	bool transit = false;
 
 	void visitTyped(ICPackVisitor & visitor) override;
 
-	template <typename Handler> void serialize(Handler & h)
+	template<typename Handler>
+	void serialize(Handler & h)
 	{
 		h & static_cast<CPackForServer &>(*this);
-		h & dest;
+		h & path;
 		h & hid;
 		h & transit;
 	}

+ 11 - 1
server/NetPacksServer.cpp

@@ -59,7 +59,17 @@ void ApplyGhNetPackVisitor::visitDismissHero(DismissHero & pack)
 void ApplyGhNetPackVisitor::visitMoveHero(MoveHero & pack)
 {
 	gh.throwIfWrongOwner(&pack, pack.hid);
-	result = gh.moveHero(pack.hid, pack.dest, 0, pack.transit, pack.player);
+
+	for (auto const & dest : pack.path)
+	{
+		if (!gh.moveHero(pack.hid, dest, 0, pack.transit, pack.player))
+		{
+			result = false;
+			return;
+		}
+	}
+
+	result = true;
 }
 
 void ApplyGhNetPackVisitor::visitCastleTeleportHero(CastleTeleportHero & pack)