diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index ed23f4bc0..64d717f94 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -1216,7 +1216,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(); @@ -1278,7 +1278,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(); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 2cfb7cd10..38a6f4b8b 100644 --- a/AI/VCAI/VCAI.cpp +++ b/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(); diff --git a/CCallback.cpp b/CCallback.cpp index b892bb8ab..d78c0a660 100644 --- a/CCallback.cpp +++ b/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 & path, bool transit) +{ + MoveHero pack(path, h->id, transit); sendRequest(&pack); - return true; } int CCallback::selectionMade(int selection, QueryID queryID) diff --git a/CCallback.h b/CCallback.h index 091371e3f..9c559e768 100644 --- a/CCallback.h +++ b/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 & 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 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 & 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 reply, QueryID queryID) override; diff --git a/client/HeroMovementController.cpp b/client/HeroMovementController.cpp index e5bf1a9c8..6c70a425a 100644 --- a/client/HeroMovementController.cpp +++ b/client/HeroMovementController.cpp @@ -355,22 +355,38 @@ void HeroMovementController::requestMovementStart(const CGHeroInstance * h, cons void HeroMovementController::moveInstant(const CGHeroInstance * h, const CGPath & path) { - stopMovementSound(); + bool useTransit = path.nextNode().layer == EPathfindingLayer::AIR || path.nextNode().layer == EPathfindingLayer::WATER; + std::vector 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()) - return; // pause after monolith / subterra gates + break; // pause after monolith / subterra gates if (node.turns != 0) - return; // ran out of MP + break; // ran out of move points + + bool useTransitHere = node.layer == EPathfindingLayer::AIR || node.layer == EPathfindingLayer::WATER; + if (useTransitHere != useTransit) + break; int3 coord = h->convertFromVisitablePos(node.coord); + pathToMove.push_back(coord); - bool useTransit = node.layer == EPathfindingLayer::AIR || node.layer == EPathfindingLayer::WATER; - LOCPLINT->cb->moveHero(h, coord, useTransit); + 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 + } + + if (!pathToMove.empty()) + { + //updateMovementSound(h, path.currNode().coord, path.nextNode().coord, path.nextNode().action); + LOCPLINT->cb->moveHero(h, pathToMove, useTransit); } } diff --git a/client/adventureMap/AdventureMapShortcuts.cpp b/client/adventureMap/AdventureMapShortcuts.cpp index cda82c8cc..847666223 100644 --- a/client/adventureMap/AdventureMapShortcuts.cpp +++ b/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() diff --git a/lib/networkPacks/PacksForServer.h b/lib/networkPacks/PacksForServer.h index 0c86a9769..19f0b72fd 100644 --- a/lib/networkPacks/PacksForServer.h +++ b/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 & path, const ObjectInstanceID & HID, bool Transit) + : path(path) , hid(HID) , transit(Transit) { } - int3 dest; + std::vector path; ObjectInstanceID hid; bool transit = false; void visitTyped(ICPackVisitor & visitor) override; - template void serialize(Handler & h) + template + void serialize(Handler & h) { h & static_cast(*this); - h & dest; + h & path; h & hid; h & transit; } diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index aabe9e0bf..9e9d7e7f5 100644 --- a/server/NetPacksServer.cpp +++ b/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)