1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Pathfinding: implement duration checking for fly and water walking

Now pathfinder take into account different bonuses for different tuns. So if you only have FLYING_MOVEMENT bonus from Fly spell for one turn then pathfinder will only let you use air layer within one turn only.

That work for cost calculations too. Let's say you have two bonuses:
 - FLYING_MOVEMENT with 20% penalty for next 2 turns
 - FLYING_MOVEMENT with 40% penalty for 5 turns
Now pathfinder using correct penalty for each turn so movements in air layer going to be more expensive on 3-5 turns.
This commit is contained in:
ArseniyShestakov 2015-11-09 19:57:26 +03:00
parent 5106738160
commit d3c8ca7c1c
7 changed files with 47 additions and 26 deletions

View File

@ -2100,7 +2100,7 @@ void CGameState::getNeighbours(const TerrainTile &srct, int3 tile, std::vector<i
}
}
int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints, bool checkLast)
int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints, const int &turn, bool checkLast)
{
if(src == dest) //same tile
return 0;
@ -2109,19 +2109,21 @@ int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const
&d = map->getTile(dest);
//get basic cost
int ret = h->getTileCost(d,s);
int ret = h->getTileCost(d, s, turn);
if(d.blocked && h->canFly())
auto flyBonus = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn);
auto waterWalkingBonus = h->getBonusAtTurn(Bonus::WATER_WALKING, turn);
if(d.blocked && flyBonus)
{
ret *= (100.0 + h->valOfBonuses(Bonus::FLYING_MOVEMENT)) / 100.0;
ret *= (100.0 + flyBonus->val) / 100.0;
}
else if(d.terType == ETerrainType::WATER)
{
if(h->boat && s.hasFavourableWinds() && d.hasFavourableWinds()) //Favourable Winds
ret *= 0.666;
else if(!h->boat && h->canWalkOnSea())
else if(!h->boat && waterWalkingBonus)
{
ret *= (100.0 + h->valOfBonuses(Bonus::WATER_WALKING)) / 100.0;
ret *= (100.0 + waterWalkingBonus->val) / 100.0;
}
}
@ -2145,7 +2147,7 @@ int CGameState::getMovementCost(const CGHeroInstance *h, const int3 &src, const
getNeighbours(d, dest, vec, s.terType != ETerrainType::WATER, true);
for(auto & elem : vec)
{
int fcost = getMovementCost(h, dest, elem, left, false);
int fcost = getMovementCost(h, dest, elem, left, turn, false);
if(fcost <= left)
{
return ret;

View File

@ -340,7 +340,7 @@ public:
bool isVisible(const CGObjectInstance *obj, boost::optional<PlayerColor> player);
void getNeighbours(const TerrainTile &srct, int3 tile, std::vector<int3> &vec, const boost::logic::tribool &onLand, bool limitCoastSailing);
int getMovementCost(const CGHeroInstance *h, const int3 &src, const int3 &dest, int remainingMovePoints=-1, bool checkLast=true);
int getMovementCost(const CGHeroInstance * h, const int3 &src, const int3 &dest, int remainingMovePoints =- 1, const int &turn = 0, bool checkLast = true);
int getDate(Date::EDateType mode=Date::DAY) const; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
// ----- getters, setters -----

View File

@ -48,9 +48,9 @@ CPathfinder::CPathfinder(CPathsInfo &_out, CGameState *_gs, const CGHeroInstance
throw std::runtime_error("Wrong checksum");
}
if(hero->canFly())
if(hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT))
options.useFlying = true;
if(hero->canWalkOnSea())
if(hero->getBonusAtTurn(Bonus::WATER_WALKING))
options.useWaterWalking = true;
if(CGWhirlpool::isProtected(hero))
options.useTeleportWhirlpool = true;
@ -132,6 +132,9 @@ void CPathfinder::calculatePaths()
if(!passOneTurnLimitCheck(cp->turns != turn))
continue;
if(!isLayerAvailable(i, turn))
continue;
if(cp->layer != i && !isLayerTransitionPossible())
continue;
@ -274,6 +277,25 @@ void CPathfinder::addTeleportExits(bool noTeleportExcludes)
}
}
bool CPathfinder::isLayerAvailable(const ELayer &layer, const int &turn) const
{
switch(layer)
{
case ELayer::AIR:
if(!hero->getBonusAtTurn(Bonus::FLYING_MOVEMENT, turn))
return false;
break;
case ELayer::WATER:
if(!hero->getBonusAtTurn(Bonus::WATER_WALKING, turn))
return false;
break;
}
return true;
}
bool CPathfinder::isLayerTransitionPossible() const
{
if((cp->layer == ELayer::AIR || cp->layer == ELayer::WATER)

View File

@ -154,6 +154,7 @@ private:
void addNeighbours(const int3 &coord);
void addTeleportExits(bool noTeleportExcludes = false);
bool isLayerAvailable(const ELayer &layer, const int &turn) const;
bool isLayerTransitionPossible() const;
bool isMovementToDestPossible();
bool isMovementAfterDestPossible() const;

View File

@ -56,7 +56,7 @@ static int lowestSpeed(const CGHeroInstance * chi)
return ret;
}
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from) const
ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &from, const int &turn) const
{
unsigned ret = GameConstants::BASE_MOVEMENT_COST;
@ -80,7 +80,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
break;
}
}
else if(!hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
else if(!getBonusAtTurn(Bonus::NO_TERRAIN_PENALTY, turn, from.terType))
{
// 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.
@ -129,14 +129,9 @@ int3 CGHeroInstance::getPosition(bool h3m) const //h3m=true - returns position o
}
}
bool CGHeroInstance::canFly() const
const Bonus * CGHeroInstance::getBonusAtTurn(const Bonus::BonusType &type, const int &turn, const TBonusSubtype &subType) const
{
return hasBonusOfType(Bonus::FLYING_MOVEMENT);
}
bool CGHeroInstance::canWalkOnSea() const
{
return hasBonusOfType(Bonus::WATER_WALKING);
return getBonus(Selector::type(type).And(Selector::days(turn)).And(Selector::subtype(subType)));
}
ui8 CGHeroInstance::getSecSkillLevel(SecondarySkill skill) const

