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

Added encapsulation for movement points access

This commit is contained in:
Ivan Savenko 2023-06-21 20:38:26 +03:00
parent edf7756783
commit 08cfbe79cf
24 changed files with 86 additions and 74 deletions

View File

@ -802,8 +802,8 @@ void AIGateway::makeTurn()
//for debug purpose //for debug purpose
for (auto h : cb->getHeroesInfo()) for (auto h : cb->getHeroesInfo())
{ {
if (h->movement) if (h->movementPointsRemaining())
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movement); logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
} }
#if NKAI_TRACE_LEVEL == 0 #if NKAI_TRACE_LEVEL == 0
} }

View File

@ -187,7 +187,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(ai->nullkiller->isHeroLocked(existingHero) if(ai->nullkiller->isHeroLocked(existingHero)
|| existingHero->getArmyStrength() > hero->getArmyStrength() || existingHero->getArmyStrength() > hero->getArmyStrength()
|| ai->nullkiller->heroManager->getHeroRole(existingHero) == HeroRole::MAIN || ai->nullkiller->heroManager->getHeroRole(existingHero) == HeroRole::MAIN
|| existingHero->movement || existingHero->movementPointsRemaining()
|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1)) || existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
continue; continue;

View File

@ -206,7 +206,7 @@ Goals::TGoalVec StartupBehavior::decompose() const
for(const CGTownInstance * town : towns) for(const CGTownInstance * town : towns)
{ {
if(town->garrisonHero if(town->garrisonHero
&& town->garrisonHero->movement && town->garrisonHero->movementPointsRemaining()
&& !town->visitingHero && !town->visitingHero
&& ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE) && ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE)
{ {

View File

@ -790,7 +790,7 @@ public:
if(garrisonHero && swapCommand.getLockingReason() == HeroLockedReason::DEFENCE) if(garrisonHero && swapCommand.getLockingReason() == HeroLockedReason::DEFENCE)
{ {
auto defenderRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(garrisonHero); auto defenderRole = evaluationContext.evaluator.ai->heroManager->getHeroRole(garrisonHero);
auto mpLeft = garrisonHero->movement / (float)garrisonHero->maxMovePoints(true); auto mpLeft = garrisonHero->movementPointsRemaining() / (float)garrisonHero->movementPointsLimit(true);
evaluationContext.movementCost += mpLeft; evaluationContext.movementCost += mpLeft;
evaluationContext.movementCostByRole[defenderRole] += mpLeft; evaluationContext.movementCostByRole[defenderRole] += mpLeft;

View File

@ -78,7 +78,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
try try
{ {
if(hero->movement) if(hero->movementPointsRemaining() > 0)
{ {
ai->nullkiller->setActive(hero, node.coord); ai->nullkiller->setActive(hero, node.coord);
@ -117,7 +117,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
} }
} }
if(hero->movement) if(hero->movementPointsRemaining())
{ {
try try
{ {
@ -135,14 +135,14 @@ void ExecuteHeroChain::accept(AIGateway * ai)
return; return;
} }
if(hero->movement > 0) if(hero->movementPointsRemaining() > 0)
{ {
CGPath path; CGPath path;
bool isOk = cb->getPathsInfo(hero)->getPath(path, node.coord); bool isOk = cb->getPathsInfo(hero)->getPath(path, node.coord);
if(isOk && path.nodes.back().turns > 0) if(isOk && path.nodes.back().turns > 0)
{ {
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->getNameTranslated(), hero->movement, node.coord.toString()); logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->getNameTranslated(), hero->movementPointsRemaining(), node.coord.toString());
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN); ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
return; return;

View File

@ -888,7 +888,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
if(actor->hero->tempOwner != ai->playerID) if(actor->hero->tempOwner != ai->playerID)
{ {
bool onLand = !actor->hero->boat || actor->hero->boat->layer != EPathfindingLayer::SAIL; bool onLand = !actor->hero->boat || actor->hero->boat->layer != EPathfindingLayer::SAIL;
actor->initialMovement = actor->hero->maxMovePoints(onLand); actor->initialMovement = actor->hero->movementPointsLimit(onLand);
} }
playerID = actor->hero->tempOwner; playerID = actor->hero->tempOwner;
@ -1053,7 +1053,7 @@ struct TowmPortalFinder
return std::nullopt; return std::nullopt;
AIPathNode * node = nodeOptional.value(); AIPathNode * node = nodeOptional.value();
float movementCost = (float)movementNeeded / (float)hero->maxMovePoints(EPathfindingLayer::LAND); float movementCost = (float)movementNeeded / (float)hero->movementPointsLimit(EPathfindingLayer::LAND);
movementCost += bestNode->getCost(); movementCost += bestNode->getCost();

View File

@ -43,7 +43,7 @@ ChainActor::ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t
{ {
initialPosition = hero->visitablePos(); initialPosition = hero->visitablePos();
layer = hero->boat ? hero->boat->layer : EPathfindingLayer::LAND; layer = hero->boat ? hero->boat->layer : EPathfindingLayer::LAND;
initialMovement = hero->movement; initialMovement = hero->movementPointsRemaining();
initialTurn = 0; initialTurn = 0;
armyValue = hero->getArmyStrength(); armyValue = hero->getArmyStrength();
heroFightingStrength = hero->getFightingStrength(); heroFightingStrength = hero->getFightingStrength();
@ -75,7 +75,7 @@ int ChainActor::maxMovePoints(CGPathNode::ELayer layer)
throw std::logic_error("Asking movement points for static actor"); throw std::logic_error("Asking movement points for static actor");
#endif #endif
return hero->maxMovePointsCached(layer, tiCache.get()); return hero->movementPointsLimitCached(layer, tiCache.get());
} }
std::string ChainActor::toString() const std::string ChainActor::toString() const

View File

@ -267,7 +267,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
if(!ai->isAbleToExplore(h)) if(!ai->isAbleToExplore(h))
return true; return true;
return !h->movement; //saves time, immobile heroes are useless anyway return !h->movementPointsRemaining(); //saves time, immobile heroes are useless anyway
}); });
} }

View File

@ -113,7 +113,7 @@ std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
auto initialNode = getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value(); auto initialNode = getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value();
initialNode->turns = 0; initialNode->turns = 0;
initialNode->moveRemains = hero->movement; initialNode->moveRemains = hero->movementPointsRemaining();
initialNode->danger = 0; initialNode->danger = 0;
initialNode->setCost(0.0); initialNode->setCost(0.0);
@ -245,7 +245,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
auto skillLevel = hero->getSpellSchoolLevel(townPortal); auto skillLevel = hero->getSpellSchoolLevel(townPortal);
auto movementCost = GameConstants::BASE_MOVEMENT_COST * (skillLevel >= 3 ? 2 : 3); auto movementCost = GameConstants::BASE_MOVEMENT_COST * (skillLevel >= 3 ? 2 : 3);
if(hero->movement < movementCost) if(hero->movementPointsRemaining() < movementCost)
{ {
return; return;
} }

View File

@ -813,8 +813,8 @@ void VCAI::makeTurn()
//for debug purpose //for debug purpose
for (auto h : cb->getHeroesInfo()) for (auto h : cb->getHeroesInfo())
{ {
if (h->movement) if (h->movementPointsRemaining())
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movement); logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movementPointsRemaining());
} }
} }
catch (boost::thread_interrupted & e) catch (boost::thread_interrupted & e)
@ -949,7 +949,7 @@ void VCAI::mainLoop()
if (bestGoal->hero) //lock this hero to fulfill goal if (bestGoal->hero) //lock this hero to fulfill goal
{ {
setGoal(bestGoal->hero, bestGoal); setGoal(bestGoal->hero, bestGoal);
if (!bestGoal->hero->movement || vstd::contains(invalidPathHeroes, bestGoal->hero)) if (!bestGoal->hero->movementPointsRemaining() || vstd::contains(invalidPathHeroes, bestGoal->hero))
{ {
if (!vstd::erase_if_present(possibleGoals, bestGoal)) if (!vstd::erase_if_present(possibleGoals, bestGoal))
{ {
@ -1354,7 +1354,7 @@ void VCAI::wander(HeroPtr h)
TimeCheck tc("looking for wander destination"); TimeCheck tc("looking for wander destination");
while(h->movement) while(h->movementPointsRemaining())
{ {
validateVisitableObjs(); validateVisitableObjs();
ah->updatePaths(getMyHeroes()); ah->updatePaths(getMyHeroes());
@ -2031,7 +2031,7 @@ void VCAI::tryRealize(Goals::RecruitHero & g)
void VCAI::tryRealize(Goals::VisitTile & g) void VCAI::tryRealize(Goals::VisitTile & g)
{ {
if(!g.hero->movement) if(!g.hero->movementPointsRemaining())
throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!"); throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{ {
@ -2047,7 +2047,7 @@ void VCAI::tryRealize(Goals::VisitTile & g)
void VCAI::tryRealize(Goals::VisitObj & g) void VCAI::tryRealize(Goals::VisitObj & g)
{ {
auto position = g.tile; auto position = g.tile;
if(!g.hero->movement) if(!g.hero->movementPointsRemaining())
throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!"); throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!");
if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2) if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{ {
@ -2062,7 +2062,7 @@ void VCAI::tryRealize(Goals::VisitObj & g)
void VCAI::tryRealize(Goals::VisitHero & g) void VCAI::tryRealize(Goals::VisitHero & g)
{ {
if(!g.hero->movement) if(!g.hero->movementPointsRemaining())
throw cannotFulfillGoalException("Cannot visit target hero: hero is out of MPs!"); throw cannotFulfillGoalException("Cannot visit target hero: hero is out of MPs!");
const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid)); const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(g.objid));
@ -2263,7 +2263,7 @@ bool VCAI::canAct(HeroPtr h) const
return false; return false;
} }
return h->movement; return h->movementPointsRemaining();
} }
HeroPtr VCAI::primaryHero() const HeroPtr VCAI::primaryHero() const
@ -2412,7 +2412,7 @@ void VCAI::performTypicalActions()
if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
continue; continue;
logAi->debug("Hero %s started wandering, MP=%d", h->getNameTranslated(), h->movement); logAi->debug("Hero %s started wandering, MP=%d", h->getNameTranslated(), h->movementPointsRemaining());
makePossibleUpgrades(*h); makePossibleUpgrades(*h);
pickBestArtifacts(*h); pickBestArtifacts(*h);
try try

View File

@ -388,7 +388,7 @@ void ClientCommandManager::handleTellCommand(std::istringstream& singleWordBuffe
void ClientCommandManager::handleMpCommand() void ClientCommandManager::handleMpCommand()
{ {
if(const CGHeroInstance* h = LOCPLINT->localState->getCurrentHero()) if(const CGHeroInstance* h = LOCPLINT->localState->getCurrentHero())
printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n"); printCommandMessage(std::to_string(h->movementPointsRemaining()) + "; max: " + std::to_string(h->movementPointsLimit(true)) + "/" + std::to_string(h->movementPointsLimit(false)) + "\n");
} }
void ClientCommandManager::handleSetCommand(std::istringstream& singleWordBuffer) void ClientCommandManager::handleSetCommand(std::istringstream& singleWordBuffer)

View File

@ -124,7 +124,7 @@ const CGHeroInstance * PlayerLocalState::getNextWanderingHero(const CGHeroInstan
if (isHeroSleeping(hero)) if (isHeroSleeping(hero))
continue; continue;
if (hero->movement == 0) if (hero->movementPointsRemaining() == 0)
continue; continue;
if (!firstSuitable) if (!firstSuitable)

View File

@ -676,7 +676,7 @@ void AdventureMapInterface::onTileHovered(const int3 &mapPos)
void AdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode) void AdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & hero, const CGPathNode & pathNode)
{ {
const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.maxMovePoints(pathNode.layer == EPathfindingLayer::LAND) : hero.movement; const int maxMovementPointsAtStartOfLastTurn = pathNode.turns > 0 ? hero.movementPointsLimit(pathNode.layer == EPathfindingLayer::LAND) : hero.movementPointsRemaining();
const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains; const int movementPointsLastTurnCost = maxMovementPointsAtStartOfLastTurn - pathNode.moveRemains;
const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0; const int remainingPointsAfterMove = pathNode.turns == 0 ? pathNode.moveRemains : 0;

View File

@ -224,7 +224,7 @@ void AdventureMapShortcuts::endTurn()
{ {
for(auto hero : LOCPLINT->localState->getWanderingHeroes()) for(auto hero : LOCPLINT->localState->getWanderingHeroes())
{ {
if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0) if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movementPointsRemaining() > 0)
{ {
// Only show hero reminder if conditions met: // Only show hero reminder if conditions met:
// - There still movement points // - There still movement points
@ -418,7 +418,7 @@ bool AdventureMapShortcuts::optionHeroSelected()
bool AdventureMapShortcuts::optionHeroCanMove() bool AdventureMapShortcuts::optionHeroCanMove()
{ {
const auto * hero = LOCPLINT->localState->getCurrentHero(); const auto * hero = LOCPLINT->localState->getCurrentHero();
return optionInMapView() && hero && hero->movement != 0 && LOCPLINT->localState->hasPath(hero); return optionInMapView() && hero && hero->movementPointsRemaining() != 0 && LOCPLINT->localState->hasPath(hero);
} }
bool AdventureMapShortcuts::optionHasNextHero() bool AdventureMapShortcuts::optionHasNextHero()

View File

@ -234,7 +234,7 @@ CHeroList::CHeroItem::CHeroItem(CHeroList *parent, const CGHeroInstance * Hero)
void CHeroList::CHeroItem::update() void CHeroList::CHeroItem::update()
{ {
movement->setFrame(std::min<size_t>(movement->size()-1, hero->movement / 100)); movement->setFrame(std::min<size_t>(movement->size()-1, hero->movementPointsRemaining() / 100));
mana->setFrame(std::min<size_t>(mana->size()-1, hero->mana / 5)); mana->setFrame(std::min<size_t>(mana->size()-1, hero->mana / 5));
redraw(); redraw();
} }

View File

@ -903,11 +903,9 @@ void SetMovePoints::applyGs(CGameState * gs) const
assert(hero); assert(hero);
if(absolute) if(absolute)
hero->movement = val; hero->setMovementPoints(val);
else else
hero->movement += val; hero->setMovementPoints(hero->movementPointsRemaining() + val);
vstd::amax(hero->movement, 0); //not less than 0
} }
void FoWChange::applyGs(CGameState *gs) void FoWChange::applyGs(CGameState *gs)
@ -1276,7 +1274,7 @@ void TryMoveHero::applyGs(CGameState *gs)
return; return;
} }
h->movement = movePoints; h->setMovementPoints(movePoints);
if((result == SUCCESS || result == BLOCKING_VISIT || result == EMBARK || result == DISEMBARK) && start != end) if((result == SUCCESS || result == BLOCKING_VISIT || result == EMBARK || result == DISEMBARK) && start != end)
{ {
@ -1422,11 +1420,11 @@ void HeroRecruited::applyGs(CGameState * gs) const
{ // this is a fresh hero who hasn't appeared yet { // this is a fresh hero who hasn't appeared yet
if (boatId >= 0) //Hero spawns on water if (boatId >= 0) //Hero spawns on water
{ {
h->movement = h->maxMovePoints(false); h->setMovementPoints(h->movementPointsLimit(false));
} }
else else
{ {
h->movement = h->maxMovePoints(true); h->setMovementPoints(h->movementPointsLimit(true));
} }
} }
@ -1479,7 +1477,7 @@ void GiveHero::applyGs(CGameState * gs) const
h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front(); h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front();
h->setOwner(player); h->setOwner(player);
h->movement = h->maxMovePoints(true); h->setMovementPoints(h->movementPointsLimit(true));
h->pos = h->convertFromVisitablePos(oldVisitablePos); h->pos = h->convertFromVisitablePos(oldVisitablePos);
gs->map->heroesOnMap.emplace_back(h); gs->map->heroesOnMap.emplace_back(h);
gs->getPlayerState(h->getOwner())->heroes.emplace_back(h); gs->getPlayerState(h->getOwner())->heroes.emplace_back(h);
@ -2052,7 +2050,7 @@ void NewTurn::applyGs(CGameState *gs)
logGlobal->error("Hero %d not found in NewTurn::applyGs", h.id.getNum()); logGlobal->error("Hero %d not found in NewTurn::applyGs", h.id.getNum());
continue; continue;
} }
hero->movement = h.move; hero->setMovementPoints(h.move);
hero->mana = h.mana; hero->mana = h.mana;
} }

View File

@ -65,7 +65,7 @@ static int lowestSpeed(const CGHeroInstance * chi)
return ret; return ret;
} }
ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const
{ {
int64_t ret = GameConstants::BASE_MOVEMENT_COST; int64_t ret = GameConstants::BASE_MOVEMENT_COST;
@ -201,10 +201,20 @@ bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const
return true; return true;
} }
int CGHeroInstance::maxMovePoints(bool onLand) const int CGHeroInstance::movementPointsRemaining() const
{
return movement;
}
void CGHeroInstance::setMovementPoints(int points)
{
movement = std::max(0, points);
}
int CGHeroInstance::movementPointsLimit(bool onLand) const
{ {
TurnInfo ti(this); TurnInfo ti(this);
return maxMovePointsCached(onLand, &ti); return movementPointsLimitCached(onLand, &ti);
} }
int CGHeroInstance::getLowestCreatureSpeed() const int CGHeroInstance::getLowestCreatureSpeed() const
@ -224,7 +234,7 @@ void CGHeroInstance::updateArmyMovementBonus(bool onLand, const TurnInfo * ti) c
} }
} }
int CGHeroInstance::maxMovePointsCached(bool onLand, const TurnInfo * ti) const int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti) const
{ {
updateArmyMovementBonus(onLand, ti); updateArmyMovementBonus(onLand, ti);
return ti->valOfBonuses(BonusType::MOVEMENT, !!onLand); return ti->valOfBonuses(BonusType::MOVEMENT, !!onLand);
@ -454,14 +464,14 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
const auto boatPos = visitablePos(); const auto boatPos = visitablePos();
if (cb->gameState()->map->getTile(boatPos).isWater()) if (cb->gameState()->map->getTile(boatPos).isWater())
{ {
smp.val = maxMovePoints(false); smp.val = movementPointsLimit(false);
//Create a new boat for hero //Create a new boat for hero
cb->createObject(boatPos, Obj::BOAT, getBoatType().getNum()); cb->createObject(boatPos, Obj::BOAT, getBoatType().getNum());
boatId = cb->getTopObj(boatPos)->id; boatId = cb->getTopObj(boatPos)->id;
} }
else else
{ {
smp.val = maxMovePoints(true); smp.val = movementPointsLimit(true);
} }
cb->giveHero(id, h->tempOwner, boatId); //recreates def and adds hero to player cb->giveHero(id, h->tempOwner, boatId); //recreates def and adds hero to player
cb->setObjProperty(id, ObjProperty::ID, Obj::HERO); //set ID to 34 AFTER hero gets correct flag color cb->setObjProperty(id, ObjProperty::ID, Obj::HERO); //set ID to 34 AFTER hero gets correct flag color
@ -1170,7 +1180,7 @@ int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool
EDiggingStatus CGHeroInstance::diggingStatus() const EDiggingStatus CGHeroInstance::diggingStatus() const
{ {
if(static_cast<int>(movement) < maxMovePoints(true)) if(static_cast<int>(movement) < movementPointsLimit(true))
return EDiggingStatus::LACK_OF_MOVEMENT; return EDiggingStatus::LACK_OF_MOVEMENT;
if(!VLC->arth->objects[ArtifactID::GRAIL]->canBePutAt(this)) if(!VLC->arth->objects[ArtifactID::GRAIL]->canBePutAt(this))
return EDiggingStatus::BACKPACK_IS_FULL; return EDiggingStatus::BACKPACK_IS_FULL;

View File

@ -49,6 +49,7 @@ class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator,
private: private:
std::set<SpellID> spells; //known spells (spell IDs) std::set<SpellID> spells; //known spells (spell IDs)
mutable int lowestCreatureSpeed; mutable int lowestCreatureSpeed;
ui32 movement; //remaining movement points
public: public:
@ -67,7 +68,6 @@ public:
si32 portrait; //may be custom si32 portrait; //may be custom
si32 mana; // remaining spell points si32 mana; // remaining spell points
std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities std::vector<std::pair<SecondarySkill,ui8> > secSkills; //first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert); if hero has ability (-1, -1) it meansthat it should have default secondary abilities
ui32 movement; //remaining movement points
EHeroGender gender; EHeroGender gender;
std::string nameCustom; std::string nameCustom;
@ -155,7 +155,6 @@ public:
EAlignment getAlignment() const; EAlignment getAlignment() const;
bool needsLastStack()const override; bool needsLastStack()const override;
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
//INativeTerrainProvider //INativeTerrainProvider
FactionID getFaction() const override; FactionID getFaction() const override;
TerrainId getNativeTerrain() const override; TerrainId getNativeTerrain() const override;
@ -196,9 +195,14 @@ public:
void setSecSkillLevel(const SecondarySkill & which, int val, bool abs); // abs == 0 - changes by value; 1 - sets to value void setSecSkillLevel(const SecondarySkill & which, int val, bool abs); // abs == 0 - changes by value; 1 - sets to value
void levelUp(const std::vector<SecondarySkill> & skills); void levelUp(const std::vector<SecondarySkill> & skills);
int maxMovePoints(bool onLand) const; /// returns base movement cost for movement between specific tiles. Does not accounts for diagonal movement or last tile exception
ui32 getTileMovementCost(const TerrainTile & dest, const TerrainTile & from, const TurnInfo * ti) const;
void setMovementPoints(int points);
int movementPointsRemaining() const;
int movementPointsLimit(bool onLand) const;
//cached version is much faster, TurnInfo construction is costly //cached version is much faster, TurnInfo construction is costly
int maxMovePointsCached(bool onLand, const TurnInfo * ti) const; int movementPointsLimitCached(bool onLand, const TurnInfo * ti) const;
//update army movement bonus //update army movement bonus
void updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const; void updateArmyMovementBonus(bool onLand, const TurnInfo * ti) const;

View File

@ -621,7 +621,7 @@ int CPathfinderHelper::getMovementCost(
bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasBonusOfType(BonusType::FLYING_MOVEMENT); bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasBonusOfType(BonusType::FLYING_MOVEMENT);
int ret = hero->getTileCost(*dt, *ct, ti); int ret = hero->getTileMovementCost(*dt, *ct, ti);
if(isSailLayer) if(isSailLayer)
{ {
if(ct->hasFavorableWinds()) if(ct->hasFavorableWinds())

View File

@ -123,7 +123,7 @@ std::vector<CGPathNode *> NodeStorage::getInitialNodes()
auto * initialNode = getNode(out.hpos, out.hero->boat ? out.hero->boat->layer : EPathfindingLayer::LAND); auto * initialNode = getNode(out.hpos, out.hero->boat ? out.hero->boat->layer : EPathfindingLayer::LAND);
initialNode->turns = 0; initialNode->turns = 0;
initialNode->moveRemains = out.hero->movement; initialNode->moveRemains = out.hero->movementPointsRemaining();
initialNode->setCost(0.0); initialNode->setCost(0.0);
if(!initialNode->coord.valid()) if(!initialNode->coord.valid())

View File

@ -107,9 +107,9 @@ int TurnInfo::valOfBonuses(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->maxMovePointsCached(true, this); maxMovePointsLand = hero->movementPointsLimitCached(true, this);
if(maxMovePointsWater == -1) if(maxMovePointsWater == -1)
maxMovePointsWater = hero->maxMovePointsCached(false, this); maxMovePointsWater = hero->movementPointsLimitCached(false, this);
return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand; return layer == EPathfindingLayer::SAIL ? maxMovePointsWater : maxMovePointsLand;
} }

View File

@ -80,10 +80,10 @@ void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Re
{ {
SetMovePoints smp; SetMovePoints smp;
smp.hid = hero->id; smp.hid = hero->id;
smp.val = hero->movement; smp.val = hero->movementPointsRemaining();
if (info.reward.movePercentage >= 0) // percent from max if (info.reward.movePercentage >= 0) // percent from max
smp.val = hero->maxMovePoints(hero->boat && hero->boat->layer == EPathfindingLayer::SAIL) * info.reward.movePercentage / 100; smp.val = hero->movementPointsLimit(hero->boat && hero->boat->layer == EPathfindingLayer::SAIL) * info.reward.movePercentage / 100;
smp.val = std::max<si32>(0, smp.val + info.reward.movePoints); smp.val = std::max<si32>(0, smp.val + info.reward.movePoints);
cb->setMovePoints(&smp); cb->setMovePoints(&smp);

View File

@ -297,7 +297,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
} }
if(parameters.caster->getHeroCaster()->movement <= 0) //unlike town portal non-zero MP is enough if(parameters.caster->getHeroCaster()->movementPointsRemaining() <= 0) //unlike town portal non-zero MP is enough
{ {
env->complain("Hero needs movement points to cast Dimension Door!"); env->complain("Hero needs movement points to cast Dimension Door!");
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
@ -335,8 +335,8 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
{ {
SetMovePoints smp; SetMovePoints smp;
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
if(movementCost < static_cast<int>(parameters.caster->getHeroCaster()->movement)) if(movementCost < static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()))
smp.val = parameters.caster->getHeroCaster()->movement - movementCost; smp.val = parameters.caster->getHeroCaster()->movementPointsRemaining() - movementCost;
else else
smp.val = 0; smp.val = 0;
env->apply(&smp); env->apply(&smp);
@ -369,7 +369,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
if(nullptr == destination) if(nullptr == destination)
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
if(static_cast<int>(parameters.caster->getHeroCaster()->movement) < moveCost) if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost)
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
if(destination->visitingHero) if(destination->visitingHero)
@ -419,7 +419,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
} }
if(static_cast<int>(parameters.caster->getHeroCaster()->movement) < moveCost) if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost)
{ {
env->complain("This hero has not enough movement points!"); env->complain("This hero has not enough movement points!");
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
@ -441,7 +441,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
{ {
SetMovePoints smp; SetMovePoints smp;
smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId()); smp.hid = ObjectInstanceID(parameters.caster->getCasterUnitId());
smp.val = std::max<ui32>(0, parameters.caster->getHeroCaster()->movement - moveCost); smp.val = std::max<ui32>(0, parameters.caster->getHeroCaster()->movementPointsRemaining() - moveCost);
env->apply(&smp); env->apply(&smp);
} }
return ESpellCastResult::OK; return ESpellCastResult::OK;
@ -468,7 +468,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
const int moveCost = movementCost(parameters); const int moveCost = movementCost(parameters);
if(static_cast<int>(parameters.caster->getHeroCaster()->movement) < moveCost) if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost)
{ {
InfoWindow iw; InfoWindow iw;
iw.player = parameters.caster->getCasterOwner(); iw.player = parameters.caster->getCasterOwner();

View File

@ -1773,9 +1773,9 @@ void CGameHandler::newTurn()
if (hero->isInitialized() && hero->stacks.size()) if (hero->isInitialized() && hero->stacks.size())
{ {
// reset retreated or surrendered heroes // reset retreated or surrendered heroes
auto maxmove = hero->maxMovePoints(true); auto maxmove = hero->movementPointsLimit(true);
// if movement is greater than maxmove, we should decrease it // if movement is greater than maxmove, we should decrease it
if (hero->movement != maxmove || hero->mana < hero->manaLimit()) if (hero->movementPointsRemaining() != maxmove || hero->mana < hero->manaLimit())
{ {
NewTurn::Hero hth; NewTurn::Hero hth;
hth.id = hero->id; hth.id = hero->id;
@ -1864,7 +1864,7 @@ void CGameHandler::newTurn()
hth.id = h->id; hth.id = h->id;
auto ti = std::make_unique<TurnInfo>(h, 1); auto ti = std::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->maxMovePointsCached(gs->map->getTile(h->visitablePos()).terType->isLand(), ti.get()); hth.move = h->movementPointsLimitCached(gs->map->getTile(h->visitablePos()).terType->isLand(), ti.get());
hth.mana = h->getManaNewTurn(); hth.mana = h->getManaNewTurn();
n.heroes.insert(hth); n.heroes.insert(hth);
@ -2280,7 +2280,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
tmh.start = h->pos; tmh.start = h->pos;
tmh.end = dst; tmh.end = dst;
tmh.result = TryMoveHero::FAILED; tmh.result = TryMoveHero::FAILED;
tmh.movePoints = h->movement; tmh.movePoints = h->movementPointsRemaining();
//check if destination tile is available //check if destination tile is available
auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, h, PathfinderOptions()); auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gs, h, PathfinderOptions());
@ -2288,7 +2288,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
const bool canFly = pathfinderHelper->hasBonusOfType(BonusType::FLYING_MOVEMENT) || (h->boat && h->boat->layer == EPathfindingLayer::AIR); const bool canFly = pathfinderHelper->hasBonusOfType(BonusType::FLYING_MOVEMENT) || (h->boat && h->boat->layer == EPathfindingLayer::AIR);
const bool canWalkOnSea = pathfinderHelper->hasBonusOfType(BonusType::WATER_WALKING) || (h->boat && h->boat->layer == EPathfindingLayer::WATER); const bool canWalkOnSea = pathfinderHelper->hasBonusOfType(BonusType::WATER_WALKING) || (h->boat && h->boat->layer == EPathfindingLayer::WATER);
const int cost = pathfinderHelper->getMovementCost(h->visitablePos(), hmpos, nullptr, nullptr, h->movement); const int cost = pathfinderHelper->getMovementCost(h->visitablePos(), hmpos, nullptr, nullptr, h->movementPointsRemaining());
//it's a rock or blocked and not visitable tile //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) //OR hero is on land and dest is water and (there is not present only one object - boat)
@ -2302,7 +2302,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
&& complain("Tiles are not neighboring!")) && complain("Tiles are not neighboring!"))
|| ((h->inTownGarrison) || ((h->inTownGarrison)
&& complain("Can not move garrisoned hero!")) && complain("Can not move garrisoned hero!"))
|| (((int)h->movement < cost && dst != h->pos && !teleporting) || (((int)h->movementPointsRemaining() < cost && dst != h->pos && !teleporting)
&& complain("Hero doesn't have any movement points left!")) && complain("Hero doesn't have any movement points left!"))
|| ((transit && !canFly && !CGTeleport::isTeleport(t.topVisitableObj())) || ((transit && !canFly && !CGTeleport::isTeleport(t.topVisitableObj()))
&& complain("Hero cannot transit over this tile!")) && complain("Hero cannot transit over this tile!"))
@ -2387,14 +2387,14 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
if (!transit && embarking) if (!transit && embarking)
{ {
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, false, ti); tmh.movePoints = h->movementPointsAfterEmbark(h->movementPointsRemaining(), cost, false, ti);
return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE); return doMove(TryMoveHero::EMBARK, IGNORE_GUARDS, DONT_VISIT_DEST, LEAVING_TILE);
// In H3 embark ignore guards // In H3 embark ignore guards
} }
if (disembarking) if (disembarking)
{ {
tmh.movePoints = h->movementPointsAfterEmbark(h->movement, cost, true, ti); tmh.movePoints = h->movementPointsAfterEmbark(h->movementPointsRemaining(), cost, true, ti);
return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE); return doMove(TryMoveHero::DISEMBARK, CHECK_FOR_GUARDS, VISIT_DEST, LEAVING_TILE);
} }
@ -2420,8 +2420,8 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
//still here? it is standard movement! //still here? it is standard movement!
{ {
tmh.movePoints = (int)h->movement >= cost tmh.movePoints = (int)h->movementPointsRemaining() >= cost
? h->movement - cost ? h->movementPointsRemaining() - cost
: 0; : 0;
EGuardLook lookForGuards = CHECK_FOR_GUARDS; EGuardLook lookForGuards = CHECK_FOR_GUARDS;