Răsfoiți Sursa

Remove pointer to boat from CGHeroInstance

Ivan Savenko 6 luni în urmă
părinte
comite
9e6397d1f9

+ 1 - 1
AI/Nullkiller/AIGateway.cpp

@@ -124,7 +124,7 @@ void AIGateway::heroMoved(const TryMoveHero & details, bool verbose)
 	else if(details.result == TryMoveHero::EMBARK && hero)
 	{
 		//make sure AI not attempt to visit used boat
-		validateObject(hero->boat);
+		validateObject(hero->getBoat());
 	}
 	else if(details.result == TryMoveHero::DISEMBARK && o1)
 	{

+ 4 - 4
AI/Nullkiller/AIUtility.cpp

@@ -375,10 +375,10 @@ double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_
 	switch (bonus->type)
 	{
 		case BonusType::MOVEMENT:
-			if (hero->boat && bonus->subtype == BonusCustomSubtype::heroMovementSea)
+			if (hero->getBoat() && bonus->subtype == BonusCustomSubtype::heroMovementSea)
 				return veryRelevant;
 
-			if (!hero->boat && bonus->subtype == BonusCustomSubtype::heroMovementLand)
+			if (!hero->getBoat() && bonus->subtype == BonusCustomSubtype::heroMovementLand)
 				return relevant;
 			return notRelevant;
 		case BonusType::STACKS_SPEED:
@@ -395,9 +395,9 @@ double getArtifactBonusRelevance(const CGHeroInstance * hero, const std::shared_
 				return relevant; // spellpower / knowledge - always relevant
 		case BonusType::WATER_WALKING:
 		case BonusType::FLYING_MOVEMENT:
-			return hero->boat ? notRelevant : relevant; // boat can't fly
+			return hero->getBoat() ? notRelevant : relevant; // boat can't fly
 		case BonusType::WHIRLPOOL_PROTECTION:
-			return hero->boat ? relevant : notRelevant;
+			return hero->getBoat() ? relevant : notRelevant;
 		case BonusType::UNDEAD_RAISE_PERCENTAGE:
 			return hero->hasBonusOfType(BonusType::IMPROVED_NECROMANCY) ? veryRelevant : notRelevant;
 		case BonusType::SPELL_DAMAGE:

+ 3 - 3
AI/Nullkiller/Engine/AIMemory.cpp

@@ -22,10 +22,10 @@ void AIMemory::removeFromMemory(const CGObjectInstance * obj)
 	//TODO: Find better way to handle hero boat removal
 	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
 	{
-		if(hero->boat)
+		if(hero->inBoat())
 		{
-			vstd::erase_if_present(visitableObjs, hero->boat);
-			vstd::erase_if_present(alreadyVisited, hero->boat);
+			vstd::erase_if_present(visitableObjs, hero->getBoat());
+			vstd::erase_if_present(alreadyVisited, hero->getBoat());
 		}
 	}
 }

+ 1 - 1
AI/Nullkiller/Goals/StayAtTown.cpp

@@ -23,7 +23,7 @@ StayAtTown::StayAtTown(const CGTownInstance * town, AIPath & path)
 {
 	sethero(path.targetHero);
 	settown(town);
-	movementWasted = static_cast<float>(hero->movementPointsRemaining()) / hero->movementPointsLimit(!hero->boat) - path.movementCost();
+	movementWasted = static_cast<float>(hero->movementPointsRemaining()) / hero->movementPointsLimit(!hero->inBoat()) - path.movementCost();
 	vstd::amax(movementWasted, 0);
 }
 

+ 1 - 1
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -969,7 +969,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
 
 		if(actor->hero->tempOwner != ai->playerID)
 		{
-			bool onLand = !actor->hero->boat || actor->hero->boat->layer != EPathfindingLayer::SAIL;
+			bool onLand = !actor->hero->inBoat() || actor->hero->getBoat()->layer != EPathfindingLayer::SAIL;
 			actor->initialMovement = actor->hero->movementPointsLimit(onLand);
 		}
 

+ 1 - 1
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -43,7 +43,7 @@ ChainActor::ChainActor(const CGHeroInstance * hero, HeroRole heroRole, uint64_t
 	baseActor(this), carrierParent(nullptr), otherParent(nullptr), actorExchangeCount(1), armyCost(), actorAction()
 {
 	initialPosition = hero->visitablePos();
-	layer = hero->boat ? hero->boat->layer : EPathfindingLayer::LAND;
+	layer = hero->inBoat() ? hero->getBoat()->layer : EPathfindingLayer::LAND;
 	initialMovement = hero->movementPointsRemaining();
 	initialTurn = 0;
 	armyValue = getHeroArmyStrengthWithCommander(hero, hero);

+ 2 - 2
AI/Nullkiller/Pathfinding/ObjectGraphCalculator.cpp

@@ -298,7 +298,7 @@ void ObjectGraphCalculator::addObjectActor(const CGObjectInstance * obj)
 
 	if(cb->getTile(visitablePos)->isWater())
 	{
-		objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
+		objectActor->setBoat(temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get());
 	}
 
 	assert(objectActor->visitablePos() == visitablePos);
@@ -335,7 +335,7 @@ void ObjectGraphCalculator::addJunctionActor(const int3 & visitablePos, bool isV
 
 	if(isVirtualBoat || ai->cb->getTile(visitablePos)->isWater())
 	{
-		objectActor->boat = temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get();
+		objectActor->setBoat(temporaryBoats.emplace_back(std::make_unique<CGBoat>(objectActor->cb)).get());
 	}
 
 	assert(objectActor->visitablePos() == visitablePos);

+ 1 - 1
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -111,7 +111,7 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, con
 std::vector<CGPathNode *> AINodeStorage::getInitialNodes()
 {
 	auto hpos = hero->visitablePos();
-	auto initialNode = getOrCreateNode(hpos, hero->boat ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value();
+	auto initialNode = getOrCreateNode(hpos, hero->inBoat() ? EPathfindingLayer::SAIL : EPathfindingLayer::LAND, NORMAL_CHAIN).value();
 
 	initialNode->turns = 0;
 	initialNode->moveRemains = hero->movementPointsRemaining();

+ 5 - 5
AI/VCAI/VCAI.cpp

@@ -138,7 +138,7 @@ void VCAI::heroMoved(const TryMoveHero & details, bool verbose)
 	else if(details.result == TryMoveHero::EMBARK && hero)
 	{
 		//make sure AI not attempt to visit used boat
-		validateObject(hero->boat);
+		validateObject(hero->getBoat());
 	}
 	else if(details.result == TryMoveHero::DISEMBARK && o1)
 	{
@@ -448,13 +448,13 @@ void VCAI::objectRemoved(const CGObjectInstance * obj, const PlayerColor & initi
 	//TODO: Find better way to handle hero boat removal
 	if(auto hero = dynamic_cast<const CGHeroInstance *>(obj))
 	{
-		if(hero->boat)
+		if(hero->inBoat())
 		{
-			vstd::erase_if_present(visitableObjs, hero->boat);
-			vstd::erase_if_present(alreadyVisited, hero->boat);
+			vstd::erase_if_present(visitableObjs, hero->getBoat());
+			vstd::erase_if_present(alreadyVisited, hero->getBoat());
 
 			for(auto h : cb->getHeroesInfo())
-				unreserveObject(h, hero->boat);
+				unreserveObject(h, hero->getBoat());
 		}
 	}
 

+ 1 - 1
client/mapView/MapRendererContext.cpp

@@ -135,7 +135,7 @@ double MapRendererBaseContext::objectTransparency(ObjectInstanceID objectID, con
 		if(hero->isGarrisoned())
 			return 0;
 
-		if(hero->boat)
+		if(hero->inBoat())
 			return 0;
 	}
 	return 1;

+ 11 - 11
client/mapView/MapViewController.cpp

@@ -372,8 +372,8 @@ void MapViewController::fadeOutObject(const CGObjectInstance * obj)
 	if (obj->ID == Obj::HERO)
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance*>(obj);
-		if (hero->boat)
-			movingObject = hero->boat;
+		if (hero->inBoat())
+			movingObject = hero->getBoat();
 	}
 
 	fadingOutContext->target = movingObject->id;
@@ -393,8 +393,8 @@ void MapViewController::fadeInObject(const CGObjectInstance * obj)
 	if (obj->ID == Obj::HERO)
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance*>(obj);
-		if (hero->boat)
-			movingObject = hero->boat;
+		if (hero->inBoat())
+			movingObject = hero->getBoat();
 	}
 
 	fadingInContext->target = movingObject->id;
@@ -416,10 +416,10 @@ void MapViewController::removeObject(const CGObjectInstance * obj)
 	if (obj->ID == Obj::HERO)
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance*>(obj);
-		if (hero->boat)
+		if (hero->inBoat())
 		{
-			view->invalidate(context, hero->boat->id);
-			state->removeObject(hero->boat);
+			view->invalidate(context, hero->getBoat()->id);
+			state->removeObject(hero->getBoat());
 		}
 	}
 
@@ -514,8 +514,8 @@ void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const
 	assert(!hasOngoingAnimations());
 
 	const CGObjectInstance * movingObject = obj;
-	if(obj->boat)
-		movingObject = obj->boat;
+	if(obj->inBoat())
+		movingObject = obj->getBoat();
 
 	removeObject(movingObject);
 	addObject(movingObject);
@@ -541,8 +541,8 @@ void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & fro
 		return;
 
 	const CGObjectInstance * movingObject = obj;
-	if(obj->boat)
-		movingObject = obj->boat;
+	if(obj->inBoat())
+		movingObject = obj->getBoat();
 
 	removeObject(movingObject);
 

+ 1 - 1
lib/gameState/CGameState.cpp

@@ -610,7 +610,7 @@ void CGameState::initHeroes()
 			boat->setAnchorPos(hero->anchorPos());
 			boat->appearance = handler->getTemplates().front();
 			map->addNewObject(boat);
-			hero->attachToBoat(boat.get());
+			hero->setBoat(boat.get());
 		}
 	}
 

+ 19 - 6
lib/mapObjects/CGHeroInstance.cpp

@@ -586,7 +586,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
 			if (cb->gameState()->getMap().getTile(boatPos).isWater())
 			{
 				smp.val = movementPointsLimit(false);
-				if (!boat)
+				if (!inBoat())
 				{
 					//Create a new boat for hero
 					cb->createBoat(boatPos, getBoatType(), h->getOwner());
@@ -635,7 +635,7 @@ std::string CGHeroInstance::getMovementPointsTextIfOwner(PlayerColor player) con
 	if(player == getOwner())
 	{
 		output += " " + LIBRARY->generaltexth->translate("vcmi.adventureMap.movementPointsHeroInfo");
-		boost::replace_first(output, "%POINTS", std::to_string(movementPointsLimit(!boat)));
+		boost::replace_first(output, "%POINTS", std::to_string(movementPointsLimit(!inBoat())));
 		boost::replace_first(output, "%REMAINING", std::to_string(movementPointsRemaining()));
 	}
 
@@ -1295,10 +1295,22 @@ int CGHeroInstance::maxSpellLevel() const
 	return std::min(GameConstants::SPELL_LEVELS, valOfBonuses(BonusType::MAX_LEARNABLE_SPELL_LEVEL));
 }
 
-void CGHeroInstance::attachToBoat(CGBoat* newBoat)
+bool CGHeroInstance::inBoat() const
+{
+	return boardedBoat.hasValue();
+}
+
+const CGBoat * CGHeroInstance::getBoat() const
+{
+	if (boardedBoat.hasValue())
+		return dynamic_cast<const CGBoat*>(cb->getObjInstance(boardedBoat));
+	return nullptr;
+}
+
+void CGHeroInstance::setBoat(CGBoat* newBoat)
 {
 	assert(newBoat);
-	boat = newBoat;
+	boardedBoat = newBoat->id;
 	attachTo(*newBoat);
 	newBoat->setBoardedHero(this);
 }
@@ -1311,8 +1323,9 @@ void CGHeroInstance::deserializationFix()
 
 void CGHeroInstance::boatDeserializationFix()
 {
+	auto boat = cb->gameState()->getObjInstance(boardedBoat);
 	if (boat)
-		attachTo(const_cast<CGBoat&>(*boat));
+		attachTo(dynamic_cast<CGBoat&>(*boat));
 }
 
 CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(const bool isBattleOutsideTown) const
@@ -1351,7 +1364,7 @@ int CGHeroInstance::movementPointsAfterEmbark(int MPsBefore, int basicCost, bool
 	if(!ti->hasFreeShipBoarding())
 		return 0; // take all MPs by default
 	
-	auto boatLayer = boat ? boat->layer : EPathfindingLayer::SAIL;
+	auto boatLayer = inBoat() ? getBoat()->layer : EPathfindingLayer::SAIL;
 
 	int mp1 = ti->getMaxMovePoints(disembark ? EPathfindingLayer::LAND : boatLayer);
 	int mp2 = ti->getMaxMovePoints(disembark ? boatLayer : EPathfindingLayer::LAND);

+ 7 - 4
lib/mapObjects/CGHeroInstance.h

@@ -69,6 +69,8 @@ private:
 
 	std::set<SpellID> spells; //known spells (spell IDs)
 	ObjectInstanceID visitedTown; //set if hero is visiting town or in the town garrison
+	ObjectInstanceID boardedBoat; //set to CGBoat when sailing
+
 	ui32 movement; //remaining movement points
 	bool inTownGarrison; // if hero is in town garrison
 
@@ -94,8 +96,6 @@ public:
 	std::string nameCustomTextId;
 	std::string biographyCustomTextId;
 
-	const CGBoat * boat = nullptr; //set to CGBoat when sailing
-
 	static constexpr si32 UNINITIALIZED_MANA = -1;
 	static constexpr ui32 UNINITIALIZED_MOVEMENT = -1;
 	static constexpr auto UNINITIALIZED_EXPERIENCE = std::numeric_limits<TExpType>::max();
@@ -160,6 +160,10 @@ public:
 	std::string getClassNameTranslated() const;
 	std::string getClassNameTextID() const;
 
+	bool inBoat() const;
+	const CGBoat * getBoat() const;
+	void setBoat(CGBoat * getBoat);
+
 	bool hasSpellbook() const;
 	int maxSpellLevel() const;
 	void addSpellToSpellbook(const SpellID & spell);
@@ -313,7 +317,6 @@ public:
 	void getCastDescription(const spells::Spell * spell, const battle::Units & attacked, MetaString & text) const override;
 	void spendMana(ServerCallback * server, const int spellCost) const override;
 
-	void attachToBoat(CGBoat* newBoat);
 	void boatDeserializationFix();
 	void deserializationFix();
 	void updateAppearance();
@@ -373,7 +376,7 @@ public:
 		h & moveDir;
 		h & skillsInfo;
 		h & visitedTown;
-		h & boat;
+		h & boardedBoat;
 		h & commander;
 		h & visitedObjects;
 		BONUS_TREE_DESERIALIZATION_FIX

+ 10 - 10
lib/networkPacks/NetPacksLib.cpp

@@ -1226,11 +1226,11 @@ void RemoveObject::applyGs(CGameState *gs)
 		gs->heroesPool->addHeroToPool(beatenHero->getHeroTypeID());
 
 		//If hero on Boat is removed, the Boat disappears
-		if(beatenHero->boat)
+		if(beatenHero->inBoat())
 		{
-			beatenHero->detachFrom(const_cast<CGBoat&>(*beatenHero->boat));
-			gs->getMap().eraseObject(beatenHero->boat->id);
-			beatenHero->boat = nullptr;
+			beatenHero->detachFrom(const_cast<CGBoat&>(*beatenHero->getBoat()));
+			gs->getMap().eraseObject(beatenHero->getBoat()->id);
+			beatenHero->setBoat(nullptr);
 		}
 		return;
 	}
@@ -1321,26 +1321,26 @@ void TryMoveHero::applyGs(CGameState *gs)
 		assert(boat);
 
 		gs->getMap().removeBlockVisTiles(boat); //hero blockvis mask will be used, we don't need to duplicate it with boat
-		h->boat = boat;
+		h->setBoat(boat);
 		h->attachTo(*boat);
 		boat->setBoardedHero(h);
 	}
 	else if(result == DISEMBARK) //hero leaves boat to destination tile
 	{
-		auto * b = const_cast<CGBoat *>(h->boat);
+		auto * b = const_cast<CGBoat *>(h->getBoat());
 		b->direction = h->moveDir;
 		b->pos = start;
 		b->setBoardedHero(nullptr);
 		gs->getMap().addBlockVisTiles(b);
 		h->detachFrom(*b);
-		h->boat = nullptr;
+		h->setBoat(nullptr);
 	}
 
 	if(start!=end && (result == SUCCESS || result == TELEPORTATION || result == EMBARK || result == DISEMBARK))
 	{
 		gs->getMap().removeBlockVisTiles(h);
 		h->pos = end;
-		if(auto * b = const_cast<CGBoat *>(h->boat))
+		if(auto * b = const_cast<CGBoat *>(h->getBoat()))
 			b->pos = end;
 		gs->getMap().addBlockVisTiles(h);
 	}
@@ -1429,7 +1429,7 @@ void HeroRecruited::applyGs(CGameState *gs)
 		if (boat)
 		{
 			gs->getMap().removeBlockVisTiles(boat);
-			h->attachToBoat(boat);
+			h->setBoat(boat);
 		}
 	}
 
@@ -1463,7 +1463,7 @@ void GiveHero::applyGs(CGameState *gs)
 		if (boat)
 		{
 			gs->getMap().removeBlockVisTiles(boat);
-			h->attachToBoat(boat);
+			h->setBoat(boat);
 		}
 	}
 

+ 3 - 3
lib/pathfinder/CPathfinder.cpp

@@ -649,17 +649,17 @@ int CPathfinderHelper::getMovementCost(
 
 	bool isSailLayer;
 	if(indeterminate(isDstSailLayer))
-		isSailLayer = hero->boat && hero->boat->layer == EPathfindingLayer::SAIL && dt->isWater();
+		isSailLayer = hero->inBoat() && hero->getBoat()->layer == EPathfindingLayer::SAIL && dt->isWater();
 	else
 		isSailLayer = static_cast<bool>(isDstSailLayer);
 
 	bool isWaterLayer;
 	if(indeterminate(isDstWaterLayer))
-		isWaterLayer = ((hero->boat && hero->boat->layer == EPathfindingLayer::WATER) || ti->hasWaterWalking()) && dt->isWater();
+		isWaterLayer = ((hero->inBoat() && hero->getBoat()->layer == EPathfindingLayer::WATER) || ti->hasWaterWalking()) && dt->isWater();
 	else
 		isWaterLayer = static_cast<bool>(isDstWaterLayer);
 	
-	bool isAirLayer = (hero->boat && hero->boat->layer == EPathfindingLayer::AIR) || ti->hasFlyingMovement();
+	bool isAirLayer = (hero->inBoat() && hero->getBoat()->layer == EPathfindingLayer::AIR) || ti->hasFlyingMovement();
 
 	int movementCost = getTileMovementCost(*dt, *ct, ti);
 	if(isSailLayer)

+ 1 - 1
lib/pathfinder/NodeStorage.cpp

@@ -126,7 +126,7 @@ void NodeStorage::resetTile(const int3 & tile, const EPathfindingLayer & layer,
 
 std::vector<CGPathNode *> NodeStorage::getInitialNodes()
 {
-	auto * initialNode = getNode(out.hpos, out.hero->boat ? out.hero->boat->layer : EPathfindingLayer::LAND);
+	auto * initialNode = getNode(out.hpos, out.hero->inBoat() ? out.hero->getBoat()->layer : EPathfindingLayer::LAND);
 
 	initialNode->turns = 0;
 	initialNode->moveRemains = out.hero->movementPointsRemaining();

+ 2 - 2
lib/pathfinder/TurnInfo.cpp

@@ -184,7 +184,7 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
 	switch(layer.toEnum())
 	{
 	case EPathfindingLayer::AIR:
-		if(target && target->boat && target->boat->layer == EPathfindingLayer::AIR)
+		if(target && target->inBoat() && target->getBoat()->layer == EPathfindingLayer::AIR)
 			break;
 
 		if(!hasFlyingMovement())
@@ -193,7 +193,7 @@ bool TurnInfo::isLayerAvailable(const EPathfindingLayer & layer) const
 		break;
 
 	case EPathfindingLayer::WATER:
-		if(target && target->boat && target->boat->layer == EPathfindingLayer::WATER)
+		if(target && target->inBoat() && target->getBoat()->layer == EPathfindingLayer::WATER)
 			break;
 
 		if(!hasWaterWalking())

+ 1 - 1
lib/rewardable/Interface.cpp

@@ -145,7 +145,7 @@ void Rewardable::Interface::grantRewardAfterLevelup(const Rewardable::VisitInfo
 		smp.val = hero->movementPointsRemaining();
 
 		if (info.reward.movePercentage >= 0) // percent from max
-			smp.val = hero->movementPointsLimit(hero->boat && hero->boat->layer == EPathfindingLayer::SAIL) * info.reward.movePercentage / 100;
+			smp.val = hero->movementPointsLimit(hero->inBoat() && hero->getBoat()->layer == EPathfindingLayer::SAIL) * info.reward.movePercentage / 100;
 		smp.val = std::max<si32>(0, smp.val + info.reward.movePoints);
 
 		cb->setMovePoints(&smp);

+ 1 - 1
lib/spells/AdventureSpellMechanics.cpp

@@ -162,7 +162,7 @@ bool SummonBoatMechanics::canBeCastImpl(spells::Problem & problem, const CGameIn
 	if(!caster->getHeroCaster())
 		return false;
 
-	if(caster->getHeroCaster()->boat)
+	if(caster->getHeroCaster()->inBoat())
 	{
 		MetaString message = MetaString::createFromTextID("core.genrltxt.333");
 		caster->getCasterName(message);

+ 1 - 1
mapeditor/maphandler.cpp

@@ -306,7 +306,7 @@ ObjectRect::~ObjectRect()
 
 std::shared_ptr<QImage> MapHandler::findFlagBitmap(const CGHeroInstance * hero, int anim, const PlayerColor color, int group) const
 {
-	if(!hero || hero->boat)
+	if(!hero || hero->inBoat())
 		return std::shared_ptr<QImage>();
 	
 	return findFlagBitmapInternal(graphics->heroFlagAnimations.at(color.getNum()), anim, group, hero->moveDir, true);

+ 10 - 10
server/CGameHandler.cpp

@@ -837,10 +837,10 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		}
 	}
 
-	const bool embarking = !h->boat && objectToVisit && objectToVisit->ID == Obj::BOAT;
-	const bool disembarking = h->boat
+	const bool embarking = !h->inBoat() && objectToVisit && objectToVisit->ID == Obj::BOAT;
+	const bool disembarking = h->inBoat()
 		&& t.isLand()
-		&& (dst == h->pos || (h->boat->layer == EPathfindingLayer::SAIL && !t.blocked()));
+		&& (dst == h->pos || (h->getBoat()->layer == EPathfindingLayer::SAIL && !t.blocked()));
 
 	//result structure for start - movement failed, no move points used
 	TryMoveHero tmh;
@@ -854,13 +854,13 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	auto pathfinderHelper = std::make_unique<CPathfinderHelper>(gameState(), h, PathfinderOptions(this));
 	auto ti = pathfinderHelper->getTurnInfo();
 
-	const bool canFly = ti->hasFlyingMovement() || (h->boat && h->boat->layer == EPathfindingLayer::AIR);
-	const bool canWalkOnSea = ti->hasWaterWalking() || (h->boat && h->boat->layer == EPathfindingLayer::WATER);
+	const bool canFly = ti->hasFlyingMovement() || (h->inBoat() && h->getBoat()->layer == EPathfindingLayer::AIR);
+	const bool canWalkOnSea = ti->hasWaterWalking() || (h->inBoat() && h->getBoat()->layer == EPathfindingLayer::WATER);
 	const int cost = pathfinderHelper->getMovementCost(h->visitablePos(), hmpos, nullptr, nullptr, h->movementPointsRemaining());
 
 	const bool movingOntoObstacle = t.blocked() && !t.visitable();
 	const bool objectCoastVisitable = objectToVisit && objectToVisit->isCoastVisitable();
-	const bool movingOntoWater = !h->boat && t.isWater() && !objectCoastVisitable;
+	const bool movingOntoWater = !h->inBoat() && t.isWater() && !objectCoastVisitable;
 
 	const auto complainRet = [&](const std::string & message)
 	{
@@ -891,7 +891,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	if(movingOntoWater && !canFly && !canWalkOnSea)
 		return complainRet("Cannot move hero, destination tile is on water!");
 
-	if(h->boat && h->boat->layer == EPathfindingLayer::SAIL && t.isLand() && t.blocked())
+	if(h->inBoat() && h->getBoat()->layer == EPathfindingLayer::SAIL && t.isLand() && t.blocked())
 		return complainRet("Cannot disembark hero, tile is blocked!");
 
 	if(distance(h->pos, dst) >= 1.5 && movementMode == EMovementMode::STANDARD)
@@ -961,13 +961,13 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		{
 			const CGObjectInstance * object = getObj(objectID);
 
-			if(h->boat && !object->isBlockedVisitable() && !h->boat->onboardVisitAllowed)
+			if(h->inBoat() && !object->isBlockedVisitable() && !h->getBoat()->onboardVisitAllowed)
 				return doMove(TryMoveHero::SUCCESS, this->IGNORE_GUARDS, DONT_VISIT_DEST, REMAINING_ON_TILE);
 			
 			if (object != h && object->isBlockedVisitable() && !object->passableFor(h->tempOwner))
 			{
 				EVisitDest visitDest = VISIT_DEST;
-				if(h->boat && !h->boat->onboardVisitAllowed)
+				if(h->inBoat() && !h->getBoat()->onboardVisitAllowed)
 					visitDest = DONT_VISIT_DEST;
 				
 				return doMove(TryMoveHero::BLOCKING_VISIT, this->IGNORE_GUARDS, visitDest, REMAINING_ON_TILE);
@@ -1035,7 +1035,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		else if (blockingVisit())
 			return true;
 		
-		if(h->boat && !h->boat->onboardAssaultAllowed)
+		if(h->getBoat() && !h->getBoat()->onboardAssaultAllowed)
 			lookForGuards = IGNORE_GUARDS;
 
 		turnTimerHandler->setEndTurnAllowed(h->getOwner(), !movingOntoWater && !movingOntoObstacle);

+ 1 - 1
server/battles/BattleProcessor.cpp

@@ -170,7 +170,7 @@ BattleID BattleProcessor::setupBattle(int3 tile, BattleSideArray<const CArmedIns
 		const TerrainType* terrainData = LIBRARY->terrainTypeHandler->getById(terrain);
 		battlefieldType = BattleField(*RandomGeneratorUtil::nextItem(terrainData->battleFields, gameHandler->getRandomGenerator()));
 	}
-	else if (heroes[BattleSide::ATTACKER] && heroes[BattleSide::ATTACKER]->boat && heroes[BattleSide::DEFENDER] && heroes[BattleSide::DEFENDER]->boat)
+	else if (heroes[BattleSide::ATTACKER] && heroes[BattleSide::ATTACKER]->inBoat() && heroes[BattleSide::DEFENDER] && heroes[BattleSide::DEFENDER]->inBoat())
 		battlefieldType = BattleField(*LIBRARY->identifiers()->getIdentifier("core", "battlefield.ship_to_ship"));
 
 	//send info about battles

+ 1 - 1
server/processors/HeroPoolProcessor.cpp

@@ -225,7 +225,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 	hr.hid = recruitedHero->getHeroTypeID();
 	hr.player = player;
 	hr.tile = recruitedHero->convertFromVisitablePos(targetPos );
-	if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat)
+	if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->inBoat())
 	{
 		//Create a new boat for hero
 		gameHandler->createBoat(targetPos, recruitedHero->getBoatType(), player);