mirror of
https://github.com/vcmi/vcmi.git
synced 2025-09-16 09:26:28 +02:00
CPathfinderHelper: implement TurnInfo to avoid bonus re-checking
TurnInfo contain information about what movement-related bonuses and MP hero going to have on certain turn. This way we can collect this information once for each turn so it's huge performance boost.
This commit is contained in:
@@ -48,9 +48,10 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
|
|||||||
throw std::runtime_error("Wrong checksum");
|
throw std::runtime_error("Wrong checksum");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT))
|
hlp = new CPathfinderHelper(hero);
|
||||||
|
if(hlp->ti->bonusFlying)
|
||||||
options.useFlying = true;
|
options.useFlying = true;
|
||||||
if(hero->getBonusAtTurn(Bonus::WATER_WALKING))
|
if(hlp->ti->bonusWaterWalking)
|
||||||
options.useWaterWalking = true;
|
options.useWaterWalking = true;
|
||||||
if(CGWhirlpool::isProtected(hero))
|
if(CGWhirlpool::isProtected(hero))
|
||||||
options.useTeleportWhirlpool = true;
|
options.useTeleportWhirlpool = true;
|
||||||
@@ -61,14 +62,6 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
|
|||||||
|
|
||||||
void CPathfinder::calculatePaths()
|
void CPathfinder::calculatePaths()
|
||||||
{
|
{
|
||||||
int maxMovePointsLand = hero->maxMovePoints(true);
|
|
||||||
int maxMovePointsWater = hero->maxMovePoints(false);
|
|
||||||
|
|
||||||
auto maxMovePoints = [&](CGPathNode *cp) -> int
|
|
||||||
{
|
|
||||||
return cp->layer == ELayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool
|
auto passOneTurnLimitCheck = [&](bool shouldCheck) -> bool
|
||||||
{
|
{
|
||||||
if(options.oneTurnSpecialLayersLimit && shouldCheck)
|
if(options.oneTurnSpecialLayersLimit && shouldCheck)
|
||||||
@@ -109,10 +102,11 @@ void CPathfinder::calculatePaths()
|
|||||||
cp->locked = true;
|
cp->locked = true;
|
||||||
|
|
||||||
int movement = cp->moveRemains, turn = cp->turns;
|
int movement = cp->moveRemains, turn = cp->turns;
|
||||||
|
hlp->updateTurnInfo(turn);
|
||||||
if(!movement)
|
if(!movement)
|
||||||
{
|
{
|
||||||
movement = maxMovePoints(cp);
|
hlp->updateTurnInfo(++turn);
|
||||||
turn++;
|
movement = hlp->getMaxMovePoints(cp->layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
//add accessible neighbouring nodes to the queue
|
//add accessible neighbouring nodes to the queue
|
||||||
@@ -143,7 +137,7 @@ void CPathfinder::calculatePaths()
|
|||||||
if(!isMovementToDestPossible())
|
if(!isMovementToDestPossible())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement);
|
int cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, movement, hlp->ti);
|
||||||
int remains = movement - cost;
|
int remains = movement - cost;
|
||||||
if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
|
if(destAction == CGPathNode::EMBARK || destAction == CGPathNode::DISEMBARK)
|
||||||
{
|
{
|
||||||
@@ -154,9 +148,9 @@ void CPathfinder::calculatePaths()
|
|||||||
if(remains < 0)
|
if(remains < 0)
|
||||||
{
|
{
|
||||||
//occurs rarely, when hero with low movepoints tries to leave the road
|
//occurs rarely, when hero with low movepoints tries to leave the road
|
||||||
turnAtNextTile++;
|
hlp->updateTurnInfo(++turnAtNextTile);
|
||||||
int moveAtNextTile = maxMovePoints(cp);
|
int moveAtNextTile = hlp->getMaxMovePoints(i);
|
||||||
cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile); //cost must be updated, movement points changed :(
|
cost = CPathfinderHelper::getMovementCost(hero, cp->coord, dp->coord, moveAtNextTile, hlp->ti); //cost must be updated, movement points changed :(
|
||||||
remains = moveAtNextTile - cost;
|
remains = moveAtNextTile - cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,13 +276,13 @@ bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const
|
|||||||
switch(layer)
|
switch(layer)
|
||||||
{
|
{
|
||||||
case ELayer::AIR:
|
case ELayer::AIR:
|
||||||
if(!hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn))
|
if(!hlp->ti->bonusFlying)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ELayer::WATER:
|
case ELayer::WATER:
|
||||||
if(!hero->getBonusAtTurn(Bonus::WATER_WALKING, turn))
|
if(!hlp->ti->bonusWaterWalking)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -625,6 +619,43 @@ bool CPathfinder::canVisitObject() const
|
|||||||
return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL;
|
return cp->layer == ELayer::LAND || cp->layer == ELayer::SAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CPathfinderHelper::CPathfinderHelper(const CGHeroInstance * Hero)
|
||||||
|
: ti(nullptr), hero(Hero)
|
||||||
|
{
|
||||||
|
turnsInfo.reserve(16);
|
||||||
|
updateTurnInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPathfinderHelper::updateTurnInfo(const int &turn)
|
||||||
|
{
|
||||||
|
if(!ti || ti->turn != turn)
|
||||||
|
{
|
||||||
|
if(turn < turnsInfo.size())
|
||||||
|
ti = turnsInfo[turn];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ti = getTurnInfo(hero, turn);
|
||||||
|
turnsInfo.push_back(ti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer &layer) const
|
||||||
|
{
|
||||||
|
return layer == EPathfindingLayer::SAIL ? ti->maxMovePointsWater : ti->maxMovePointsLand;
|
||||||
|
}
|
||||||
|
|
||||||
|
TurnInfo * CPathfinderHelper::getTurnInfo(const CGHeroInstance * h, const int &turn)
|
||||||
|
{
|
||||||
|
auto turnInfo = new TurnInfo;
|
||||||
|
turnInfo->turn = turn;
|
||||||
|
turnInfo->maxMovePointsLand = h->maxMovePoints(true);
|
||||||
|
turnInfo->maxMovePointsWater = h->maxMovePoints(false);
|
||||||
|
turnInfo->bonusFlying = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn);
|
||||||
|
turnInfo->bonusWaterWalking = h->getBonusAtTurn(Bonus::WATER_WALKING, turn);
|
||||||
|
return turnInfo;
|
||||||
|
}
|
||||||
|
|
||||||
void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing)
|
void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing)
|
||||||
{
|
{
|
||||||
static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
|
static const int3 dirs[] = { int3(0,1,0),int3(0,-1,0),int3(-1,0,0),int3(+1,0,0),
|
||||||
@@ -664,27 +695,28 @@ void CPathfinderHelper::getNeighbours(CGameState * gs, const TerrainTile &srct,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const int &turn, const bool &checkLast)
|
int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints, const TurnInfo * ti, const bool &checkLast)
|
||||||
{
|
{
|
||||||
if(src == dst) //same tile
|
if(src == dst) //same tile
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
auto s = h->cb->getTile(src), d = h->cb->getTile(dst);
|
if(!ti)
|
||||||
int ret = h->getTileCost(*d, *s, turn);
|
ti = getTurnInfo(h);
|
||||||
|
|
||||||
auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn);
|
auto s = h->cb->getTile(src), d = h->cb->getTile(dst);
|
||||||
auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn);
|
int ret = h->getTileCost(*d, *s, ti);
|
||||||
if(d->blocked && flyBonus)
|
|
||||||
|
if(d->blocked && ti->bonusFlying)
|
||||||
{
|
{
|
||||||
ret *= (100.0 + flyBonus->val) / 100.0;
|
ret *= (100.0 + ti->bonusFlying->val) / 100.0;
|
||||||
}
|
}
|
||||||
else if(d->terType == ETerrainType::WATER)
|
else if(d->terType == ETerrainType::WATER)
|
||||||
{
|
{
|
||||||
if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds
|
if(h->boat && s->hasFavourableWinds() && d->hasFavourableWinds()) //Favourable Winds
|
||||||
ret *= 0.666;
|
ret *= 0.666;
|
||||||
else if(!h->boat && waterWalkingBonus)
|
else if(!h->boat && ti->bonusWaterWalking)
|
||||||
{
|
{
|
||||||
ret *= (100.0 + waterWalkingBonus->val) / 100.0;
|
ret *= (100.0 + ti->bonusWaterWalking->val) / 100.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,7 +737,7 @@ int CPathfinderHelper::getMovementCost(const CGHeroInstance * h, const int3 &src
|
|||||||
getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true);
|
getNeighbours(h->cb->gameState(), *d, dst, vec, s->terType != ETerrainType::WATER, true);
|
||||||
for(auto & elem : vec)
|
for(auto & elem : vec)
|
||||||
{
|
{
|
||||||
int fcost = getMovementCost(h, dst, elem, left, turn, false);
|
int fcost = getMovementCost(h, dst, elem, left, ti, false);
|
||||||
if(fcost <= left)
|
if(fcost <= left)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class CGObjectInstance;
|
class CGObjectInstance;
|
||||||
struct TerrainTile;
|
struct TerrainTile;
|
||||||
|
class CPathfinderHelper;
|
||||||
|
|
||||||
struct DLL_LINKAGE CGPathNode
|
struct DLL_LINKAGE CGPathNode
|
||||||
{
|
{
|
||||||
@@ -128,6 +129,7 @@ private:
|
|||||||
|
|
||||||
CPathsInfo &out;
|
CPathsInfo &out;
|
||||||
const CGHeroInstance *hero;
|
const CGHeroInstance *hero;
|
||||||
|
CPathfinderHelper * hlp;
|
||||||
|
|
||||||
struct NodeComparer
|
struct NodeComparer
|
||||||
{
|
{
|
||||||
@@ -179,11 +181,31 @@ private:
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TurnInfo
|
||||||
|
{
|
||||||
|
int turn;
|
||||||
|
int maxMovePointsLand;
|
||||||
|
int maxMovePointsWater;
|
||||||
|
const Bonus * bonusFlying;
|
||||||
|
const Bonus * bonusWaterWalking;
|
||||||
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CPathfinderHelper
|
class DLL_LINKAGE CPathfinderHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
TurnInfo * ti;
|
||||||
|
const CGHeroInstance * hero;
|
||||||
|
|
||||||
|
CPathfinderHelper(const CGHeroInstance * Hero);
|
||||||
|
void updateTurnInfo(const int &turn = 0);
|
||||||
|
int getMaxMovePoints(const EPathfindingLayer &layer) const;
|
||||||
|
static TurnInfo * getTurnInfo(const CGHeroInstance * h, const int &turn = 0);
|
||||||
|
|
||||||
static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing);
|
static void getNeighbours(CGameState * gs, const TerrainTile &srct, const int3 &tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, const bool &limitCoastSailing);
|
||||||
|
|
||||||
static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const int &turn = 0, const bool &checkLast = true);
|
static int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dst, const int &remainingMovePoints =- 1, const TurnInfo * ti = nullptr, const bool &checkLast = true);
|
||||||
static int getMovementCost(const CGHeroInstance * h, const int3 &dst);
|
static int getMovementCost(const CGHeroInstance * h, const int3 &dst);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<TurnInfo *> turnsInfo;
|
||||||
};
|
};
|
||||||
|
@@ -56,7 +56,7 @@ static int lowestSpeed(const CGHeroInstance * chi)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) const
|
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const
|
||||||
{
|
{
|
||||||
unsigned ret = GameConstants::BASE_MOVEMENT_COST;
|
unsigned ret = GameConstants::BASE_MOVEMENT_COST;
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, turn, from.terType))
|
else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, ti->turn, from.terType))
|
||||||
{
|
{
|
||||||
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
|
// NOTE: in H3 neutral stacks will ignore terrain penalty only if placed as topmost stack(s) in hero army.
|
||||||
// This is clearly bug in H3 however intended behaviour is not clear.
|
// This is clearly bug in H3 however intended behaviour is not clear.
|
||||||
|
@@ -21,6 +21,7 @@ class CHero;
|
|||||||
class CGBoat;
|
class CGBoat;
|
||||||
class CGTownInstance;
|
class CGTownInstance;
|
||||||
struct TerrainTile;
|
struct TerrainTile;
|
||||||
|
struct TurnInfo;
|
||||||
|
|
||||||
class CGHeroPlaceholder : public CGObjectInstance
|
class CGHeroPlaceholder : public CGObjectInstance
|
||||||
{
|
{
|
||||||
@@ -129,7 +130,7 @@ public:
|
|||||||
EAlignment::EAlignment getAlignment() const;
|
EAlignment::EAlignment getAlignment() const;
|
||||||
const std::string &getBiography() const;
|
const std::string &getBiography() const;
|
||||||
bool needsLastStack()const override;
|
bool needsLastStack()const override;
|
||||||
ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
|
ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from, const TurnInfo * ti) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
|
||||||
ui32 getLowestCreatureSpeed() const;
|
ui32 getLowestCreatureSpeed() const;
|
||||||
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
|
int3 getPosition(bool h3m = false) const; //h3m=true - returns position of hero object; h3m=false - returns position of hero 'manifestation'
|
||||||
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
|
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
|
||||||
|
Reference in New Issue
Block a user