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:
parent
5106738160
commit
d3c8ca7c1c
@ -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;
|
||||
|
@ -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 -----
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user