1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-06 23:26:26 +02:00

Remove ConstTransitivePtr from hero and town instances

This commit is contained in:
Ivan Savenko 2025-03-09 21:51:33 +00:00
parent 62234fcf92
commit 417ea6451a
57 changed files with 436 additions and 390 deletions

View File

@ -904,19 +904,19 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
switch(obj->ID) switch(obj->ID)
{ {
case Obj::TOWN: case Obj::TOWN:
if(h->visitedTown) //we are inside, not just attacking if(h->getVisitedTown()) //we are inside, not just attacking
{ {
makePossibleUpgrades(h.get()); makePossibleUpgrades(h.get());
std::unique_lock lockGuard(nullkiller->aiStateMutex); std::unique_lock lockGuard(nullkiller->aiStateMutex);
if(!h->visitedTown->garrisonHero || !nullkiller->isHeroLocked(h->visitedTown->garrisonHero)) if(!h->getVisitedTown()->getGarrisonHero() || !nullkiller->isHeroLocked(h->getVisitedTown()->getGarrisonHero()))
moveCreaturesToHero(h->visitedTown); moveCreaturesToHero(h->getVisitedTown());
if(nullkiller->heroManager->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook() if(nullkiller->heroManager->getHeroRole(h) == HeroRole::MAIN && !h->hasSpellbook()
&& nullkiller->getFreeGold() >= GameConstants::SPELLBOOK_GOLD_COST) && nullkiller->getFreeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
{ {
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1)) if(h->getVisitedTown()->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK); cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
} }
} }
@ -929,9 +929,9 @@ void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h
void AIGateway::moveCreaturesToHero(const CGTownInstance * t) void AIGateway::moveCreaturesToHero(const CGTownInstance * t)
{ {
if(t->visitingHero && t->armedGarrison() && t->visitingHero->tempOwner == t->tempOwner) if(t->getVisitingHero() && t->armedGarrison() && t->getVisitingHero()->tempOwner == t->tempOwner)
{ {
pickBestCreatures(t->visitingHero, t->getUpperArmy()); pickBestCreatures(t->getVisitingHero(), t->getUpperArmy());
} }
} }
@ -1282,10 +1282,10 @@ void AIGateway::addVisitableObj(const CGObjectInstance * obj)
bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h) bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
{ {
if(h->inTownGarrison && h->visitedTown) if(h->isGarrisoned() && h->getVisitedTown())
{ {
cb->swapGarrisonHero(h->visitedTown); cb->swapGarrisonHero(h->getVisitedTown());
moveCreaturesToHero(h->visitedTown); moveCreaturesToHero(h->getVisitedTown());
} }
//TODO: consider if blockVisit objects change something in our checks: AIUtility::isBlockVisitObj() //TODO: consider if blockVisit objects change something in our checks: AIUtility::isBlockVisitObj()
@ -1596,7 +1596,7 @@ void AIGateway::endTurn()
void AIGateway::buildArmyIn(const CGTownInstance * t) void AIGateway::buildArmyIn(const CGTownInstance * t)
{ {
makePossibleUpgrades(t->visitingHero); makePossibleUpgrades(t->getVisitingHero());
makePossibleUpgrades(t); makePossibleUpgrades(t);
recruitCreatures(t, t->getUpperArmy()); recruitCreatures(t, t->getUpperArmy());
moveCreaturesToHero(t); moveCreaturesToHero(t);

View File

@ -767,7 +767,7 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
bool townHasFreeTavern(const CGTownInstance * town) bool townHasFreeTavern(const CGTownInstance * town)
{ {
if(!town->hasBuilt(BuildingID::TAVERN)) return false; if(!town->hasBuilt(BuildingID::TAVERN)) return false;
if(!town->visitingHero) return true; if(!town->getVisitingHero()) return true;
bool canMoveVisitingHeroToGarrison = !town->getUpperArmy()->stacksCount(); bool canMoveVisitingHeroToGarrison = !town->getUpperArmy()->stacksCount();
@ -778,9 +778,9 @@ uint64_t getHeroArmyStrengthWithCommander(const CGHeroInstance * hero, const CCr
{ {
auto armyStrength = heroArmy->getArmyStrength(fortLevel); auto armyStrength = heroArmy->getArmyStrength(fortLevel);
if(hero && hero->commander && hero->commander->alive) if(hero && hero->getCommander() && hero->getCommander()->alive)
{ {
armyStrength += 100 * hero->commander->level; armyStrength += 100 * hero->getCommander()->level;
} }
return armyStrength; return armyStrength;

View File

@ -93,8 +93,8 @@ void DangerHitMapAnalyzer::updateHitMap()
{ {
auto town = dynamic_cast<const CGTownInstance *>(obj); auto town = dynamic_cast<const CGTownInstance *>(obj);
if(town->garrisonHero) if(town->getGarrisonHero())
heroes[town->garrisonHero->tempOwner][town->garrisonHero] = HeroRole::MAIN; heroes[town->getGarrisonHero()->tempOwner][town->getGarrisonHero()] = HeroRole::MAIN;
} }
} }

View File

@ -293,7 +293,7 @@ const CGHeroInstance * HeroManager::findWeakHeroToDismiss(uint64_t armyLimit, co
|| existingHero->getArmyStrength() >armyLimit || existingHero->getArmyStrength() >armyLimit
|| getHeroRole(existingHero) == HeroRole::MAIN || getHeroRole(existingHero) == HeroRole::MAIN
|| existingHero->movementPointsRemaining() || existingHero->movementPointsRemaining()
|| (townToSpare != nullptr && existingHero->visitedTown == townToSpare) || (townToSpare != nullptr && existingHero->getVisitedTown() == townToSpare)
|| existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1)) || existingHero->artifactsWorn.size() > (existingHero->hasSpellbook() ? 2 : 1))
{ {
continue; continue;

View File

@ -109,30 +109,30 @@ void handleCounterAttack(
bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoalVec & tasks, const Nullkiller * ai) bool handleGarrisonHeroFromPreviousTurn(const CGTownInstance * town, Goals::TGoalVec & tasks, const Nullkiller * ai)
{ {
if(ai->isHeroLocked(town->garrisonHero.get())) if(ai->isHeroLocked(town->getGarrisonHero()))
{ {
logAi->trace( logAi->trace(
"Hero %s in garrison of town %s is supposed to defend the town", "Hero %s in garrison of town %s is supposed to defend the town",
town->garrisonHero->getNameTranslated(), town->getGarrisonHero()->getNameTranslated(),
town->getNameTranslated()); town->getNameTranslated());
return true; return true;
} }
if(!town->visitingHero) if(!town->getVisitingHero())
{ {
if(ai->cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER) if(ai->cb->getHeroCount(ai->playerID, false) < GameConstants::MAX_HEROES_PER_PLAYER)
{ {
logAi->trace( logAi->trace(
"Extracting hero %s from garrison of town %s", "Extracting hero %s from garrison of town %s",
town->garrisonHero->getNameTranslated(), town->getGarrisonHero()->getNameTranslated(),
town->getNameTranslated()); town->getNameTranslated());
tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5))); tasks.push_back(Goals::sptr(Goals::ExchangeSwapTownHeroes(town, nullptr).setpriority(5)));
return false; return false;
} }
else if(ai->heroManager->getHeroRole(town->garrisonHero.get()) == HeroRole::MAIN) else if(ai->heroManager->getHeroRole(town->getGarrisonHero()) == HeroRole::MAIN)
{ {
auto armyDismissLimit = 1000; auto armyDismissLimit = 1000;
auto heroToDismiss = ai->heroManager->findWeakHeroToDismiss(armyDismissLimit); auto heroToDismiss = ai->heroManager->findWeakHeroToDismiss(armyDismissLimit);
@ -160,7 +160,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
threats.push_back(threatNode.fastestDanger); // no guarantee that fastest danger will be there threats.push_back(threatNode.fastestDanger); // no guarantee that fastest danger will be there
if (town->garrisonHero && handleGarrisonHeroFromPreviousTurn(town, tasks, ai)) if (town->getGarrisonHero() && handleGarrisonHeroFromPreviousTurn(town, tasks, ai))
{ {
return; return;
} }
@ -233,16 +233,16 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
path.toString()); path.toString());
#endif #endif
auto townDefenseStrength = town->garrisonHero auto townDefenseStrength = town->getGarrisonHero()
? town->garrisonHero->getTotalStrength() ? town->getGarrisonHero()->getTotalStrength()
: (town->visitingHero ? town->visitingHero->getTotalStrength() : town->getUpperArmy()->getArmyStrength()); : (town->getVisitingHero() ? town->getVisitingHero()->getTotalStrength() : town->getUpperArmy()->getArmyStrength());
if(town->visitingHero && path.targetHero == town->visitingHero.get()) if(town->getVisitingHero() && path.targetHero == town->getVisitingHero())
{ {
if(path.getHeroStrength() < townDefenseStrength) if(path.getHeroStrength() < townDefenseStrength)
continue; continue;
} }
else if(town->garrisonHero && path.targetHero == town->garrisonHero.get()) else if(town->getGarrisonHero() && path.targetHero == town->getGarrisonHero())
{ {
if(path.getHeroStrength() < townDefenseStrength) if(path.getHeroStrength() < townDefenseStrength)
continue; continue;
@ -271,7 +271,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
continue; continue;
} }
if(path.targetHero == town->visitingHero.get() && path.exchangeCount == 1) if(path.targetHero == town->getVisitingHero() && path.exchangeCount == 1)
{ {
#if NKAI_TRACE_LEVEL >= 1 #if NKAI_TRACE_LEVEL >= 1
logAi->trace("Put %s to garrison of town %s", logAi->trace("Put %s to garrison of town %s",
@ -280,7 +280,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
#endif #endif
// dismiss creatures we are not able to pick to be able to hide in garrison // dismiss creatures we are not able to pick to be able to hide in garrison
if(town->garrisonHero if(town->getGarrisonHero()
|| town->getUpperArmy()->stacksCount() == 0 || town->getUpperArmy()->stacksCount() == 0
|| path.targetHero->canBeMergedWith(*town) || path.targetHero->canBeMergedWith(*town)
|| (town->getUpperArmy()->getArmyStrength() < 500 && town->fortLevel() >= CGTownInstance::CITADEL)) || (town->getUpperArmy()->getArmyStrength() < 500 && town->fortLevel() >= CGTownInstance::CITADEL))
@ -288,25 +288,25 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
tasks.push_back( tasks.push_back(
Goals::sptr(Composition() Goals::sptr(Composition()
.addNext(DefendTown(town, threat, path.targetHero)) .addNext(DefendTown(town, threat, path.targetHero))
.addNext(ExchangeSwapTownHeroes(town, town->visitingHero.get(), HeroLockedReason::DEFENCE)))); .addNext(ExchangeSwapTownHeroes(town, town->getVisitingHero(), HeroLockedReason::DEFENCE))));
} }
continue; continue;
} }
// main without army and visiting scout with army, very specific case // main without army and visiting scout with army, very specific case
if(town->visitingHero && town->getUpperArmy()->stacksCount() == 0 if(town->getVisitingHero() && town->getUpperArmy()->stacksCount() == 0
&& path.targetHero != town->visitingHero.get() && path.exchangeCount == 1 && path.turn() == 0 && path.targetHero != town->getVisitingHero() && path.exchangeCount == 1 && path.turn() == 0
&& ai->heroManager->evaluateHero(path.targetHero) > ai->heroManager->evaluateHero(town->visitingHero.get()) && ai->heroManager->evaluateHero(path.targetHero) > ai->heroManager->evaluateHero(town->getVisitingHero())
&& 10 * path.targetHero->getTotalStrength() < town->visitingHero->getTotalStrength()) && 10 * path.targetHero->getTotalStrength() < town->getVisitingHero()->getTotalStrength())
{ {
path.heroArmy = town->visitingHero.get(); path.heroArmy = town->getVisitingHero();
tasks.push_back( tasks.push_back(
Goals::sptr(Composition() Goals::sptr(Composition()
.addNext(DefendTown(town, threat, path)) .addNext(DefendTown(town, threat, path))
.addNextSequence({ .addNextSequence({
sptr(ExchangeSwapTownHeroes(town, town->visitingHero.get())), sptr(ExchangeSwapTownHeroes(town, town->getVisitingHero())),
sptr(ExecuteHeroChain(path, town)), sptr(ExecuteHeroChain(path, town)),
sptr(ExchangeSwapTownHeroes(town, path.targetHero, HeroLockedReason::DEFENCE)) sptr(ExchangeSwapTownHeroes(town, path.targetHero, HeroLockedReason::DEFENCE))
}))); })));
@ -350,22 +350,22 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
composition.addNext(DefendTown(town, threat, path)); composition.addNext(DefendTown(town, threat, path));
TGoalVec sequence; TGoalVec sequence;
if(town->garrisonHero && path.targetHero == town->garrisonHero.get() && path.exchangeCount == 1) if(town->getGarrisonHero() && path.targetHero == town->getGarrisonHero() && path.exchangeCount == 1)
{ {
composition.addNext(ExchangeSwapTownHeroes(town, town->garrisonHero.get(), HeroLockedReason::DEFENCE)); composition.addNext(ExchangeSwapTownHeroes(town, town->getGarrisonHero(), HeroLockedReason::DEFENCE));
tasks.push_back(Goals::sptr(composition)); tasks.push_back(Goals::sptr(composition));
#if NKAI_TRACE_LEVEL >= 1 #if NKAI_TRACE_LEVEL >= 1
logAi->trace("Locking hero %s in garrison of %s", logAi->trace("Locking hero %s in garrison of %s",
town->garrisonHero.get()->getObjectName(), town->getGarrisonHero()->getObjectName(),
town->getObjectName()); town->getObjectName());
#endif #endif
continue; continue;
} }
else if(town->visitingHero && path.targetHero != town->visitingHero && !path.containsHero(town->visitingHero)) else if(town->getVisitingHero() && path.targetHero != town->getVisitingHero() && !path.containsHero(town->getVisitingHero()))
{ {
if(town->garrisonHero && town->garrisonHero != path.targetHero) if(town->getGarrisonHero() && town->getGarrisonHero() != path.targetHero)
{ {
#if NKAI_TRACE_LEVEL >= 1 #if NKAI_TRACE_LEVEL >= 1
logAi->trace("Cancel moving %s to defend town %s as the town has garrison hero", logAi->trace("Cancel moving %s to defend town %s as the town has garrison hero",
@ -376,7 +376,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
} }
else if(path.turn() == 0) else if(path.turn() == 0)
{ {
sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->visitingHero.get()))); sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->getVisitingHero())));
} }
} }
@ -425,7 +425,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town, const Nullkiller * ai) const void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitMapInfo & threat, const CGTownInstance * town, const Nullkiller * ai) const
{ {
if (threat.turn > 0 || town->garrisonHero || town->visitingHero) if (threat.turn > 0 || town->getGarrisonHero() || town->getVisitingHero())
return; return;
if(town->hasBuilt(BuildingID::TAVERN) if(town->hasBuilt(BuildingID::TAVERN)
@ -461,25 +461,25 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
bool needSwap = false; bool needSwap = false;
const CGHeroInstance * heroToDismiss = nullptr; const CGHeroInstance * heroToDismiss = nullptr;
if(town->visitingHero) if(town->getVisitingHero())
{ {
if(!town->garrisonHero) if(!town->getGarrisonHero())
needSwap = true; needSwap = true;
else else
{ {
if(town->visitingHero->getArmyStrength() < town->garrisonHero->getArmyStrength()) if(town->getVisitingHero()->getArmyStrength() < town->getGarrisonHero()->getArmyStrength())
{ {
if(town->visitingHero->getArmyStrength() >= hero->getArmyStrength()) if(town->getVisitingHero()->getArmyStrength() >= hero->getArmyStrength())
continue; continue;
heroToDismiss = town->visitingHero.get(); heroToDismiss = town->getVisitingHero();
} }
else if(town->garrisonHero->getArmyStrength() >= hero->getArmyStrength()) else if(town->getGarrisonHero()->getArmyStrength() >= hero->getArmyStrength())
continue; continue;
else else
{ {
needSwap = true; needSwap = true;
heroToDismiss = town->garrisonHero.get(); heroToDismiss = town->getGarrisonHero();
} }
} }
@ -499,7 +499,7 @@ void DefenceBehavior::evaluateRecruitingHero(Goals::TGoalVec & tasks, const HitM
Goals::Composition recruitHeroComposition; Goals::Composition recruitHeroComposition;
if(needSwap) if(needSwap)
sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->visitingHero.get()))); sequence.push_back(sptr(ExchangeSwapTownHeroes(town, town->getVisitingHero())));
if(heroToDismiss) if(heroToDismiss)
sequence.push_back(sptr(DismissHero(heroToDismiss))); sequence.push_back(sptr(DismissHero(heroToDismiss)));

View File

@ -167,21 +167,21 @@ Goals::TGoalVec GatherArmyBehavior::deliverArmyToHero(const Nullkiller * ai, con
composition.addNext(heroExchange); composition.addNext(heroExchange);
if(hero->inTownGarrison && path.turn() == 0) if(hero->isGarrisoned() && path.turn() == 0)
{ {
auto lockReason = ai->getHeroLockedReason(hero); auto lockReason = ai->getHeroLockedReason(hero);
if(path.targetHero->visitedTown == hero->visitedTown) if(path.targetHero->getVisitedTown() == hero->getVisitedTown())
{ {
composition.addNextSequence({ composition.addNextSequence({
sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))}); sptr(ExchangeSwapTownHeroes(hero->getVisitedTown(), hero, lockReason))});
} }
else else
{ {
composition.addNextSequence({ composition.addNextSequence({
sptr(ExchangeSwapTownHeroes(hero->visitedTown)), sptr(ExchangeSwapTownHeroes(hero->getVisitedTown())),
sptr(exchangePath), sptr(exchangePath),
sptr(ExchangeSwapTownHeroes(hero->visitedTown, hero, lockReason))}); sptr(ExchangeSwapTownHeroes(hero->getVisitedTown(), hero, lockReason))});
} }
} }
else else
@ -262,7 +262,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
continue; continue;
} }
if(upgrader->visitingHero && (upgrader->visitingHero.get() != path.targetHero || path.exchangeCount == 1)) if(upgrader->getVisitingHero() && (upgrader->getVisitingHero() != path.targetHero || path.exchangeCount == 1))
{ {
#if NKAI_TRACE_LEVEL >= 2 #if NKAI_TRACE_LEVEL >= 2
logAi->trace("Ignore path. Town has visiting hero."); logAi->trace("Ignore path. Town has visiting hero.");
@ -289,7 +289,7 @@ Goals::TGoalVec GatherArmyBehavior::upgradeArmy(const Nullkiller * ai, const CGT
auto upgrade = ai->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources); auto upgrade = ai->armyManager->calculateCreaturesUpgrade(path.heroArmy, upgrader, availableResources);
if(!upgrader->garrisonHero if(!upgrader->getGarrisonHero()
&& ( && (
hasMainAround hasMainAround
|| ai->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN)) || ai->heroManager->getHeroRole(path.targetHero) == HeroRole::MAIN))

View File

@ -68,7 +68,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
closestThreat = std::min(closestThreat, threat.turn); closestThreat = std::min(closestThreat, threat.turn);
} }
//Don't hire a hero where there already is one present //Don't hire a hero where there already is one present
if (town->visitingHero && town->garrisonHero) if (town->getVisitingHero() && town->getGarrisonHero())
continue; continue;
float visitability = 0; float visitability = 0;
for (auto checkHero : ourHeroes) for (auto checkHero : ourHeroes)
@ -98,7 +98,7 @@ Goals::TGoalVec RecruitHeroBehavior::decompose(const Nullkiller * ai) const
for(auto hero : availableHeroes) for(auto hero : availableHeroes)
{ {
if ((town->visitingHero || town->garrisonHero) if ((town->getVisitingHero() || town->getGarrisonHero())
&& closestThreat < 1 && closestThreat < 1
&& hero->getArmyCost() < GameConstants::HERO_GOLD_COST / 3.0) && hero->getArmyCost() < GameConstants::HERO_GOLD_COST / 3.0)
continue; continue;

View File

@ -32,7 +32,7 @@ const AIPath getShortestPath(const CGTownInstance * town, const std::vector<AIPa
{ {
auto shortestPath = *vstd::minElementByFun(paths, [town](const AIPath & path) -> float auto shortestPath = *vstd::minElementByFun(paths, [town](const AIPath & path) -> float
{ {
if(town->garrisonHero && path.targetHero == town->garrisonHero.get()) if(town->getGarrisonHero() && path.targetHero == town->getGarrisonHero())
return 1; return 1;
return path.movementCost(); return path.movementCost();
@ -53,7 +53,7 @@ const CGHeroInstance * getNearestHero(const Nullkiller * ai, const CGTownInstanc
if(shortestPath.nodes.size() > 1 if(shortestPath.nodes.size() > 1
|| shortestPath.turn() != 0 || shortestPath.turn() != 0
|| shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4 || shortestPath.targetHero->visitablePos().dist2dSQ(town->visitablePos()) > 4
|| (town->garrisonHero && shortestPath.targetHero == town->garrisonHero.get())) || (town->getGarrisonHero() && shortestPath.targetHero == town->getGarrisonHero()))
return nullptr; return nullptr;
return shortestPath.targetHero; return shortestPath.targetHero;
@ -64,7 +64,7 @@ bool needToRecruitHero(const Nullkiller * ai, const CGTownInstance * startupTown
if(!ai->heroManager->canRecruitHero(startupTown)) if(!ai->heroManager->canRecruitHero(startupTown))
return false; return false;
if(!startupTown->garrisonHero && !startupTown->visitingHero) if(!startupTown->getGarrisonHero() && !startupTown->getVisitingHero())
return true; return true;
int treasureSourcesCount = 0; int treasureSourcesCount = 0;
@ -122,8 +122,8 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
{ {
startupTown = *vstd::maxElementByFun(towns, [ai](const CGTownInstance * town) -> float startupTown = *vstd::maxElementByFun(towns, [ai](const CGTownInstance * town) -> float
{ {
if(town->garrisonHero) if(town->getGarrisonHero())
return ai->heroManager->evaluateHero(town->garrisonHero.get()); return ai->heroManager->evaluateHero(town->getGarrisonHero());
auto closestHero = getNearestHero(ai, town); auto closestHero = getNearestHero(ai, town);
@ -147,7 +147,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
if(closestHero) if(closestHero)
{ {
if(!startupTown->visitingHero) if(!startupTown->getVisitingHero())
{ {
if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero, TerrainId::NONE) > 200) if(ai->armyManager->howManyReinforcementsCanGet(startupTown->getUpperArmy(), startupTown->getUpperArmy(), closestHero, TerrainId::NONE) > 200)
{ {
@ -163,12 +163,12 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
} }
else else
{ {
auto visitingHero = startupTown->visitingHero.get(); auto visitingHero = startupTown->getVisitingHero();
auto visitingHeroScore = ai->heroManager->evaluateHero(visitingHero); auto visitingHeroScore = ai->heroManager->evaluateHero(visitingHero);
if(startupTown->garrisonHero) if(startupTown->getGarrisonHero())
{ {
auto garrisonHero = startupTown->garrisonHero.get(); auto garrisonHero = startupTown->getGarrisonHero();
auto garrisonHeroScore = ai->heroManager->evaluateHero(garrisonHero); auto garrisonHeroScore = ai->heroManager->evaluateHero(garrisonHero);
if(visitingHeroScore > garrisonHeroScore if(visitingHeroScore > garrisonHeroScore
@ -187,7 +187,7 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
else if(canRecruitHero) else if(canRecruitHero)
{ {
auto canPickTownArmy = startupTown->stacksCount() == 0 auto canPickTownArmy = startupTown->stacksCount() == 0
|| ai->armyManager->howManyReinforcementsCanGet(startupTown->visitingHero, startupTown) > 0; || ai->armyManager->howManyReinforcementsCanGet(startupTown->getVisitingHero(), startupTown) > 0;
if(canPickTownArmy) if(canPickTownArmy)
{ {
@ -197,16 +197,16 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
} }
} }
if(tasks.empty() && canRecruitHero && !startupTown->visitingHero) if(tasks.empty() && canRecruitHero && !startupTown->getVisitingHero())
{ {
tasks.push_back(Goals::sptr(Goals::RecruitHero(startupTown))); tasks.push_back(Goals::sptr(Goals::RecruitHero(startupTown)));
} }
if(tasks.empty() && !startupTown->visitingHero) if(tasks.empty() && !startupTown->getVisitingHero())
{ {
for(auto town : towns) for(auto town : towns)
{ {
if(!town->visitingHero && needToRecruitHero(ai, town)) if(!town->getVisitingHero() && needToRecruitHero(ai, town))
{ {
tasks.push_back(Goals::sptr(Goals::RecruitHero(town))); tasks.push_back(Goals::sptr(Goals::RecruitHero(town)));
@ -219,10 +219,10 @@ Goals::TGoalVec StartupBehavior::decompose(const Nullkiller * ai) const
{ {
for(const CGTownInstance * town : towns) for(const CGTownInstance * town : towns)
{ {
if(town->garrisonHero if(town->getGarrisonHero()
&& town->garrisonHero->movementPointsRemaining() && town->getGarrisonHero()->movementPointsRemaining()
&& !town->visitingHero && !town->getVisitingHero()
&& ai->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE) && ai->getHeroLockedReason(town->getGarrisonHero()) != HeroLockedReason::DEFENCE)
{ {
tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(MIN_PRIORITY))); tasks.push_back(Goals::sptr(ExchangeSwapTownHeroes(town, nullptr).setpriority(MIN_PRIORITY)));
} }

View File

@ -43,7 +43,7 @@ Goals::TGoalVec StayAtTownBehavior::decompose(const Nullkiller * ai) const
for(auto & path : paths) for(auto & path : paths)
{ {
if(town->visitingHero && town->visitingHero.get() != path.targetHero) if(town->getVisitingHero() && town->getVisitingHero() != path.targetHero)
continue; continue;
if(!path.getFirstBlockedAction() && path.exchangeCount <= 1) if(!path.getFirstBlockedAction() && path.exchangeCount <= 1)

View File

@ -47,16 +47,16 @@ ui64 FuzzyHelper::evaluateDanger(const int3 & tile, const CGHeroInstance * visit
{ {
auto hero = dynamic_cast<const CGHeroInstance *>(dangerousObject); auto hero = dynamic_cast<const CGHeroInstance *>(dangerousObject);
if(hero->visitedTown && !hero->visitedTown->garrisonHero) if(hero->getVisitedTown() && !hero->getVisitedTown()->getGarrisonHero())
{ {
objectDanger += evaluateDanger(hero->visitedTown.get()); objectDanger += evaluateDanger(hero->getVisitedTown());
} }
objectDanger *= ai->heroManager->getFightingStrengthCached(hero); objectDanger *= ai->heroManager->getFightingStrengthCached(hero);
} }
if (objWithID<Obj::TOWN>(dangerousObject)) if (objWithID<Obj::TOWN>(dangerousObject))
{ {
auto town = dynamic_cast<const CGTownInstance*>(dangerousObject); auto town = dynamic_cast<const CGTownInstance*>(dangerousObject);
auto hero = town->garrisonHero; auto hero = town->getGarrisonHero();
if (hero) if (hero)
objectDanger *= ai->heroManager->getFightingStrengthCached(hero); objectDanger *= ai->heroManager->getFightingStrengthCached(hero);
@ -121,7 +121,7 @@ ui64 FuzzyHelper::evaluateDanger(const CGObjectInstance * obj)
const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(obj); const CGTownInstance * town = dynamic_cast<const CGTownInstance *>(obj);
auto danger = town->getUpperArmy()->getArmyStrength(); auto danger = town->getUpperArmy()->getArmyStrength();
if(danger || town->visitingHero) if(danger || town->getVisitingHero())
{ {
auto fortLevel = town->fortLevel(); auto fortLevel = town->fortLevel();

View File

@ -44,17 +44,17 @@ void AdventureSpellCast::accept(AIGateway * ai)
{ {
ai->selectedObject = town->id; ai->selectedObject = town->id;
if(town->visitingHero && town->tempOwner == ai->playerID && !town->getUpperArmy()->stacksCount()) if(town->getVisitingHero() && town->tempOwner == ai->playerID && !town->getUpperArmy()->stacksCount())
{ {
ai->myCb->swapGarrisonHero(town); ai->myCb->swapGarrisonHero(town);
} }
if(town->visitingHero) if(town->getVisitingHero())
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated()); throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
} }
if (hero->inTownGarrison) if (hero->isGarrisoned())
ai->myCb->swapGarrisonHero(hero->visitedTown); ai->myCb->swapGarrisonHero(hero->getVisitedTown());
auto wait = cb->waitTillRealize; auto wait = cb->waitTillRealize;

View File

@ -97,9 +97,9 @@ void BuyArmy::accept(AIGateway * ai)
throw cannotFulfillGoalException("No creatures to buy."); throw cannotFulfillGoalException("No creatures to buy.");
} }
if(town->visitingHero && !town->garrisonHero) if(town->getVisitingHero() && !town->getGarrisonHero())
{ {
ai->moveHeroToTile(town->visitablePos(), town->visitingHero.get()); ai->moveHeroToTile(town->visitablePos(), town->getVisitingHero());
} }
} }

View File

@ -30,11 +30,11 @@ std::vector<ObjectInstanceID> ExchangeSwapTownHeroes::getAffectedObjects() const
{ {
std::vector<ObjectInstanceID> affectedObjects = { town->id }; std::vector<ObjectInstanceID> affectedObjects = { town->id };
if(town->garrisonHero) if(town->getGarrisonHero())
affectedObjects.push_back(town->garrisonHero->id); affectedObjects.push_back(town->getGarrisonHero()->id);
if(town->visitingHero) if(town->getVisitingHero())
affectedObjects.push_back(town->visitingHero->id); affectedObjects.push_back(town->getVisitingHero()->id);
return affectedObjects; return affectedObjects;
} }
@ -42,8 +42,8 @@ std::vector<ObjectInstanceID> ExchangeSwapTownHeroes::getAffectedObjects() const
bool ExchangeSwapTownHeroes::isObjectAffected(ObjectInstanceID id) const bool ExchangeSwapTownHeroes::isObjectAffected(ObjectInstanceID id) const
{ {
return town->id == id return town->id == id
|| (town->visitingHero && town->visitingHero->id == id) || (town->getVisitingHero() && town->getVisitingHero()->id == id)
|| (town->garrisonHero && town->garrisonHero->id == id); || (town->getGarrisonHero() && town->getGarrisonHero()->id == id);
} }
std::string ExchangeSwapTownHeroes::toString() const std::string ExchangeSwapTownHeroes::toString() const
@ -58,39 +58,39 @@ bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) co
void ExchangeSwapTownHeroes::accept(AIGateway * ai) void ExchangeSwapTownHeroes::accept(AIGateway * ai)
{ {
if(!garrisonHero) if(!getGarrisonHero())
{ {
auto currentGarrisonHero = town->garrisonHero; auto currentGarrisonHero = town->getGarrisonHero();
if(!currentGarrisonHero) if(!currentGarrisonHero)
throw cannotFulfillGoalException("Invalid configuration. There is no hero in town garrison."); throw cannotFulfillGoalException("Invalid configuration. There is no hero in town garrison.");
cb->swapGarrisonHero(town); cb->swapGarrisonHero(town);
if(currentGarrisonHero.get() != town->visitingHero.get()) if(currentGarrisonHero != town->getVisitingHero())
{ {
logAi->error("VisitingHero is empty, expected %s", currentGarrisonHero->getNameTranslated()); logAi->error("VisitingHero is empty, expected %s", currentGarrisonHero->getNameTranslated());
return; return;
} }
ai->buildArmyIn(town); ai->buildArmyIn(town);
ai->nullkiller->unlockHero(currentGarrisonHero.get()); ai->nullkiller->unlockHero(currentGarrisonHero);
logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->getNameTranslated(), town->getNameTranslated()); logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->getNameTranslated(), town->getNameTranslated());
return; return;
} }
if(town->visitingHero && town->visitingHero.get() != garrisonHero) if(town->getVisitingHero() && town->getVisitingHero() != getGarrisonHero())
cb->swapGarrisonHero(town); cb->swapGarrisonHero(town);
ai->makePossibleUpgrades(town); ai->makePossibleUpgrades(town);
ai->moveHeroToTile(town->visitablePos(), garrisonHero); ai->moveHeroToTile(town->visitablePos(), getGarrisonHero());
auto upperArmy = town->getUpperArmy(); auto upperArmy = town->getUpperArmy();
if(!town->garrisonHero) if(!town->getGarrisonHero())
{ {
if (!garrisonHero->canBeMergedWith(*town)) if (!getGarrisonHero()->canBeMergedWith(*town))
{ {
while (upperArmy->stacksCount() != 0) while (upperArmy->stacksCount() != 0)
{ {
@ -103,16 +103,16 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
if(lockingReason != HeroLockedReason::NOT_LOCKED) if(lockingReason != HeroLockedReason::NOT_LOCKED)
{ {
ai->nullkiller->lockHero(garrisonHero, lockingReason); ai->nullkiller->lockHero(getGarrisonHero(), lockingReason);
} }
if(town->visitingHero && town->visitingHero != garrisonHero) if(town->getVisitingHero() && town->getVisitingHero() != getGarrisonHero())
{ {
ai->nullkiller->unlockHero(town->visitingHero.get()); ai->nullkiller->unlockHero(town->getVisitingHero());
ai->makePossibleUpgrades(town->visitingHero); ai->makePossibleUpgrades(town->getVisitingHero());
} }
logAi->debug("Put hero %s to garrison of %s", garrisonHero->getNameTranslated(), town->getNameTranslated()); logAi->debug("Put hero %s to garrison of %s", getGarrisonHero()->getNameTranslated(), town->getNameTranslated());
} }
} }

View File

@ -59,12 +59,12 @@ void RecruitHero::accept(AIGateway * ai)
if(!heroToHire) if(!heroToHire)
throw cannotFulfillGoalException("No hero to hire!"); throw cannotFulfillGoalException("No hero to hire!");
if(t->visitingHero) if(t->getVisitingHero())
{ {
cb->swapGarrisonHero(t); cb->swapGarrisonHero(t);
} }
if(t->visitingHero) if(t->getVisitingHero())
throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!"); throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!");
cb->recruitHero(t, heroToHire); cb->recruitHero(t, heroToHire);

View File

@ -958,7 +958,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
{ {
// do not allow our own heroes in garrison to act on map // do not allow our own heroes in garrison to act on map
if(hero.first->getOwner() == ai->playerID if(hero.first->getOwner() == ai->playerID
&& hero.first->inTownGarrison && hero.first->isGarrisoned()
&& (ai->isHeroLocked(hero.first) || ai->heroManager->heroCapReached(false))) && (ai->isHeroLocked(hero.first) || ai->heroManager->heroCapReached(false)))
{ {
continue; continue;
@ -987,9 +987,9 @@ void AINodeStorage::setTownsAndDwellings(
{ {
uint64_t mask = FirstActorMask << actors.size(); uint64_t mask = FirstActorMask << actors.size();
// TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->garrisonHero) != HeroLockedReason::DEFENCE // TODO: investigate logix of second condition || ai->nullkiller->getHeroLockedReason(town->getGarrisonHero()) != HeroLockedReason::DEFENCE
// check defence imrove // check defence imrove
if(!town->garrisonHero) if(!town->getGarrisonHero())
{ {
actors.push_back(std::make_shared<TownGarrisonActor>(town, mask)); actors.push_back(std::make_shared<TownGarrisonActor>(town, mask));
} }
@ -1185,19 +1185,19 @@ void AINodeStorage::calculateTownPortal(
{ {
for(const CGTownInstance * targetTown : towns) for(const CGTownInstance * targetTown : towns)
{ {
if(targetTown->visitingHero if(targetTown->getVisitingHero()
&& targetTown->getUpperArmy()->stacksCount() && targetTown->getUpperArmy()->stacksCount()
&& maskMap.find(targetTown->visitingHero.get()) != maskMap.end()) && maskMap.find(targetTown->getVisitingHero()) != maskMap.end())
{ {
auto basicMask = maskMap.at(targetTown->visitingHero.get()); auto basicMask = maskMap.at(targetTown->getVisitingHero());
bool sameActorInTown = actor->chainMask == basicMask; bool sameActorInTown = actor->chainMask == basicMask;
if(!sameActorInTown) if(!sameActorInTown)
continue; continue;
} }
if (targetTown->visitingHero if (targetTown->getVisitingHero()
&& (targetTown->visitingHero.get()->getFactionID() != actor->hero->getFactionID() && (targetTown->getVisitingHero()->getFactionID() != actor->hero->getFactionID()
|| targetTown->getUpperArmy()->stacksCount())) || targetTown->getUpperArmy()->stacksCount()))
continue; continue;

View File

@ -20,13 +20,13 @@ namespace AIPathfinding
{ {
void BuyArmyAction::execute(AIGateway * ai, const CGHeroInstance * hero) const void BuyArmyAction::execute(AIGateway * ai, const CGHeroInstance * hero) const
{ {
if(!hero->visitedTown) if(!hero->getVisitedTown())
{ {
throw cannotFulfillGoalException( throw cannotFulfillGoalException(
hero->getNameTranslated() + " being at " + hero->visitablePos().toString() + " has no town to recruit creatures."); hero->getNameTranslated() + " being at " + hero->visitablePos().toString() + " has no town to recruit creatures.");
} }
ai->recruitCreatures(hero->visitedTown, hero); ai->recruitCreatures(hero->getVisitedTown(), hero);
} }
std::string BuyArmyAction::toString() const std::string BuyArmyAction::toString() const

View File

@ -39,8 +39,8 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
if(hero->mana < hero->getSpellCost(spell)) if(hero->mana < hero->getSpellCost(spell))
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated()); throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero) if(spellID == SpellID::TOWN_PORTAL && town && town->getVisitingHero())
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated()); throw cannotFulfillGoalException("The town is already occupied by " + town->getVisitingHero()->getNameTranslated());
return iAmElementar(); return iAmElementar();
} }

View File

@ -32,7 +32,7 @@ TSubgoal BuildThis::whatToDoToAchieve()
// find town if not set // find town if not set
if(!town && hero) if(!town && hero)
town = hero->visitedTown; town = hero->getVisitedTown();
if(!town) if(!town)
{ {

View File

@ -56,14 +56,14 @@ TGoalVec GatherArmy::getAllPossibleSubgoals()
if(waysToVisit.size()) if(waysToVisit.size())
{ {
//grab army from town //grab army from town
if(!t->visitingHero && ai->ah->howManyReinforcementsCanGet(hero.get(), t)) if(!t->getVisitingHero() && ai->ah->howManyReinforcementsCanGet(hero.get(), t))
{ {
if(!vstd::contains(ai->townVisitsThisWeek[hero], t)) if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
vstd::concatenate(ret, waysToVisit); vstd::concatenate(ret, waysToVisit);
} }
//buy army in town //buy army in town
if (!t->visitingHero || t->visitingHero == hero.get(true)) if (!t->getVisitingHero() || t->getVisitingHero() == hero.get(true))
{ {
std::vector<int> values = { std::vector<int> values = {
value, value,

View File

@ -75,9 +75,9 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
if(count >= this->value) if(count >= this->value)
{ {
if(t->visitingHero) if(t->getVisitingHero())
{ {
solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->visitingHero.get()))); solutions.push_back(sptr(VisitObj(t->id.getNum()).sethero(t->getVisitingHero())));
} }
else else
{ {

View File

@ -83,9 +83,9 @@ TSubgoal Win::whatToDoToAchieve()
if(auto h = ai->getHeroWithGrail()) if(auto h = ai->getHeroWithGrail())
{ {
//hero is in a town that can host Grail //hero is in a town that can host Grail
if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL)) if(h->getVisitedTown() && !vstd::contains(h->getVisitedTown()->forbiddenBuildings, BuildingID::GRAIL))
{ {
const CGTownInstance * t = h->visitedTown; const CGTownInstance * t = h->getVisitedTown();
return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10)); return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
} }
else else

View File

@ -267,7 +267,7 @@ void AINodeStorage::calculateTownPortalTeleportations(
for(const CGTownInstance * targetTown : towns) for(const CGTownInstance * targetTown : towns)
{ {
if(targetTown->visitingHero) if(targetTown->getVisitingHero())
continue; continue;
auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN); auto nodeOptional = getOrCreateNode(targetTown->visitablePos(), EPathfindingLayer::LAND, srcNode->chainMask | CAST_CHAIN);

View File

@ -486,8 +486,8 @@ void VCAI::playerBonusChanged(const Bonus & bonus, bool gain)
void VCAI::heroCreated(const CGHeroInstance * h) void VCAI::heroCreated(const CGHeroInstance * h)
{ {
LOG_TRACE(logAi); LOG_TRACE(logAi);
if(h->visitedTown) if(h->getVisitedTown())
townVisitsThisWeek[HeroPtr(h)].insert(h->visitedTown); townVisitsThisWeek[HeroPtr(h)].insert(h->getVisitedTown());
NET_EVENT_HANDLER; NET_EVENT_HANDLER;
} }
@ -1073,12 +1073,12 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{ {
case Obj::TOWN: case Obj::TOWN:
moveCreaturesToHero(dynamic_cast<const CGTownInstance *>(obj)); moveCreaturesToHero(dynamic_cast<const CGTownInstance *>(obj));
if(h->visitedTown) //we are inside, not just attacking if(h->getVisitedTown()) //we are inside, not just attacking
{ {
townVisitsThisWeek[h].insert(h->visitedTown); townVisitsThisWeek[h].insert(h->getVisitedTown());
if(!h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST) if(!h->hasSpellbook() && ah->freeGold() >= GameConstants::SPELLBOOK_GOLD_COST)
{ {
if(h->visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1)) if(h->getVisitedTown()->hasBuilt(BuildingID::MAGES_GUILD_1))
cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK); cb->buyArtifact(h.get(), ArtifactID::SPELLBOOK);
} }
} }
@ -1089,9 +1089,9 @@ void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
void VCAI::moveCreaturesToHero(const CGTownInstance * t) void VCAI::moveCreaturesToHero(const CGTownInstance * t)
{ {
if(t->visitingHero && t->armedGarrison() && t->visitingHero->tempOwner == t->tempOwner) if(t->getVisitingHero() && t->armedGarrison() && t->getVisitingHero()->tempOwner == t->tempOwner)
{ {
pickBestCreatures(t->visitingHero, t); pickBestCreatures(t->getVisitingHero(), t);
} }
} }
@ -1363,10 +1363,10 @@ void VCAI::wander(HeroPtr h)
{ {
auto visitTownIfAny = [this](HeroPtr h) -> bool auto visitTownIfAny = [this](HeroPtr h) -> bool
{ {
if (h->visitedTown) if (h->getVisitedTown())
{ {
townVisitsThisWeek[h].insert(h->visitedTown); townVisitsThisWeek[h].insert(h->getVisitedTown());
buildArmyIn(h->visitedTown); buildArmyIn(h->getVisitedTown());
return true; return true;
} }
return false; return false;
@ -1434,7 +1434,7 @@ void VCAI::wander(HeroPtr h)
std::vector<const CGTownInstance *> townsNotReachable; std::vector<const CGTownInstance *> townsNotReachable;
for(const CGTownInstance * t : cb->getTownsInfo()) for(const CGTownInstance * t : cb->getTownsInfo())
{ {
if(!t->visitingHero && !vstd::contains(townVisitsThisWeek[h], t)) if(!t->getVisitingHero() && !vstd::contains(townVisitsThisWeek[h], t))
{ {
if(isAccessibleForHero(t->visitablePos(), h)) if(isAccessibleForHero(t->visitablePos(), h))
townsReachable.push_back(t); townsReachable.push_back(t);
@ -2251,7 +2251,7 @@ void VCAI::tryRealize(Goals::AbstractGoal & g)
const CGTownInstance * VCAI::findTownWithTavern() const const CGTownInstance * VCAI::findTownWithTavern() const
{ {
for(const CGTownInstance * t : cb->getTownsInfo()) for(const CGTownInstance * t : cb->getTownsInfo())
if(t->hasBuilt(BuildingID::TAVERN) && !t->visitingHero) if(t->hasBuilt(BuildingID::TAVERN) && !t->getVisitingHero())
return t; return t;
return nullptr; return nullptr;
@ -2455,7 +2455,7 @@ void VCAI::performTypicalActions()
void VCAI::buildArmyIn(const CGTownInstance * t) void VCAI::buildArmyIn(const CGTownInstance * t)
{ {
makePossibleUpgrades(t->visitingHero); makePossibleUpgrades(t->getVisitingHero());
makePossibleUpgrades(t); makePossibleUpgrades(t);
recruitCreatures(t, t->getUpperArmy()); recruitCreatures(t, t->getUpperArmy());
moveCreaturesToHero(t); moveCreaturesToHero(t);

View File

@ -273,7 +273,7 @@ void CCallback::spellResearch( const CGTownInstance *town, SpellID spellAtSlot,
void CCallback::swapGarrisonHero( const CGTownInstance *town ) void CCallback::swapGarrisonHero( const CGTownInstance *town )
{ {
if(town->tempOwner == *player || (town->garrisonHero && town->garrisonHero->tempOwner == *player )) if(town->tempOwner == *player || (town->getGarrisonHero() && town->getGarrisonHero()->tempOwner == *player ))
{ {
GarrisonHeroSwap pack(town->id); GarrisonHeroSwap pack(town->id);
sendRequest(pack); sendRequest(pack);

View File

@ -118,18 +118,6 @@
std::shared_ptr<BattleInterface> CPlayerInterface::battleInt; std::shared_ptr<BattleInterface> CPlayerInterface::battleInt;
struct HeroObjectRetriever
{
const CGHeroInstance * operator()(const ConstTransitivePtr<CGHeroInstance> &h) const
{
return h;
}
const CGHeroInstance * operator()(const ConstTransitivePtr<CStackInstance> &s) const
{
return nullptr;
}
};
CPlayerInterface::CPlayerInterface(PlayerColor Player): CPlayerInterface::CPlayerInterface(PlayerColor Player):
localState(std::make_unique<PlayerLocalState>(*this)), localState(std::make_unique<PlayerLocalState>(*this)),
movementController(std::make_unique<HeroMovementController>()), movementController(std::make_unique<HeroMovementController>()),
@ -542,18 +530,18 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
{ {
EVENT_HANDLER_CALLED_BY_CLIENT; EVENT_HANDLER_CALLED_BY_CLIENT;
if(town->garrisonHero) //wandering hero moved to the garrison if(town->getGarrisonHero()) //wandering hero moved to the garrison
{ {
// This method also gets called on hero recruitment -> garrisoned hero is already in garrison // This method also gets called on hero recruitment -> garrisoned hero is already in garrison
if(town->garrisonHero->tempOwner == playerID && vstd::contains(localState->getWanderingHeroes(), town->garrisonHero)) if(town->getGarrisonHero()->tempOwner == playerID && vstd::contains(localState->getWanderingHeroes(), town->getGarrisonHero()))
localState->removeWanderingHero(town->garrisonHero); localState->removeWanderingHero(town->getGarrisonHero());
} }
if(town->visitingHero) //hero leaves garrison if(town->getVisitingHero()) //hero leaves garrison
{ {
// This method also gets called on hero recruitment -> wandering heroes already contains new hero // This method also gets called on hero recruitment -> wandering heroes already contains new hero
if(town->visitingHero->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->visitingHero)) if(town->getVisitingHero()->tempOwner == playerID && !vstd::contains(localState->getWanderingHeroes(), town->getVisitingHero()))
localState->addWanderingHero(town->visitingHero); localState->addWanderingHero(town->getVisitingHero());
} }
adventureInt->onHeroChanged(nullptr); adventureInt->onHeroChanged(nullptr);
adventureInt->onTownChanged(town); adventureInt->onTownChanged(town);
@ -611,8 +599,8 @@ void CPlayerInterface::garrisonsChanged(std::vector<const CArmedInstance *> objs
localState->verifyPath(hero); localState->verifyPath(hero);
adventureInt->onHeroChanged(hero); adventureInt->onHeroChanged(hero);
if(hero->inTownGarrison && hero->visitedTown != town) if(hero->isGarrisoned() && hero->getVisitedTown() != town)
adventureInt->onTownChanged(hero->visitedTown); adventureInt->onTownChanged(hero->getVisitedTown());
} }
} }
@ -1363,7 +1351,7 @@ void CPlayerInterface::initializeHeroTownList()
{ {
for(auto & hero : cb->getHeroesInfo()) for(auto & hero : cb->getHeroesInfo())
{ {
if(!hero->inTownGarrison) if(!hero->isGarrisoned())
localState->addWanderingHero(hero); localState->addWanderingHero(hero);
} }
} }

View File

@ -703,7 +703,7 @@ void ApplyClientNetPackVisitor::visitCommanderLevelUp(CommanderLevelUp & pack)
{ {
const CGHeroInstance * hero = cl.getHero(pack.heroId); const CGHeroInstance * hero = cl.getHero(pack.heroId);
assert(hero); assert(hero);
const CCommanderInstance * commander = hero->commander; const auto & commander = hero->getCommander();
assert(commander); assert(commander);
assert(commander->armyObj); //is it possible for Commander to exist beyond armed instance? assert(commander->armyObj); //is it possible for Commander to exist beyond armed instance?
callOnlyThatInterface(cl, pack.player, &CGameInterface::commanderGotLevel, commander, pack.skills, pack.queryID); callOnlyThatInterface(cl, pack.player, &CGameInterface::commanderGotLevel, commander, pack.skills, pack.queryID);

View File

@ -132,7 +132,7 @@ double MapRendererBaseContext::objectTransparency(ObjectInstanceID objectID, con
{ {
const auto * hero = dynamic_cast<const CGHeroInstance *>(object); const auto * hero = dynamic_cast<const CGHeroInstance *>(object);
if(hero->inTownGarrison) if(hero->isGarrisoned())
return 0; return 0;
if(hero->boat) if(hero->boat)

View File

@ -81,7 +81,7 @@ void CGarrisonSlot::hover (bool on)
const bool isHeroOnMap = owner->upperArmy() // Hero is not a visitor and not a garrison defender const bool isHeroOnMap = owner->upperArmy() // Hero is not a visitor and not a garrison defender
&& owner->upperArmy()->ID == Obj::HERO && owner->upperArmy()->ID == Obj::HERO
&& (!owner->lowerArmy() || owner->lowerArmy()->ID == Obj::HERO) // one hero or we are in the Heroes exchange window && (!owner->lowerArmy() || owner->lowerArmy()->ID == Obj::HERO) // one hero or we are in the Heroes exchange window
&& !(static_cast<const CGHeroInstance*>(owner->upperArmy()))->inTownGarrison; && !(static_cast<const CGHeroInstance*>(owner->upperArmy()))->isGarrisoned();
if(isHeroOnMap) if(isHeroOnMap)
{ {

View File

@ -595,16 +595,16 @@ HeroSlots::HeroSlots(const CGTownInstance * Town, Point garrPos, Point visitPos,
garr(Garrison) garr(Garrison)
{ {
OBJECT_CONSTRUCTION; OBJECT_CONSTRUCTION;
garrisonedHero = std::make_shared<CHeroGSlot>(garrPos.x, garrPos.y, 0, town->garrisonHero, this); garrisonedHero = std::make_shared<CHeroGSlot>(garrPos.x, garrPos.y, 0, town->getGarrisonHero(), this);
visitingHero = std::make_shared<CHeroGSlot>(visitPos.x, visitPos.y, 1, town->visitingHero, this); visitingHero = std::make_shared<CHeroGSlot>(visitPos.x, visitPos.y, 1, town->getVisitingHero(), this);
} }
HeroSlots::~HeroSlots() = default; HeroSlots::~HeroSlots() = default;
void HeroSlots::update() void HeroSlots::update()
{ {
garrisonedHero->set(town->garrisonHero); garrisonedHero->set(town->getGarrisonHero());
visitingHero->set(town->visitingHero); visitingHero->set(town->getVisitingHero());
} }
void HeroSlots::swapArmies() void HeroSlots::swapArmies()
@ -612,9 +612,9 @@ void HeroSlots::swapArmies()
bool allow = true; bool allow = true;
//moving hero out of town - check if it is allowed //moving hero out of town - check if it is allowed
if (town->garrisonHero) if (town->getGarrisonHero())
{ {
if (!town->visitingHero && GAME->interface()->cb->howManyHeroes(false) >= GAME->interface()->cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP)) if (!town->getVisitingHero() && GAME->interface()->cb->howManyHeroes(false) >= GAME->interface()->cb->getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP))
{ {
std::string text = LIBRARY->generaltexth->translate("core.genrltxt.18"); //You already have %d adventuring heroes under your command. std::string text = LIBRARY->generaltexth->translate("core.genrltxt.18"); //You already have %d adventuring heroes under your command.
boost::algorithm::replace_first(text,"%d",std::to_string(GAME->interface()->cb->howManyHeroes(false))); boost::algorithm::replace_first(text,"%d",std::to_string(GAME->interface()->cb->howManyHeroes(false)));
@ -622,7 +622,7 @@ void HeroSlots::swapArmies()
GAME->interface()->showInfoDialog(text, std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo); GAME->interface()->showInfoDialog(text, std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
allow = false; allow = false;
} }
else if (town->garrisonHero->stacksCount() == 0) else if (town->getGarrisonHero()->stacksCount() == 0)
{ {
//This hero has no creatures. A hero must have creatures before he can brave the dangers of the countryside. //This hero has no creatures. A hero must have creatures before he can brave the dangers of the countryside.
GAME->interface()->showInfoDialog(LIBRARY->generaltexth->translate("core.genrltxt.19"), {}, soundBase::sound_todo); GAME->interface()->showInfoDialog(LIBRARY->generaltexth->translate("core.genrltxt.19"), {}, soundBase::sound_todo);
@ -630,9 +630,9 @@ void HeroSlots::swapArmies()
} }
} }
if(!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies: town army => hero army if(!town->getGarrisonHero() && town->getVisitingHero()) //visiting => garrison, merge armies: town army => hero army
{ {
if(!town->visitingHero->canBeMergedWith(*town)) if(!town->getVisitingHero()->canBeMergedWith(*town))
{ {
GAME->interface()->showInfoDialog(LIBRARY->generaltexth->allTexts[275], std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo); GAME->interface()->showInfoDialog(LIBRARY->generaltexth->allTexts[275], std::vector<std::shared_ptr<CComponent>>(), soundBase::sound_todo);
allow = false; allow = false;
@ -809,10 +809,10 @@ void CCastleBuildings::removeBuilding(BuildingID building)
const CGHeroInstance * CCastleBuildings::getHero() const CGHeroInstance * CCastleBuildings::getHero()
{ {
if(town->visitingHero) if(town->getVisitingHero())
return town->visitingHero; return town->getVisitingHero();
else else
return town->garrisonHero; return town->getGarrisonHero();
} }
void CCastleBuildings::buildingClicked(BuildingID building) void CCastleBuildings::buildingClicked(BuildingID building)
@ -983,7 +983,7 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
void CCastleBuildings::enterRewardable(BuildingID building) void CCastleBuildings::enterRewardable(BuildingID building)
{ {
if (town->visitingHero == nullptr) if (town->getVisitingHero() == nullptr)
{ {
MetaString message; MetaString message;
message.appendTextID("core.genrltxt.273"); // only visiting heroes may visit %s message.appendTextID("core.genrltxt.273"); // only visiting heroes may visit %s
@ -993,7 +993,7 @@ void CCastleBuildings::enterRewardable(BuildingID building)
} }
else else
{ {
if (town->rewardableBuildings.at(building)->wasVisited(town->visitingHero)) if (town->rewardableBuildings.at(building)->wasVisited(town->getVisitingHero()))
enterBuilding(building); enterBuilding(building);
else else
GAME->interface()->cb->visitTownBuilding(town, building); GAME->interface()->cb->visitTownBuilding(town, building);
@ -1002,7 +1002,7 @@ void CCastleBuildings::enterRewardable(BuildingID building)
void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactID) void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactID)
{ {
const CGHeroInstance *hero = town->visitingHero; const CGHeroInstance *hero = town->getVisitingHero();
if(!hero) if(!hero)
{ {
GAME->interface()->showInfoDialog(boost::str(boost::format(LIBRARY->generaltexth->allTexts[273]) % town->getTown()->buildings.find(building)->second->getNameTranslated())); GAME->interface()->showInfoDialog(boost::str(boost::format(LIBRARY->generaltexth->allTexts[273]) % town->getTown()->buildings.find(building)->second->getNameTranslated()));
@ -1040,7 +1040,7 @@ void CCastleBuildings::enterBuilding(BuildingID building)
void CCastleBuildings::enterCastleGate(BuildingID building) void CCastleBuildings::enterCastleGate(BuildingID building)
{ {
if (!town->visitingHero) if (!town->getVisitingHero())
{ {
GAME->interface()->showInfoDialog(LIBRARY->generaltexth->allTexts[126]); GAME->interface()->showInfoDialog(LIBRARY->generaltexth->allTexts[126]);
return;//only visiting hero can use castle gates return;//only visiting hero can use castle gates
@ -1051,7 +1051,7 @@ void CCastleBuildings::enterCastleGate(BuildingID building)
for(auto & Town : Towns) for(auto & Town : Towns)
{ {
const CGTownInstance *t = Town; const CGTownInstance *t = Town;
if (t->id != this->town->id && t->visitingHero == nullptr && //another town, empty and this is if (t->id != this->town->id && t->getVisitingHero() == nullptr && //another town, empty and this is
t->getFactionID() == town->getFactionID() && //the town of the same faction t->getFactionID() == town->getFactionID() && //the town of the same faction
t->hasBuilt(BuildingSubID::CASTLE_GATE)) //and the town has a castle gate t->hasBuilt(BuildingSubID::CASTLE_GATE)) //and the town has a castle gate
{ {
@ -1169,7 +1169,7 @@ void CCastleBuildings::enterMagesGuild()
void CCastleBuildings::enterTownHall() void CCastleBuildings::enterTownHall()
{ {
if(town->visitingHero && town->visitingHero->hasArt(ArtifactID::GRAIL) && if(town->getVisitingHero() && town->getVisitingHero()->hasArt(ArtifactID::GRAIL) &&
!town->hasBuilt(BuildingID::GRAIL)) //hero has grail, but town does not have it !town->hasBuilt(BuildingID::GRAIL)) //hero has grail, but town does not have it
{ {
if(!vstd::contains(town->forbiddenBuildings, BuildingID::GRAIL)) if(!vstd::contains(town->forbiddenBuildings, BuildingID::GRAIL))
@ -1427,7 +1427,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
center(); center();
updateShadow(); updateShadow();
garr = std::make_shared<CGarrisonInt>(Point(305, 387), 4, Point(0,96), town->getUpperArmy(), town->visitingHero); garr = std::make_shared<CGarrisonInt>(Point(305, 387), 4, Point(0,96), town->getUpperArmy(), town->getVisitingHero());
garr->setRedrawParent(true); garr->setRedrawParent(true);
heroes = std::make_shared<HeroSlots>(town, Point(241, 387), Point(241, 483), garr, true); heroes = std::make_shared<HeroSlots>(town, Point(241, 387), Point(241, 483), garr, true);
@ -1476,7 +1476,7 @@ CCastleInterface::~CCastleInterface()
void CCastleInterface::updateGarrisons() void CCastleInterface::updateGarrisons()
{ {
garr->setArmy(town->getUpperArmy(), EGarrisonType::UPPER); garr->setArmy(town->getUpperArmy(), EGarrisonType::UPPER);
garr->setArmy(town->visitingHero, EGarrisonType::LOWER); garr->setArmy(town->getVisitingHero(), EGarrisonType::LOWER);
garr->recreateSlots(); garr->recreateSlots();
heroes->update(); heroes->update();
@ -1485,15 +1485,15 @@ void CCastleInterface::updateGarrisons()
bool CCastleInterface::holdsGarrison(const CArmedInstance * army) bool CCastleInterface::holdsGarrison(const CArmedInstance * army)
{ {
return army == town || army == town->getUpperArmy() || army == town->visitingHero; return army == town || army == town->getUpperArmy() || army == town->getVisitingHero();
} }
void CCastleInterface::close() void CCastleInterface::close()
{ {
if(town->tempOwner == GAME->interface()->playerID) //we may have opened window for an allied town if(town->tempOwner == GAME->interface()->playerID) //we may have opened window for an allied town
{ {
if(town->visitingHero && town->visitingHero->tempOwner == GAME->interface()->playerID) if(town->getVisitingHero() && town->getVisitingHero()->tempOwner == GAME->interface()->playerID)
GAME->interface()->localState->setSelection(town->visitingHero); GAME->interface()->localState->setSelection(town->getVisitingHero());
else else
GAME->interface()->localState->setSelection(town); GAME->interface()->localState->setSelection(town);
} }
@ -1503,9 +1503,9 @@ void CCastleInterface::close()
void CCastleInterface::castleTeleport(int where) void CCastleInterface::castleTeleport(int where)
{ {
const CGTownInstance * dest = GAME->interface()->cb->getTown(ObjectInstanceID(where)); const CGTownInstance * dest = GAME->interface()->cb->getTown(ObjectInstanceID(where));
GAME->interface()->localState->setSelection(town->visitingHero);//according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf GAME->interface()->localState->setSelection(town->getVisitingHero());//according to assert(ho == adventureInt->selection) in the eraseCurrentPathOf
GAME->interface()->cb->teleportHero(town->visitingHero, dest); GAME->interface()->cb->teleportHero(town->getVisitingHero(), dest);
GAME->interface()->localState->erasePath(town->visitingHero); GAME->interface()->localState->erasePath(town->getVisitingHero());
} }
void CCastleInterface::townChange() void CCastleInterface::townChange()
@ -1605,22 +1605,22 @@ void CCastleInterface::keyPressed(EShortcut key)
case EShortcut::TOWN_OPEN_THIEVES_GUILD: case EShortcut::TOWN_OPEN_THIEVES_GUILD:
break; break;
case EShortcut::TOWN_OPEN_HERO_EXCHANGE: case EShortcut::TOWN_OPEN_HERO_EXCHANGE:
if (town->visitingHero && town->garrisonHero) if (town->getVisitingHero() && town->getGarrisonHero())
GAME->interface()->showHeroExchange(town->visitingHero->id, town->garrisonHero->id); GAME->interface()->showHeroExchange(town->getVisitingHero()->id, town->getGarrisonHero()->id);
break; break;
case EShortcut::TOWN_OPEN_HERO: case EShortcut::TOWN_OPEN_HERO:
if (town->visitingHero) if (town->getVisitingHero())
GAME->interface()->openHeroWindow(town->visitingHero); GAME->interface()->openHeroWindow(town->getVisitingHero());
else if (town->garrisonHero) else if (town->getGarrisonHero())
GAME->interface()->openHeroWindow(town->garrisonHero); GAME->interface()->openHeroWindow(town->getGarrisonHero());
break; break;
case EShortcut::TOWN_OPEN_VISITING_HERO: case EShortcut::TOWN_OPEN_VISITING_HERO:
if (town->visitingHero) if (town->getVisitingHero())
GAME->interface()->openHeroWindow(town->visitingHero); GAME->interface()->openHeroWindow(town->getVisitingHero());
break; break;
case EShortcut::TOWN_OPEN_GARRISONED_HERO: case EShortcut::TOWN_OPEN_GARRISONED_HERO:
if (town->garrisonHero) if (town->getGarrisonHero())
GAME->interface()->openHeroWindow(town->garrisonHero); GAME->interface()->openHeroWindow(town->getGarrisonHero());
break; break;
case EShortcut::TOWN_SWAP_ARMIES: case EShortcut::TOWN_SWAP_ARMIES:
heroes->swapArmies(); heroes->swapArmies();

View File

@ -104,7 +104,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
formations->addToggle(0, std::make_shared<CToggleButton>(Point(481, 483), AnimationPath::builtin("hsbtns6.def"), std::make_pair(heroscrn[23], heroscrn[29]), 0, EShortcut::HERO_TIGHT_FORMATION)); formations->addToggle(0, std::make_shared<CToggleButton>(Point(481, 483), AnimationPath::builtin("hsbtns6.def"), std::make_pair(heroscrn[23], heroscrn[29]), 0, EShortcut::HERO_TIGHT_FORMATION));
formations->addToggle(1, std::make_shared<CToggleButton>(Point(481, 519), AnimationPath::builtin("hsbtns7.def"), std::make_pair(heroscrn[24], heroscrn[30]), 0, EShortcut::HERO_LOOSE_FORMATION)); formations->addToggle(1, std::make_shared<CToggleButton>(Point(481, 519), AnimationPath::builtin("hsbtns7.def"), std::make_pair(heroscrn[24], heroscrn[30]), 0, EShortcut::HERO_LOOSE_FORMATION));
if(hero->commander) if(hero->getCommander())
{ {
commanderButton = std::make_shared<CButton>(Point(317, 18), AnimationPath::builtin("heroCommander"), CButton::tooltipLocalized("vcmi.heroWindow.openCommander"), [&](){ commanderWindow(); }, EShortcut::HERO_COMMANDER); commanderButton = std::make_shared<CButton>(Point(317, 18), AnimationPath::builtin("heroCommander"), CButton::tooltipLocalized("vcmi.heroWindow.openCommander"), [&](){ commanderWindow(); }, EShortcut::HERO_COMMANDER);
commanderButton->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/commanderButtonIcon"))); commanderButton->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/commanderButtonIcon")));
@ -328,7 +328,7 @@ void CHeroWindow::commanderWindow()
if(pickedArtInst) if(pickedArtInst)
{ {
const auto freeSlot = ArtifactUtils::getArtAnyPosition(curHero->commander, pickedArtInst->getTypeId()); const auto freeSlot = ArtifactUtils::getArtAnyPosition(curHero->getCommander(), pickedArtInst->getTypeId());
if(vstd::contains(ArtifactUtils::commanderSlots(), freeSlot)) // We don't want to put it in commander's backpack! if(vstd::contains(ArtifactUtils::commanderSlots(), freeSlot)) // We don't want to put it in commander's backpack!
{ {
ArtifactLocation dst(curHero->id, freeSlot); ArtifactLocation dst(curHero->id, freeSlot);
@ -338,7 +338,7 @@ void CHeroWindow::commanderWindow()
} }
else else
{ {
ENGINE->windows().createAndPushWindow<CStackWindow>(curHero->commander, false); ENGINE->windows().createAndPushWindow<CStackWindow>(curHero->getCommander(), false);
} }
} }

View File

@ -785,7 +785,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
hall = std::make_shared<CTownInfo>( 69, 31, town, true); hall = std::make_shared<CTownInfo>( 69, 31, town, true);
fort = std::make_shared<CTownInfo>(111, 31, town, false); fort = std::make_shared<CTownInfo>(111, 31, town, false);
garr = std::make_shared<CGarrisonInt>(Point(313, 3), 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, CGarrisonInt::ESlotsLayout::TWO_ROWS); garr = std::make_shared<CGarrisonInt>(Point(313, 3), 4, Point(232,0), town->getUpperArmy(), town->getVisitingHero(), true, true, CGarrisonInt::ESlotsLayout::TWO_ROWS);
heroes = std::make_shared<HeroSlots>(town, Point(244,6), Point(475,6), garr, false); heroes = std::make_shared<HeroSlots>(town, Point(244,6), Point(475,6), garr, false);
size_t iconIndex = town->getTown()->clientInfo.icons[town->hasFort()][town->built >= GAME->interface()->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)]; size_t iconIndex = town->getTown()->clientInfo.icons[town->hasFort()][town->built >= GAME->interface()->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
@ -837,13 +837,13 @@ void CTownItem::updateGarrisons()
{ {
garr->selectSlot(nullptr); garr->selectSlot(nullptr);
garr->setArmy(town->getUpperArmy(), EGarrisonType::UPPER); garr->setArmy(town->getUpperArmy(), EGarrisonType::UPPER);
garr->setArmy(town->visitingHero, EGarrisonType::LOWER); garr->setArmy(town->getVisitingHero(), EGarrisonType::LOWER);
garr->recreateSlots(); garr->recreateSlots();
} }
bool CTownItem::holdsGarrison(const CArmedInstance * army) bool CTownItem::holdsGarrison(const CArmedInstance * army)
{ {
return army == town || army == town->getUpperArmy() || army == town->visitingHero; return army == town || army == town->getUpperArmy() || army == town->getVisitingHero();
} }
void CTownItem::update() void CTownItem::update()

View File

@ -517,7 +517,7 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func
recruit->addHoverText(EButtonState::NORMAL, message.toString()); recruit->addHoverText(EButtonState::NORMAL, message.toString());
recruit->block(true); recruit->block(true);
} }
else if(dynamic_cast<const CGTownInstance *>(TavernObj) && dynamic_cast<const CGTownInstance *>(TavernObj)->visitingHero) else if(dynamic_cast<const CGTownInstance *>(TavernObj) && dynamic_cast<const CGTownInstance *>(TavernObj)->getVisitingHero())
{ {
recruit->addHoverText(EButtonState::NORMAL, LIBRARY->generaltexth->tavernInfo[2]); //Cannot recruit. You already have a Hero in this town. recruit->addHoverText(EButtonState::NORMAL, LIBRARY->generaltexth->tavernInfo[2]); //Cannot recruit. You already have a Hero in this town.
recruit->block(true); recruit->block(true);

View File

@ -499,7 +499,7 @@ bool CCreatureSet::contains(const CStackInstance *stack) const
SlotID CCreatureSet::findStack(const CStackInstance *stack) const SlotID CCreatureSet::findStack(const CStackInstance *stack) const
{ {
const auto * h = dynamic_cast<const CGHeroInstance *>(this); const auto * h = dynamic_cast<const CGHeroInstance *>(this);
if (h && h->commander == stack) if (h && h->getCommander() == stack)
return SlotID::COMMANDER_SLOT_PLACEHOLDER; return SlotID::COMMANDER_SLOT_PLACEHOLDER;
if(!stack) if(!stack)

View File

@ -721,7 +721,7 @@ int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned
return static_cast<int>(p->getHeroes().size()); return static_cast<int>(p->getHeroes().size());
else else
for(const auto & elem : p->getHeroes()) for(const auto & elem : p->getHeroes())
if(!elem->inTownGarrison) if(!elem->isGarrisoned())
ret++; ret++;
return ret; return ret;
} }
@ -792,7 +792,7 @@ std::vector < const CGHeroInstance *> CPlayerSpecificInfoCallback::getHeroesInfo
int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned) const int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool includeGarrisoned) const
{ {
if (hero->inTownGarrison && !includeGarrisoned) if (hero->isGarrisoned() && !includeGarrisoned)
return -1; return -1;
size_t index = 0; size_t index = 0;
@ -800,7 +800,7 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
for (auto & possibleHero : heroes) for (auto & possibleHero : heroes)
{ {
if (includeGarrisoned || !(possibleHero)->inTownGarrison) if (includeGarrisoned || !(possibleHero)->isGarrisoned())
index++; index++;
if (possibleHero == hero) if (possibleHero == hero)
@ -853,7 +853,7 @@ const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId,
if (!includeGarrisoned) if (!includeGarrisoned)
{ {
for(ui32 i = 0; i < p->getHeroes().size() && static_cast<int>(i) <= serialId; i++) for(ui32 i = 0; i < p->getHeroes().size() && static_cast<int>(i) <= serialId; i++)
if(p->getHeroes()[i]->inTownGarrison) if(p->getHeroes()[i]->isGarrisoned())
serialId++; serialId++;
} }
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getHeroes().size(), "No player info", nullptr); ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getHeroes().size(), "No player info", nullptr);

View File

@ -410,7 +410,7 @@ void CStack::postDeserialize(const CArmedInstance * army, const SlotID & extSlot
{ {
const auto * hero = dynamic_cast<const CGHeroInstance *>(army); const auto * hero = dynamic_cast<const CGHeroInstance *>(army);
assert(hero); assert(hero);
base = hero->commander; base = hero->getCommander();
} }
else if(slot == SlotID::SUMMONED_SLOT_PLACEHOLDER || slot == SlotID::ARROW_TOWERS_SLOT || slot == SlotID::WAR_MACHINES_SLOT) else if(slot == SlotID::SUMMONED_SLOT_PLACEHOLDER || slot == SlotID::ARROW_TOWERS_SLOT || slot == SlotID::WAR_MACHINES_SLOT)
{ {

View File

@ -271,7 +271,7 @@ CArtifactSet * CNonConstInfoCallback::getArtSet(const ArtifactLocation & loc)
if(loc.creature.has_value()) if(loc.creature.has_value())
{ {
if(loc.creature.value() == SlotID::COMMANDER_SLOT_PLACEHOLDER) if(loc.creature.value() == SlotID::COMMANDER_SLOT_PLACEHOLDER)
return hero->commander; return hero->getCommander();
else else
return hero->getStackPtr(loc.creature.value()); return hero->getStackPtr(loc.creature.value());
} }

View File

@ -351,9 +351,9 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
//adding commanders //adding commanders
for(BattleSide i : {BattleSide::ATTACKER, BattleSide::DEFENDER}) for(BattleSide i : {BattleSide::ATTACKER, BattleSide::DEFENDER})
{ {
if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive) if (heroes[i] && heroes[i]->getCommander() && heroes[i]->getCommander()->alive)
{ {
currentBattle->generateNewStack(currentBattle->nextUnitId(), *heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, layout.commanders.at(i)); currentBattle->generateNewStack(currentBattle->nextUnitId(), *heroes[i]->getCommander(), i, SlotID::COMMANDER_SLOT_PLACEHOLDER, layout.commanders.at(i));
} }
} }

View File

@ -992,7 +992,7 @@ void CGameState::initVisitingAndGarrisonedHeroes()
if (t->visitableAt(h->visitablePos())) if (t->visitableAt(h->visitablePos()))
{ {
assert(t->visitingHero == nullptr); assert(t->getVisitingHero() == nullptr);
t->setVisitingHero(h); t->setVisitingHero(h);
} }
} }
@ -1000,9 +1000,9 @@ void CGameState::initVisitingAndGarrisonedHeroes()
} }
for (auto hero : map->heroesOnMap) for (auto hero : map->heroesOnMap)
{ {
if (hero->visitedTown) if (hero->getVisitedTown())
{ {
assert (hero->visitedTown->visitingHero == hero); assert (hero->getVisitedTown()->getVisitingHero() == hero);
} }
} }
} }
@ -1092,9 +1092,9 @@ UpgradeInfo CGameState::fillUpgradeInfo(const CStackInstance & stack) const
auto hero = dynamic_cast<const CGHeroInstance *>(stack.armyObj); auto hero = dynamic_cast<const CGHeroInstance *>(stack.armyObj);
hero->fillUpgradeInfo(ret, stack); hero->fillUpgradeInfo(ret, stack);
if (hero->visitedTown) if (hero->getVisitedTown())
{ {
hero->visitedTown->fillUpgradeInfo(ret, stack); hero->getVisitedTown()->fillUpgradeInfo(ret, stack);
} }
else else
{ {
@ -1382,8 +1382,8 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
case EventCondition::TRANSPORT: case EventCondition::TRANSPORT:
{ {
const auto * t = getTown(condition.objectID); const auto * t = getTown(condition.objectID);
bool garrisonedWon = t->garrisonHero && t->garrisonHero->getOwner() == player && t->garrisonHero->hasArt(condition.objectType.as<ArtifactID>()); bool garrisonedWon = t->getGarrisonHero() && t->getGarrisonHero()->getOwner() == player && t->getGarrisonHero()->hasArt(condition.objectType.as<ArtifactID>());
bool visitingWon = t->visitingHero && t->visitingHero->getOwner() == player && t->visitingHero->hasArt(condition.objectType.as<ArtifactID>()); bool visitingWon = t->getVisitingHero() && t->getVisitingHero()->getOwner() == player && t->getVisitingHero()->hasArt(condition.objectType.as<ArtifactID>());
return garrisonedWon || visitingWon; return garrisonedWon || visitingWon;
} }

View File

@ -244,7 +244,7 @@ si64 Statistic::getArmyStrength(const PlayerState * ps, bool withTownGarrison)
for(auto h : ps->getHeroes()) for(auto h : ps->getHeroes())
{ {
if(!h->inTownGarrison || withTownGarrison) //original h3 behavior if(!h->isGarrisoned() || withTownGarrison) //original h3 behavior
str += h->getArmyStrength(); str += h->getArmyStrength();
} }
return str; return str;

View File

@ -183,7 +183,7 @@ void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
details->goldIncome = income[EGameResID::GOLD]; details->goldIncome = income[EGameResID::GOLD];
details->customRes = t->hasBuilt(BuildingID::RESOURCE_SILO); details->customRes = t->hasBuilt(BuildingID::RESOURCE_SILO);
details->hallLevel = t->hallLevel(); details->hallLevel = t->hallLevel();
details->garrisonedHero = t->garrisonHero; details->garrisonedHero = t->getGarrisonHero();
} }
} }

View File

@ -308,6 +308,47 @@ void CGHeroInstance::setHeroType(HeroTypeID heroType)
subID = heroType; subID = heroType;
} }
bool CGHeroInstance::isGarrisoned() const
{
return inTownGarrison;
}
const CGTownInstance * CGHeroInstance::getVisitedTown() const
{
if (!visitedTown.hasValue())
return nullptr;
return cb->getTown(visitedTown);
}
CGTownInstance * CGHeroInstance::getVisitedTown()
{
if (!visitedTown.hasValue())
return nullptr;
return dynamic_cast<CGTownInstance*>(cb->gameState()->getObjInstance(visitedTown));
}
void CGHeroInstance::setVisitedTown(const CGTownInstance * town, bool garrisoned)
{
if (town)
visitedTown = town->id;
else
visitedTown = {};
inTownGarrison = garrisoned;
}
const CCommanderInstance * CGHeroInstance::getCommander() const
{
return commander.get();
}
CCommanderInstance * CGHeroInstance::getCommander()
{
return commander.get();
}
void CGHeroInstance::initObj(vstd::RNG & rand) void CGHeroInstance::initObj(vstd::RNG & rand)
{ {
if (ID == Obj::HERO) if (ID == Obj::HERO)
@ -423,7 +464,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
if (cb->getSettings().getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && getHeroClass()->commander.hasValue()) if (cb->getSettings().getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && getHeroClass()->commander.hasValue())
{ {
commander = new CCommanderInstance(getHeroClass()->commander); commander = std::make_unique<CCommanderInstance>(getHeroClass()->commander);
commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
commander->giveStackExp (exp); //after our exp is set commander->giveStackExp (exp); //after our exp is set
} }
@ -504,10 +545,7 @@ void CGHeroInstance::initArmy(vstd::RNG & rand, IArmyDescriptor * dst)
} }
} }
CGHeroInstance::~CGHeroInstance() CGHeroInstance::~CGHeroInstance() = default;
{
commander.dellNull();
}
bool CGHeroInstance::needsLastStack() const bool CGHeroInstance::needsLastStack() const
{ {
@ -527,8 +565,8 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
} }
else //battle else //battle
{ {
if(visitedTown) //we're in town if(getVisitedTown()) //we're in town
visitedTown->onHeroVisit(h); //town will handle attacking getVisitedTown()->onHeroVisit(h); //town will handle attacking
else else
cb->startBattle(h, this); cb->startBattle(h, this);
} }
@ -1060,7 +1098,7 @@ si32 CGHeroInstance::manaRegain() const
si32 CGHeroInstance::getManaNewTurn() const si32 CGHeroInstance::getManaNewTurn() const
{ {
if(visitedTown && visitedTown->hasBuilt(BuildingID::MAGES_GUILD_1)) if(getVisitedTown() && getVisitedTown()->hasBuilt(BuildingID::MAGES_GUILD_1))
{ {
//if hero starts turn in town with mage guild - restore all mana //if hero starts turn in town with mage guild - restore all mana
return std::max(mana, manaLimit()); return std::max(mana, manaLimit());
@ -1280,30 +1318,30 @@ void CGHeroInstance::boatDeserializationFix()
CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(const bool isBattleOutsideTown) const CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(const bool isBattleOutsideTown) const
{ {
if(!visitedTown) if(!getVisitedTown())
return nullptr; return nullptr;
return isBattleOutsideTown ? (CBonusSystemNode *)(& visitedTown->townAndVis) return isBattleOutsideTown ? (CBonusSystemNode *)(& getVisitedTown()->townAndVis)
: (CBonusSystemNode *)(visitedTown.get()); : (CBonusSystemNode *)(getVisitedTown());
} }
CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(CGameState * gs) CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(CGameState * gs)
{ {
if(visitedTown) if(getVisitedTown())
return whereShouldBeAttachedOnSiege(visitedTown->isBattleOutsideTown(this)); return whereShouldBeAttachedOnSiege(getVisitedTown()->isBattleOutsideTown(this));
return &CArmedInstance::whereShouldBeAttached(gs); return &CArmedInstance::whereShouldBeAttached(gs);
} }
CBonusSystemNode & CGHeroInstance::whereShouldBeAttached(CGameState * gs) CBonusSystemNode & CGHeroInstance::whereShouldBeAttached(CGameState * gs)
{ {
if(visitedTown) if(getVisitedTown())
{ {
if(inTownGarrison) if(isGarrisoned())
return *visitedTown; return *getVisitedTown();
else else
return visitedTown->townAndVis; return getVisitedTown()->townAndVis;
} }
else else
return CArmedInstance::whereShouldBeAttached(gs); return CArmedInstance::whereShouldBeAttached(gs);

View File

@ -17,7 +17,6 @@
#include "../bonuses/BonusCache.h" #include "../bonuses/BonusCache.h"
#include "../entities/hero/EHeroGender.h" #include "../entities/hero/EHeroGender.h"
#include "../CArtHandler.h" // For CArtifactSet #include "../CArtHandler.h" // For CArtifactSet
#include "../ConstTransitivePtr.h"
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
@ -66,12 +65,14 @@ private:
MagicSchoolMasteryCache magicSchoolMastery; MagicSchoolMasteryCache magicSchoolMastery;
BonusValueCache manaPerKnowledgeCached; BonusValueCache manaPerKnowledgeCached;
std::unique_ptr<TurnInfoCache> turnInfoCache; std::unique_ptr<TurnInfoCache> turnInfoCache;
std::unique_ptr<CCommanderInstance> commander;
std::set<SpellID> spells; //known spells (spell IDs) std::set<SpellID> spells; //known spells (spell IDs)
ObjectInstanceID visitedTown; //set if hero is visiting town or in the town garrison
ui32 movement; //remaining movement points ui32 movement; //remaining movement points
bool inTownGarrison; // if hero is in town garrison
public: public:
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//format: 123 //format: 123
// 8 4 // 8 4
@ -93,9 +94,6 @@ public:
std::string nameCustomTextId; std::string nameCustomTextId;
std::string biographyCustomTextId; std::string biographyCustomTextId;
bool inTownGarrison; // if hero is in town garrison
ConstTransitivePtr<CGTownInstance> visitedTown; //set if hero is visiting town or in the town garrison
ConstTransitivePtr<CCommanderInstance> commander;
const CGBoat * boat = nullptr; //set to CGBoat when sailing const CGBoat * boat = nullptr; //set to CGBoat when sailing
static constexpr si32 UNINITIALIZED_MANA = -1; static constexpr si32 UNINITIALIZED_MANA = -1;
@ -103,8 +101,6 @@ public:
static constexpr auto UNINITIALIZED_EXPERIENCE = std::numeric_limits<TExpType>::max(); static constexpr auto UNINITIALIZED_EXPERIENCE = std::numeric_limits<TExpType>::max();
static const ui32 NO_PATROLLING; static const ui32 NO_PATROLLING;
//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
std::set<ObjectInstanceID> visitedObjects; std::set<ObjectInstanceID> visitedObjects;
struct DLL_LINKAGE Patrol struct DLL_LINKAGE Patrol
@ -252,6 +248,14 @@ public:
HeroTypeID getHeroTypeID() const; HeroTypeID getHeroTypeID() const;
void setHeroType(HeroTypeID type); void setHeroType(HeroTypeID type);
bool isGarrisoned() const;
const CGTownInstance * getVisitedTown() const;
CGTownInstance * getVisitedTown();
void setVisitedTown(const CGTownInstance * town, bool garrisoned);
const CCommanderInstance * getCommander() const;
CCommanderInstance * getCommander();
void initObj(vstd::RNG & rand) override; void initObj(vstd::RNG & rand) override;
void initHero(vstd::RNG & rand); void initHero(vstd::RNG & rand);
void initHero(vstd::RNG & rand, const HeroTypeID & SUBID); void initHero(vstd::RNG & rand, const HeroTypeID & SUBID);

View File

@ -59,13 +59,13 @@ void CGTownInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
switch (what) switch (what)
{ {
case ObjProperty::STRUCTURE_ADD_VISITING_HERO: case ObjProperty::STRUCTURE_ADD_VISITING_HERO:
rewardableBuildings.at(identifier.getNum())->setProperty(ObjProperty::VISITORS, visitingHero->id); rewardableBuildings.at(identifier.getNum())->setProperty(ObjProperty::VISITORS, getVisitingHero()->id);
break; break;
case ObjProperty::STRUCTURE_CLEAR_VISITORS: case ObjProperty::STRUCTURE_CLEAR_VISITORS:
rewardableBuildings.at(identifier.getNum())->setProperty(ObjProperty::STRUCTURE_CLEAR_VISITORS, NumericID(0)); rewardableBuildings.at(identifier.getNum())->setProperty(ObjProperty::STRUCTURE_CLEAR_VISITORS, NumericID(0));
break; break;
case ObjProperty::STRUCTURE_ADD_GARRISONED_HERO: //add garrisoned hero to visitors case ObjProperty::STRUCTURE_ADD_GARRISONED_HERO: //add garrisoned hero to visitors
rewardableBuildings.at(identifier.getNum())->setProperty(ObjProperty::VISITORS, garrisonHero->id); rewardableBuildings.at(identifier.getNum())->setProperty(ObjProperty::VISITORS, getGarrisonHero()->id);
break; break;
case ObjProperty::BONUS_VALUE_FIRST: case ObjProperty::BONUS_VALUE_FIRST:
bonusValue.first = identifier.getNum(); bonusValue.first = identifier.getNum();
@ -296,7 +296,7 @@ int CGTownInstance::spellsAtLevel(int level, bool checkGuild) const
bool CGTownInstance::needsLastStack() const bool CGTownInstance::needsLastStack() const
{ {
return garrisonHero != nullptr; return getGarrisonHero() != nullptr;
} }
void CGTownInstance::setOwner(const PlayerColor & player) const void CGTownInstance::setOwner(const PlayerColor & player) const
@ -309,13 +309,13 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
{ {
if(cb->gameState()->getPlayerRelations( getOwner(), h->getOwner() ) == PlayerRelations::ENEMIES) if(cb->gameState()->getPlayerRelations( getOwner(), h->getOwner() ) == PlayerRelations::ENEMIES)
{ {
if(armedGarrison() || visitingHero) if(armedGarrison() || getVisitingHero())
{ {
const CGHeroInstance * defendingHero = visitingHero ? visitingHero : garrisonHero; const CGHeroInstance * defendingHero = getVisitingHero() ? getVisitingHero() : getGarrisonHero();
const CArmedInstance * defendingArmy = defendingHero ? (CArmedInstance *)defendingHero : this; const CArmedInstance * defendingArmy = defendingHero ? (CArmedInstance *)defendingHero : this;
const bool isBattleOutside = isBattleOutsideTown(defendingHero); const bool isBattleOutside = isBattleOutsideTown(defendingHero);
if(!isBattleOutside && visitingHero && defendingHero == visitingHero) if(!isBattleOutside && getVisitingHero() && defendingHero == getVisitingHero())
{ {
//we have two approaches to merge armies: mergeGarrisonOnSiege() and used in the CGameHandler::garrisonSwap(ObjectInstanceID tid) //we have two approaches to merge armies: mergeGarrisonOnSiege() and used in the CGameHandler::garrisonSwap(ObjectInstanceID tid)
auto * nodeSiege = defendingHero->whereShouldBeAttachedOnSiege(isBattleOutside); auto * nodeSiege = defendingHero->whereShouldBeAttachedOnSiege(isBattleOutside);
@ -323,7 +323,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
if(nodeSiege == (CBonusSystemNode *)this) if(nodeSiege == (CBonusSystemNode *)this)
cb->swapGarrisonOnSiege(this->id); cb->swapGarrisonOnSiege(this->id);
const_cast<CGHeroInstance *>(defendingHero)->inTownGarrison = false; //hack to return visitor from garrison after battle const_cast<CGHeroInstance *>(defendingHero)->setVisitedTown(this, false); //hack to return visitor from garrison after battle
} }
cb->startBattle(h, defendingArmy, getSightCenter(), h, defendingHero, BattleLayout::createDefaultLayout(cb, h, defendingArmy), (isBattleOutside ? nullptr : this)); cb->startBattle(h, defendingArmy, getSightCenter(), h, defendingHero, BattleLayout::createDefaultLayout(cb, h, defendingArmy), (isBattleOutside ? nullptr : this));
} }
@ -343,7 +343,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
else else
{ {
assert(h->visitablePos() == this->visitablePos()); assert(h->visitablePos() == this->visitablePos());
bool commander_recover = h->commander && !h->commander->alive; bool commander_recover = h->getCommander() && !h->getCommander()->alive;
if (commander_recover) // rise commander from dead if (commander_recover) // rise commander from dead
{ {
SetCommanderProperty scp; SetCommanderProperty scp;
@ -358,8 +358,8 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
{ {
InfoWindow iw; InfoWindow iw;
iw.player = h->tempOwner; iw.player = h->tempOwner;
iw.text.appendRawString(h->commander->getName()); iw.text.appendRawString(h->getCommander()->getName());
iw.components.emplace_back(ComponentType::CREATURE, h->commander->getId(), h->commander->getCount()); iw.components.emplace_back(ComponentType::CREATURE, h->getCommander()->getId(), h->getCommander()->getCount());
cb->showInfoDialog(&iw); cb->showInfoDialog(&iw);
} }
} }
@ -368,7 +368,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
{ {
//FIXME: find out why this issue appears on random maps //FIXME: find out why this issue appears on random maps
if(visitingHero == h) if(getVisitingHero() == h)
{ {
cb->stopHeroVisitCastle(this, h); cb->stopHeroVisitCastle(this, h);
logGlobal->trace("%s correctly left town %s", h->getNameTranslated(), getNameTranslated()); logGlobal->trace("%s correctly left town %s", h->getNameTranslated(), getNameTranslated());
@ -584,13 +584,13 @@ void CGTownInstance::mergeGarrisonOnSiege() const
auto getWeakestStackSlot = [&](ui64 powerLimit) auto getWeakestStackSlot = [&](ui64 powerLimit)
{ {
std::vector<SlotID> weakSlots; std::vector<SlotID> weakSlots;
auto stacksList = visitingHero->stacks; auto stacksList = getVisitingHero()->stacks;
std::pair<SlotID, CStackInstance *> pair; std::pair<SlotID, CStackInstance *> pair;
while(!stacksList.empty()) while(!stacksList.empty())
{ {
pair = *vstd::minElementByFun(stacksList, [&](const std::pair<SlotID, CStackInstance *> & elem) { return elem.second->getPower(); }); pair = *vstd::minElementByFun(stacksList, [&](const std::pair<SlotID, CStackInstance *> & elem) { return elem.second->getPower(); });
if(powerLimit > pair.second->getPower() && if(powerLimit > pair.second->getPower() &&
(weakSlots.empty() || pair.second->getPower() == visitingHero->getStack(weakSlots.front()).getPower())) (weakSlots.empty() || pair.second->getPower() == getVisitingHero()->getStack(weakSlots.front()).getPower()))
{ {
weakSlots.push_back(pair.first); weakSlots.push_back(pair.first);
stacksList.erase(pair.first); stacksList.erase(pair.first);
@ -612,20 +612,20 @@ void CGTownInstance::mergeGarrisonOnSiege() const
auto pair = *vstd::maxElementByFun(stacks, [&](const std::pair<SlotID, CStackInstance *> & elem) auto pair = *vstd::maxElementByFun(stacks, [&](const std::pair<SlotID, CStackInstance *> & elem)
{ {
ui64 power = elem.second->getPower(); ui64 power = elem.second->getPower();
auto dst = visitingHero->getSlotFor(elem.second->getCreatureID()); auto dst = getVisitingHero()->getSlotFor(elem.second->getCreatureID());
if(dst.validSlot() && visitingHero->hasStackAtSlot(dst)) if(dst.validSlot() && getVisitingHero()->hasStackAtSlot(dst))
power += visitingHero->getStack(dst).getPower(); power += getVisitingHero()->getStack(dst).getPower();
return power; return power;
}); });
auto dst = visitingHero->getSlotFor(pair.second->getCreatureID()); auto dst = getVisitingHero()->getSlotFor(pair.second->getCreatureID());
if(dst.validSlot()) if(dst.validSlot())
cb->moveStack(StackLocation(id, pair.first), StackLocation(visitingHero->id, dst), -1); cb->moveStack(StackLocation(id, pair.first), StackLocation(getVisitingHero()->id, dst), -1);
else else
{ {
dst = getWeakestStackSlot(static_cast<int>(pair.second->getPower())); dst = getWeakestStackSlot(static_cast<int>(pair.second->getPower()));
if(dst.validSlot()) if(dst.validSlot())
cb->swapStacks(StackLocation(id, pair.first), StackLocation(visitingHero->id, dst)); cb->swapStacks(StackLocation(id, pair.first), StackLocation(getVisitingHero()->id, dst));
} }
} }
} }
@ -722,10 +722,10 @@ void CGTownInstance::deserializationFix()
//Hero is already handled by CGameState::attachArmedObjects //Hero is already handled by CGameState::attachArmedObjects
// if(visitingHero) // if(getVisitingHero())
// visitingHero->attachTo(&townAndVis); // getVisitingHero()->attachTo(&townAndVis);
// if(garrisonHero) // if(getGarrisonHero())
// garrisonHero->attachTo(this); // getGarrisonHero()->attachTo(this);
} }
void CGTownInstance::updateMoraleBonusFromArmy() void CGTownInstance::updateMoraleBonusFromArmy()
@ -737,7 +737,7 @@ void CGTownInstance::updateMoraleBonusFromArmy()
addNewBonus(b); addNewBonus(b);
} }
if (garrisonHero) if (getGarrisonHero())
{ {
b->val = 0; b->val = 0;
nodeHasChanged(); nodeHasChanged();
@ -786,7 +786,7 @@ void CGTownInstance::recreateBuildingsBonuses()
void CGTownInstance::setVisitingHero(CGHeroInstance *h) void CGTownInstance::setVisitingHero(CGHeroInstance *h)
{ {
if(visitingHero.get() == h) if(getVisitingHero() == h)
return; return;
if(h) if(h)
@ -795,23 +795,23 @@ void CGTownInstance::setVisitingHero(CGHeroInstance *h)
assert(p); assert(p);
h->detachFrom(*p); h->detachFrom(*p);
h->attachTo(townAndVis); h->attachTo(townAndVis);
visitingHero = h; h->setVisitedTown(this, false);
h->visitedTown = this; visitingHero = h->id;
h->inTownGarrison = false;
} }
else else
{ {
PlayerState *p = cb->gameState()->getPlayerState(visitingHero->tempOwner); auto oldVisitor = dynamic_cast<CGHeroInstance*>(cb->gameState()->getObjInstance(visitingHero));
visitingHero->visitedTown = nullptr; PlayerState *p = cb->gameState()->getPlayerState(getVisitingHero()->tempOwner);
visitingHero->detachFrom(townAndVis); oldVisitor->setVisitedTown(nullptr, false);
visitingHero->attachTo(*p); oldVisitor->detachFrom(townAndVis);
visitingHero = nullptr; oldVisitor->attachTo(*p);
visitingHero = {};
} }
} }
void CGTownInstance::setGarrisonedHero(CGHeroInstance *h) void CGTownInstance::setGarrisonedHero(CGHeroInstance *h)
{ {
if(garrisonHero.get() == h) if(getGarrisonHero() == h)
return; return;
if(h) if(h)
@ -820,25 +820,40 @@ void CGTownInstance::setGarrisonedHero(CGHeroInstance *h)
assert(p); assert(p);
h->detachFrom(*p); h->detachFrom(*p);
h->attachTo(*this); h->attachTo(*this);
garrisonHero = h; h->setVisitedTown(this, true);
h->visitedTown = this; garrisonHero = h->id;
h->inTownGarrison = true;
} }
else else
{ {
PlayerState *p = cb->gameState()->getPlayerState(garrisonHero->tempOwner); PlayerState *p = cb->gameState()->getPlayerState(getGarrisonHero()->tempOwner);
garrisonHero->visitedTown = nullptr; auto oldVisitor = dynamic_cast<CGHeroInstance*>(cb->gameState()->getObjInstance(visitingHero));
garrisonHero->inTownGarrison = false; oldVisitor->setVisitedTown(nullptr, false);
garrisonHero->detachFrom(*this); oldVisitor->detachFrom(*this);
garrisonHero->attachTo(*p); oldVisitor->attachTo(*p);
garrisonHero = nullptr; garrisonHero = {};
} }
updateMoraleBonusFromArmy(); //avoid giving morale bonus for same army twice updateMoraleBonusFromArmy(); //avoid giving morale bonus for same army twice
} }
const CGHeroInstance * CGTownInstance::getVisitingHero() const
{
if (!visitingHero.hasValue())
return nullptr;
return cb->getHero(visitingHero);
}
const CGHeroInstance * CGTownInstance::getGarrisonHero() const
{
if (!garrisonHero.hasValue())
return nullptr;
return cb->getHero(garrisonHero);
}
bool CGTownInstance::armedGarrison() const bool CGTownInstance::armedGarrison() const
{ {
return !stacks.empty() || garrisonHero; return !stacks.empty() || getGarrisonHero();
} }
int CGTownInstance::getTownLevel() const int CGTownInstance::getTownLevel() const
@ -876,8 +891,8 @@ void CGTownInstance::setNameTextId( const std::string & newName )
const CArmedInstance * CGTownInstance::getUpperArmy() const const CArmedInstance * CGTownInstance::getUpperArmy() const
{ {
if(garrisonHero) if(getGarrisonHero())
return garrisonHero; return getGarrisonHero();
return this; return this;
} }
@ -1014,9 +1029,9 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
void CGTownInstance::addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID ) const void CGTownInstance::addHeroToStructureVisitors(const CGHeroInstance *h, si64 structureInstanceID ) const
{ {
if(visitingHero == h) if(getVisitingHero() == h)
cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_ADD_VISITING_HERO, structureInstanceID); //add to visitors
else if(garrisonHero == h) else if(getGarrisonHero() == h)
cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_ADD_GARRISONED_HERO, structureInstanceID); //then it must be garrisoned hero cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_ADD_GARRISONED_HERO, structureInstanceID); //then it must be garrisoned hero
else else
{ {

View File

@ -11,7 +11,6 @@
#include "IMarket.h" #include "IMarket.h"
#include "CGDwelling.h" #include "CGDwelling.h"
#include "../ConstTransitivePtr.h"
#include "../entities/faction/CFaction.h" // TODO: remove #include "../entities/faction/CFaction.h" // TODO: remove
#include "../entities/faction/CTown.h" // TODO: remove #include "../entities/faction/CTown.h" // TODO: remove
@ -57,13 +56,14 @@ class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public I
std::map<BuildingID, TownRewardableBuildingInstance*> convertOldBuildings(std::vector<TownRewardableBuildingInstance*> oldVector); std::map<BuildingID, TownRewardableBuildingInstance*> convertOldBuildings(std::vector<TownRewardableBuildingInstance*> oldVector);
std::set<BuildingID> builtBuildings; std::set<BuildingID> builtBuildings;
ObjectInstanceID garrisonHero;
ObjectInstanceID visitingHero;
public: public:
enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3}; enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
CTownAndVisitingHero townAndVis; CTownAndVisitingHero townAndVis;
si32 built; //how many buildings has been built this turn si32 built; //how many buildings has been built this turn
si32 destroyed; //how many buildings has been destroyed this turn si32 destroyed; //how many buildings has been destroyed this turn
ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
ui32 identifier; //special identifier from h3m (only > RoE maps) ui32 identifier; //special identifier from h3m (only > RoE maps)
PlayerColor alignmentToPlayer; // if set to non-neutral, random town will have same faction as specified player PlayerColor alignmentToPlayer; // if set to non-neutral, random town will have same faction as specified player
std::set<BuildingID> forbiddenBuildings; std::set<BuildingID> forbiddenBuildings;
@ -115,6 +115,8 @@ public:
void setVisitingHero(CGHeroInstance *h); void setVisitingHero(CGHeroInstance *h);
void setGarrisonedHero(CGHeroInstance *h); void setGarrisonedHero(CGHeroInstance *h);
const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself
const CGHeroInstance * getVisitingHero() const;
const CGHeroInstance * getGarrisonHero() const;
std::string getNameTranslated() const; std::string getNameTranslated() const;
std::string getNameTextID() const; std::string getNameTextID() const;
@ -211,7 +213,7 @@ public:
inline bool isBattleOutsideTown(const CGHeroInstance * defendingHero) const inline bool isBattleOutsideTown(const CGHeroInstance * defendingHero) const
{ {
return defendingHero && garrisonHero && defendingHero != garrisonHero; return defendingHero && getGarrisonHero() && defendingHero != getGarrisonHero();
} }
protected: protected:
void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;

View File

@ -616,7 +616,7 @@ bool CGWhirlpool::isProtected(const CGHeroInstance * h)
{ {
return h->hasBonusOfType(BonusType::WHIRLPOOL_PROTECTION) return h->hasBonusOfType(BonusType::WHIRLPOOL_PROTECTION)
|| (h->stacksCount() == 1 && h->Slots().begin()->second->count == 1) || (h->stacksCount() == 1 && h->Slots().begin()->second->count == 1)
|| (h->stacksCount() == 0 && h->commander && h->commander->alive); || (h->stacksCount() == 0 && h->getCommander() && h->getCommander()->alive);
} }
ArtifactID CGArtifact::getArtifact() const ArtifactID CGArtifact::getArtifact() const

View File

@ -872,7 +872,7 @@ void SetSecSkill::applyGs(CGameState *gs)
void SetCommanderProperty::applyGs(CGameState *gs) void SetCommanderProperty::applyGs(CGameState *gs)
{ {
CCommanderInstance * commander = gs->getHero(heroid)->commander; const auto & commander = gs->getHero(heroid)->getCommander();
assert (commander); assert (commander);
switch (which) switch (which)
@ -1219,15 +1219,14 @@ void RemoveObject::applyGs(CGameState *gs)
return asi.artifact->getTypeId() == ArtifactID::GRAIL; return asi.artifact->getTypeId() == ArtifactID::GRAIL;
}); });
if(beatenHero->visitedTown) if(beatenHero->getVisitedTown())
{ {
if(beatenHero->visitedTown->garrisonHero == beatenHero) if(beatenHero->getVisitedTown()->getGarrisonHero() == beatenHero)
beatenHero->visitedTown->garrisonHero = nullptr; beatenHero->getVisitedTown()->setGarrisonedHero(nullptr);
else else
beatenHero->visitedTown->visitingHero = nullptr; beatenHero->getVisitedTown()->setVisitingHero(nullptr);
beatenHero->visitedTown = nullptr; beatenHero->setVisitedTown(nullptr, false);
beatenHero->inTownGarrison = false;
} }
//return hero to the pool, so he may reappear in tavern //return hero to the pool, so he may reappear in tavern
@ -1403,8 +1402,8 @@ void SetHeroesInTown::applyGs(CGameState *gs)
CGHeroInstance * v = gs->getHero(visiting); CGHeroInstance * v = gs->getHero(visiting);
CGHeroInstance * g = gs->getHero(garrison); CGHeroInstance * g = gs->getHero(garrison);
bool newVisitorComesFromGarrison = v && v == t->garrisonHero; bool newVisitorComesFromGarrison = v && v == t->getGarrisonHero();
bool newGarrisonComesFromVisiting = g && g == t->visitingHero; bool newGarrisonComesFromVisiting = g && g == t->getVisitingHero();
if(newVisitorComesFromGarrison) if(newVisitorComesFromGarrison)
t->setGarrisonedHero(nullptr); t->setGarrisonedHero(nullptr);
@ -1493,7 +1492,7 @@ void GiveHero::applyGs(CGameState *gs)
gs->getPlayerState(h->getOwner())->addOwnedObject(h); gs->getPlayerState(h->getOwner())->addOwnedObject(h);
gs->getMap().addBlockVisTiles(h); gs->getMap().addBlockVisTiles(h);
h->inTownGarrison = false; h->setVisitedTown(nullptr, false);
} }
void NewObject::applyGs(CGameState *gs) void NewObject::applyGs(CGameState *gs)
@ -2032,7 +2031,7 @@ void CommanderLevelUp::applyGs(CGameState *gs)
{ {
auto * hero = gs->getHero(heroId); auto * hero = gs->getHero(heroId);
assert(hero); assert(hero);
auto commander = hero->commander; const auto & commander = hero->getCommander();
assert(commander); assert(commander);
commander->levelUp(); commander->levelUp();
} }
@ -2128,9 +2127,9 @@ void BattleResultAccepted::applyGs(CGameState *gs)
// Grow up growing artifacts // Grow up growing artifacts
if(const auto winnerHero = gs->getHero(heroResult[winnerSide].heroId)) if(const auto winnerHero = gs->getHero(heroResult[winnerSide].heroId))
{ {
if(winnerHero->commander && winnerHero->commander->alive) if(winnerHero->getCommander() && winnerHero->getCommander()->alive)
{ {
for(auto & art : winnerHero->commander->artifactsWorn) for(auto & art : winnerHero->getCommander()->artifactsWorn)
art.second.artifact->growingUp(); art.second.artifact->growingUp();
} }
for(auto & art : winnerHero->artifactsWorn) for(auto & art : winnerHero->artifactsWorn)

View File

@ -268,7 +268,7 @@ TeleporterTilesVector CPathfinderHelper::getCastleGates(const PathNodeInfo & sou
for(const auto & town : getPlayerState(hero->tempOwner)->getTowns()) for(const auto & town : getPlayerState(hero->tempOwner)->getTowns())
{ {
if(town->id != source.nodeObject->id && town->visitingHero == nullptr if(town->id != source.nodeObject->id && town->getVisitingHero() == nullptr
&& town->hasBuilt(BuildingSubID::CASTLE_GATE)) && town->hasBuilt(BuildingSubID::CASTLE_GATE))
{ {
allowedExits.push_back(town->visitablePos()); allowedExits.push_back(town->visitablePos());

View File

@ -70,8 +70,8 @@ class BinaryDeserializer : public CLoaderBase
{ {
auto * hero = dynamic_cast<CGHeroInstance *>(armyPtr); auto * hero = dynamic_cast<CGHeroInstance *>(armyPtr);
assert(hero); assert(hero);
assert(hero->commander); assert(hero->getCommander());
data = hero->commander; data = hero->getCommander();
} }
return true; return true;
} }

View File

@ -43,7 +43,7 @@ bool AdventureSpellMechanics::canBeCast(spells::Problem & problem, const CGameIn
if (heroCaster) if (heroCaster)
{ {
if(heroCaster->inTownGarrison) if(heroCaster->isGarrisoned())
return false; return false;
const auto level = heroCaster->getSpellSchoolLevel(owner); const auto level = heroCaster->getSpellSchoolLevel(owner);
@ -467,7 +467,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost) if(static_cast<int>(parameters.caster->getHeroCaster()->movementPointsRemaining()) < moveCost)
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
if(destination->visitingHero) if(destination->getVisitingHero())
{ {
InfoWindow iw; InfoWindow iw;
iw.player = parameters.caster->getCasterOwner(); iw.player = parameters.caster->getCasterOwner();
@ -520,7 +520,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
} }
if(destination->visitingHero) if(destination->getVisitingHero())
{ {
env->complain("[Internal error] Can't teleport to occupied town"); env->complain("[Internal error] Can't teleport to occupied town");
return ESpellCastResult::ERROR; return ESpellCastResult::ERROR;
@ -635,7 +635,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
for(const auto * t : towns) for(const auto * t : towns)
{ {
if(t->visitingHero == nullptr) //empty town if(t->getVisitingHero() == nullptr) //empty town
request.objects.push_back(t->id); request.objects.push_back(t->id);
} }

View File

@ -174,7 +174,7 @@ void setPlayerColor(QImage * sur, PlayerColor player)
std::shared_ptr<QImage> MapHandler::getObjectImage(const CGObjectInstance * obj) std::shared_ptr<QImage> MapHandler::getObjectImage(const CGObjectInstance * obj)
{ {
if( !obj if( !obj
|| (obj->ID==Obj::HERO && static_cast<const CGHeroInstance*>(obj)->inTownGarrison) //garrisoned hero || (obj->ID==Obj::HERO && static_cast<const CGHeroInstance*>(obj)->isGarrisoned()) //garrisoned hero
|| (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero)) //boat with hero (hero graphics is used) || (obj->ID==Obj::BOAT && static_cast<const CGBoat*>(obj)->hero)) //boat with hero (hero graphics is used)
{ {
return nullptr; return nullptr;

View File

@ -334,8 +334,8 @@ void CGameHandler::expGiven(const CGHeroInstance *hero)
{ {
if (hero->gainsLevel()) if (hero->gainsLevel())
levelUpHero(hero); levelUpHero(hero);
else if (hero->commander && hero->commander->gainsLevel()) else if (hero->getCommander() && hero->getCommander()->gainsLevel())
levelUpCommander(hero->commander); levelUpCommander(hero->getCommander());
//if (hero->commander && hero->level > hero->commander->level && hero->commander->gainsLevel()) //if (hero->commander && hero->level > hero->commander->level && hero->commander->gainsLevel())
// levelUpCommander(hero->commander); // levelUpCommander(hero->commander);
@ -375,7 +375,7 @@ void CGameHandler::giveExperience(const CGHeroInstance * hero, TExpType amountTo
sendAndApply(sps); sendAndApply(sps);
//hero may level up //hero may level up
if (hero->commander && hero->commander->alive) if (hero->getCommander() && hero->getCommander()->alive)
{ {
//FIXME: trim experience according to map limit? //FIXME: trim experience according to map limit?
SetCommanderProperty scp; SetCommanderProperty scp;
@ -412,8 +412,8 @@ void CGameHandler::changeSecSkill(const CGHeroInstance * hero, SecondarySkill wh
sss.abs = abs; sss.abs = abs;
sendAndApply(sss); sendAndApply(sss);
if (hero->visitedTown) if (hero->getVisitedTown())
giveSpells(hero->visitedTown, hero); giveSpells(hero->getVisitedTown(), hero);
// Our scouting range may have changed - update it // Our scouting range may have changed - update it
if (hero->getOwner().isValidPlayer()) if (hero->getOwner().isValidPlayer())
@ -893,7 +893,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
if(distance(h->pos, dst) >= 1.5 && movementMode == EMovementMode::STANDARD) if(distance(h->pos, dst) >= 1.5 && movementMode == EMovementMode::STANDARD)
return complainRet("Tiles " + h->pos.toString()+ " and "+ dst.toString() +" are not neighboring!"); return complainRet("Tiles " + h->pos.toString()+ " and "+ dst.toString() +" are not neighboring!");
if(h->inTownGarrison) if(h->isGarrisoned())
return complainRet("Can not move garrisoned hero!"); return complainRet("Can not move garrisoned hero!");
if(h->movementPointsRemaining() < cost && dst != h->pos && movementMode == EMovementMode::STANDARD) if(h->movementPointsRemaining() < cost && dst != h->pos && movementMode == EMovementMode::STANDARD)
@ -1048,7 +1048,7 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
if (!h || !t) if (!h || !t)
COMPLAIN_RET("Invalid call to teleportHero!"); COMPLAIN_RET("Invalid call to teleportHero!");
const CGTownInstance *from = h->visitedTown; const CGTownInstance *from = h->getVisitedTown();
if (((h->getOwner() != t->getOwner()) if (((h->getOwner() != t->getOwner())
&& complain("Cannot teleport hero to another player")) && complain("Cannot teleport hero to another player"))
@ -1186,7 +1186,7 @@ void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStac
void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero)
{ {
if (obj->visitingHero != hero && obj->garrisonHero != hero) if (obj->getVisitingHero() != hero && obj->getGarrisonHero() != hero)
{ {
HeroVisitCastle vc; HeroVisitCastle vc;
vc.hid = hero->id; vc.hid = hero->id;
@ -1196,8 +1196,8 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta
} }
visitCastleObjects(obj, hero); visitCastleObjects(obj, hero);
if (obj->visitingHero && obj->garrisonHero) if (obj->getVisitingHero() && obj->getGarrisonHero())
useScholarSkill(obj->visitingHero->id, obj->garrisonHero->id); useScholarSkill(obj->getVisitingHero()->id, obj->getGarrisonHero()->id);
checkVictoryLossConditionsForPlayer(hero->tempOwner); //transported artifact? checkVictoryLossConditionsForPlayer(hero->tempOwner); //transported artifact?
} }
@ -2073,10 +2073,10 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
case CBuilding::BUILD_GRAIL : case CBuilding::BUILD_GRAIL :
if(requestedBuilding->mode == CBuilding::BUILD_GRAIL) //needs grail if(requestedBuilding->mode == CBuilding::BUILD_GRAIL) //needs grail
{ {
if(!t->visitingHero || !t->visitingHero->hasArt(ArtifactID::GRAIL)) if(!t->getVisitingHero() || !t->getVisitingHero()->hasArt(ArtifactID::GRAIL))
COMPLAIN_RET("Cannot build this without grail!") COMPLAIN_RET("Cannot build this without grail!")
else else
removeArtifact(ArtifactLocation(t->visitingHero->id, t->visitingHero->getArtPos(ArtifactID::GRAIL, false))); removeArtifact(ArtifactLocation(t->getVisitingHero()->id, t->getVisitingHero()->getArtPos(ArtifactID::GRAIL, false)));
} }
break; break;
} }
@ -2123,10 +2123,10 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
if(isMageGuild || isLibrary || (t->getFactionID() == ETownType::CONFLUX && buildingID == BuildingID::GRAIL)) if(isMageGuild || isLibrary || (t->getFactionID() == ETownType::CONFLUX && buildingID == BuildingID::GRAIL))
{ {
if(t->visitingHero) if(t->getVisitingHero())
giveSpells(t,t->visitingHero); giveSpells(t,t->getVisitingHero());
if(t->garrisonHero) if(t->getGarrisonHero())
giveSpells(t,t->garrisonHero); giveSpells(t,t->getGarrisonHero());
} }
}; };
@ -2201,10 +2201,10 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
{ {
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order //garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
std::vector<const CGHeroInstance *> visitors; std::vector<const CGHeroInstance *> visitors;
if (t->garrisonHero) if (t->getGarrisonHero())
visitors.push_back(t->garrisonHero); visitors.push_back(t->getGarrisonHero());
if (t->visitingHero) if (t->getVisitingHero())
visitors.push_back(t->visitingHero); visitors.push_back(t->getVisitingHero());
if (!visitors.empty()) if (!visitors.empty())
visitCastleObjects(t, visitors); visitCastleObjects(t, visitors);
@ -2233,12 +2233,12 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
return true; return true;
} }
if (t->rewardableBuildings.count(bid) && t->visitingHero && t->getTown()->buildings.at(bid)->manualHeroVisit) if (t->rewardableBuildings.count(bid) && t->getVisitingHero() && t->getTown()->buildings.at(bid)->manualHeroVisit)
{ {
std::vector<BuildingID> buildingsToVisit; std::vector<BuildingID> buildingsToVisit;
std::vector<const CGHeroInstance*> visitors; std::vector<const CGHeroInstance*> visitors;
buildingsToVisit.push_back(bid); buildingsToVisit.push_back(bid);
visitors.push_back(t->visitingHero); visitors.push_back(t->getVisitingHero());
auto visitQuery = std::make_shared<TownBuildingVisitQuery>(this, t, visitors, buildingsToVisit); auto visitQuery = std::make_shared<TownBuildingVisitQuery>(this, t, visitors, buildingsToVisit);
queries->addQuery(visitQuery); queries->addQuery(visitQuery);
return true; return true;
@ -2316,10 +2316,10 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool
setResearchedSpells(t, level, spells, accepted); setResearchedSpells(t, level, spells, accepted);
if(t->visitingHero) if(t->getVisitingHero())
giveSpells(t, t->visitingHero); giveSpells(t, t->getVisitingHero());
if(t->garrisonHero) if(t->getGarrisonHero())
giveSpells(t, t->garrisonHero); giveSpells(t, t->getGarrisonHero());
return true; return true;
} }
@ -2342,7 +2342,7 @@ bool CGameHandler::recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst
if (town) if (town)
{ {
COMPLAIN_RET_FALSE_IF(town != army && !hero, "Cannot recruit: invalid destination!"); COMPLAIN_RET_FALSE_IF(town != army && !hero, "Cannot recruit: invalid destination!");
COMPLAIN_RET_FALSE_IF(hero != town->garrisonHero && hero != town->visitingHero, "Cannot recruit: can only recruit to town or hero in town!!"); COMPLAIN_RET_FALSE_IF(hero != town->getGarrisonHero() && hero != town->getVisitingHero(), "Cannot recruit: can only recruit to town or hero in town!!");
} }
else else
{ {
@ -2505,16 +2505,16 @@ bool CGameHandler::swapGarrisonOnSiege(ObjectInstanceID tid)
{ {
const CGTownInstance * town = getTown(tid); const CGTownInstance * town = getTown(tid);
if(!town->garrisonHero == !town->visitingHero) if(!town->getGarrisonHero() == !town->getVisitingHero())
return false; return false;
SetHeroesInTown intown; SetHeroesInTown intown;
intown.tid = tid; intown.tid = tid;
if(town->garrisonHero) //garrison -> vising if(town->getGarrisonHero()) //garrison -> vising
{ {
intown.garrison = ObjectInstanceID(); intown.garrison = ObjectInstanceID();
intown.visiting = town->garrisonHero->id; intown.visiting = town->getGarrisonHero()->id;
} }
else //visiting -> garrison else //visiting -> garrison
{ {
@ -2522,7 +2522,7 @@ bool CGameHandler::swapGarrisonOnSiege(ObjectInstanceID tid)
town->mergeGarrisonOnSiege(); town->mergeGarrisonOnSiege();
intown.visiting = ObjectInstanceID(); intown.visiting = ObjectInstanceID();
intown.garrison = town->visitingHero->id; intown.garrison = town->getVisitingHero()->id;
} }
sendAndApply(intown); sendAndApply(intown);
return true; return true;
@ -2531,29 +2531,29 @@ bool CGameHandler::swapGarrisonOnSiege(ObjectInstanceID tid)
bool CGameHandler::garrisonSwap(ObjectInstanceID tid) bool CGameHandler::garrisonSwap(ObjectInstanceID tid)
{ {
const CGTownInstance * town = getTown(tid); const CGTownInstance * town = getTown(tid);
if (!town->garrisonHero && town->visitingHero) //visiting => garrison, merge armies: town army => hero army if (!town->getGarrisonHero() && town->getVisitingHero()) //visiting => garrison, merge armies: town army => hero army
{ {
if (!town->visitingHero->canBeMergedWith(*town)) if (!town->getVisitingHero()->canBeMergedWith(*town))
{ {
complain("Cannot make garrison swap, not enough free slots!"); complain("Cannot make garrison swap, not enough free slots!");
return false; return false;
} }
moveArmy(town, town->visitingHero, true); moveArmy(town, town->getVisitingHero(), true);
SetHeroesInTown intown; SetHeroesInTown intown;
intown.tid = tid; intown.tid = tid;
intown.visiting = ObjectInstanceID(); intown.visiting = ObjectInstanceID();
intown.garrison = town->visitingHero->id; intown.garrison = town->getVisitingHero()->id;
sendAndApply(intown); sendAndApply(intown);
return true; return true;
} }
else if (town->garrisonHero && !town->visitingHero) //move hero out of the garrison else if (town->getGarrisonHero() && !town->getVisitingHero()) //move hero out of the garrison
{ {
int mapCap = getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP); int mapCap = getSettings().getInteger(EGameSettings::HEROES_PER_PLAYER_ON_MAP_CAP);
//check if moving hero out of town will break wandering heroes limit //check if moving hero out of town will break wandering heroes limit
if (getHeroCount(town->garrisonHero->tempOwner,false) >= mapCap) if (getHeroCount(town->getGarrisonHero()->tempOwner,false) >= mapCap)
{ {
complain("Cannot move hero out of the garrison, there are already " + std::to_string(mapCap) + " wandering heroes!"); complain("Cannot move hero out of the garrison, there are already " + std::to_string(mapCap) + " wandering heroes!");
return false; return false;
@ -2562,16 +2562,16 @@ bool CGameHandler::garrisonSwap(ObjectInstanceID tid)
SetHeroesInTown intown; SetHeroesInTown intown;
intown.tid = tid; intown.tid = tid;
intown.garrison = ObjectInstanceID(); intown.garrison = ObjectInstanceID();
intown.visiting = town->garrisonHero->id; intown.visiting = town->getGarrisonHero()->id;
sendAndApply(intown); sendAndApply(intown);
return true; return true;
} }
else if (!!town->garrisonHero && town->visitingHero) //swap visiting and garrison hero else if (!!town->getGarrisonHero() && town->getVisitingHero()) //swap visiting and garrison hero
{ {
SetHeroesInTown intown; SetHeroesInTown intown;
intown.tid = tid; intown.tid = tid;
intown.garrison = town->visitingHero->id; intown.garrison = town->getVisitingHero()->id;
intown.visiting = town->garrisonHero->id; intown.visiting = town->getGarrisonHero()->id;
sendAndApply(intown); sendAndApply(intown);
return true; return true;
} }
@ -2991,7 +2991,7 @@ bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
{ {
const CGHeroInstance * hero = getHero(hid); const CGHeroInstance * hero = getHero(hid);
COMPLAIN_RET_FALSE_IF(nullptr == hero, "Invalid hero index"); COMPLAIN_RET_FALSE_IF(nullptr == hero, "Invalid hero index");
const CGTownInstance * town = hero->visitedTown; const CGTownInstance * town = hero->getVisitedTown();
COMPLAIN_RET_FALSE_IF(nullptr == town, "Hero not in town"); COMPLAIN_RET_FALSE_IF(nullptr == town, "Hero not in town");
if (aid==ArtifactID::SPELLBOOK) if (aid==ArtifactID::SPELLBOOK)
@ -3342,13 +3342,13 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
if (o1->ID == Obj::TOWN) if (o1->ID == Obj::TOWN)
{ {
const CGTownInstance *t = static_cast<const CGTownInstance*>(o1); const CGTownInstance *t = static_cast<const CGTownInstance*>(o1);
if (t->visitingHero == o2 || t->garrisonHero == o2) if (t->getVisitingHero() == o2 || t->getGarrisonHero() == o2)
return true; return true;
} }
if (o2->ID == Obj::TOWN) if (o2->ID == Obj::TOWN)
{ {
const CGTownInstance *t = static_cast<const CGTownInstance*>(o2); const CGTownInstance *t = static_cast<const CGTownInstance*>(o2);
if (t->visitingHero == o1 || t->garrisonHero == o1) if (t->getVisitingHero() == o1 || t->getGarrisonHero() == o1)
return true; return true;
} }
@ -3364,7 +3364,7 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
const CGHeroInstance *h2 = static_cast<const CGHeroInstance*>(o2); const CGHeroInstance *h2 = static_cast<const CGHeroInstance*>(o2);
// two heroes in same town (garrisoned and visiting) // two heroes in same town (garrisoned and visiting)
if (h1->visitedTown != nullptr && h2->visitedTown != nullptr && h1->visitedTown == h2->visitedTown) if (h1->getVisitedTown() != nullptr && h2->getVisitedTown() != nullptr && h1->getVisitedTown() == h2->getVisitedTown())
return true; return true;
} }
@ -3408,7 +3408,7 @@ void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInsta
if(obj->ID == Obj::HERO) if(obj->ID == Obj::HERO)
{ {
auto visitedHero = static_cast<const CGHeroInstance *>(obj); auto visitedHero = static_cast<const CGHeroInstance *>(obj);
const auto visitedTown = visitedHero->visitedTown; const auto visitedTown = visitedHero->getVisitedTown();
if(visitedTown) if(visitedTown)
{ {

View File

@ -183,7 +183,7 @@ void ApplyGhNetPackVisitor::visitUpgradeCreature(UpgradeCreature & pack)
void ApplyGhNetPackVisitor::visitGarrisonHeroSwap(GarrisonHeroSwap & pack) void ApplyGhNetPackVisitor::visitGarrisonHeroSwap(GarrisonHeroSwap & pack)
{ {
const CGTownInstance * town = gh.getTown(pack.tid); const CGTownInstance * town = gh.getTown(pack.tid);
if(!gh.isPlayerOwns(connection, &pack, pack.tid) && !(town->garrisonHero && gh.isPlayerOwns(connection, &pack, town->garrisonHero->id))) if(!gh.isPlayerOwns(connection, &pack, pack.tid) && !(town->getGarrisonHero() && gh.isPlayerOwns(connection, &pack, town->getGarrisonHero()->id)))
gh.throwNotAllowedAction(connection); //neither town nor garrisoned hero (if present) is ours gh.throwNotAllowedAction(connection); //neither town nor garrisoned hero (if present) is ours
gh.throwIfPlayerNotActive(connection, &pack); gh.throwIfPlayerNotActive(connection, &pack);

View File

@ -106,7 +106,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle,
if(c) if(c)
{ {
auto h = dynamic_cast <const CGHeroInstance *>(army); auto h = dynamic_cast <const CGHeroInstance *>(army);
if(h && h->commander == c && (st->getCount() == 0 || !st->alive())) if(h && h->getCommander() == c && (st->getCount() == 0 || !st->alive()))
{ {
logGlobal->debug("Commander is dead."); logGlobal->debug("Commander is dead.");
heroWithDeadCommander = army->id; //TODO: unify commander handling heroWithDeadCommander = army->id; //TODO: unify commander handling
@ -323,11 +323,11 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
if(battleResult->winner == BattleSide::DEFENDER if(battleResult->winner == BattleSide::DEFENDER
&& winnerHero && winnerHero
&& winnerHero->visitedTown && winnerHero->getVisitedTown()
&& !winnerHero->inTownGarrison && !winnerHero->isGarrisoned()
&& winnerHero->visitedTown->garrisonHero == winnerHero) && winnerHero->getVisitedTown()->getGarrisonHero() == winnerHero)
{ {
gameHandler->swapGarrisonOnSiege(winnerHero->visitedTown->id); //return defending visitor from garrison to its rightful place gameHandler->swapGarrisonOnSiege(winnerHero->getVisitedTown()->id); //return defending visitor from garrison to its rightful place
} }
//give exp //give exp
if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide] && winnerHero) if(!finishingBattle->isDraw() && battleResult->exp[finishingBattle->winnerSide] && winnerHero)
@ -336,7 +336,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
// Add statistics // Add statistics
if(loserHero && !finishingBattle->isDraw()) if(loserHero && !finishingBattle->isDraw())
{ {
ConstTransitivePtr<CGHeroInstance> strongestHero = nullptr; const CGHeroInstance * strongestHero = nullptr;
for(auto & hero : gameHandler->gameState()->getPlayerState(finishingBattle->loser)->getHeroes()) for(auto & hero : gameHandler->gameState()->getPlayerState(finishingBattle->loser)->getHeroes())
if(!strongestHero || hero->exp > strongestHero->exp) if(!strongestHero || hero->exp > strongestHero->exp)
strongestHero = hero; strongestHero = hero;
@ -460,11 +460,11 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt
addArtifactToTransfer(packHero, loserHero->getArtPos(art), art); addArtifactToTransfer(packHero, loserHero->getArtPos(art), art);
} }
if(loserHero->commander) if(loserHero->getCommander())
{ {
auto & packCommander = resultsApplied.artifacts.emplace_back(finishingBattle->victor, finishingBattle->loserId, finishingBattle->winnerId, false); auto & packCommander = resultsApplied.artifacts.emplace_back(finishingBattle->victor, finishingBattle->loserId, finishingBattle->winnerId, false);
packCommander.srcCreature = loserHero->findStack(loserHero->commander); packCommander.srcCreature = loserHero->findStack(loserHero->getCommander());
for(const auto & artSlot : loserHero->commander->artifactsWorn) for(const auto & artSlot : loserHero->getCommander()->artifactsWorn)
addArtifactToTransfer(packCommander, artSlot.first, artSlot.second.getArt()); addArtifactToTransfer(packCommander, artSlot.first, artSlot.second.getArt());
} }
auto armyObj = dynamic_cast<const CArmedInstance*>(gameHandler->getObj(finishingBattle->loserId)); auto armyObj = dynamic_cast<const CArmedInstance*>(gameHandler->getObj(finishingBattle->loserId));

View File

@ -185,7 +185,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
if(!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!")) if(!town->hasBuilt(BuildingID::TAVERN) && gameHandler->complain("No tavern!"))
return false; return false;
if(town->visitingHero && gameHandler->complain("There is visiting hero - no place!")) if(town->getVisitingHero() && gameHandler->complain("There is visiting hero - no place!"))
return false; return false;
} }

View File

@ -143,11 +143,11 @@ void NewTurnProcessor::onPlayerTurnStarted(PlayerColor which)
for (const auto * t : playerState->getTowns()) for (const auto * t : playerState->getTowns())
{ {
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order //garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
if (t->garrisonHero != nullptr) if (t->getGarrisonHero() != nullptr)
gameHandler->objectVisited(t, t->garrisonHero); gameHandler->objectVisited(t, t->getGarrisonHero());
if (t->visitingHero != nullptr) if (t->getVisitingHero() != nullptr)
gameHandler->objectVisited(t, t->visitingHero); gameHandler->objectVisited(t, t->getVisitingHero());
} }
} }

View File

@ -773,7 +773,7 @@ void PlayerMessageProcessor::executeCheatCode(const std::string & cheatName, Pla
const CGHeroInstance * hero = gameHandler->getHero(currObj); const CGHeroInstance * hero = gameHandler->getHero(currObj);
const CGTownInstance * town = gameHandler->getTown(currObj); const CGTownInstance * town = gameHandler->getTown(currObj);
if (!town && hero) if (!town && hero)
town = hero->visitedTown; town = hero->getVisitedTown();
const auto & doCheatGiveSpells = [&]() { cheatGiveSpells(player, hero); }; const auto & doCheatGiveSpells = [&]() { cheatGiveSpells(player, hero); };
const auto & doCheatBuildTown = [&]() { cheatBuildTown(player, town); }; const auto & doCheatBuildTown = [&]() { cheatBuildTown(player, town); };

View File

@ -237,7 +237,7 @@ void CCommanderLevelUpDialogQuery::onRemoval(PlayerColor color)
{ {
assert(answer); assert(answer);
logGlobal->trace("Completing commander level-up query. Commander of hero %s gains skill %s", hero->getObjectName(), answer.value()); logGlobal->trace("Completing commander level-up query. Commander of hero %s gains skill %s", hero->getObjectName(), answer.value());
gh->levelUpCommander(hero->commander, clu.skills[*answer]); gh->levelUpCommander(hero->getCommander(), clu.skills[*answer]);
} }
void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const