mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
Use the same cost value in Pathfinder and fuzzy evaluations.
This commit is contained in:
@@ -191,15 +191,7 @@ bool CDistanceSorter::operator()(const CGObjectInstance * lhs, const CGObjectIns
|
|||||||
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
|
const CGPathNode * ln = ai->myCb->getPathsInfo(hero)->getPathInfo(lhs->visitablePos());
|
||||||
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
|
const CGPathNode * rn = ai->myCb->getPathsInfo(hero)->getPathInfo(rhs->visitablePos());
|
||||||
|
|
||||||
if(ln->turns != rn->turns)
|
return ln->cost < rn->cost;
|
||||||
return ln->turns < rn->turns;
|
|
||||||
|
|
||||||
return (ln->moveRemains > rn->moveRemains);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool compareMovement(HeroPtr lhs, HeroPtr rhs)
|
|
||||||
{
|
|
||||||
return lhs->movement > rhs->movement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui64 evaluateDanger(crint3 tile)
|
ui64 evaluateDanger(crint3 tile)
|
||||||
@@ -410,6 +402,7 @@ bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should
|
|||||||
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
|
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
|
||||||
return !gate->passableFor(ai->playerID);
|
return !gate->passableFor(ai->playerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isBlockVisitObj(const int3 & pos)
|
bool isBlockVisitObj(const int3 & pos)
|
||||||
{
|
{
|
||||||
if(auto obj = cb->getTopObj(pos))
|
if(auto obj = cb->getTopObj(pos))
|
||||||
@@ -439,7 +432,6 @@ creInfo infoFromDC(const dwellingContent & dc)
|
|||||||
return ci;
|
return ci;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t)
|
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t)
|
||||||
{
|
{
|
||||||
ui64 aivalue = 0;
|
ui64 aivalue = 0;
|
||||||
@@ -529,14 +521,3 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
|
|||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos)
|
|
||||||
{
|
|
||||||
auto pathInfo = cb->getPathsInfo(hero)->getPathInfo(pos);
|
|
||||||
uint32_t totalMovementPoints = pathInfo->turns * hero->maxMovePoints(true) + hero->movement;
|
|
||||||
|
|
||||||
if(totalMovementPoints < pathInfo->moveRemains) // should not be but who knows
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return totalMovementPoints - pathInfo->moveRemains;
|
|
||||||
}
|
|
||||||
|
@@ -172,13 +172,11 @@ bool isObjectRemovable(const CGObjectInstance * obj); //FIXME FIXME: move logic
|
|||||||
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
|
bool isSafeToVisit(HeroPtr h, uint64_t dangerStrength);
|
||||||
bool isSafeToVisit(HeroPtr h, crint3 tile);
|
bool isSafeToVisit(HeroPtr h, crint3 tile);
|
||||||
|
|
||||||
bool compareMovement(HeroPtr lhs, HeroPtr rhs);
|
|
||||||
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
||||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
||||||
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
||||||
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
|
ui64 howManyReinforcementsCanBuy(const CArmedInstance * h, const CGDwelling * t);
|
||||||
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
|
ui64 howManyReinforcementsCanGet(const CArmedInstance * h, const CGTownInstance * t);
|
||||||
uint32_t distanceToTile(const CGHeroInstance * hero, int3 pos);
|
|
||||||
|
|
||||||
class CDistanceSorter
|
class CDistanceSorter
|
||||||
{
|
{
|
||||||
|
@@ -89,14 +89,15 @@ armyStructure evaluateArmyStructure(const CArmedInstance * army)
|
|||||||
|
|
||||||
float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const
|
float HeroMovementGoalEngineBase::calculateTurnDistanceInputValue(const Goals::AbstractGoal & goal) const
|
||||||
{
|
{
|
||||||
const float maxMovePoints = (float)goal.hero->maxMovePoints(true);
|
if(goal.evaluationContext.movementCost > 0)
|
||||||
|
|
||||||
if(goal.evaluationContext.movementCost != 0)
|
|
||||||
{
|
{
|
||||||
return goal.evaluationContext.movementCost / maxMovePoints;
|
return goal.evaluationContext.movementCost;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto pathInfo = ai->myCb->getPathsInfo(goal.hero.h)->getPathInfo(goal.tile);
|
||||||
|
return pathInfo->cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
return distanceToTile(goal.hero.h, goal.tile) / maxMovePoints;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TacticalAdvantageEngine::TacticalAdvantageEngine()
|
TacticalAdvantageEngine::TacticalAdvantageEngine()
|
||||||
|
@@ -91,12 +91,14 @@ namespace Goals
|
|||||||
|
|
||||||
struct DLL_EXPORT EvaluationContext
|
struct DLL_EXPORT EvaluationContext
|
||||||
{
|
{
|
||||||
uint64_t movementCost;
|
float movementCost;
|
||||||
int manaCost;
|
int manaCost;
|
||||||
uint64_t danger;
|
uint64_t danger;
|
||||||
|
|
||||||
EvaluationContext()
|
EvaluationContext()
|
||||||
:movementCost(0), danger(0), manaCost(0)
|
: movementCost(0.0),
|
||||||
|
manaCost(0),
|
||||||
|
danger(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -253,11 +253,12 @@ TSubgoal Explore::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) con
|
|||||||
if(dstToRevealedTiles.empty()) //yes, it DID happen!
|
if(dstToRevealedTiles.empty()) //yes, it DID happen!
|
||||||
return sptr(Invalid());
|
return sptr(Invalid());
|
||||||
|
|
||||||
|
auto paths = cb->getPathsInfo(h.get());
|
||||||
|
|
||||||
auto best = dstToRevealedTiles.begin();
|
auto best = dstToRevealedTiles.begin();
|
||||||
for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
|
for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++)
|
||||||
{
|
{
|
||||||
const CGPathNode * pn = cb->getPathsInfo(h.get())->getPathInfo(i->first);
|
const CGPathNode * pn = paths->getPathInfo(i->first);
|
||||||
//const TerrainTile *t = cb->getTile(i->first);
|
|
||||||
if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE)
|
if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE)
|
||||||
best = i;
|
best = i;
|
||||||
}
|
}
|
||||||
@@ -336,7 +337,7 @@ TSubgoal Explore::explorationScanRange(HeroPtr h, std::vector<int3> & range) con
|
|||||||
auto waysToVisit = aip->ah->howToVisitTile(h, tile, allowGatherArmy);
|
auto waysToVisit = aip->ah->howToVisitTile(h, tile, allowGatherArmy);
|
||||||
for(auto goal : waysToVisit)
|
for(auto goal : waysToVisit)
|
||||||
{
|
{
|
||||||
if(goal->evaluationContext.movementCost == 0) // should not happen
|
if(goal->evaluationContext.movementCost <= 0.0) // should not happen
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost;
|
float ourValue = (float)tilesDiscovered * tilesDiscovered / goal->evaluationContext.movementCost;
|
||||||
@@ -374,7 +375,7 @@ TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
|
|||||||
|
|
||||||
//look for nearby objs -> visit them if they're close enough
|
//look for nearby objs -> visit them if they're close enough
|
||||||
const int DIST_LIMIT = 3;
|
const int DIST_LIMIT = 3;
|
||||||
const int MP_LIMIT = DIST_LIMIT * 150; // approximate cost of diagonal movement
|
const float COST_LIMIT = .2; //todo: fine tune
|
||||||
|
|
||||||
std::vector<const CGObjectInstance *> nearbyVisitableObjs;
|
std::vector<const CGObjectInstance *> nearbyVisitableObjs;
|
||||||
for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map
|
for(int x = hpos.x - DIST_LIMIT; x <= hpos.x + DIST_LIMIT; ++x) //get only local objects instead of all possible objects on the map
|
||||||
@@ -383,7 +384,7 @@ TSubgoal Explore::exploreNearestNeighbour(HeroPtr h) const
|
|||||||
{
|
{
|
||||||
for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
|
for(auto obj : cb->getVisitableObjs(int3(x, y, hpos.z), false))
|
||||||
{
|
{
|
||||||
if(ai->isGoodForVisit(obj, h, MP_LIMIT))
|
if(ai->isGoodForVisit(obj, h, COST_LIMIT))
|
||||||
{
|
{
|
||||||
nearbyVisitableObjs.push_back(obj);
|
nearbyVisitableObjs.push_back(obj);
|
||||||
}
|
}
|
||||||
|
@@ -120,6 +120,7 @@ CGPathNode * AINodeStorage::getInitialNode()
|
|||||||
initialNode->turns = 0;
|
initialNode->turns = 0;
|
||||||
initialNode->moveRemains = hero->movement;
|
initialNode->moveRemains = hero->movement;
|
||||||
initialNode->danger = 0;
|
initialNode->danger = 0;
|
||||||
|
initialNode->cost = 0.0;
|
||||||
|
|
||||||
return initialNode;
|
return initialNode;
|
||||||
}
|
}
|
||||||
@@ -146,6 +147,7 @@ void AINodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInf
|
|||||||
{
|
{
|
||||||
dstNode->moveRemains = destination.movementLeft;
|
dstNode->moveRemains = destination.movementLeft;
|
||||||
dstNode->turns = destination.turn;
|
dstNode->turns = destination.turn;
|
||||||
|
dstNode->cost = destination.cost;
|
||||||
dstNode->danger = srcNode->danger;
|
dstNode->danger = srcNode->danger;
|
||||||
dstNode->action = destination.action;
|
dstNode->action = destination.action;
|
||||||
dstNode->theNodeBefore = srcNode->theNodeBefore;
|
dstNode->theNodeBefore = srcNode->theNodeBefore;
|
||||||
@@ -322,8 +324,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
|
|||||||
|
|
||||||
if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0)
|
if(node.danger <= destinationNode->danger && destinationNode->chainMask == 1 && node.chainMask == 0)
|
||||||
{
|
{
|
||||||
if(node.turns < destinationNode->turns
|
if(node.cost < destinationNode->cost)
|
||||||
|| (node.turns == destinationNode->turns && node.moveRemains >= destinationNode->moveRemains))
|
|
||||||
{
|
{
|
||||||
#ifdef VCMI_TRACE_PATHFINDER
|
#ifdef VCMI_TRACE_PATHFINDER
|
||||||
logAi->trace(
|
logAi->trace(
|
||||||
@@ -343,7 +344,6 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
|
|||||||
|
|
||||||
bool AINodeStorage::isTileAccessible(int3 pos, const EPathfindingLayer layer) const
|
bool AINodeStorage::isTileAccessible(int3 pos, const EPathfindingLayer layer) const
|
||||||
{
|
{
|
||||||
std::vector<AIPath> paths;
|
|
||||||
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
auto chains = nodes[pos.x][pos.y][pos.z][layer];
|
||||||
|
|
||||||
for(const AIPathNode & node : chains)
|
for(const AIPathNode & node : chains)
|
||||||
@@ -376,9 +376,7 @@ std::vector<AIPath> AINodeStorage::getChainInfo(int3 pos, bool isOnLand) const
|
|||||||
while(current != nullptr && current->coord != initialPos)
|
while(current != nullptr && current->coord != initialPos)
|
||||||
{
|
{
|
||||||
AIPathNodeInfo pathNode;
|
AIPathNodeInfo pathNode;
|
||||||
|
pathNode.cost = current->cost;
|
||||||
pathNode.movementPointsLeft = current->moveRemains;
|
|
||||||
pathNode.movementPointsUsed = (int)(current->turns * hero->maxMovePoints(true) + hero->movement) - (int)current->moveRemains;
|
|
||||||
pathNode.turns = current->turns;
|
pathNode.turns = current->turns;
|
||||||
pathNode.danger = current->danger;
|
pathNode.danger = current->danger;
|
||||||
pathNode.coord = current->coord;
|
pathNode.coord = current->coord;
|
||||||
@@ -420,15 +418,15 @@ uint64_t AIPath::getPathDanger() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AIPath::movementCost() const
|
float AIPath::movementCost() const
|
||||||
{
|
{
|
||||||
if(nodes.size())
|
if(nodes.size())
|
||||||
{
|
{
|
||||||
return nodes.front().movementPointsUsed;
|
return nodes.front().cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: boost:optional?
|
// TODO: boost:optional?
|
||||||
return 0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t AIPath::getTotalDanger(HeroPtr hero) const
|
uint64_t AIPath::getTotalDanger(HeroPtr hero) const
|
||||||
|
@@ -42,8 +42,7 @@ struct AIPathNode : public CGPathNode
|
|||||||
|
|
||||||
struct AIPathNodeInfo
|
struct AIPathNodeInfo
|
||||||
{
|
{
|
||||||
uint32_t movementPointsLeft;
|
float cost;
|
||||||
uint32_t movementPointsUsed;
|
|
||||||
int turns;
|
int turns;
|
||||||
int3 coord;
|
int3 coord;
|
||||||
uint64_t danger;
|
uint64_t danger;
|
||||||
@@ -64,7 +63,7 @@ struct AIPath
|
|||||||
|
|
||||||
int3 firstTileToGet() const;
|
int3 firstTileToGet() const;
|
||||||
|
|
||||||
uint32_t movementCost() const;
|
float movementCost() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AINodeStorage : public INodeStorage
|
class AINodeStorage : public INodeStorage
|
||||||
|
@@ -65,31 +65,6 @@ struct SetGlobalState
|
|||||||
#define NET_EVENT_HANDLER SET_GLOBAL_STATE(this)
|
#define NET_EVENT_HANDLER SET_GLOBAL_STATE(this)
|
||||||
#define MAKING_TURN SET_GLOBAL_STATE(this)
|
#define MAKING_TURN SET_GLOBAL_STATE(this)
|
||||||
|
|
||||||
void foreach_tile(std::vector<std::vector<std::vector<unsigned char>>> & vectors, std::function<void(unsigned char & in)> foo)
|
|
||||||
{
|
|
||||||
for(auto & vector : vectors)
|
|
||||||
{
|
|
||||||
for(auto j = vector.begin(); j != vector.end(); j++)
|
|
||||||
{
|
|
||||||
for(auto & elem : *j)
|
|
||||||
foo(elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ObjInfo
|
|
||||||
{
|
|
||||||
int3 pos;
|
|
||||||
std::string name;
|
|
||||||
ObjInfo(){}
|
|
||||||
ObjInfo(const CGObjectInstance * obj)
|
|
||||||
: pos(obj->pos), name(obj->getObjectName())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::map<const CGObjectInstance *, ObjInfo> helperObjInfo;
|
|
||||||
|
|
||||||
VCAI::VCAI()
|
VCAI::VCAI()
|
||||||
{
|
{
|
||||||
LOG_TRACE(logAi);
|
LOG_TRACE(logAi);
|
||||||
@@ -1303,12 +1278,12 @@ void VCAI::recruitCreatures(const CGDwelling * d, const CArmedInstance * recruit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<uint32_t> movementCostLimit)
|
bool VCAI::isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit)
|
||||||
{
|
{
|
||||||
int3 op = obj->visitablePos();
|
int3 op = obj->visitablePos();
|
||||||
auto paths = ah->getPathsToTile(h, op);
|
auto paths = ah->getPathsToTile(h, op);
|
||||||
|
|
||||||
for(auto path : paths)
|
for(const auto & path : paths)
|
||||||
{
|
{
|
||||||
if(movementCostLimit && movementCostLimit.get() < path.movementCost())
|
if(movementCostLimit && movementCostLimit.get() < path.movementCost())
|
||||||
return false;
|
return false;
|
||||||
@@ -1424,17 +1399,18 @@ void VCAI::wander(HeroPtr h)
|
|||||||
});
|
});
|
||||||
|
|
||||||
int pass = 0;
|
int pass = 0;
|
||||||
std::vector<boost::optional<ui32>> distanceLimits = {
|
std::vector<boost::optional<float>> distanceLimits =
|
||||||
h->movement,
|
{
|
||||||
h->movement + h->maxMovePoints(true),
|
1.0,
|
||||||
|
2.0,
|
||||||
boost::none
|
boost::none
|
||||||
};
|
};
|
||||||
|
|
||||||
while(!dests.size() && pass < distanceLimits.size())
|
while(!dests.size() && pass < distanceLimits.size())
|
||||||
{
|
{
|
||||||
boost::optional<ui32> distanceLimit = distanceLimits[pass];
|
auto & distanceLimit = distanceLimits[pass];
|
||||||
|
|
||||||
logAi->debug("Looking for wander destination pass=%i, distance limit=%i", pass, distanceLimit.get_value_or(-1));
|
logAi->debug("Looking for wander destination pass=%i, cost limit=%f", pass, distanceLimit.get_value_or(-1.0));
|
||||||
|
|
||||||
vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
|
vstd::copy_if(visitableObjs, std::back_inserter(dests), [&](ObjectIdRef obj) -> bool
|
||||||
{
|
{
|
||||||
@@ -1773,7 +1749,6 @@ std::vector<const CGObjectInstance *> VCAI::getFlaggedObjects() const
|
|||||||
void VCAI::addVisitableObj(const CGObjectInstance * obj)
|
void VCAI::addVisitableObj(const CGObjectInstance * obj)
|
||||||
{
|
{
|
||||||
visitableObjs.insert(obj);
|
visitableObjs.insert(obj);
|
||||||
helperObjInfo[obj] = ObjInfo(obj);
|
|
||||||
|
|
||||||
// All teleport objects seen automatically assigned to appropriate channels
|
// All teleport objects seen automatically assigned to appropriate channels
|
||||||
auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
|
auto teleportObj = dynamic_cast<const CGTeleport *>(obj);
|
||||||
|
@@ -210,7 +210,7 @@ public:
|
|||||||
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
|
void completeGoal(Goals::TSubgoal goal); //safely removes goal from reserved hero
|
||||||
|
|
||||||
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<uint32_t> movementCostLimit = boost::none);
|
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, boost::optional<float> movementCostLimit = boost::none);
|
||||||
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
|
bool isGoodForVisit(const CGObjectInstance * obj, HeroPtr h, const AIPath & path) const;
|
||||||
//void recruitCreatures(const CGTownInstance * t);
|
//void recruitCreatures(const CGTownInstance * t);
|
||||||
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
|
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
|
||||||
|
@@ -161,6 +161,7 @@ CGPathNode * NodeStorage::getInitialNode()
|
|||||||
|
|
||||||
initialNode->turns = 0;
|
initialNode->turns = 0;
|
||||||
initialNode->moveRemains = out.hero->movement;
|
initialNode->moveRemains = out.hero->movement;
|
||||||
|
initialNode->cost = 0.0;
|
||||||
|
|
||||||
return initialNode;
|
return initialNode;
|
||||||
}
|
}
|
||||||
@@ -168,6 +169,7 @@ CGPathNode * NodeStorage::getInitialNode()
|
|||||||
void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
|
void NodeStorage::commit(CDestinationNodeInfo & destination, const PathNodeInfo & source)
|
||||||
{
|
{
|
||||||
assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other
|
assert(destination.node != source.node->theNodeBefore); //two tiles can't point to each other
|
||||||
|
destination.node->cost = destination.cost;
|
||||||
destination.node->moveRemains = destination.movementLeft;
|
destination.node->moveRemains = destination.movementLeft;
|
||||||
destination.node->turns = destination.turn;
|
destination.node->turns = destination.turn;
|
||||||
destination.node->theNodeBefore = source.node;
|
destination.node->theNodeBefore = source.node;
|
||||||
@@ -197,24 +199,36 @@ void MovementCostRule::process(
|
|||||||
const PathfinderConfig * pathfinderConfig,
|
const PathfinderConfig * pathfinderConfig,
|
||||||
CPathfinderHelper * pathfinderHelper) const
|
CPathfinderHelper * pathfinderHelper) const
|
||||||
{
|
{
|
||||||
int turnAtNextTile = destination.turn, moveAtNextTile = destination.movementLeft;
|
float costAtNextTile = destination.cost;
|
||||||
|
int turnAtNextTile = destination.turn;
|
||||||
|
int moveAtNextTile = destination.movementLeft;
|
||||||
int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
|
int cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile);
|
||||||
int remains = moveAtNextTile - cost;
|
int remains = moveAtNextTile - cost;
|
||||||
|
int maxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
|
||||||
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
|
||||||
|
costAtNextTile += static_cast<float>(moveAtNextTile) / maxMovePoints;//we spent all points of current turn
|
||||||
pathfinderHelper->updateTurnInfo(++turnAtNextTile);
|
pathfinderHelper->updateTurnInfo(++turnAtNextTile);
|
||||||
moveAtNextTile = pathfinderHelper->getMaxMovePoints(destination.node->layer);
|
|
||||||
|
maxMovePoints = pathfinderHelper->getMaxMovePoints(destination.node->layer);
|
||||||
|
moveAtNextTile = maxMovePoints;
|
||||||
|
|
||||||
cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
|
cost = pathfinderHelper->getMovementCost(source, destination, moveAtNextTile); //cost must be updated, movement points changed :(
|
||||||
remains = moveAtNextTile - cost;
|
remains = moveAtNextTile - cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
|
if(destination.action == CGPathNode::EMBARK || destination.action == CGPathNode::DISEMBARK)
|
||||||
{
|
{
|
||||||
/// FREE_SHIP_BOARDING bonus only remove additional penalty
|
/// FREE_SHIP_BOARDING bonus only remove additional penalty
|
||||||
/// land <-> sail transition still cost movement points as normal movement
|
/// land <-> sail transition still cost movement points as normal movement
|
||||||
remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, destination.action - 1);
|
remains = pathfinderHelper->movementPointsAfterEmbark(moveAtNextTile, cost, (destination.action == CGPathNode::DISEMBARK));
|
||||||
|
cost = moveAtNextTile - remains;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
costAtNextTile += static_cast<float>(cost) / maxMovePoints;
|
||||||
|
|
||||||
|
destination.cost = costAtNextTile;
|
||||||
destination.turn = turnAtNextTile;
|
destination.turn = turnAtNextTile;
|
||||||
destination.movementLeft = remains;
|
destination.movementLeft = remains;
|
||||||
|
|
||||||
@@ -301,7 +315,10 @@ void CPathfinder::calculatePaths()
|
|||||||
pq.pop();
|
pq.pop();
|
||||||
source.node->locked = true;
|
source.node->locked = true;
|
||||||
|
|
||||||
int movement = source.node->moveRemains, turn = source.node->turns;
|
int movement = source.node->moveRemains;
|
||||||
|
uint8_t turn = source.node->turns;
|
||||||
|
float cost = source.node->cost;
|
||||||
|
|
||||||
hlp->updateTurnInfo(turn);
|
hlp->updateTurnInfo(turn);
|
||||||
if(!movement)
|
if(!movement)
|
||||||
{
|
{
|
||||||
@@ -336,7 +353,7 @@ void CPathfinder::calculatePaths()
|
|||||||
|
|
||||||
destination.turn = turn;
|
destination.turn = turn;
|
||||||
destination.movementLeft = movement;
|
destination.movementLeft = movement;
|
||||||
|
destination.cost = cost;
|
||||||
destination.guarded = isDestinationGuarded();
|
destination.guarded = isDestinationGuarded();
|
||||||
destination.isGuardianTile = destination.guarded && isDestinationGuardian();
|
destination.isGuardianTile = destination.guarded && isDestinationGuardian();
|
||||||
if(destination.nodeObject)
|
if(destination.nodeObject)
|
||||||
@@ -378,6 +395,7 @@ void CPathfinder::calculatePaths()
|
|||||||
destination.setNode(gs, teleportNode);
|
destination.setNode(gs, teleportNode);
|
||||||
destination.turn = turn;
|
destination.turn = turn;
|
||||||
destination.movementLeft = movement;
|
destination.movementLeft = movement;
|
||||||
|
destination.cost = cost;
|
||||||
|
|
||||||
if(destination.isBetterWay())
|
if(destination.isBetterWay())
|
||||||
{
|
{
|
||||||
@@ -951,14 +969,9 @@ bool CPathfinderHelper::addTeleportWhirlpool(const CGWhirlpool * obj) const
|
|||||||
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
return options.useTeleportWhirlpool && hasBonusOfType(Bonus::WHIRLPOOL_PROTECTION) && obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathfinderHelper::getHeroMaxMovementPoints(EPathfindingLayer layer) const
|
int CPathfinderHelper::movementPointsAfterEmbark(int movement, int basicCost, bool disembark) const
|
||||||
{
|
{
|
||||||
return hero->maxMovePoints(layer);
|
return hero->movementPointsAfterEmbark(movement, basicCost, disembark, getTurnInfo());
|
||||||
}
|
|
||||||
|
|
||||||
int CPathfinderHelper::movementPointsAfterEmbark(int movement, int turn, int action) const
|
|
||||||
{
|
|
||||||
return hero->movementPointsAfterEmbark(movement, turn, action, getTurnInfo());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const
|
bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const
|
||||||
@@ -1058,9 +1071,9 @@ int TurnInfo::valOfBonuses(Bonus::BonusType type, int subtype) const
|
|||||||
int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
|
int TurnInfo::getMaxMovePoints(const EPathfindingLayer layer) const
|
||||||
{
|
{
|
||||||
if(maxMovePointsLand == -1)
|
if(maxMovePointsLand == -1)
|
||||||
maxMovePointsLand = hero->maxMovePoints(true, this);
|
maxMovePointsLand = hero->maxMovePointsCached(true, this);
|
||||||
if(maxMovePointsWater == -1)
|
if(maxMovePointsWater == -1)
|
||||||
maxMovePointsWater = hero->maxMovePoints(false, this);
|
maxMovePointsWater = hero->maxMovePointsCached(false, this);
|
||||||
|
|
||||||
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
|
||||||
}
|
}
|
||||||
@@ -1304,15 +1317,6 @@ bool CPathsInfo::getPath(CGPath & out, const int3 & dst) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CPathsInfo::getDistance(const int3 & tile) const
|
|
||||||
{
|
|
||||||
CGPath ret;
|
|
||||||
if(getPath(ret, tile))
|
|
||||||
return ret.nodes.size();
|
|
||||||
else
|
|
||||||
return 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
|
const CGPathNode * CPathsInfo::getNode(const int3 & coord) const
|
||||||
{
|
{
|
||||||
auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND];
|
auto landNode = &nodes[coord.x][coord.y][coord.z][ELayer::LAND];
|
||||||
@@ -1344,7 +1348,9 @@ void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n, bool excludeTopObjec
|
|||||||
}
|
}
|
||||||
|
|
||||||
CDestinationNodeInfo::CDestinationNodeInfo()
|
CDestinationNodeInfo::CDestinationNodeInfo()
|
||||||
: PathNodeInfo(), blocked(false), action(CGPathNode::ENodeAction::UNKNOWN)
|
: PathNodeInfo(),
|
||||||
|
blocked(false),
|
||||||
|
action(CGPathNode::ENodeAction::UNKNOWN)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1360,13 +1366,8 @@ bool CDestinationNodeInfo::isBetterWay() const
|
|||||||
{
|
{
|
||||||
if(node->turns == 0xff) //we haven't been here before
|
if(node->turns == 0xff) //we haven't been here before
|
||||||
return true;
|
return true;
|
||||||
else if(node->turns > turn)
|
else
|
||||||
return true;
|
return cost < node->cost; //this route is faster
|
||||||
else if(node->turns >= turn && node->moveRemains < movementLeft) //this route is faster
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PathNodeInfo::isNodeObjectVisitable() const
|
bool PathNodeInfo::isNodeObjectVisitable() const
|
||||||
|
@@ -49,22 +49,24 @@ struct DLL_LINKAGE CGPathNode
|
|||||||
NOT_SET = 0,
|
NOT_SET = 0,
|
||||||
ACCESSIBLE = 1, //tile can be entered and passed
|
ACCESSIBLE = 1, //tile can be entered and passed
|
||||||
VISITABLE, //tile can be entered as the last tile in path
|
VISITABLE, //tile can be entered as the last tile in path
|
||||||
BLOCKVIS, //visitable from neighbouring tile but not passable
|
BLOCKVIS, //visitable from neighboring tile but not passable
|
||||||
FLYABLE, //can only be accessed in air layer
|
FLYABLE, //can only be accessed in air layer
|
||||||
BLOCKED //tile can't be entered nor visited
|
BLOCKED //tile can't be entered nor visited
|
||||||
};
|
};
|
||||||
|
|
||||||
CGPathNode * theNodeBefore;
|
CGPathNode * theNodeBefore;
|
||||||
int3 coord; //coordinates
|
int3 coord; //coordinates
|
||||||
ui32 moveRemains; //remaining tiles after hero reaches the tile
|
|
||||||
ui8 turns; //how many turns we have to wait before reachng the tile - 0 means current turn
|
|
||||||
ELayer layer;
|
ELayer layer;
|
||||||
|
ui32 moveRemains; //remaining movement points after hero reaches the tile
|
||||||
|
float cost; //total cost of the path to this tile measured in turns with fractions
|
||||||
|
ui8 turns; //how many turns we have to wait before reaching the tile - 0 means current turn
|
||||||
|
|
||||||
EAccessibility accessible;
|
EAccessibility accessible;
|
||||||
ENodeAction action;
|
ENodeAction action;
|
||||||
bool locked;
|
bool locked;
|
||||||
|
|
||||||
CGPathNode()
|
CGPathNode()
|
||||||
: coord(int3(-1, -1, -1)),
|
: coord(-1),
|
||||||
layer(ELayer::WRONG)
|
layer(ELayer::WRONG)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
@@ -76,6 +78,7 @@ struct DLL_LINKAGE CGPathNode
|
|||||||
locked = false;
|
locked = false;
|
||||||
accessible = NOT_SET;
|
accessible = NOT_SET;
|
||||||
moveRemains = 0;
|
moveRemains = 0;
|
||||||
|
cost = std::numeric_limits<float>::max();
|
||||||
turns = 255;
|
turns = 255;
|
||||||
theNodeBefore = nullptr;
|
theNodeBefore = nullptr;
|
||||||
action = UNKNOWN;
|
action = UNKNOWN;
|
||||||
@@ -126,7 +129,6 @@ struct DLL_LINKAGE CPathsInfo
|
|||||||
~CPathsInfo();
|
~CPathsInfo();
|
||||||
const CGPathNode * getPathInfo(const int3 & tile) const;
|
const CGPathNode * getPathInfo(const int3 & tile) const;
|
||||||
bool getPath(CGPath & out, const int3 & dst) const;
|
bool getPath(CGPath & out, const int3 & dst) const;
|
||||||
int getDistance(const int3 & tile) const;
|
|
||||||
const CGPathNode * getNode(const int3 & coord) const;
|
const CGPathNode * getNode(const int3 & coord) const;
|
||||||
|
|
||||||
STRONG_INLINE
|
STRONG_INLINE
|
||||||
@@ -157,6 +159,7 @@ struct DLL_LINKAGE CDestinationNodeInfo : public PathNodeInfo
|
|||||||
CGPathNode::ENodeAction action;
|
CGPathNode::ENodeAction action;
|
||||||
int turn;
|
int turn;
|
||||||
int movementLeft;
|
int movementLeft;
|
||||||
|
float cost; //same as CGPathNode::cost
|
||||||
bool blocked;
|
bool blocked;
|
||||||
bool isGuardianTile;
|
bool isGuardianTile;
|
||||||
|
|
||||||
@@ -414,14 +417,10 @@ private:
|
|||||||
|
|
||||||
struct NodeComparer
|
struct NodeComparer
|
||||||
{
|
{
|
||||||
|
STRONG_INLINE
|
||||||
bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
|
bool operator()(const CGPathNode * lhs, const CGPathNode * rhs) const
|
||||||
{
|
{
|
||||||
if(rhs->turns > lhs->turns)
|
return lhs->cost > rhs->cost;
|
||||||
return false;
|
|
||||||
else if(rhs->turns == lhs->turns && rhs->moveRemains <= lhs->moveRemains)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
|
boost::heap::priority_queue<CGPathNode *, boost::heap::compare<NodeComparer> > pq;
|
||||||
@@ -532,7 +531,6 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getHeroMaxMovementPoints(EPathfindingLayer layer) const;
|
int movementPointsAfterEmbark(int movement, int basicCost, bool disembark) const;
|
||||||
int movementPointsAfterEmbark(int movement, int cost, int action) const;
|
|
||||||
bool passOneTurnLimitCheck(const PathNodeInfo & source) const;
|
bool passOneTurnLimitCheck(const PathNodeInfo & source) const;
|
||||||
};
|
};
|
||||||
|
@@ -193,15 +193,14 @@ bool CGHeroInstance::canLearnSkill() const
|
|||||||
return secSkills.size() < GameConstants::SKILL_PER_HERO;
|
return secSkills.size() < GameConstants::SKILL_PER_HERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
|
int CGHeroInstance::maxMovePoints(bool onLand) const
|
||||||
{
|
{
|
||||||
bool localTi = false;
|
TurnInfo ti(this);
|
||||||
if(!ti)
|
return maxMovePointsCached(onLand, &ti);
|
||||||
{
|
|
||||||
localTi = true;
|
|
||||||
ti = new TurnInfo(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CGHeroInstance::maxMovePointsCached(bool onLand, const TurnInfo * ti) const
|
||||||
|
{
|
||||||
int base;
|
int base;
|
||||||
|
|
||||||
if(onLand)
|
if(onLand)
|
||||||
@@ -225,9 +224,6 @@ int CGHeroInstance::maxMovePoints(bool onLand, const TurnInfo * ti) const
|
|||||||
const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
|
const int subtype = onLand ? SecondarySkill::LOGISTICS : SecondarySkill::NAVIGATION;
|
||||||
const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
|
const double modifier = ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, subtype) / 100.0;
|
||||||
|
|
||||||
if(localTi)
|
|
||||||
delete ti;
|
|
||||||
|
|
||||||
return int(base * (1 + modifier)) + bonus;
|
return int(base * (1 + modifier)) + bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -195,7 +195,10 @@ public:
|
|||||||
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
void setSecSkillLevel(SecondarySkill which, int val, bool abs);// abs == 0 - changes by value; 1 - sets to value
|
||||||
void levelUp(std::vector<SecondarySkill> skills);
|
void levelUp(std::vector<SecondarySkill> skills);
|
||||||
|
|
||||||
int maxMovePoints(bool onLand, const TurnInfo * ti = nullptr) const;
|
int maxMovePoints(bool onLand) const;
|
||||||
|
//cached version is much faster, TurnInfo construction is costly
|
||||||
|
int maxMovePointsCached(bool onLand, const TurnInfo * ti) const;
|
||||||
|
|
||||||
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
|
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false, const TurnInfo * ti = nullptr) const;
|
||||||
|
|
||||||
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
||||||
|
@@ -1750,7 +1750,7 @@ void CGameHandler::newTurn()
|
|||||||
hth.id = h->id;
|
hth.id = h->id;
|
||||||
auto ti = make_unique<TurnInfo>(h, 1);
|
auto ti = make_unique<TurnInfo>(h, 1);
|
||||||
// TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
|
// TODO: this code executed when bonuses of previous day not yet updated (this happen in NewTurn::applyGs). See issue 2356
|
||||||
hth.move = h->maxMovePoints(gs->map->getTile(h->getPosition(false)).terType != ETerrainType::WATER, ti.get());
|
hth.move = h->maxMovePointsCached(gs->map->getTile(h->getPosition(false)).terType != ETerrainType::WATER, ti.get());
|
||||||
hth.mana = h->getManaNewTurn();
|
hth.mana = h->getManaNewTurn();
|
||||||
|
|
||||||
n.heroes.insert(hth);
|
n.heroes.insert(hth);
|
||||||
|
Reference in New Issue
Block a user