mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-17 20:58:07 +02:00
Merge pull request #5304 from IvanSavenko/oneway_ai
Enable one-way monoliths for AI
This commit is contained in:
commit
5ea4014589
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -17,6 +17,7 @@ class CArmedInstance;
|
||||
class JsonNode;
|
||||
struct CGPath;
|
||||
class int3;
|
||||
struct CPathsInfo;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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_);
|
||||
|
66
lib/pathfinder/PathfinderCache.cpp
Normal file
66
lib/pathfinder/PathfinderCache.cpp
Normal file
@ -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
|
40
lib/pathfinder/PathfinderCache.h
Normal file
40
lib/pathfinder/PathfinderCache.h
Normal file
@ -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
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user