diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 0e8d6672f..4e7d07bf5 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -1296,7 +1296,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h) else { CGPath path; - cb->getPathsInfo(h.get())->getPath(path, dst); + nullkiller->getPathsInfo(h.get())->getPath(path, dst); if(path.nodes.empty()) { logAi->error("Hero %s cannot reach %s.", h->getNameTranslated(), dst.toString()); @@ -1808,4 +1808,9 @@ bool AIStatus::channelProbing() return ongoingChannelProbing; } +void AIGateway::invalidatePaths() +{ + nullkiller->invalidatePaths(); +} + } diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index a4a8a845a..969467d27 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -159,6 +159,8 @@ public: void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) override; void battleEnd(const BattleID & battleID, const BattleResult * br, QueryID queryID) override; + void invalidatePaths() override; + void makeTurn(); void buildArmyIn(const CGTownInstance * t); diff --git a/AI/Nullkiller/Engine/Nullkiller.cpp b/AI/Nullkiller/Engine/Nullkiller.cpp index 69c32974c..5d7686f9b 100644 --- a/AI/Nullkiller/Engine/Nullkiller.cpp +++ b/AI/Nullkiller/Engine/Nullkiller.cpp @@ -24,6 +24,8 @@ #include "../Goals/Composition.h" #include "../../../lib/CPlayerState.h" #include "../../lib/StartInfo.h" +#include "../../lib/pathfinder/PathfinderCache.h" +#include "../../lib/pathfinder/PathfinderOptions.h" namespace NKAI { @@ -43,6 +45,8 @@ Nullkiller::Nullkiller() } +Nullkiller::~Nullkiller() = default; + bool canUseOpenMap(std::shared_ptr<CCallback> cb, PlayerColor playerID) { if(!cb->getStartInfo()->extraOptionsInfo.cheatsAllowed) @@ -73,6 +77,14 @@ void Nullkiller::init(std::shared_ptr<CCallback> cb, AIGateway * gateway) settings = std::make_unique<Settings>(cb->getStartInfo()->difficulty); + PathfinderOptions pathfinderOptions(cb.get()); + + pathfinderOptions.useTeleportTwoWay = true; + pathfinderOptions.useTeleportOneWay = settings->isOneWayMonolithUsageAllowed(); + pathfinderOptions.useTeleportOneWayRandom = settings->isOneWayMonolithUsageAllowed(); + + pathfinderCache = std::make_unique<PathfinderCache>(cb.get(), pathfinderOptions); + if(canUseOpenMap(cb, playerID)) { useObjectGraph = settings->isObjectGraphAllowed(); @@ -721,4 +733,14 @@ bool Nullkiller::handleTrading() return haveTraded; } +std::shared_ptr<const CPathsInfo> Nullkiller::getPathsInfo(const CGHeroInstance * h) const +{ + return pathfinderCache->getPathsInfo(h); +} + +void Nullkiller::invalidatePaths() +{ + pathfinderCache->invalidatePaths(); +} + } diff --git a/AI/Nullkiller/Engine/Nullkiller.h b/AI/Nullkiller/Engine/Nullkiller.h index 369fe7116..c6240e116 100644 --- a/AI/Nullkiller/Engine/Nullkiller.h +++ b/AI/Nullkiller/Engine/Nullkiller.h @@ -21,6 +21,12 @@ #include "../Analyzers/ObjectClusterizer.h" #include "../Helpers/ArmyFormation.h" +VCMI_LIB_NAMESPACE_BEGIN + +class PathfinderCache; + +VCMI_LIB_NAMESPACE_END + namespace NKAI { @@ -72,6 +78,7 @@ private: int3 targetTile; ObjectInstanceID targetObject; std::map<const CGHeroInstance *, HeroLockedReason> lockedHeroes; + std::unique_ptr<PathfinderCache> pathfinderCache; ScanDepth scanDepth; TResources lockedResources; bool useHeroChain; @@ -101,6 +108,7 @@ public: std::mutex aiStateMutex; Nullkiller(); + ~Nullkiller(); void init(std::shared_ptr<CCallback> cb, AIGateway * gateway); void makeTurn(); bool isActive(const CGHeroInstance * hero) const { return activeHero == hero; } @@ -124,6 +132,9 @@ public: bool handleTrading(); void invalidatePathfinderData(); + std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h) const; + void invalidatePaths(); + private: void resetAiState(); void updateAiState(int pass, bool fast = false); diff --git a/AI/Nullkiller/Engine/Settings.cpp b/AI/Nullkiller/Engine/Settings.cpp index 11357f9c1..b41f5b1ca 100644 --- a/AI/Nullkiller/Engine/Settings.cpp +++ b/AI/Nullkiller/Engine/Settings.cpp @@ -38,6 +38,7 @@ namespace NKAI pathfinderBucketsCount(1), pathfinderBucketSize(32), allowObjectGraph(true), + useOneWayMonoliths(false), useTroopsFromGarrisons(false), updateHitmapOnTileReveal(false), openMap(true), @@ -64,5 +65,6 @@ namespace NKAI openMap = node["openMap"].Bool(); useFuzzy = node["useFuzzy"].Bool(); useTroopsFromGarrisons = node["useTroopsFromGarrisons"].Bool(); + useOneWayMonoliths = node["useOneWayMonoliths"].Bool(); } } diff --git a/AI/Nullkiller/Engine/Settings.h b/AI/Nullkiller/Engine/Settings.h index ff2d1b859..f7947762e 100644 --- a/AI/Nullkiller/Engine/Settings.h +++ b/AI/Nullkiller/Engine/Settings.h @@ -36,6 +36,7 @@ namespace NKAI float maxArmyLossTarget; bool allowObjectGraph; bool useTroopsFromGarrisons; + bool useOneWayMonoliths; bool updateHitmapOnTileReveal; bool openMap; bool useFuzzy; @@ -58,6 +59,7 @@ namespace NKAI int getPathfinderBucketSize() const { return pathfinderBucketSize; } bool isObjectGraphAllowed() const { return allowObjectGraph; } bool isGarrisonTroopsUsageAllowed() const { return useTroopsFromGarrisons; } + bool isOneWayMonolithUsageAllowed() const { return useOneWayMonoliths; } bool isUpdateHitmapOnTileReveal() const { return updateHitmapOnTileReveal; } bool isOpenMap() const { return openMap; } bool isUseFuzzy() const { return useFuzzy; } diff --git a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp index 0391a4585..f2d14560b 100644 --- a/AI/Nullkiller/Goals/ExecuteHeroChain.cpp +++ b/AI/Nullkiller/Goals/ExecuteHeroChain.cpp @@ -166,7 +166,7 @@ void ExecuteHeroChain::accept(AIGateway * ai) if(nextNode.specialAction || nextNode.chainMask != chainMask) break; - auto targetNode = cb->getPathsInfo(hero)->getPathInfo(nextNode.coord); + auto targetNode = ai->nullkiller->getPathsInfo(hero)->getPathInfo(nextNode.coord); if(!targetNode->reachable() || targetNode->getCost() > nextNode.cost) @@ -182,7 +182,7 @@ void ExecuteHeroChain::accept(AIGateway * ai) if(node->turns == 0 && node->coord != hero->visitablePos()) { - auto targetNode = cb->getPathsInfo(hero)->getPathInfo(node->coord); + auto targetNode = ai->nullkiller->getPathsInfo(hero)->getPathInfo(node->coord); if(targetNode->accessible == EPathAccessibility::NOT_SET || targetNode->accessible == EPathAccessibility::BLOCKED @@ -239,7 +239,7 @@ void ExecuteHeroChain::accept(AIGateway * ai) if(hero->movementPointsRemaining() > 0) { CGPath path; - bool isOk = cb->getPathsInfo(hero)->getPath(path, node->coord); + bool isOk = ai->nullkiller->getPathsInfo(hero)->getPath(path, node->coord); if(isOk && path.nodes.back().turns > 0) { diff --git a/AI/Nullkiller/Goals/ExploreNeighbourTile.cpp b/AI/Nullkiller/Goals/ExploreNeighbourTile.cpp index 6efa0e0b4..99dd54da8 100644 --- a/AI/Nullkiller/Goals/ExploreNeighbourTile.cpp +++ b/AI/Nullkiller/Goals/ExploreNeighbourTile.cpp @@ -35,7 +35,7 @@ void ExploreNeighbourTile::accept(AIGateway * ai) int3 target = int3(-1); foreach_neighbour(pos, [&](int3 tile) { - auto pathInfo = ai->myCb->getPathsInfo(hero)->getPathInfo(tile); + auto pathInfo = ai->nullkiller->getPathsInfo(hero)->getPathInfo(tile); if(pathInfo->turns > 0) return; diff --git a/AI/Nullkiller/Helpers/ExplorationHelper.cpp b/AI/Nullkiller/Helpers/ExplorationHelper.cpp index 0c17e0cc2..b0d093921 100644 --- a/AI/Nullkiller/Helpers/ExplorationHelper.cpp +++ b/AI/Nullkiller/Helpers/ExplorationHelper.cpp @@ -223,7 +223,7 @@ bool ExplorationHelper::hasReachableNeighbor(const int3 & pos) const if(cbp->isInTheMap(tile)) { auto isAccessible = useCPathfinderAccessibility - ? ai->cb->getPathsInfo(hero)->getPathInfo(tile)->reachable() + ? ai->getPathsInfo(hero)->getPathInfo(tile)->reachable() : ai->pathfinder->isTileAccessible(hero, tile); if(isAccessible) diff --git a/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp b/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp index 7bb43fabb..25e55dcb6 100644 --- a/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp +++ b/AI/Nullkiller/Pathfinding/AIPathfinderConfig.cpp @@ -50,6 +50,8 @@ namespace AIPathfinding options.allowLayerTransitioningAfterBattle = true; options.useTeleportWhirlpool = true; options.forceUseTeleportWhirlpool = true; + options.useTeleportOneWay = ai->settings->isOneWayMonolithUsageAllowed();; + options.useTeleportOneWayRandom = ai->settings->isOneWayMonolithUsageAllowed();; } AIPathfinderConfig::~AIPathfinderConfig() = default; diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index ff391587f..94cb618a4 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -133,8 +133,8 @@ bool HeroPtr::operator==(const HeroPtr & rhs) const bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectInstance * rhs) const { - const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); - const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos()); + const CGPathNode * ln = ai->getPathsInfo(hero)->getPathInfo(lhs->visitablePos()); + const CGPathNode * rn = ai->getPathsInfo(hero)->getPathInfo(rhs->visitablePos()); return ln->getCost() < rn->getCost(); } diff --git a/AI/VCAI/FuzzyEngines.cpp b/AI/VCAI/FuzzyEngines.cpp index 05db1b15a..ba914a52a 100644 --- a/AI/VCAI/FuzzyEngines.cpp +++ b/AI/VCAI/FuzzyEngines.cpp @@ -96,7 +96,7 @@ float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::A } else { - auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile); + auto pathInfo = ai->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile); return pathInfo->getCost(); } } diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 3b8b11793..eb086b47f 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -31,6 +31,8 @@ #include "../../lib/networkPacks/PacksForClientBattle.h" #include "../../lib/networkPacks/PacksForServer.h" #include "../../lib/serializer/CTypeList.h" +#include "../../lib/pathfinder/PathfinderCache.h" +#include "../../lib/pathfinder/PathfinderOptions.h" #include "AIhelper.h" @@ -621,6 +623,7 @@ void VCAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<C playerID = *myCb->getPlayerID(); myCb->waitTillRealize = true; myCb->unlockGsWhenWaiting = true; + pathfinderCache = std::make_unique<PathfinderCache>(myCb.get(), PathfinderOptions(myCb.get())); if(!fh) fh = new FuzzyHelper(); @@ -628,6 +631,16 @@ void VCAI::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<C retrieveVisitableObjs(); } +std::shared_ptr<const CPathsInfo> VCAI::getPathsInfo(const CGHeroInstance * h) const +{ + return pathfinderCache->getPathsInfo(h); +} + +void VCAI::invalidatePaths() +{ + pathfinderCache->invalidatePaths(); +} + void VCAI::yourTurn(QueryID queryID) { LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID); @@ -1800,7 +1813,7 @@ bool VCAI::isAccessibleForHero(const int3 & pos, HeroPtr h, bool includeAllies) } } } - return cb->getPathsInfo(h.get())->getPathInfo(pos)->reachable(); + return getPathsInfo(h.get())->getPathInfo(pos)->reachable(); } bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) @@ -1837,7 +1850,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h) else { CGPath path; - cb->getPathsInfo(h.get())->getPath(path, dst); + getPathsInfo(h.get())->getPath(path, dst); if(path.nodes.empty()) { logAi->error("Hero %s cannot reach %s.", h->getNameTranslated(), dst.toString()); diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 57f68de19..bf7ebd7b9 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -26,6 +26,7 @@ VCMI_LIB_NAMESPACE_BEGIN struct QuestInfo; +class PathfinderCache; VCMI_LIB_NAMESPACE_END @@ -80,6 +81,7 @@ public: 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; + std::unique_ptr<PathfinderCache> pathfinderCache; //part of mainLoop, but accessible from outside std::vector<Goals::TSubgoal> basicGoals; @@ -254,6 +256,8 @@ public: std::vector<HeroPtr> getMyHeroes() const; HeroPtr primaryHero() const; void checkHeroArmy(HeroPtr h); + std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h) const; + void invalidatePaths() override; void requestSent(const CPackForServer * pack, int requestID) override; void answerQuery(QueryID queryID, int selection); diff --git a/CCallback.cpp b/CCallback.cpp index 61c741cc8..a0f8a1aa7 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -384,11 +384,6 @@ bool CCallback::canMoveBetween(const int3 &a, const int3 &b) return gs->map->canMoveBetween(a, b); } -std::shared_ptr<const CPathsInfo> CCallback::getPathsInfo(const CGHeroInstance * h) -{ - return cl->getPathsInfo(h); -} - std::optional<PlayerColor> CCallback::getPlayerID() const { return CBattleCallback::getPlayerID(); diff --git a/CCallback.h b/CCallback.h index 6e30299c6..db3455890 100644 --- a/CCallback.h +++ b/CCallback.h @@ -157,7 +157,6 @@ public: //client-specific functionalities (pathfinding) virtual bool canMoveBetween(const int3 &a, const int3 &b); virtual int3 getGuardingCreaturePosition(int3 tile); - virtual std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h); std::optional<PlayerColor> getPlayerID() const override; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 3c2204559..39fc84306 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -97,6 +97,8 @@ #include "../lib/networkPacks/PacksForServer.h" #include "../lib/pathfinder/CGPathNode.h" +#include "../lib/pathfinder/PathfinderCache.h" +#include "../lib/pathfinder/PathfinderOptions.h" #include "../lib/serializer/CTypeList.h" #include "../lib/serializer/ESerializationVersion.h" @@ -156,6 +158,7 @@ CPlayerInterface::~CPlayerInterface() if (LOCPLINT == this) LOCPLINT = nullptr; } + void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) { cb = CB; @@ -164,9 +167,20 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std:: CCS->musich->loadTerrainMusicThemes(); initializeHeroTownList(); + pathfinderCache = std::make_unique<PathfinderCache>(cb.get(), PathfinderOptions(cb.get())); adventureInt.reset(new AdventureMapInterface()); } +std::shared_ptr<const CPathsInfo> CPlayerInterface::getPathsInfo(const CGHeroInstance * h) +{ + return pathfinderCache->getPathsInfo(h); +} + +void CPlayerInterface::invalidatePaths() +{ + pathfinderCache->invalidatePaths(); +} + void CPlayerInterface::closeAllDialogs() { // remove all active dialogs that do not expect query answer @@ -467,6 +481,8 @@ void CPlayerInterface::heroSecondarySkillChanged(const CGHeroInstance * hero, in EVENT_HANDLER_CALLED_BY_CLIENT; for (auto cuw : GH.windows().findWindows<IMarketHolder>()) cuw->updateSecondarySkills(); + + localState->verifyPath(hero); } void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero) @@ -583,6 +599,8 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CArmedInstance *> objs if (hero) { + localState->verifyPath(hero); + adventureInt->onHeroChanged(hero); if(hero->inTownGarrison && hero->visitedTown != town) adventureInt->onTownChanged(hero->visitedTown); diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index 3aefb35d9..1845c5b9b 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -27,6 +27,7 @@ class CGObjectInstance; class UpgradeInfo; class ConditionalWait; struct CPathsInfo; +class PathfinderCache; VCMI_LIB_NAMESPACE_END @@ -64,6 +65,7 @@ class CPlayerInterface : public CGameInterface, public IUpdateable std::list<std::shared_ptr<CInfoWindow>> dialogs; //queue of dialogs awaiting to be shown (not currently shown!) std::unique_ptr<HeroMovementController> movementController; + std::unique_ptr<PathfinderCache> pathfinderCache; public: // TODO: make private std::unique_ptr<ArtifactsUIController> artifactController; std::shared_ptr<Environment> env; @@ -198,6 +200,8 @@ public: // public interface for use by client via LOCPLINT access void gamePause(bool pause); void endNetwork(); void closeAllDialogs(); + std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h); + void invalidatePaths() override; ///returns true if all events are processed internally bool capturedAllEvents(); diff --git a/client/Client.cpp b/client/Client.cpp index 0f55cbd52..4392de9c2 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -222,8 +222,6 @@ void CClient::initMapHandler() CGI->mh = std::make_shared<CMapHandler>(gs->map); logNetwork->trace("Creating mapHandler: %d ms", CSH->th->getDiff()); } - - pathCache.clear(); } void CClient::initPlayerEnvironments() @@ -494,24 +492,7 @@ void CClient::startPlayerBattleAction(const BattleID & battleID, PlayerColor col } } -void CClient::updatePath(const ObjectInstanceID & id) -{ - invalidatePaths(); - auto hero = getHero(id); - updatePath(hero); -} -void CClient::updatePath(const CGHeroInstance * hero) -{ - if(LOCPLINT && hero) - LOCPLINT->localState->verifyPath(hero); -} - -void CClient::invalidatePaths() -{ - boost::unique_lock<boost::mutex> pathLock(pathCacheMutex); - pathCache.clear(); -} vstd::RNG & CClient::getRandomGenerator() { @@ -520,28 +501,6 @@ vstd::RNG & CClient::getRandomGenerator() throw std::runtime_error("Illegal access to random number generator from client code!"); } -std::shared_ptr<const CPathsInfo> CClient::getPathsInfo(const CGHeroInstance * h) -{ - assert(h); - boost::unique_lock<boost::mutex> pathLock(pathCacheMutex); - - auto iter = pathCache.find(h); - - if(iter == std::end(pathCache)) - { - auto paths = std::make_shared<CPathsInfo>(getMapSize(), h); - - gs->calculatePaths(h, *paths.get()); - - pathCache[h] = paths; - return paths; - } - else - { - return iter->second; - } -} - #if SCRIPTING_ENABLED scripting::Pool * CClient::getGlobalContextPool() const { diff --git a/client/Client.h b/client/Client.h index ce1276b06..ba4f2b34e 100644 --- a/client/Client.h +++ b/client/Client.h @@ -149,11 +149,6 @@ public: void battleFinished(const BattleID & battleID); void startPlayerBattleAction(const BattleID & battleID, PlayerColor color); - void invalidatePaths(); // clears this->pathCache() - void updatePath(const ObjectInstanceID & heroID); // invalidatePaths and update displayed hero path - void updatePath(const CGHeroInstance * hero); - std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h); - friend class CCallback; //handling players actions friend class CBattleCallback; //handling players actions @@ -235,8 +230,5 @@ private: #endif std::unique_ptr<events::EventBus> clientEventBus; - mutable boost::mutex pathCacheMutex; - std::map<const CGHeroInstance *, std::shared_ptr<CPathsInfo>> pathCache; - void reinitScripting(); }; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 9b6482b47..dad032b69 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -168,7 +168,6 @@ void ApplyClientNetPackVisitor::visitSetMana(SetMana & pack) void ApplyClientNetPackVisitor::visitSetMovePoints(SetMovePoints & pack) { const CGHeroInstance *h = cl.getHero(pack.hid); - cl.updatePath(h); callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroMovePointsChanged, h); } @@ -194,7 +193,7 @@ void ApplyClientNetPackVisitor::visitFoWChange(FoWChange & pack) i.second->tileHidden(pack.tiles); } } - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); } static void dispatchGarrisonChange(CClient & cl, ObjectInstanceID army1, ObjectInstanceID army2) @@ -235,33 +234,21 @@ void ApplyClientNetPackVisitor::visitSetStackType(SetStackType & pack) void ApplyClientNetPackVisitor::visitEraseStack(EraseStack & pack) { dispatchGarrisonChange(cl, pack.army, ObjectInstanceID()); - cl.updatePath(pack.army); //it is possible to remove last non-native unit for current terrain and lose movement penalty } void ApplyClientNetPackVisitor::visitSwapStacks(SwapStacks & pack) { dispatchGarrisonChange(cl, pack.srcArmy, pack.dstArmy); - - if(pack.srcArmy != pack.dstArmy) - cl.updatePath(pack.dstArmy); // adding/removing units may change terrain type penalty based on creature native terrains } void ApplyClientNetPackVisitor::visitInsertNewStack(InsertNewStack & pack) { dispatchGarrisonChange(cl, pack.army, ObjectInstanceID()); - - cl.updatePath(pack.army); // adding/removing units may change terrain type penalty based on creature native terrains } void ApplyClientNetPackVisitor::visitRebalanceStacks(RebalanceStacks & pack) { dispatchGarrisonChange(cl, pack.srcArmy, pack.dstArmy); - - if(pack.srcArmy != pack.dstArmy) - { - cl.updatePath(pack.srcArmy); // adding/removing units may change terrain type penalty based on creature native terrains - cl.updatePath(pack.dstArmy); - } } void ApplyClientNetPackVisitor::visitBulkRebalanceStacks(BulkRebalanceStacks & pack) @@ -272,12 +259,6 @@ void ApplyClientNetPackVisitor::visitBulkRebalanceStacks(BulkRebalanceStacks & p ? ObjectInstanceID() : pack.moves[0].dstArmy; dispatchGarrisonChange(cl, pack.moves[0].srcArmy, destArmy); - - if(pack.moves[0].srcArmy != destArmy) - { - cl.updatePath(destArmy); // adding/removing units may change terrain type penalty based on creature native terrains - cl.updatePath(pack.moves[0].srcArmy); - } } } @@ -303,7 +284,6 @@ void ApplyClientNetPackVisitor::visitPutArtifact(PutArtifact & pack) void ApplyClientNetPackVisitor::visitEraseArtifact(BulkEraseArtifacts & pack) { - cl.updatePath(pack.artHolder); for(const auto & slotErase : pack.posPack) callInterfaceIfPresent(cl, cl.getOwner(pack.artHolder), &IGameEventsReceiver::artifactRemoved, ArtifactLocation(pack.artHolder, slotErase)); } @@ -323,9 +303,6 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack) callInterfaceIfPresent(cl, pack.interfaceOwner, &IGameEventsReceiver::askToAssembleArtifact, dstLoc); if(pack.interfaceOwner != dstOwner) callInterfaceIfPresent(cl, dstOwner, &IGameEventsReceiver::artifactMoved, srcLoc, dstLoc); - - cl.updatePath(pack.srcArtHolder); // hero might have equipped/unequipped Angel Wings - cl.updatePath(pack.dstArtHolder); } }; @@ -354,15 +331,11 @@ void ApplyClientNetPackVisitor::visitBulkMoveArtifacts(BulkMoveArtifacts & pack) void ApplyClientNetPackVisitor::visitAssembledArtifact(AssembledArtifact & pack) { callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactAssembled, pack.al); - - cl.updatePath(pack.al.artHolder); // hero might have equipped/unequipped Angel Wings } void ApplyClientNetPackVisitor::visitDisassembledArtifact(DisassembledArtifact & pack) { callInterfaceIfPresent(cl, cl.getOwner(pack.al.artHolder), &IGameEventsReceiver::artifactDisassembled, pack.al); - - cl.updatePath(pack.al.artHolder); // hero might have equipped/unequipped Angel Wings } void ApplyClientNetPackVisitor::visitHeroVisit(HeroVisit & pack) @@ -374,7 +347,7 @@ void ApplyClientNetPackVisitor::visitHeroVisit(HeroVisit & pack) void ApplyClientNetPackVisitor::visitNewTurn(NewTurn & pack) { - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); if(pack.newWeekNotification) { @@ -387,7 +360,8 @@ void ApplyClientNetPackVisitor::visitNewTurn(NewTurn & pack) void ApplyClientNetPackVisitor::visitGiveBonus(GiveBonus & pack) { - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); + switch(pack.who) { case GiveBonus::ETarget::OBJECT: @@ -423,7 +397,7 @@ void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack) CGI->mh->onObjectFadeIn(obj, pack.initiator); CGI->mh->waitForOngoingAnimations(); } - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); } void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack) @@ -490,7 +464,6 @@ void ApplyClientNetPackVisitor::visitPlayerReinitInterface(PlayerReinitInterface void ApplyClientNetPackVisitor::visitRemoveBonus(RemoveBonus & pack) { - cl.invalidatePaths(); switch(pack.who) { case GiveBonus::ETarget::OBJECT: @@ -531,7 +504,8 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack) void ApplyClientNetPackVisitor::visitRemoveObject(RemoveObject & pack) { - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); + for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++) i->second->objectRemovedAfter(); } @@ -561,7 +535,7 @@ void ApplyFirstClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack) void ApplyClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack) { const CGHeroInstance *h = cl.getHero(pack.id); - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); if(CGI->mh) { @@ -976,7 +950,8 @@ void ApplyClientNetPackVisitor::visitPlayerMessageClient(PlayerMessageClient & p void ApplyClientNetPackVisitor::visitAdvmapSpellCast(AdvmapSpellCast & pack) { - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); + auto caster = cl.getHero(pack.casterID); if(caster) //consider notifying other interfaces that see hero? @@ -1068,7 +1043,7 @@ void ApplyClientNetPackVisitor::visitCenterView(CenterView & pack) void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack) { - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); const CGObjectInstance *obj = pack.newObject; if(CGI->mh) @@ -1101,5 +1076,5 @@ void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts void ApplyClientNetPackVisitor::visitEntitiesChanged(EntitiesChanged & pack) { - cl.invalidatePaths(); + callAllInterfaces(cl, &CGameInterface::invalidatePaths); } diff --git a/client/PlayerLocalState.cpp b/client/PlayerLocalState.cpp index 29e664498..3574f5620 100644 --- a/client/PlayerLocalState.cpp +++ b/client/PlayerLocalState.cpp @@ -54,7 +54,7 @@ bool PlayerLocalState::hasPath(const CGHeroInstance * h) const bool PlayerLocalState::setPath(const CGHeroInstance * h, const int3 & destination) { CGPath path; - if(!owner.cb->getPathsInfo(h)->getPath(path, destination)) + if(!owner.getPathsInfo(h)->getPath(path, destination)) { paths.erase(h); //invalidate previously possible path if selected (before other hero blocked only path / fly spell expired) syncronizeState(); diff --git a/client/PlayerLocalState.h b/client/PlayerLocalState.h index 3372b6052..009b6c698 100644 --- a/client/PlayerLocalState.h +++ b/client/PlayerLocalState.h @@ -17,6 +17,7 @@ class CArmedInstance; class JsonNode; struct CGPath; class int3; +struct CPathsInfo; VCMI_LIB_NAMESPACE_END diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 3b918de77..55dd66e1d 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -107,6 +107,9 @@ void AdventureMapInterface::onHeroMovementStarted(const CGHeroInstance * hero) void AdventureMapInterface::onHeroChanged(const CGHeroInstance *h) { + if (h) + LOCPLINT->localState->verifyPath(h); + widget->getHeroList()->updateElement(h); if (h && h == LOCPLINT->localState->getCurrentHero() && !widget->getInfoBar()->showingComponents()) @@ -546,7 +549,7 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition) { isHero = true; - const CGPathNode *pn = LOCPLINT->cb->getPathsInfo(currentHero)->getPathInfo(targetPosition); + const CGPathNode *pn = LOCPLINT->getPathsInfo(currentHero)->getPathInfo(targetPosition); if(currentHero == topBlocking) //clicked selected hero { LOCPLINT->openHeroWindow(currentHero); @@ -685,7 +688,7 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition) std::array<Cursor::Map, 4> cursorVisit = { Cursor::Map::T1_VISIT, Cursor::Map::T2_VISIT, Cursor::Map::T3_VISIT, Cursor::Map::T4_VISIT, }; std::array<Cursor::Map, 4> cursorSailVisit = { Cursor::Map::T1_SAIL_VISIT, Cursor::Map::T2_SAIL_VISIT, Cursor::Map::T3_SAIL_VISIT, Cursor::Map::T4_SAIL_VISIT, }; - const CGPathNode * pathNode = LOCPLINT->cb->getPathsInfo(hero)->getPathInfo(targetPosition); + const CGPathNode * pathNode = LOCPLINT->getPathsInfo(hero)->getPathInfo(targetPosition); assert(pathNode); if((GH.isKeyboardAltDown() || settings["gameTweaks"]["forceMovementInfo"].Bool()) && pathNode->reachable()) //overwrite status bar text with movement info diff --git a/config/ai/nkai/nkai-settings.json b/config/ai/nkai/nkai-settings.json index 25cbcda38..10b49811f 100644 --- a/config/ai/nkai/nkai-settings.json +++ b/config/ai/nkai/nkai-settings.json @@ -42,6 +42,7 @@ "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : true, "useTroopsFromGarrisons" : true, + "useOneWayMonoliths" : false, "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 3, @@ -63,6 +64,7 @@ "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : true, "useTroopsFromGarrisons" : true, + "useOneWayMonoliths" : false, "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 3, @@ -84,6 +86,7 @@ "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : true, "useTroopsFromGarrisons" : true, + "useOneWayMonoliths" : false, "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 3, @@ -105,6 +108,7 @@ "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : true, "useTroopsFromGarrisons" : true, + "useOneWayMonoliths" : false, "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 3, @@ -126,6 +130,7 @@ "maxGoldPressure" : 0.3, "updateHitmapOnTileReveal" : true, "useTroopsFromGarrisons" : true, + "useOneWayMonoliths" : false, "openMap": true, "allowObjectGraph": false, "pathfinderBucketsCount" : 3, diff --git a/lib/CGameInfoCallback.cpp b/lib/CGameInfoCallback.cpp index 1b642c25f..6ae9c1afe 100644 --- a/lib/CGameInfoCallback.cpp +++ b/lib/CGameInfoCallback.cpp @@ -473,7 +473,7 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getVisitableObjs(int3 for(const CGObjectInstance * obj : t->visitableObjects) { - if(getPlayerID() || obj->ID != Obj::EVENT) //hide events from players + if(!getPlayerID().has_value() || obj->ID != Obj::EVENT) //hide events from players ret.push_back(obj); } @@ -945,16 +945,11 @@ void CGameInfoCallback::getVisibleTilesInRange(std::unordered_set<int3> &tiles, gs->getTilesInRange(tiles, pos, radious, ETileVisibility::REVEALED, *getPlayerID(), distanceFormula); } -void CGameInfoCallback::calculatePaths(const std::shared_ptr<PathfinderConfig> & config) +void CGameInfoCallback::calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const { gs->calculatePaths(config); } -void CGameInfoCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &out) -{ - gs->calculatePaths(hero, out); -} - const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const { return gs->map->artInstances.at(aid.num); diff --git a/lib/CGameInfoCallback.h b/lib/CGameInfoCallback.h index a4df86d3f..fcd923601 100644 --- a/lib/CGameInfoCallback.h +++ b/lib/CGameInfoCallback.h @@ -207,8 +207,7 @@ public: virtual std::shared_ptr<const boost::multi_array<TerrainTile*, 3>> getAllVisibleTiles() const; virtual bool isInTheMap(const int3 &pos) const; virtual void getVisibleTilesInRange(std::unordered_set<int3> &tiles, int3 pos, int radious, int3::EDistanceFormula distanceFormula = int3::DIST_2D) const; - virtual void calculatePaths(const std::shared_ptr<PathfinderConfig> & config); - virtual void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out); + virtual void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const; virtual EDiggingStatus getTileDigStatus(int3 tile, bool verbose = true) const; //town diff --git a/lib/CGameInterface.h b/lib/CGameInterface.h index 9719801d7..6f5c3964f 100644 --- a/lib/CGameInterface.h +++ b/lib/CGameInterface.h @@ -56,6 +56,7 @@ class CSaveFile; class BattleStateInfo; struct ArtifactLocation; class BattleStateInfoForRetreat; +struct CPathsInfo; #if SCRIPTING_ENABLED namespace scripting @@ -108,6 +109,9 @@ public: virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain){}; virtual std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) = 0; + + /// Invalidates and destroys all paths for all heroes + virtual void invalidatePaths(){}; }; class DLL_LINKAGE CDynLibHandler diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6be9b534f..2f0740163 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -172,6 +172,7 @@ set(lib_MAIN_SRCS pathfinder/CGPathNode.cpp pathfinder/CPathfinder.cpp pathfinder/NodeStorage.cpp + pathfinder/PathfinderCache.cpp pathfinder/PathfinderOptions.cpp pathfinder/PathfindingRules.cpp pathfinder/TurnInfo.cpp @@ -584,6 +585,7 @@ set(lib_MAIN_HEADERS pathfinder/CGPathNode.h pathfinder/CPathfinder.h pathfinder/NodeStorage.h + pathfinder/PathfinderCache.h pathfinder/PathfinderOptions.h pathfinder/PathfinderUtil.h pathfinder/PathfindingRules.h diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 9c3cc7387..9b57cfff9 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -1144,15 +1144,9 @@ void CGameState::apply(CPackForClient & pack) pack.applyGs(this); } -void CGameState::calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) +void CGameState::calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const { - calculatePaths(std::make_shared<SingleHeroPathfinderConfig>(out, this, hero)); -} - -void CGameState::calculatePaths(const std::shared_ptr<PathfinderConfig> & config) -{ - //FIXME: creating pathfinder is costly, maybe reset / clear is enough? - CPathfinder pathfinder(this, config); + CPathfinder pathfinder(const_cast<CGameState*>(this), config); pathfinder.calculatePaths(); } diff --git a/lib/gameState/CGameState.h b/lib/gameState/CGameState.h index 5d4249e08..47c52a916 100644 --- a/lib/gameState/CGameState.h +++ b/lib/gameState/CGameState.h @@ -96,8 +96,7 @@ public: void fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const override; PlayerRelations getPlayerRelations(PlayerColor color1, PlayerColor color2) const override; bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile - void calculatePaths(const CGHeroInstance *hero, CPathsInfo &out) override; //calculates possible paths for hero, by default uses current hero position and movement left; returns pointer to newly allocated CPath or nullptr if path does not exists - void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) override; + void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override; int3 guardingCreaturePosition (int3 pos) const override; std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const; diff --git a/lib/pathfinder/CGPathNode.cpp b/lib/pathfinder/CGPathNode.cpp index 057161295..9cdc20808 100644 --- a/lib/pathfinder/CGPathNode.cpp +++ b/lib/pathfinder/CGPathNode.cpp @@ -56,6 +56,7 @@ CPathsInfo::CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_) : sizes(Sizes), hero(hero_) { nodes.resize(boost::extents[ELayer::NUM_LAYERS][sizes.z][sizes.x][sizes.y]); + heroBonusTreeVersion = hero->getTreeVersion(); } CPathsInfo::~CPathsInfo() = default; diff --git a/lib/pathfinder/CGPathNode.h b/lib/pathfinder/CGPathNode.h index 2598633cc..7821143f0 100644 --- a/lib/pathfinder/CGPathNode.h +++ b/lib/pathfinder/CGPathNode.h @@ -188,6 +188,8 @@ struct DLL_LINKAGE CPathsInfo const CGHeroInstance * hero; int3 hpos; int3 sizes; + /// Bonus tree version for which this information can be considered to be valid + int heroBonusTreeVersion = 0; boost::multi_array<CGPathNode, 4> nodes; //[layer][level][w][h] CPathsInfo(const int3 & Sizes, const CGHeroInstance * hero_); diff --git a/lib/pathfinder/PathfinderCache.cpp b/lib/pathfinder/PathfinderCache.cpp new file mode 100644 index 000000000..8de2b0bac --- /dev/null +++ b/lib/pathfinder/PathfinderCache.cpp @@ -0,0 +1,66 @@ +/* + * PathfinderCache.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "PathfinderCache.h" + +#include "CGPathNode.h" +#include "PathfinderOptions.h" + +#include "../CGameInfoCallback.h" +#include "../mapObjects/CGHeroInstance.h" + +VCMI_LIB_NAMESPACE_BEGIN + +std::shared_ptr<PathfinderConfig> PathfinderCache::createConfig(const CGHeroInstance * h, CPathsInfo & out) +{ + auto config = std::make_shared<SingleHeroPathfinderConfig>(out, cb, h); + config->options = options; + + return config; +} + +std::shared_ptr<CPathsInfo> PathfinderCache::buildPaths(const CGHeroInstance * h) +{ + std::shared_ptr<CPathsInfo> result = std::make_shared<CPathsInfo>(cb->getMapSize(), h); + auto config = createConfig(h, *result); + + cb->calculatePaths(config); + return result; +} + +PathfinderCache::PathfinderCache(const CGameInfoCallback * cb, const PathfinderOptions & options) + : cb(cb) + , options(options) +{ +} + +void PathfinderCache::invalidatePaths() +{ + std::lock_guard lock(pathCacheMutex); + pathCache.clear(); +} + +std::shared_ptr<const CPathsInfo> PathfinderCache::getPathsInfo(const CGHeroInstance * h) +{ + std::lock_guard lock(pathCacheMutex); + + auto iter = pathCache.find(h); + if(iter == std::end(pathCache) || iter->second->heroBonusTreeVersion != h->getTreeVersion()) + { + auto result = buildPaths(h); + pathCache[h] = result; + + return result; + } + else + return iter->second; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/pathfinder/PathfinderCache.h b/lib/pathfinder/PathfinderCache.h new file mode 100644 index 000000000..e6fd823d3 --- /dev/null +++ b/lib/pathfinder/PathfinderCache.h @@ -0,0 +1,40 @@ +/* + * PathfinderCache.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "PathfinderOptions.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class CGameInfoCallback; +class CGHeroInstance; +class PathfinderConfig; +struct CPathsInfo; + +class DLL_LINKAGE PathfinderCache +{ + const CGameInfoCallback * cb; + std::mutex pathCacheMutex; + std::map<const CGHeroInstance *, std::shared_ptr<CPathsInfo>> pathCache; + PathfinderOptions options; + + std::shared_ptr<PathfinderConfig> createConfig(const CGHeroInstance *h, CPathsInfo &out); + std::shared_ptr<CPathsInfo> buildPaths(const CGHeroInstance *h); +public: + PathfinderCache(const CGameInfoCallback * cb, const PathfinderOptions & options); + + /// Invalidates and erases all existing paths from the cache + void invalidatePaths(); + + /// Returns compute path information for requested hero + std::shared_ptr<const CPathsInfo> getPathsInfo(const CGHeroInstance * h); +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/pathfinder/PathfinderOptions.cpp b/lib/pathfinder/PathfinderOptions.cpp index 51dbdddb3..017ae7487 100644 --- a/lib/pathfinder/PathfinderOptions.cpp +++ b/lib/pathfinder/PathfinderOptions.cpp @@ -59,14 +59,17 @@ std::vector<std::shared_ptr<IPathfindingRule>> SingleHeroPathfinderConfig::build SingleHeroPathfinderConfig::~SingleHeroPathfinderConfig() = default; -SingleHeroPathfinderConfig::SingleHeroPathfinderConfig(CPathsInfo & out, CGameState * gs, const CGHeroInstance * hero) +SingleHeroPathfinderConfig::SingleHeroPathfinderConfig(CPathsInfo & out, const CGameInfoCallback * gs, const CGHeroInstance * hero) : PathfinderConfig(std::make_shared<NodeStorage>(out, hero), gs, buildRuleSet()) + , hero(hero) { - pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, hero, options); } CPathfinderHelper * SingleHeroPathfinderConfig::getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) { + if (!pathfinderHelper) + pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, hero, options); + return pathfinderHelper.get(); } diff --git a/lib/pathfinder/PathfinderOptions.h b/lib/pathfinder/PathfinderOptions.h index d7c39d4f5..98cc3026a 100644 --- a/lib/pathfinder/PathfinderOptions.h +++ b/lib/pathfinder/PathfinderOptions.h @@ -108,9 +108,10 @@ class DLL_LINKAGE SingleHeroPathfinderConfig : public PathfinderConfig { private: std::unique_ptr<CPathfinderHelper> pathfinderHelper; + const CGHeroInstance * hero; public: - SingleHeroPathfinderConfig(CPathsInfo & out, CGameState * gs, const CGHeroInstance * hero); + SingleHeroPathfinderConfig(CPathsInfo & out, const CGameInfoCallback * gs, const CGHeroInstance * hero); virtual ~SingleHeroPathfinderConfig(); CPathfinderHelper * getOrCreatePathfinderHelper(const PathNodeInfo & source, CGameState * gs) override; diff --git a/lib/pathfinder/TurnInfo.cpp b/lib/pathfinder/TurnInfo.cpp index d8911332a..515f9fa14 100644 --- a/lib/pathfinder/TurnInfo.cpp +++ b/lib/pathfinder/TurnInfo.cpp @@ -120,7 +120,7 @@ TurnInfo::TurnInfo(TurnInfoCache * sharedCache, const CGHeroInstance * target, i { static const CSelector selector = Selector::type()(BonusType::ROUGH_TERRAIN_DISCOUNT); const auto & bonuses = sharedCache->roughTerrainDiscount.getBonusList(target, selector); - roughTerrainDiscountValue = bonuses->getFirst(daySelector) != nullptr; + roughTerrainDiscountValue = bonuses->valOfBonuses(daySelector); } {