View File

@ -129,12 +129,11 @@ public:
EAlignment::EAlignment getAlignment() const;
const std::string &getBiography() const;
bool needsLastStack()const override;
ui32 getTileCost(const TerrainTile &dest, const TerrainTile &from) 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 int &turn) const; //move cost - applying pathfinding skill, road and terrain modifiers. NOT includes diagonal move penalty, last move levelling
ui32 getLowestCreatureSpeed() const;
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
bool canFly() const;
bool canWalkOnSea() const;
const Bonus * getBonusAtTurn(const Bonus::BonusType &type, const int &turn = 0, const TBonusSubtype &subType = -1) const;
int getCurrentLuck(int stack=-1, bool town=false) const;
int getSpellCost(const CSpell *sp) const; //do not use during battles -> bonuses from army would be ignored

View File

@ -1779,12 +1779,14 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
tmh.movePoints = h->movement;
//check if destination tile is available
bool canFly = h->getBonusAtTurn(Bonus::FLYING_MOVEMENT);
bool canWalkOnSea = h->getBonusAtTurn(Bonus::WATER_WALKING);
//it's a rock or blocked and not visitable tile
//OR hero is on land and dest is water and (there is not present only one object - boat)
if(((t.terType == ETerrainType::ROCK || (t.blocked && !t.visitable && !h->canFly() ))
if(((t.terType == ETerrainType::ROCK || (t.blocked && !t.visitable && !canFly))
&& complain("Cannot move hero, destination tile is blocked!"))
|| ((!h->boat && !h->canWalkOnSea() && !h->canFly() && t.terType == ETerrainType::WATER && (t.visitableObjects.size() < 1 || (t.visitableObjects.back()->ID != Obj::BOAT && t.visitableObjects.back()->ID != Obj::HERO))) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276)
|| ((!h->boat && !canWalkOnSea && !canFly && t.terType == ETerrainType::WATER && (t.visitableObjects.size() < 1 || (t.visitableObjects.back()->ID != Obj::BOAT && t.visitableObjects.back()->ID != Obj::HERO))) //hero is not on boat/water walking and dst water tile doesn't contain boat/hero (objs visitable from land) -> we test back cause boat may be on top of another object (#276)
&& complain("Cannot move hero, destination tile is on water!"))
|| ((h->boat && t.terType != ETerrainType::WATER && t.blocked)
&& complain("Cannot disembark hero, tile is blocked!"))
@ -1794,7 +1796,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
&& complain("Can not move garrisoned hero!"))
|| ((h->movement < cost && dst != h->pos && !teleporting)
&& complain("Hero doesn't have any movement points left!"))
|| ((transit && !h->canFly() && !CGTeleport::isTeleport(t.topVisitableObj()))
|| ((transit && !canFly && !CGTeleport::isTeleport(t.topVisitableObj()))
&& complain("Hero cannot transit over this tile!"))
/*|| (states.checkFlag(h->tempOwner, &PlayerStatus::engagedIntoBattle)
&& complain("Cannot move hero during the battle"))*/)
@ -1913,7 +1915,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, bo
if(CGTeleport::isTeleport(t.topVisitableObj()))
visitDest = DONT_VISIT_DEST;
if(h->canFly())
if(canFly)
{
lookForGuards = IGNORE_GUARDS;
visitDest = DONT_VISIT_DEST;