Procházet zdrojové kódy

Merge pull request #4731 from IvanSavenko/reduce_pointers

Reduce usage of pointers to VLC entities
Ivan Savenko před 1 rokem
rodič
revize
22ac88ecc4
83 změnil soubory, kde provedl 445 přidání a 468 odebrání
  1. 1 1
      AI/Nullkiller/AIGateway.cpp
  2. 2 2
      AI/Nullkiller/Analyzers/ArmyManager.cpp
  3. 5 5
      AI/Nullkiller/Analyzers/BuildAnalyzer.cpp
  4. 1 1
      AI/Nullkiller/Analyzers/HeroManager.cpp
  5. 1 1
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  6. 2 2
      AI/Nullkiller/Goals/BuildThis.cpp
  7. 4 4
      AI/VCAI/BuildingManager.cpp
  8. 1 1
      AI/VCAI/Goals/BuildThis.cpp
  9. 4 4
      AI/VCAI/Goals/GatherTroops.cpp
  10. 1 1
      AI/VCAI/MapObjectsEvaluator.cpp
  11. 2 2
      AI/VCAI/VCAI.cpp
  12. 1 1
      CCallback.cpp
  13. 1 1
      client/CPlayerInterface.cpp
  14. 1 1
      client/ClientCommandManager.cpp
  15. 1 1
      client/NetPacksClient.cpp
  16. 1 1
      client/adventureMap/CList.cpp
  17. 5 5
      client/battle/BattleInterfaceClasses.cpp
  18. 8 8
      client/battle/BattleSiegeController.cpp
  19. 4 5
      client/widgets/MiscWidgets.cpp
  20. 58 58
      client/windows/CCastleInterface.cpp
  21. 2 2
      client/windows/CExchangeWindow.cpp
  22. 3 3
      client/windows/CHeroWindow.cpp
  23. 4 4
      client/windows/CKingdomInterface.cpp
  24. 1 1
      client/windows/CMarketWindow.cpp
  25. 5 5
      client/windows/GUIClasses.cpp
  26. 5 5
      client/windows/QuickRecruitmentWindow.cpp
  27. 1 1
      include/vcmi/Entity.h
  28. 1 1
      lib/BasicTypes.cpp
  29. 1 1
      lib/CCreatureHandler.cpp
  30. 1 1
      lib/CCreatureHandler.h
  31. 2 2
      lib/CCreatureSet.cpp
  32. 1 1
      lib/CCreatureSet.h
  33. 3 3
      lib/CGameInfoCallback.cpp
  34. 2 2
      lib/battle/CUnitState.cpp
  35. 1 1
      lib/battle/CUnitState.h
  36. 3 3
      lib/bonuses/Limiters.cpp
  37. 4 4
      lib/campaign/CampaignState.cpp
  38. 1 1
      lib/entities/faction/CFaction.cpp
  39. 1 1
      lib/entities/faction/CFaction.h
  40. 15 20
      lib/gameState/CGameState.cpp
  41. 8 8
      lib/gameState/CGameStateCampaign.cpp
  42. 1 1
      lib/gameState/GameStatistics.cpp
  43. 1 1
      lib/gameState/InfoAboutArmy.cpp
  44. 4 4
      lib/gameState/TavernHeroesPool.cpp
  45. 1 7
      lib/mapObjectConstructors/CommonConstructors.cpp
  46. 0 1
      lib/mapObjectConstructors/CommonConstructors.h
  47. 1 1
      lib/mapObjects/CArmedInstance.cpp
  48. 20 15
      lib/mapObjects/CGCreature.cpp
  49. 2 1
      lib/mapObjects/CGCreature.h
  50. 1 1
      lib/mapObjects/CGDwelling.cpp
  51. 56 57
      lib/mapObjects/CGHeroInstance.cpp
  52. 11 4
      lib/mapObjects/CGHeroInstance.h
  53. 58 62
      lib/mapObjects/CGTownInstance.cpp
  54. 17 11
      lib/mapObjects/CGTownInstance.h
  55. 1 1
      lib/mapObjects/CQuest.cpp
  56. 2 2
      lib/mapObjects/TownBuildingInstance.cpp
  57. 1 1
      lib/mapping/CMap.cpp
  58. 6 6
      lib/mapping/MapFormatH3M.cpp
  59. 4 6
      lib/mapping/MapFormatJson.cpp
  60. 2 2
      lib/networkPacks/NetPacksLib.cpp
  61. 1 1
      lib/pathfinder/CPathfinder.cpp
  62. 2 2
      lib/rewardable/Limiter.cpp
  63. 1 1
      lib/rmg/modificators/ObjectManager.cpp
  64. 2 2
      lib/rmg/modificators/TownPlacer.cpp
  65. 6 6
      lib/rmg/modificators/TreasurePlacer.cpp
  66. 1 1
      lib/serializer/CSerializer.cpp
  67. 2 1
      lib/serializer/ESerializationVersion.h
  68. 1 1
      lib/spells/effects/Moat.cpp
  69. 1 1
      lib/spells/effects/Summon.cpp
  70. 13 30
      mapeditor/inspector/inspector.cpp
  71. 1 1
      mapeditor/inspector/townbuildingswidget.cpp
  72. 2 2
      mapeditor/inspector/towneventdialog.cpp
  73. 1 5
      mapeditor/mapcontroller.cpp
  74. 2 4
      mapeditor/playerparams.cpp
  75. 5 5
      mapeditor/validator.cpp
  76. 1 1
      scripting/lua/api/Creature.cpp
  77. 22 22
      server/CGameHandler.cpp
  78. 14 14
      server/processors/HeroPoolProcessor.cpp
  79. 5 5
      server/processors/NewTurnProcessor.cpp
  80. 1 1
      server/processors/PlayerMessageProcessor.cpp
  81. 1 1
      test/entity/CCreatureTest.cpp
  82. 1 1
      test/mock/mock_Creature.h
  83. 1 1
      test/mock/mock_battle_Unit.h

+ 1 - 1
AI/Nullkiller/AIGateway.cpp

@@ -1454,7 +1454,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 
 void AIGateway::buildStructure(const CGTownInstance * t, BuildingID building)
 {
-	auto name = t->town->buildings.at(building)->getNameTranslated();
+	auto name = t->getTown()->buildings.at(building)->getNameTranslated();
 	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->anchorPos().toString());
 	cb->buildBuilding(t, building); //just do this;
 }

+ 2 - 2
AI/Nullkiller/Analyzers/ArmyManager.cpp

@@ -144,7 +144,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
 
 	for(auto & slot : sortedSlots)
 	{
-		alignmentMap[slot.creature->getFaction()] += slot.power;
+		alignmentMap[slot.creature->getFactionID()] += slot.power;
 	}
 
 	std::set<FactionID> allowedFactions;
@@ -178,7 +178,7 @@ std::vector<SlotInfo> ArmyManager::getBestArmy(const IBonusBearer * armyCarrier,
 
 		for(auto & slot : sortedSlots)
 		{
-			if(vstd::contains(allowedFactions, slot.creature->getFaction()))
+			if(vstd::contains(allowedFactions, slot.creature->getFactionID()))
 			{
 				auto slotID = newArmyInstance.getSlotFor(slot.creature->getId());
 

+ 5 - 5
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -17,7 +17,7 @@ namespace NKAI
 
 void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
 {
-	auto townInfo = developmentInfo.town->town;
+	auto townInfo = developmentInfo.town->getTown();
 	auto creatures = townInfo->creatures;
 	auto buildings = townInfo->getAllBuildings();
 
@@ -31,7 +31,7 @@ void BuildAnalyzer::updateTownDwellings(TownDevelopmentInfo & developmentInfo)
 		}
 	}
 
-	for(int level = 0; level < developmentInfo.town->town->creatures.size(); level++)
+	for(int level = 0; level < developmentInfo.town->getTown()->creatures.size(); level++)
 	{
 		logAi->trace("Checking dwelling level %d", level);
 		BuildingInfo nextToBuild = BuildingInfo();
@@ -82,7 +82,7 @@ void BuildAnalyzer::updateOtherBuildings(TownDevelopmentInfo & developmentInfo)
 	{
 		for(auto & buildingID : buildingSet)
 		{
-			if(!developmentInfo.town->hasBuilt(buildingID) && developmentInfo.town->town->buildings.count(buildingID))
+			if(!developmentInfo.town->hasBuilt(buildingID) && developmentInfo.town->getTown()->buildings.count(buildingID))
 			{
 				developmentInfo.addBuildingToBuild(getBuildingOrPrerequisite(developmentInfo.town, buildingID));
 
@@ -198,7 +198,7 @@ BuildingInfo BuildAnalyzer::getBuildingOrPrerequisite(
 	bool excludeDwellingDependencies) const
 {
 	BuildingID building = toBuild;
-	auto townInfo = town->town;
+	auto townInfo = town->getTown();
 
 	const CBuilding * buildPtr = townInfo->buildings.at(building);
 	const CCreature * creature = nullptr;
@@ -327,7 +327,7 @@ bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const
 {
 	for(auto tdi : developmentInfos)
 	{
-		if(tdi.town->getFaction() == alignment && tdi.town->hasBuilt(bid))
+		if(tdi.town->getFactionID() == alignment && tdi.town->hasBuilt(bid))
 			return true;
 	}
 

+ 1 - 1
AI/Nullkiller/Analyzers/HeroManager.cpp

@@ -71,7 +71,7 @@ float HeroManager::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance *
 
 float HeroManager::evaluateSpeciality(const CGHeroInstance * hero) const
 {
-	auto heroSpecial = Selector::source(BonusSource::HERO_SPECIAL, BonusSourceID(hero->type->getId()));
+	auto heroSpecial = Selector::source(BonusSource::HERO_SPECIAL, BonusSourceID(hero->getHeroTypeID()));
 	auto secondarySkillBonus = Selector::targetSourceType()(BonusSource::SECONDARY_SKILL);
 	auto specialSecondarySkillBonuses = hero->getBonuses(heroSpecial.And(secondarySkillBonus));
 	auto secondarySkillBonuses = hero->getBonuses(Selector::sourceTypeSel(BonusSource::SECONDARY_SKILL));

+ 1 - 1
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -1120,7 +1120,7 @@ public:
 
 uint64_t RewardEvaluator::getUpgradeArmyReward(const CGTownInstance * town, const BuildingInfo & bi) const
 {
-	if(ai->buildAnalyzer->hasAnyBuilding(town->getFaction(), bi.id))
+	if(ai->buildAnalyzer->hasAnyBuilding(town->getFactionID(), bi.id))
 		return 0;
 
 	auto creaturesToUpgrade = ai->armyManager->getTotalCreaturesAvailable(bi.baseCreatureID);

+ 2 - 2
AI/Nullkiller/Goals/BuildThis.cpp

@@ -23,7 +23,7 @@ BuildThis::BuildThis(BuildingID Bid, const CGTownInstance * tid)
 	: ElementarGoal(Goals::BUILD_STRUCTURE)
 {
 	buildingInfo = BuildingInfo(
-		tid->town->buildings.at(Bid),
+		tid->getTown()->buildings.at(Bid),
 		nullptr,
 		CreatureID::NONE,
 		tid,
@@ -52,7 +52,7 @@ void BuildThis::accept(AIGateway * ai)
 		if(cb->canBuildStructure(town, b) == EBuildingState::ALLOWED)
 		{
 			logAi->debug("Player %d will build %s in town of %s at %s",
-				ai->playerID, town->town->buildings.at(b)->getNameTranslated(), town->getNameTranslated(), town->anchorPos().toString());
+				ai->playerID, town->getTown()->buildings.at(b)->getNameTranslated(), town->getNameTranslated(), town->anchorPos().toString());
 			cb->buildBuilding(town, b);
 
 			return;

+ 4 - 4
AI/VCAI/BuildingManager.cpp

@@ -23,13 +23,13 @@ bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID
 		return false;
 	}
 
-	if (!vstd::contains(t->town->buildings, building))
+	if (!vstd::contains(t->getTown()->buildings, building))
 		return false; // no such building in town
 
 	if (t->hasBuilt(building)) //Already built? Shouldn't happen in general
 		return true;
 
-	const CBuilding * buildPtr = t->town->buildings.at(building);
+	const CBuilding * buildPtr = t->getTown()->buildings.at(building);
 
 	auto toBuild = buildPtr->requirements.getFulfillmentCandidates([&](const BuildingID & buildID)
 	{
@@ -51,7 +51,7 @@ bool BuildingManager::tryBuildThisStructure(const CGTownInstance * t, BuildingID
 
 	for (const auto & buildID : toBuild)
 	{
-		const CBuilding * b = t->town->buildings.at(buildID);
+		const CBuilding * b = t->getTown()->buildings.at(buildID);
 
 		EBuildingState canBuild = cb->canBuildStructure(t, buildID);
 		if (canBuild == EBuildingState::ALLOWED)
@@ -220,7 +220,7 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
 
 	//at the end, try to get and build any extra buildings with nonstandard slots (for example HotA 3rd level dwelling)
 	std::vector<BuildingID> extraBuildings;
-	for (auto buildingInfo : t->town->buildings)
+	for (auto buildingInfo : t->getTown()->buildings)
 	{
 		if (buildingInfo.first > BuildingID::DWELL_UP2_FIRST)
 			extraBuildings.push_back(buildingInfo.first);

+ 1 - 1
AI/VCAI/Goals/BuildThis.cpp

@@ -56,7 +56,7 @@ TSubgoal BuildThis::whatToDoToAchieve()
 		case EBuildingState::ALLOWED:
 		case EBuildingState::NO_RESOURCES:
 		{
-			auto res = town->town->buildings.at(BuildingID(bid))->resources;
+			auto res = town->getTown()->buildings.at(BuildingID(bid))->resources;
 			return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
 		}
 		break;

+ 4 - 4
AI/VCAI/Goals/GatherTroops.cpp

@@ -88,13 +88,13 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
 		}
 
 		auto creature = VLC->creatures()->getByIndex(objid);
-		if(t->getFaction() == creature->getFaction()) //TODO: how to force AI to build unupgraded creatures? :O
+		if(t->getFactionID() == creature->getFactionID()) //TODO: how to force AI to build unupgraded creatures? :O
 		{
 			auto tryFindCreature = [&]() -> std::optional<std::vector<CreatureID>>
 			{
-				if(vstd::isValidIndex(t->town->creatures, creature->getLevel() - 1))
+				if(vstd::isValidIndex(t->getTown()->creatures, creature->getLevel() - 1))
 				{
-					auto itr = t->town->creatures.begin();
+					auto itr = t->getTown()->creatures.begin();
 					std::advance(itr, creature->getLevel() - 1);
 					return make_optional(*itr);
 				}
@@ -109,7 +109,7 @@ TGoalVec GatherTroops::getAllPossibleSubgoals()
 			if(upgradeNumber < 0)
 				continue;
 
-			BuildingID bid(BuildingID::DWELL_FIRST + creature->getLevel() - 1 + upgradeNumber * t->town->creatures.size());
+			BuildingID bid(BuildingID::DWELL_FIRST + creature->getLevel() - 1 + upgradeNumber * t->getTown()->creatures.size());
 			if(t->hasBuilt(bid) && ai->ah->freeResources().canAfford(creature->getFullRecruitCost())) //this assumes only creatures with dwellings are assigned to faction
 			{
 				solutions.push_back(sptr(BuyArmy(t, creature->getAIValue() * this->value).setobjid(objid)));

+ 1 - 1
AI/VCAI/MapObjectsEvaluator.cpp

@@ -69,7 +69,7 @@ std::optional<int> MapObjectsEvaluator::getObjectValue(const CGObjectInstance *
 	{
 		//special case handling: in-game heroes have hero ID as object subID, but when reading configs available hero object subID's are hero classes
 		auto hero = dynamic_cast<const CGHeroInstance*>(obj);
-		return getObjectValue(obj->ID, hero->type->heroClass->getIndex());
+		return getObjectValue(obj->ID, hero->getHeroClassID());
 	}
 	else if(obj->ID == Obj::PRISON)
 	{

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -1994,7 +1994,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 
 void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
 {
-	auto name = t->town->buildings.at(building)->getNameTranslated();
+	auto name = t->getTown()->buildings.at(building)->getNameTranslated();
 	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->anchorPos().toString());
 	cb->buildBuilding(t, building); //just do this;
 }
@@ -2081,7 +2081,7 @@ void VCAI::tryRealize(Goals::BuildThis & g)
 		if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
 		{
 			logAi->debug("Player %d will build %s in town of %s at %s",
-				playerID, t->town->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->anchorPos().toString());
+				playerID, t->getTown()->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->anchorPos().toString());
 			cb->buildBuilding(t, b);
 			throw goalFulfilledException(sptr(g));
 		}

+ 1 - 1
CCallback.cpp

@@ -319,7 +319,7 @@ void CCallback::recruitHero(const CGObjectInstance *townOrTavern, const CGHeroIn
 	assert(townOrTavern);
 	assert(hero);
 
-	HireHero pack(hero->getHeroType(), townOrTavern->id, nextHero);
+	HireHero pack(hero->getHeroTypeID(), townOrTavern->id, nextHero);
 	pack.player = *player;
 	sendRequest(pack);
 }

+ 1 - 1
client/CPlayerInterface.cpp

@@ -1138,7 +1138,7 @@ void CPlayerInterface::showMapObjectSelectDialog(QueryID askID, const Component
 		const CGTownInstance * t = dynamic_cast<const CGTownInstance *>(cb->getObj(obj));
 		if(t)
 		{
-			auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE);
+			auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->getTown()->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE);
 			image->scaleTo(Point(35, 23));
 			images.push_back(image);
 		}

+ 1 - 1
client/ClientCommandManager.cpp

@@ -453,7 +453,7 @@ void ClientCommandManager::handleTellCommand(std::istringstream& singleWordBuffe
 	if(what == "hs")
 	{
 		for(const CGHeroInstance* h : LOCPLINT->cb->getHeroesInfo())
-			if(h->type->getIndex() == id1)
+			if(h->getHeroTypeID().getNum() == id1)
 				if(const CArtifactInstance* a = h->getArt(ArtifactPosition(id2)))
 					printCommandMessage(a->nodeName());
 	}

+ 1 - 1
client/NetPacksClient.cpp

@@ -671,7 +671,7 @@ void ApplyClientNetPackVisitor::visitSetHeroesInTown(SetHeroesInTown & pack)
 void ApplyClientNetPackVisitor::visitHeroRecruited(HeroRecruited & pack)
 {
 	CGHeroInstance *h = gs.map->heroesOnMap.back();
-	if(h->getHeroType() != pack.hid)
+	if(h->getHeroTypeID() != pack.hid)
 	{
 		logNetwork->error("Something wrong with hero recruited!");
 	}

+ 1 - 1
client/adventureMap/CList.cpp

@@ -432,7 +432,7 @@ std::shared_ptr<CIntObject> CTownList::CTownItem::genSelection()
 
 void CTownList::CTownItem::update()
 {
-	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->built >= LOCPLINT->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
+	size_t iconIndex = town->getTown()->clientInfo.icons[town->hasFort()][town->built >= LOCPLINT->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
 
 	picture->setFrame(iconIndex + 2);
 	redraw();

+ 5 - 5
client/battle/BattleInterfaceClasses.cpp

@@ -389,13 +389,13 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
 {
 	AnimationPath animationPath;
 
-	if(!hero->type->battleImage.empty())
-		animationPath = hero->type->battleImage;
+	if(!hero->getHeroType()->battleImage.empty())
+		animationPath = hero->getHeroType()->battleImage;
 	else
 	if(hero->gender == EHeroGender::FEMALE)
-		animationPath = hero->type->heroClass->imageBattleFemale;
+		animationPath = hero->getHeroClass()->imageBattleFemale;
 	else
-		animationPath = hero->type->heroClass->imageBattleMale;
+		animationPath = hero->getHeroClass()->imageBattleMale;
 
 	animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::ALPHA);
 
@@ -1027,7 +1027,7 @@ void StackQueue::update()
 
 int32_t StackQueue::getSiegeShooterIconID()
 {
-	return owner.siegeController->getSiegedTown()->town->faction->getIndex();
+	return owner.siegeController->getSiegedTown()->getFactionID().getNum();
 }
 
 std::optional<uint32_t> StackQueue::getHoveredUnitIdIfAny() const

+ 8 - 8
client/battle/BattleSiegeController.cpp

@@ -58,14 +58,14 @@ ImagePath BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual
 		};
 	};
 
-	const std::string & prefix = town->town->clientInfo.siegePrefix;
+	const std::string & prefix = town->getTown()->clientInfo.siegePrefix;
 	std::string addit = std::to_string(getImageIndex());
 
 	switch(what)
 	{
 	case EWallVisual::BACKGROUND_WALL:
 		{
-			auto faction = town->town->faction->getIndex();
+			auto faction = town->getFactionID();
 
 			if (faction == ETownType::RAMPART || faction == ETownType::NECROPOLIS || faction == ETownType::DUNGEON || faction == ETownType::STRONGHOLD)
 				return ImagePath::builtinTODO(prefix + "TPW1.BMP");
@@ -111,7 +111,7 @@ ImagePath BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual
 
 void BattleSiegeController::showWallPiece(Canvas & canvas, EWallVisual::EWallVisual what)
 {
-	auto & ci = town->town->clientInfo;
+	auto & ci = town->getTown()->clientInfo;
 	auto const & pos = ci.siegePositions[what];
 
 	if ( wallPieceImages[what] && pos.isValid())
@@ -120,7 +120,7 @@ void BattleSiegeController::showWallPiece(Canvas & canvas, EWallVisual::EWallVis
 
 ImagePath BattleSiegeController::getBattleBackgroundName() const
 {
-	const std::string & prefix = town->town->clientInfo.siegePrefix;
+	const std::string & prefix = town->getTown()->clientInfo.siegePrefix;
 	return ImagePath::builtinTODO(prefix + "BACK.BMP");
 }
 
@@ -130,8 +130,8 @@ bool BattleSiegeController::getWallPieceExistence(EWallVisual::EWallVisual what)
 
 	switch (what)
 	{
-	case EWallVisual::MOAT:              return fortifications.hasMoat && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT).isValid();
-	case EWallVisual::MOAT_BANK:         return fortifications.hasMoat && town->town->clientInfo.siegePositions.at(EWallVisual::MOAT_BANK).isValid();
+	case EWallVisual::MOAT:              return fortifications.hasMoat && town->getTown()->clientInfo.siegePositions.at(EWallVisual::MOAT).isValid();
+	case EWallVisual::MOAT_BANK:         return fortifications.hasMoat && town->getTown()->clientInfo.siegePositions.at(EWallVisual::MOAT_BANK).isValid();
 	case EWallVisual::KEEP_BATTLEMENT:   return fortifications.citadelHealth > 0 && owner.getBattle()->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
 	case EWallVisual::UPPER_BATTLEMENT:  return fortifications.upperTowerHealth > 0 && owner.getBattle()->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
 	case EWallVisual::BOTTOM_BATTLEMENT: return fortifications.lowerTowerHealth > 0 && owner.getBattle()->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;
@@ -218,8 +218,8 @@ Point BattleSiegeController::getTurretCreaturePosition( BattleHex position ) con
 	if (posID != 0)
 	{
 		return {
-			town->town->clientInfo.siegePositions[posID].x,
-			town->town->clientInfo.siegePositions[posID].y
+			town->getTown()->clientInfo.siegePositions[posID].x,
+			town->getTown()->clientInfo.siegePositions[posID].y
 		};
 	}
 

+ 4 - 5
client/widgets/MiscWidgets.cpp

@@ -468,8 +468,8 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
 				LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
 		}
 	}, [town]{
-		if(!town->town->faction->getDescriptionTranslated().empty())
-			CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
+		if(!town->getFaction()->getDescriptionTranslated().empty())
+			CRClickPopup::createAndPush(town->getFaction()->getDescriptionTranslated());
 	});
 	fastMarket = std::make_shared<LRClickableArea>(Rect(143, 31, 30, 34), []()
 	{
@@ -532,8 +532,7 @@ CreatureTooltip::CreatureTooltip(Point pos, const CGCreature * creature)
 {
 	OBJECT_CONSTRUCTION;
 
-	auto creatureID = creature->getCreature();
-	int32_t creatureIconIndex = CGI->creatures()->getById(creatureID)->getIconIndex();
+	int32_t creatureIconIndex = creature->getCreature()->getIconIndex();
 
 	creatureImage = std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), creatureIconIndex);
 	creatureImage->center(Point(parent->pos.x + parent->pos.w / 2, parent->pos.y + creatureImage->pos.h / 2 + 11));
@@ -633,7 +632,7 @@ CCreaturePic::CCreaturePic(int x, int y, const CCreature * cre, bool Big, bool A
 	pos.x+=x;
 	pos.y+=y;
 
-	auto faction = cre->getFaction();
+	auto faction = cre->getFactionID();
 
 	assert(CGI->townh->size() > faction);
 

+ 58 - 58
client/windows/CCastleInterface.cpp

@@ -82,7 +82,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town
 
 	// special animation frame manipulation for castle shipyard with and without ship
 	// done due to .def used in special way, not to animate building - first image is for shipyard without citadel moat, 2nd image is for including moat
-	if(Town->town->faction->getId() == FactionID::CASTLE && Str->building &&
+	if(Town->getFactionID() == FactionID::CASTLE && Str->building &&
 		(Str->building->bid == BuildingID::SHIPYARD || Str->building->bid == BuildingID::SHIP))
 	{
 		if(Town->hasBuilt(BuildingID::CITADEL))
@@ -107,7 +107,7 @@ const CBuilding * CBuildingRect::getBuilding()
 		return nullptr;
 
 	if (str->hiddenUpgrade) // hidden upgrades, e.g. hordes - return base (dwelling for hordes)
-		return town->town->buildings.at(str->building->getBase());
+		return town->getTown()->buildings.at(str->building->getBase());
 
 	return str->building;
 }
@@ -156,7 +156,7 @@ void CBuildingRect::showPopupWindow(const Point & cursorPosition)
 		return;
 
 	BuildingID bid = getBuilding()->bid;
-	const CBuilding *bld = town->town->buildings.at(bid);
+	const CBuilding *bld = town->getTown()->buildings.at(bid);
 	if (bid < BuildingID::DWELL_FIRST)
 	{
 		CRClickPopup::createAndPush(CInfoWindow::genText(bld->getNameTranslated(), bld->getDescriptionTranslated()),
@@ -235,10 +235,10 @@ std::string CBuildingRect::getSubtitle()//hover text for building
 	int bid = getBuilding()->bid;
 
 	if (bid<30)//non-dwellings - only building name
-		return town->town->buildings.at(getBuilding()->bid)->getNameTranslated();
+		return town->getTown()->buildings.at(getBuilding()->bid)->getNameTranslated();
 	else//dwellings - recruit %creature%
 	{
-		auto & availableCreatures = town->creatures[(bid-30)%town->town->creatures.size()].second;
+		auto & availableCreatures = town->creatures[(bid-30)%town->getTown()->creatures.size()].second;
 		if(availableCreatures.size())
 		{
 			int creaID = availableCreatures.back();//taking last of available creatures
@@ -566,7 +566,7 @@ CCastleBuildings::CCastleBuildings(const CGTownInstance* Town):
 {
 	OBJECT_CONSTRUCTION;
 
-	background = std::make_shared<CPicture>(town->town->clientInfo.townBackground);
+	background = std::make_shared<CPicture>(town->getTown()->clientInfo.townBackground);
 	background->needRefresh = true;
 	background->getSurface()->setBlitMode(EImageBlitMode::OPAQUE);
 	pos.w = background->pos.w;
@@ -602,7 +602,7 @@ void CCastleBuildings::recreate()
 		}
 	}
 
-	for(const CStructure * structure : town->town->clientInfo.structures)
+	for(const CStructure * structure : town->getTown()->clientInfo.structures)
 	{
 		if(!structure->building)
 		{
@@ -617,7 +617,7 @@ void CCastleBuildings::recreate()
 
 	for(auto & entry : groups)
 	{
-		const CBuilding * build = town->town->buildings.at(entry.first);
+		const CBuilding * build = town->getTown()->buildings.at(entry.first);
 
 		const CStructure * toAdd = *boost::max_element(entry.second, [=](const CStructure * a, const CStructure * b)
 		{
@@ -648,7 +648,7 @@ void CCastleBuildings::recreate()
 void CCastleBuildings::addBuilding(BuildingID building)
 {
 	//FIXME: implement faster method without complete recreation of town
-	BuildingID base = town->town->buildings.at(building)->getBase();
+	BuildingID base = town->getTown()->buildings.at(building)->getBase();
 
 	recreate();
 
@@ -687,7 +687,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
 	BuildingID buildingToEnter = building;
 	for(;;)
 	{
-		const CBuilding *b = town->town->buildings.find(buildingToEnter)->second;
+		const CBuilding *b = town->getTown()->buildings.find(buildingToEnter)->second;
 
 		if (buildingTryActivateCustomUI(buildingToEnter, building))
 			return;
@@ -705,7 +705,7 @@ void CCastleBuildings::buildingClicked(BuildingID building)
 bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, BuildingID buildingTarget)
 {
 	logGlobal->trace("You've clicked on %d", (int)buildingToTest.toEnum());
-	const CBuilding *b = town->town->buildings.at(buildingToTest);
+	const CBuilding *b = town->getTown()->buildings.at(buildingToTest);
 
 	if (town->getWarMachineInBuilding(buildingToTest).hasValue())
 	{
@@ -744,7 +744,7 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
 		}
 	}
 
-	if (town->rewardableBuildings.count(buildingToTest) && town->town->buildings.at(buildingToTest)->manualHeroVisit)
+	if (town->rewardableBuildings.count(buildingToTest) && town->getTown()->buildings.at(buildingToTest)->manualHeroVisit)
 	{
 		enterRewardable(buildingToTest);
 		return true;
@@ -820,10 +820,10 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
 						return false;
 
 				case BuildingSubID::PORTAL_OF_SUMMONING:
-						if (town->creatures[town->town->creatures.size()].second.empty())//No creatures
+						if (town->creatures[town->getTown()->creatures.size()].second.empty())//No creatures
 							LOCPLINT->showInfoDialog(CGI->generaltexth->tcommands[30]);
 						else
-							enterDwelling(town->town->creatures.size());
+							enterDwelling(town->getTown()->creatures.size());
 						return true;
 
 				case BuildingSubID::BANK:
@@ -850,7 +850,7 @@ void CCastleBuildings::enterRewardable(BuildingID building)
 	{
 		MetaString message;
 		message.appendTextID("core.genrltxt.273"); // only visiting heroes may visit %s
-		message.replaceTextID(town->town->buildings.at(building)->getNameTextID());
+		message.replaceTextID(town->getTown()->buildings.at(building)->getNameTextID());
 
 		LOCPLINT->showInfoDialog(message.toString());
 	}
@@ -868,7 +868,7 @@ void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactI
 	const CGHeroInstance *hero = town->visitingHero;
 	if(!hero)
 	{
-		LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(building)->second->getNameTranslated()));
+		LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->getTown()->buildings.find(building)->second->getNameTranslated()));
 		return;
 	}
 	auto art = artifactID.toArtifact();
@@ -897,8 +897,8 @@ void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactI
 
 void CCastleBuildings::enterBuilding(BuildingID building)
 {
-	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), building)));
-	LOCPLINT->showInfoDialog( town->town->buildings.find(building)->second->getDescriptionTranslated(), comps);
+	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFactionID(), building)));
+	LOCPLINT->showInfoDialog( town->getTown()->buildings.find(building)->second->getDescriptionTranslated(), comps);
 }
 
 void CCastleBuildings::enterCastleGate()
@@ -915,20 +915,20 @@ void CCastleBuildings::enterCastleGate()
 	{
 		const CGTownInstance *t = Town;
 		if (t->id != this->town->id && t->visitingHero == nullptr && //another town, empty and this is
-			t->town->faction->getId() == town->town->faction->getId() && //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
 		{
 			availableTowns.push_back(t->id.getNum());//add to the list
 			if(settings["general"]["enableUiEnhancements"].Bool())
 			{
-				auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->town->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE);
+				auto image = GH.renderHandler().loadImage(AnimationPath::builtin("ITPA"), t->getTown()->clientInfo.icons[t->hasFort()][false] + 2, 0, EImageBlitMode::OPAQUE);
 				image->scaleTo(Point(35, 23));
 				images.push_back(image);
 			}
 		}
 	}
 
-	auto gateIcon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, BuildingID::CASTLE_GATE);//will be deleted by selection window
+	auto gateIcon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, BuildingID::CASTLE_GATE);//will be deleted by selection window
 	auto wnd = std::make_shared<CObjectListWindow>(availableTowns, gateIcon, CGI->generaltexth->jktexts[40],
 		CGI->generaltexth->jktexts[41], std::bind (&CCastleInterface::castleTeleport, LOCPLINT->castleInt, _1), 0, images);
 	wnd->onPopup = [availableTowns](int index) { CRClickPopup::createAndPush(LOCPLINT->cb->getObjInstance(ObjectInstanceID(availableTowns[index])), GH.getCursorPosition()); };
@@ -940,7 +940,7 @@ void CCastleBuildings::enterDwelling(int level)
 	if (level < 0 || level >= town->creatures.size() || town->creatures[level].second.empty())
 	{
 		assert(0);
-		logGlobal->error("Attempt to enter into invalid dwelling of level %d in town %s (%s)", level, town->getNameTranslated(), town->town->faction->getNameTranslated());
+		logGlobal->error("Attempt to enter into invalid dwelling of level %d in town %s (%s)", level, town->getNameTranslated(), town->getFaction()->getNameTranslated());
 		return;
 	}
 
@@ -954,8 +954,8 @@ void CCastleBuildings::enterDwelling(int level)
 void CCastleBuildings::enterToTheQuickRecruitmentWindow()
 {
 	const auto beginIt = town->creatures.cbegin();
-	const auto afterLastIt = town->creatures.size() > town->town->creatures.size()
-		? std::next(beginIt, town->town->creatures.size())
+	const auto afterLastIt = town->creatures.size() > town->getTown()->creatures.size()
+		? std::next(beginIt, town->getTown()->creatures.size())
 		: town->creatures.cend();
 	const auto hasSomeoneToRecruit = std::any_of(beginIt, afterLastIt,
 		[](const auto & creatureInfo) { return creatureInfo.first > 0; });
@@ -967,8 +967,8 @@ void CCastleBuildings::enterToTheQuickRecruitmentWindow()
 
 void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID upgrades)
 {
-	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), building)));
-	std::string descr = town->town->buildings.find(building)->second->getDescriptionTranslated();
+	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFactionID(), building)));
+	std::string descr = town->getTown()->buildings.find(building)->second->getDescriptionTranslated();
 	std::string hasNotProduced;
 	std::string hasProduced;
 
@@ -977,10 +977,10 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID:
 
 	bool isMysticPondOrItsUpgrade = subID == BuildingSubID::MYSTIC_POND
 		|| (upgrades != BuildingID::NONE
-			&& town->town->buildings.find(BuildingID(upgrades))->second->subId == BuildingSubID::MYSTIC_POND);
+			&& town->getTown()->buildings.find(BuildingID(upgrades))->second->subId == BuildingSubID::MYSTIC_POND);
 
 	if(upgrades != BuildingID::NONE)
-		descr += "\n\n"+town->town->buildings.find(BuildingID(upgrades))->second->getDescriptionTranslated();
+		descr += "\n\n"+town->getTown()->buildings.find(BuildingID(upgrades))->second->getDescriptionTranslated();
 
 	if(isMysticPondOrItsUpgrade) //for vanila Rampart like towns
 	{
@@ -1056,7 +1056,7 @@ void CCastleBuildings::enterTownHall()
 
 void CCastleBuildings::openMagesGuild()
 {
-	auto mageGuildBackground = LOCPLINT->castleInt->town->town->clientInfo.guildBackground;
+	auto mageGuildBackground = LOCPLINT->castleInt->town->getTown()->clientInfo.guildBackground;
 	GH.windows().createAndPushWindow<CMageGuildScreen>(LOCPLINT->castleInt, mageGuildBackground);
 }
 
@@ -1247,7 +1247,7 @@ CTownInfo::CTownInfo(int posX, int posY, const CGTownInstance * Town, bool townH
 			return;//FIXME: suspicious statement, fix or comment
 		picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITMCL.DEF"), town->fortLevel()-1);
 	}
-	building = town->town->buildings.at(BuildingID(buildID));
+	building = town->getTown()->buildings.at(BuildingID(buildID));
 	pos = picture->pos;
 }
 
@@ -1322,7 +1322,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
 	recreateIcons();
 	if (!from)
 		adventureInt->onAudioPaused();
-	CCS->musich->playMusicFromSet("faction", town->town->faction->getJsonKey(), true, false);
+	CCS->musich->playMusicFromSet("faction", town->getFaction()->getJsonKey(), true, false);
 }
 
 CCastleInterface::~CCastleInterface()
@@ -1403,7 +1403,7 @@ void CCastleInterface::removeBuilding(BuildingID bid)
 void CCastleInterface::recreateIcons()
 {
 	OBJECT_CONSTRUCTION;
-	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->built >= LOCPLINT->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
+	size_t iconIndex = town->getTown()->clientInfo.icons[town->hasFort()][town->built >= LOCPLINT->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
 
 	icon->setFrame(iconIndex);
 	TResources townIncome = town->dailyIncome();
@@ -1425,8 +1425,8 @@ void CCastleInterface::recreateIcons()
 		if(town->hasBuilt(BuildingID::TAVERN))
 			LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
 	}, [this]{
-		if(!town->town->faction->getDescriptionTranslated().empty())
-			CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
+		if(!town->getFaction()->getDescriptionTranslated().empty())
+			CRClickPopup::createAndPush(town->getFaction()->getDescriptionTranslated());
 	});
 
 	creainfo.clear();
@@ -1527,7 +1527,7 @@ CHallInterface::CBuildingBox::CBuildingBox(int x, int y, const CGTownInstance *
 		-1, -1, -1, 0, 0, 1, 2, -1, 1, 1, -1, -1
 	};
 
-	icon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, building->bid, 0, 2, 2);
+	icon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building->bid, 0, 2, 2);
 	header = std::make_shared<CAnimImage>(AnimationPath::builtin("TPTHBAR"), panelIndex[static_cast<int>(state)], 0, 1, 73);
 	if(iconIndex[static_cast<int>(state)] >=0)
 		mark = std::make_shared<CAnimImage>(AnimationPath::builtin("TPTHCHK"), iconIndex[static_cast<int>(state)], 0, 136, 56);
@@ -1569,7 +1569,7 @@ void CHallInterface::CBuildingBox::showPopupWindow(const Point & cursorPosition)
 }
 
 CHallInterface::CHallInterface(const CGTownInstance * Town):
-	CWindowObject(PLAYER_COLORED | BORDERED, Town->town->clientInfo.hallBackground),
+	CWindowObject(PLAYER_COLORED | BORDERED, Town->getTown()->clientInfo.hallBackground),
 	town(Town)
 {
 	OBJECT_CONSTRUCTION;
@@ -1581,10 +1581,10 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
 	auto statusbarBackground = std::make_shared<CPicture>(background->getSurface(), barRect, 5, 556);
 	statusbar = CGStatusBar::create(statusbarBackground);
 
-	title = std::make_shared<CLabel>(399, 12, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->getNameTranslated());
+	title = std::make_shared<CLabel>(399, 12, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, town->getTown()->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->getNameTranslated());
 	exit = std::make_shared<CButton>(Point(748, 556), AnimationPath::builtin("TPMAGE1.DEF"), CButton::tooltip(CGI->generaltexth->hcommands[8]), [&](){close();}, EShortcut::GLOBAL_RETURN);
 
-	auto & boxList = town->town->clientInfo.hallSlots;
+	auto & boxList = town->getTown()->clientInfo.hallSlots;
 	boxes.resize(boxList.size());
 	for(size_t row=0; row<boxList.size(); row++) //for each row
 	{
@@ -1595,11 +1595,11 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
 			{
 				if (!buildingID.hasValue())
 				{
-					logMod->warn("Invalid building ID found in hallSlots of town '%s'", town->town->faction->getJsonKey() );
+					logMod->warn("Invalid building ID found in hallSlots of town '%s'", town->getFaction()->getJsonKey() );
 					continue;
 				}
 
-				const CBuilding * current = town->town->buildings.at(buildingID);
+				const CBuilding * current = town->getTown()->buildings.at(buildingID);
 				if(town->hasBuilt(buildingID))
 				{
 					building = current;
@@ -1629,7 +1629,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 {
 	OBJECT_CONSTRUCTION;
 
-	icon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, building->bid, 0, 125, 50);
+	icon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building->bid, 0, 125, 50);
 	auto statusbarBackground = std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26);
 	statusbar = CGStatusBar::create(statusbarBackground);
 
@@ -1711,7 +1711,7 @@ std::string CBuildWindow::getTextForState(EBuildingState state)
 		{
 			auto toStr = [&](const BuildingID build) -> std::string
 			{
-				return town->town->buildings.at(build)->getNameTranslated();
+				return town->getTown()->buildings.at(build)->getNameTranslated();
 			};
 
 			ret = CGI->generaltexth->allTexts[52];
@@ -1721,7 +1721,7 @@ std::string CBuildWindow::getTextForState(EBuildingState state)
 	case EBuildingState::MISSING_BASE:
 		{
 			std::string msg = CGI->generaltexth->translate("vcmi.townHall.missingBase");
-			ret = boost::str(boost::format(msg) % town->town->buildings.at(building->upgrade)->getNameTranslated());
+			ret = boost::str(boost::format(msg) % town->getTown()->buildings.at(building->upgrade)->getNameTranslated());
 			break;
 		}
 	}
@@ -1780,11 +1780,11 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 {
 	OBJECT_CONSTRUCTION;
 	ui32 fortSize = static_cast<ui32>(town->creatures.size());
-	if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
+	if(fortSize > town->getTown()->creatures.size() && town->creatures.back().second.empty())
 		fortSize--;
 	fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
 
-	const CBuilding * fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6));
+	const CBuilding * fortBuilding = town->getTown()->buildings.at(BuildingID(town->fortLevel()+6));
 	title = std::make_shared<CLabel>(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->getNameTranslated());
 
 	std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->getNameTranslated());
@@ -1810,7 +1810,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 	for(ui32 i=0; i<fortSize; i++)
 	{
 		BuildingID buildingID;
-		if(fortSize == town->town->creatures.size())
+		if(fortSize == town->getTown()->creatures.size())
 		{
 			BuildingID dwelling = BuildingID::getDwellingFromLevel(i, 1);
 
@@ -1839,7 +1839,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 ImagePath CFortScreen::getBgName(const CGTownInstance * town)
 {
 	ui32 fortSize = static_cast<ui32>(town->creatures.size());
-	if(fortSize > town->town->creatures.size() && town->creatures.back().second.empty())
+	if(fortSize > town->getTown()->creatures.size() && town->creatures.back().second.empty())
 		fortSize--;
 	fortSize = std::min(fortSize, static_cast<ui32>(GameConstants::CREATURES_PER_TOWN)); // for 8 creatures + portal of summoning
 
@@ -1877,7 +1877,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
 
 	if(getMyBuilding() != nullptr)
 	{
-		buildingIcon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, getMyBuilding()->bid, 0, 4, 21);
+		buildingIcon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, getMyBuilding()->bid, 0, 4, 21);
 		buildingName = std::make_shared<CLabel>(78, 101, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyBuilding()->getNameTranslated(), 152);
 
 		if(town->hasBuilt(getMyBuilding()->bid))
@@ -1913,8 +1913,8 @@ const CCreature * CFortScreen::RecruitArea::getMyCreature()
 {
 	if(!town->creatures.at(level).second.empty()) // built
 		return town->creatures.at(level).second.back().toCreature();
-	if(!town->town->creatures.at(level).empty()) // there are creatures on this level
-		return town->town->creatures.at(level).front().toCreature();
+	if(!town->getTown()->creatures.at(level).empty()) // there are creatures on this level
+		return town->getTown()->creatures.at(level).front().toCreature();
 	return nullptr;
 }
 
@@ -1922,17 +1922,17 @@ const CBuilding * CFortScreen::RecruitArea::getMyBuilding()
 {
 	BuildingID myID = BuildingID(BuildingID::getDwellingFromLevel(level, 0));
 
-	if (level == town->town->creatures.size())
-		return town->town->getSpecialBuilding(BuildingSubID::PORTAL_OF_SUMMONING);
+	if (level == town->getTown()->creatures.size())
+		return town->getTown()->getSpecialBuilding(BuildingSubID::PORTAL_OF_SUMMONING);
 
-	if (!town->town->buildings.count(myID))
+	if (!town->getTown()->buildings.count(myID))
 		return nullptr;
 
-	const CBuilding * build = town->town->buildings.at(myID);
-	while (town->town->buildings.count(myID))
+	const CBuilding * build = town->getTown()->buildings.at(myID);
+	while (town->getTown()->buildings.count(myID))
 	{
 		if (town->hasBuilt(myID))
-			build = town->town->buildings.at(myID);
+			build = town->getTown()->buildings.at(myID);
 		BuildingID::advanceDwelling(myID);
 	}
 
@@ -1972,7 +1972,7 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner, const ImagePath & i
 {
 	OBJECT_CONSTRUCTION;
 
-	window = std::make_shared<CPicture>(owner->town->town->clientInfo.guildWindow, 332, 76);
+	window = std::make_shared<CPicture>(owner->town->getTown()->clientInfo.guildWindow, 332, 76);
 
 	resdatabar = std::make_shared<CMinorResDataBar>();
 	resdatabar->moveBy(pos.topLeft(), true);
@@ -2007,7 +2007,7 @@ void CMageGuildScreen::updateSpells(ObjectInstanceID tID)
 
 	const CGTownInstance * town = LOCPLINT->cb->getTown(townId);
 
-	for(size_t i=0; i<town->town->mageLevel; i++)
+	for(size_t i=0; i<town->getTown()->mageLevel; i++)
 	{
 		size_t spellCount = town->spellsAtLevel((int)i+1,false); //spell at level with -1 hmmm?
 		for(size_t j=0; j<spellCount; j++)

+ 2 - 2
client/windows/CExchangeWindow.cpp

@@ -84,7 +84,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 		for(int m=0; m < hero->secSkills.size(); ++m)
 			secSkillIcons[leftRight].push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SECSK32"), 0, 0, 32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88));
 
-		specImages[leftRight] = std::make_shared<CAnimImage>(AnimationPath::builtin("UN32"), hero->type->imageIndex, 0, 67 + 490 * leftRight, qeLayout ? 41 : 45);
+		specImages[leftRight] = std::make_shared<CAnimImage>(AnimationPath::builtin("UN32"), hero->getHeroType()->imageIndex, 0, 67 + 490 * leftRight, qeLayout ? 41 : 45);
 
 		expImages[leftRight] = std::make_shared<CAnimImage>(AnimationPath::builtin("PSKIL32"), 4, 0, 103 + 490 * leftRight, qeLayout ? 41 : 45);
 		expValues[leftRight] = std::make_shared<CLabel>(119 + 490 * leftRight, qeLayout ? 66 : 71, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
@@ -151,7 +151,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
 		specialtyAreas[b] = std::make_shared<LRClickableAreaWText>();
 		specialtyAreas[b]->pos = Rect(Point(pos.x + 69 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));
 		specialtyAreas[b]->hoverText = CGI->generaltexth->heroscrn[27];
-		specialtyAreas[b]->text = hero->type->getSpecialtyDescriptionTranslated();
+		specialtyAreas[b]->text = hero->getHeroType()->getSpecialtyDescriptionTranslated();
 
 		experienceAreas[b] = std::make_shared<LRClickableAreaWText>();
 		experienceAreas[b]->pos = Rect(Point(pos.x + 105 + 490 * b, pos.y + (qeLayout ? 41 : 45)), Point(32, 32));

+ 3 - 3
client/windows/CHeroWindow.cpp

@@ -184,9 +184,9 @@ void CHeroWindow::update()
 	name->setText(curHero->getNameTranslated());
 	title->setText((boost::format(CGI->generaltexth->allTexts[342]) % curHero->level % curHero->getClassNameTranslated()).str());
 
-	specArea->text = curHero->type->getSpecialtyDescriptionTranslated();
-	specImage->setFrame(curHero->type->imageIndex);
-	specName->setText(curHero->type->getSpecialtyNameTranslated());
+	specArea->text = curHero->getHeroType()->getSpecialtyDescriptionTranslated();
+	specImage->setFrame(curHero->getHeroType()->imageIndex);
+	specName->setText(curHero->getHeroType()->getSpecialtyNameTranslated());
 
 	tacticsButton = std::make_shared<CToggleButton>(Point(539, 483), AnimationPath::builtin("hsbtns8.def"), std::make_pair(heroscrn[26], heroscrn[31]), 0, EShortcut::HERO_TOGGLE_TACTICS);
 	tacticsButton->addHoverText(EButtonState::HIGHLIGHTED, CGI->generaltexth->heroscrn[25]);

+ 4 - 4
client/windows/CKingdomInterface.cpp

@@ -300,7 +300,7 @@ int InfoBoxHeroData::getSubID()
 		else
 			return 0;
 	case HERO_SPECIAL:
-		return hero->type->getIndex();
+		return hero->getHeroTypeID().getNum();
 	case HERO_MANA:
 	case HERO_EXPERIENCE:
 		return 0;
@@ -800,7 +800,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
 	garr = std::make_shared<CGarrisonInt>(Point(313, 3), 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, CGarrisonInt::ESlotsLayout::TWO_ROWS);
 	heroes = std::make_shared<HeroSlots>(town, Point(244,6), Point(475,6), garr, false);
 
-	size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->built >= LOCPLINT->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
+	size_t iconIndex = town->getTown()->clientInfo.icons[town->hasFort()][town->built >= LOCPLINT->cb->getSettings().getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)];
 
 	picture = std::make_shared<CAnimImage>(AnimationPath::builtin("ITPT"), iconIndex, 0, 5, 6);
 	openTown = std::make_shared<LRClickableAreaOpenTown>(Rect(5, 6, 58, 64), town);
@@ -823,8 +823,8 @@ CTownItem::CTownItem(const CGTownInstance * Town)
 		if(town->hasBuilt(BuildingID::TAVERN))
 			LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
 	}, [&]{
-		if(!town->town->faction->getDescriptionTranslated().empty())
-			CRClickPopup::createAndPush(town->town->faction->getDescriptionTranslated());
+		if(!town->getTown()->faction->getDescriptionTranslated().empty())
+			CRClickPopup::createAndPush(town->getFaction()->getDescriptionTranslated());
 	});
 	fastMarket = std::make_shared<LRClickableArea>(Rect(153, 6, 65, 64), []()
 	{

+ 1 - 1
client/windows/CMarketWindow.cpp

@@ -192,7 +192,7 @@ std::string CMarketWindow::getMarketTitle(const ObjectInstanceID marketId, const
 	{
 		for(const auto & buildingId : town->getBuildings())
 		{
-			if(const auto building = town->town->buildings.at(buildingId); vstd::contains(building->marketModes, mode))
+			if(const auto building = town->getTown()->buildings.at(buildingId); vstd::contains(building->marketModes, mode))
 				return building->getNameTranslated();
 		}
 	}

+ 5 - 5
client/windows/GUIClasses.cpp

@@ -522,9 +522,9 @@ CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::func
 			recruit->block(true);
 	}
 	if(LOCPLINT->castleInt)
-		videoPlayer = std::make_shared<VideoWidget>(Point(70, 56), LOCPLINT->castleInt->town->town->clientInfo.tavernVideo, false);
+		videoPlayer = std::make_shared<VideoWidget>(Point(70, 56), LOCPLINT->castleInt->town->getTown()->clientInfo.tavernVideo, false);
 	else if(const auto * townObj = dynamic_cast<const CGTownInstance *>(TavernObj))
-		videoPlayer = std::make_shared<VideoWidget>(Point(70, 56), townObj->town->clientInfo.tavernVideo, false);
+		videoPlayer = std::make_shared<VideoWidget>(Point(70, 56), townObj->getTown()->clientInfo.tavernVideo, false);
 	else
 		videoPlayer = std::make_shared<VideoWidget>(Point(70, 56), VideoPath::builtin("TAVERN.BIK"), false);
 
@@ -548,7 +548,7 @@ void CTavernWindow::addInvite()
 
 	if(!inviteableHeroes.empty())
 	{
-		int imageIndex = heroToInvite ? (*CGI->heroh)[heroToInvite->getHeroType()]->imageIndex : 156; // 156 => special id for random
+		int imageIndex = heroToInvite ? heroToInvite->getIconIndex() : 156; // 156 => special id for random
 		if(!heroToInvite)
 			heroToInvite = (*RandomGeneratorUtil::nextItem(inviteableHeroes, CRandomGenerator::getDefault())).second;
 
@@ -563,7 +563,7 @@ void CTavernWindow::recruitb()
 	const CGHeroInstance *toBuy = (selected ? h2 : h1)->h;
 	const CGObjectInstance *obj = tavernObj;
 
-	LOCPLINT->cb->recruitHero(obj, toBuy, heroToInvite ? heroToInvite->getHeroType() : HeroTypeID::NONE);
+	LOCPLINT->cb->recruitHero(obj, toBuy, heroToInvite ? heroToInvite->getHeroTypeID() : HeroTypeID::NONE);
 	close();
 }
 
@@ -963,7 +963,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, BuildingID bu
 
 	if(auto town = dynamic_cast<const CGTownInstance *>(_market))
 	{
-		auto faction = town->town->faction->getId();
+		auto faction = town->getTown()->faction->getId();
 		titlePic = std::make_shared<CAnimImage>((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, building);
 	}
 	else if(auto uni = dynamic_cast<const CGUniversity *>(_market); uni->appearance)

+ 5 - 5
client/windows/QuickRecruitmentWindow.cpp

@@ -51,9 +51,9 @@ void QuickRecruitmentWindow::setCreaturePurchaseCards()
 {
 	int availableAmount = getAvailableCreatures();
 	Point position = Point((pos.w - 100*availableAmount - 8*(availableAmount-1))/2,64);
-	for (int i = 0; i < town->town->creatures.size(); i++)
+	for (int i = 0; i < town->getTown()->creatures.size(); i++)
 	{
-		if(!town->town->creatures.at(i).empty() && !town->creatures.at(i).second.empty() && town->creatures[i].first)
+		if(!town->getTown()->creatures.at(i).empty() && !town->creatures.at(i).second.empty() && town->creatures[i].first)
 		{
 			cards.push_back(std::make_shared<CreaturePurchaseCard>(town->creatures[i].second, position, town->creatures[i].first, this));
 			position.x += 108;
@@ -108,7 +108,7 @@ void QuickRecruitmentWindow::purchaseUnits()
 		{
 			int level = 0;
 			int i = 0;
-			for(auto c : town->town->creatures)
+			for(auto c : town->getTown()->creatures)
 			{
 				for(auto c2 : c)
 					if(c2 == selected->creatureOnTheCard->getId())
@@ -129,8 +129,8 @@ void QuickRecruitmentWindow::purchaseUnits()
 int QuickRecruitmentWindow::getAvailableCreatures()
 {
 	int creaturesAmount = 0;
-	for (int i=0; i< town->town->creatures.size(); i++)
-		if(!town->town->creatures.at(i).empty() && !town->creatures.at(i).second.empty() && town->creatures[i].first)
+	for (int i=0; i< town->getTown()->creatures.size(); i++)
+		if(!town->getTown()->creatures.at(i).empty() && !town->creatures.at(i).second.empty() && town->creatures[i].first)
 			creaturesAmount++;
 	return creaturesAmount;
 }

+ 1 - 1
include/vcmi/Entity.h

@@ -26,7 +26,7 @@ class DLL_LINKAGE INativeTerrainProvider
 {
 public:
 	virtual TerrainId getNativeTerrain() const = 0;
-	virtual FactionID getFaction() const = 0;
+	virtual FactionID getFactionID() const = 0;
 	virtual bool isNativeTerrain(TerrainId terrain) const;
 };
 

+ 1 - 1
lib/BasicTypes.cpp

@@ -38,7 +38,7 @@ TerrainId AFactionMember::getNativeTerrain() const
 	//this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses
 	//and in the CGHeroInstance::getNativeTerrain() to setup movement bonuses or/and penalties.
 	return getBonusBearer()->hasBonus(selectorNoTerrainPenalty, cachingStringNoTerrainPenalty)
-		? TerrainId::ANY_TERRAIN : VLC->factions()->getById(getFaction())->getNativeTerrain();
+			 ? TerrainId::ANY_TERRAIN : getFactionID().toEntity(VLC)->getNativeTerrain();
 }
 
 int32_t AFactionMember::magicResistance() const

+ 1 - 1
lib/CCreatureHandler.cpp

@@ -117,7 +117,7 @@ int32_t CCreature::getHorde() const
 	return hordeGrowth;
 }
 
-FactionID CCreature::getFaction() const
+FactionID CCreature::getFactionID() const
 {
 	return FactionID(faction);
 }

+ 1 - 1
lib/CCreatureHandler.h

@@ -127,7 +127,7 @@ public:
 	std::string getNamePluralTextID() const override;
 	std::string getNameSingularTextID() const override;
 
-	FactionID getFaction() const override;
+	FactionID getFactionID() const override;
 	int32_t getIndex() const override;
 	int32_t getIconIndex() const override;
 	std::string getJsonKey() const override;

+ 2 - 2
lib/CCreatureSet.cpp

@@ -912,10 +912,10 @@ void CStackInstance::serializeJson(JsonSerializeFormat & handler)
 	}
 }
 
-FactionID CStackInstance::getFaction() const
+FactionID CStackInstance::getFactionID() const
 {
 	if(type)
-		return type->getFaction();
+		return type->getFactionID();
 		
 	return FactionID::NEUTRAL;
 }

+ 1 - 1
lib/CCreatureSet.h

@@ -106,7 +106,7 @@ public:
 	//IConstBonusProvider
 	const IBonusBearer* getBonusBearer() const override;
 	//INativeTerrainProvider
-	FactionID getFaction() const override;
+	FactionID getFactionID() const override;
 
 	virtual ui64 getPower() const;
 	CCreature::CreatureQuantityId getQuantityID() const;

+ 3 - 3
lib/CGameInfoCallback.cpp

@@ -381,7 +381,7 @@ bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero
 
 			for(const auto & creature : VLC->creh->objects)
 			{
-				if(creature->getFaction() == factionIndex && static_cast<int>(creature->getAIValue()) > maxAIValue)
+				if(creature->getFactionID() == factionIndex && static_cast<int>(creature->getAIValue()) > maxAIValue)
 				{
 					maxAIValue = creature->getAIValue();
 					mostStrong = creature.get();
@@ -575,10 +575,10 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
 {
 	ERROR_RET_VAL_IF(!canGetFullInfo(t), "Town is not owned!", EBuildingState::TOWN_NOT_OWNED);
 
-	if(!t->town->buildings.count(ID))
+	if(!t->getTown()->buildings.count(ID))
 		return EBuildingState::BUILDING_ERROR;
 
-	const CBuilding * building = t->town->buildings.at(ID);
+	const CBuilding * building = t->getTown()->buildings.at(ID);
 
 
 	if(t->hasBuilt(ID))	//already built

+ 2 - 2
lib/battle/CUnitState.cpp

@@ -416,9 +416,9 @@ int32_t CUnitState::creatureIconIndex() const
 	return unitType()->getIconIndex();
 }
 
-FactionID CUnitState::getFaction() const
+FactionID CUnitState::getFactionID() const
 {
-	return unitType()->getFaction();
+	return unitType()->getFactionID();
 }
 
 int32_t CUnitState::getCasterUnitId() const

+ 1 - 1
lib/battle/CUnitState.h

@@ -253,7 +253,7 @@ public:
 	void localInit(const IUnitEnvironment * env_);
 	void serializeJson(JsonSerializeFormat & handler);
 
-	FactionID getFaction() const override;
+	FactionID getFactionID() const override;
 
 	void afterAttack(bool ranged, bool counter);
 

+ 3 - 3
lib/bonuses/Limiters.cpp

@@ -300,15 +300,15 @@ ILimiter::EDecision FactionLimiter::limit(const BonusLimitationContext &context)
 	if(bearer)
 	{
 		if(faction != FactionID::DEFAULT)
-			return bearer->getFaction() == faction ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
+			return bearer->getFactionID() == faction ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
 
 		switch(context.b.source)
 		{
 			case BonusSource::CREATURE_ABILITY:
-				return bearer->getFaction() == context.b.sid.as<CreatureID>().toCreature()->getFaction() ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
+				return bearer->getFactionID() == context.b.sid.as<CreatureID>().toCreature()->getFactionID() ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
 			
 			case BonusSource::TOWN_STRUCTURE:
-				return bearer->getFaction() == context.b.sid.as<BuildingTypeUniqueID>().getFaction() ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
+				return bearer->getFactionID() == context.b.sid.as<BuildingTypeUniqueID>().getFaction() ? ILimiter::EDecision::ACCEPT : ILimiter::EDecision::DISCARD;
 
 			//TODO: other sources of bonuses
 		}

+ 4 - 4
lib/campaign/CampaignState.cpp

@@ -351,14 +351,14 @@ void CampaignState::setCurrentMapAsConquered(std::vector<CGHeroInstance *> heroe
 	{
 		JsonNode node = CampaignState::crossoverSerialize(hero);
 
-		if (reservedHeroes.count(hero->getHeroType()))
+		if (reservedHeroes.count(hero->getHeroTypeID()))
 		{
-			logGlobal->info("Hero crossover: %d (%s) exported to global pool", hero->getHeroType(), hero->getNameTranslated());
-			globalHeroPool[hero->getHeroType()] = node;
+			logGlobal->info("Hero crossover: %d (%s) exported to global pool", hero->getHeroTypeID(), hero->getNameTranslated());
+			globalHeroPool[hero->getHeroTypeID()] = node;
 		}
 		else
 		{
-			logGlobal->info("Hero crossover: %d (%s) exported to scenario pool", hero->getHeroType(), hero->getNameTranslated());
+			logGlobal->info("Hero crossover: %d (%s) exported to scenario pool", hero->getHeroTypeID(), hero->getNameTranslated());
 			scenarioHeroPool[*currentMap].push_back(node);
 		}
 	}

+ 1 - 1
lib/entities/faction/CFaction.cpp

@@ -92,7 +92,7 @@ FactionID CFaction::getId() const
 	return FactionID(index);
 }
 
-FactionID CFaction::getFaction() const
+FactionID CFaction::getFactionID() const
 {
 	return FactionID(index);
 }

+ 1 - 1
lib/entities/faction/CFaction.h

@@ -39,7 +39,7 @@ class DLL_LINKAGE CFaction : public Faction
 
 	FactionID index = FactionID::NEUTRAL;
 
-	FactionID getFaction() const override; //This function should not be used
+	FactionID getFactionID() const override; //This function should not be used
 
 public:
 	TerrainId nativeTerrain;

+ 15 - 20
lib/gameState/CGameState.cpp

@@ -599,7 +599,7 @@ void CGameState::initHeroes()
 		}
 
 		hero->initHero(getRandomGenerator());
-		map->allHeroes[hero->getHeroType().getNum()] = hero;
+		map->allHeroes[hero->getHeroTypeID().getNum()] = hero;
 	}
 
 	// generate boats for all heroes on water
@@ -629,20 +629,20 @@ void CGameState::initHeroes()
 		{
 			auto * hero = dynamic_cast<CGHeroInstance*>(obj.get());
 			hero->initHero(getRandomGenerator());
-			map->allHeroes[hero->getHeroType().getNum()] = hero;
+			map->allHeroes[hero->getHeroTypeID().getNum()] = hero;
 		}
 	}
 
 	std::set<HeroTypeID> heroesToCreate = getUnusedAllowedHeroes(); //ids of heroes to be created and put into the pool
 	for(auto ph : map->predefinedHeroes)
 	{
-		if(!vstd::contains(heroesToCreate, ph->getHeroType()))
+		if(!vstd::contains(heroesToCreate, ph->getHeroTypeID()))
 			continue;
 		ph->initHero(getRandomGenerator());
 		heroesPool->addHeroToPool(ph);
-		heroesToCreate.erase(ph->type->getId());
+		heroesToCreate.erase(ph->getHeroTypeID());
 
-		map->allHeroes[ph->getHeroType().getNum()] = ph;
+		map->allHeroes[ph->getHeroTypeID().getNum()] = ph;
 	}
 
 	for(const HeroTypeID & htype : heroesToCreate) //all not used allowed heroes go with default state into the pool
@@ -756,12 +756,12 @@ void CGameState::initTownNames()
 
 	for(auto & vti : map->towns)
 	{
-		assert(vti->town);
+		assert(vti->getTown());
 
 		if(!vti->getNameTextID().empty())
 			continue;
 
-		FactionID faction = vti->getFaction();
+		FactionID faction = vti->getFactionID();
 
 		if(availableNames.empty())
 		{
@@ -798,8 +798,8 @@ void CGameState::initTowns()
 
 	for (auto & vti : map->towns)
 	{
-		assert(vti->town);
-		assert(vti->town->creatures.size() <= GameConstants::CREATURES_PER_TOWN); 
+		assert(vti->getTown());
+		assert(vti->getTown()->creatures.size() <= GameConstants::CREATURES_PER_TOWN);
 
 		constexpr std::array basicDwellings = { BuildingID::DWELL_FIRST, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3, BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7, BuildingID::DWELL_LVL_8 };
 		constexpr std::array upgradedDwellings = { BuildingID::DWELL_UP_FIRST, BuildingID::DWELL_LVL_2_UP, BuildingID::DWELL_LVL_3_UP, BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP, BuildingID::DWELL_LVL_8_UP };
@@ -828,7 +828,7 @@ void CGameState::initTowns()
 		vti->addBuilding(BuildingID::VILLAGE_HALL);
 
 		//init hordes
-		for (int i = 0; i < vti->town->creatures.size(); i++)
+		for (int i = 0; i < vti->getTown()->creatures.size(); i++)
 		{
 			if(vti->hasBuilt(hordes[i])) //if we have horde for this level
 			{
@@ -894,7 +894,7 @@ void CGameState::initTowns()
 			int sel = -1;
 
 			for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
-				total += vti->possibleSpells[ps].toSpell()->getProbability(vti->getFaction());
+				total += vti->possibleSpells[ps].toSpell()->getProbability(vti->getFactionID());
 
 			if (total == 0) // remaining spells have 0 probability
 				break;
@@ -902,7 +902,7 @@ void CGameState::initTowns()
 			auto r = getRandomGenerator().nextInt(total - 1);
 			for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
 			{
-				r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->getFaction());
+				r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->getFactionID());
 				if(r<0)
 				{
 					sel = ps;
@@ -1655,18 +1655,13 @@ std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllow
 	}
 
 	for(auto hero : map->heroesOnMap)  //heroes instances initialization
-	{
-		if(hero->type)
-			ret -= hero->type->getId();
-		else
-			ret -= hero->getHeroType();
-	}
+		ret -= hero->getHeroTypeID();
 
 	for(auto obj : map->objects) //prisons
 	{
 		auto * hero = dynamic_cast<const CGHeroInstance *>(obj.get());
 		if(hero && hero->ID == Obj::PRISON)
-			ret -= hero->getHeroType();
+			ret -= hero->getHeroTypeID();
 	}
 
 	return ret;
@@ -1690,7 +1685,7 @@ CGHeroInstance * CGameState::getUsedHero(const HeroTypeID & hid) const
 		auto * hero = dynamic_cast<CGHeroInstance *>(obj.get());
 		assert(hero);
 
-		if (hero->getHeroType() == hid)
+		if (hero->getHeroTypeID() == hid)
 			return hero;
 	}
 

+ 8 - 8
lib/gameState/CGameStateCampaign.cpp

@@ -86,7 +86,7 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & tr
 					.And(Selector::subtype()(BonusSubtypeID(g)))
 					.And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
 
-				hero.hero->getLocalBonus(sel)->val = hero.hero->type->heroClass->primarySkillInitial[g.getNum()];
+				hero.hero->getLocalBonus(sel)->val = hero.hero->getHeroClass()->primarySkillInitial[g.getNum()];
 			}
 		}
 	}
@@ -96,7 +96,7 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & tr
 		//trimming sec skills
 		for(auto & hero : campaignHeroReplacements)
 		{
-			hero.hero->secSkills = hero.hero->type->secSkillsInit;
+			hero.hero->secSkills = hero.hero->getHeroType()->secSkillsInit;
 			hero.hero->recreateSecondarySkillsBonuses();
 		}
 	}
@@ -240,7 +240,7 @@ void CGameStateCampaign::placeCampaignHeroes()
 
 	for(auto & replacement : campaignHeroReplacements)
 		if (replacement.heroPlaceholderId.hasValue())
-			heroesToRemove.insert(replacement.hero->getHeroType());
+			heroesToRemove.insert(replacement.hero->getHeroTypeID());
 
 	for(auto & heroID : heroesToRemove)
 	{
@@ -369,8 +369,8 @@ void CGameStateCampaign::replaceHeroesPlaceholders()
 		if(heroPlaceholder->tempOwner.isValidPlayer())
 			heroToPlace->tempOwner = heroPlaceholder->tempOwner;
 		heroToPlace->setAnchorPos(heroPlaceholder->anchorPos());
-		heroToPlace->type = heroToPlace->getHeroType().toHeroType();
-		heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, heroToPlace->type->heroClass->getIndex())->getTemplates().front();
+		heroToPlace->setHeroType(heroToPlace->getHeroTypeID());
+		heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, heroToPlace->getHeroTypeID())->getTemplates().front();
 
 		gameState->map->removeBlockVisTiles(heroPlaceholder, true);
 		gameState->map->objects[heroPlaceholder->id.getNum()] = nullptr;
@@ -563,7 +563,7 @@ void CGameStateCampaign::initHeroes()
 		{
 			for (auto & hero : heroes)
 			{
-				if (hero->getHeroType().getNum() == chosenBonus->info1)
+				if (hero->getHeroTypeID().getNum() == chosenBonus->info1)
 				{
 					giveCampaignBonusToHero(hero);
 					break;
@@ -662,7 +662,7 @@ void CGameStateCampaign::initTowns()
 		if(gameState->scenarioOps->campState->formatVCMI())
 			newBuilding = BuildingID(chosenBonus->info1);
 		else
-			newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->getBuildings());
+			newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFactionID(), town->getBuildings());
 
 		// Build granted building & all prerequisites - e.g. Mages Guild Lvl 3 should also give Mages Guild Lvl 1 & 2
 		while(true)
@@ -675,7 +675,7 @@ void CGameStateCampaign::initTowns()
 
 			town->addBuilding(newBuilding);
 
-			auto building = town->town->buildings.at(newBuilding);
+			auto building = town->getTown()->buildings.at(newBuilding);
 			newBuilding = building->upgrade;
 		}
 		break;

+ 1 - 1
lib/gameState/GameStatistics.cpp

@@ -381,7 +381,7 @@ float Statistic::getTownBuiltRatio(const PlayerState * ps)
 	for(const auto & t : ps->getTowns())
 	{
 		built += t->getBuildings().size();
-		for(const auto & b : t->town->buildings)
+		for(const auto & b : t->getTown()->buildings)
 			if(!t->forbiddenBuildings.count(b.first))
 				total += 1;
 	}

+ 1 - 1
lib/gameState/InfoAboutArmy.cpp

@@ -115,7 +115,7 @@ void InfoAboutHero::initFromHero(const CGHeroInstance *h, InfoAboutHero::EInfoLe
 
 	initFromArmy(h, detailed);
 
-	hclass = h->type->heroClass;
+	hclass = h->getHeroClass();
 	name = h->getNameTranslated();
 	portraitSource = h->getPortraitSource();
 

+ 4 - 4
lib/gameState/TavernHeroesPool.cpp

@@ -25,7 +25,7 @@ std::map<HeroTypeID, CGHeroInstance*> TavernHeroesPool::unusedHeroesFromPool() c
 {
 	std::map<HeroTypeID, CGHeroInstance*> pool = heroesPool;
 	for(const auto & slot : currentTavern)
-		pool.erase(slot.hero->getHeroType());
+		pool.erase(slot.hero->getHeroTypeID());
 
 	return pool;
 }
@@ -34,7 +34,7 @@ TavernSlotRole TavernHeroesPool::getSlotRole(HeroTypeID hero) const
 {
 	for (auto const & slot : currentTavern)
 	{
-		if (slot.hero->getHeroType() == hero)
+		if (slot.hero->getHeroTypeID() == hero)
 			return slot.role;
 	}
 	return TavernSlotRole::NONE;
@@ -106,7 +106,7 @@ CGHeroInstance * TavernHeroesPool::takeHeroFromPool(HeroTypeID hero)
 	heroesPool.erase(hero);
 
 	vstd::erase_if(currentTavern, [&](const TavernSlot & entry){
-		return entry.hero->type->getId() == hero;
+		return entry.hero->getHeroTypeID() == hero;
 	});
 
 	assert(result);
@@ -138,7 +138,7 @@ void TavernHeroesPool::onNewDay()
 
 void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero)
 {
-	heroesPool[hero->getHeroType()] = hero;
+	heroesPool[hero->getHeroTypeID()] = hero;
 }
 
 void TavernHeroesPool::setAvailability(HeroTypeID hero, std::set<PlayerColor> mask)

+ 1 - 7
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -96,7 +96,6 @@ bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, std
 
 void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const
 {
-	obj->town = faction->town;
 	obj->tempOwner = PlayerColor::NEUTRAL;
 }
 
@@ -144,7 +143,7 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std
 
 	auto heroTest = [&](const HeroTypeID & id)
 	{
-		return hero->type->getId() == id;
+		return hero->getHeroTypeID() == id;
 	};
 
 	if(filters.count(templ->stringID))
@@ -154,11 +153,6 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std
 	return false;
 }
 
-void CHeroInstanceConstructor::initializeObject(CGHeroInstance * obj) const
-{
-	obj->type = nullptr; //FIXME: set to valid value. somehow.
-}
-
 void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const
 {
 

+ 0 - 1
lib/mapObjectConstructors/CommonConstructors.h

@@ -81,7 +81,6 @@ public:
 	const CHeroClass * heroClass = nullptr;
 	std::map<std::string, LogicalExpression<HeroTypeID>> filters;
 
-	void initializeObject(CGHeroInstance * object) const override;
 	void randomizeObject(CGHeroInstance * object, vstd::RNG & rng) const override;
 	void afterLoadFinalization() override;
 

+ 1 - 1
lib/mapObjects/CArmedInstance.cpp

@@ -78,7 +78,7 @@ void CArmedInstance::updateMoraleBonusFromArmy()
 		const CStackInstance * inst = slot.second;
 		const auto * creature  = inst->getCreatureID().toEntity(VLC);
 
-		factions.insert(creature->getFaction());
+		factions.insert(creature->getFactionID());
 		// Check for undead flag instead of faction (undead mummies are neutral)
 		if (!hasUndead)
 		{

+ 20 - 15
lib/mapObjects/CGCreature.cpp

@@ -45,7 +45,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const
 	else
 		ms.appendLocalString(EMetaText::ARRAY_TXT, quantityTextIndex);
 	ms.appendRawString(" ");
-	ms.appendNamePlural(getCreature());
+	ms.appendNamePlural(getCreatureID());
 
 	return ms.toString();
 }
@@ -57,7 +57,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
 		MetaString ms;
 		ms.appendNumber(stacks.begin()->second->count);
 		ms.appendRawString(" ");
-		ms.appendName(getCreature(), stacks.begin()->second->count);
+		ms.appendName(getCreatureID(), stacks.begin()->second->count);
 		return ms.toString();
 	}
 	else
@@ -69,11 +69,11 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
 std::string CGCreature::getMonsterLevelText() const
 {
 	std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel");
-	bool isRanged = VLC->creatures()->getById(getCreature())->getBonusBearer()->hasBonusOfType(BonusType::SHOOTER);
+	bool isRanged = getCreature()->getBonusBearer()->hasBonusOfType(BonusType::SHOOTER);
 	std::string attackTypeKey = isRanged ? "vcmi.adventureMap.monsterRangedType" : "vcmi.adventureMap.monsterMeleeType";
 	std::string attackType = VLC->generaltexth->translate(attackTypeKey);
-	boost::replace_first(monsterLevel, "%TOWN", (*VLC->townh)[VLC->creatures()->getById(getCreature())->getFaction()]->getNameTranslated());
-	boost::replace_first(monsterLevel, "%LEVEL", std::to_string(VLC->creatures()->getById(getCreature())->getLevel()));
+	boost::replace_first(monsterLevel, "%TOWN", getCreature()->getFactionID().toEntity(VLC)->getNameTranslated());
+	boost::replace_first(monsterLevel, "%LEVEL", std::to_string(getCreature()->getLevel()));
 	boost::replace_first(monsterLevel, "%ATTACK_TYPE", attackType);
 	return monsterLevel;
 }
@@ -150,7 +150,7 @@ std::string CGCreature::getPopupText(PlayerColor player) const
 std::vector<Component> CGCreature::getPopupComponents(PlayerColor player) const
 {
 	return {
-		Component(ComponentType::CREATURE, getCreature())
+		Component(ComponentType::CREATURE, getCreatureID())
 	};
 }
 
@@ -182,7 +182,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 			BlockingDialog ynd(true,false);
 			ynd.player = h->tempOwner;
 			ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86);
-			ynd.text.replaceName(getCreature(), getStackCount(SlotID(0)));
+			ynd.text.replaceName(getCreatureID(), getStackCount(SlotID(0)));
 			cb->showBlockingDialog(this, &ynd);
 			break;
 		}
@@ -197,7 +197,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 			std::string tmp = VLC->generaltexth->advobtxt[90];
 			boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
 			boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
-			boost::algorithm::replace_first(tmp,"%s",VLC->creatures()->getById(getCreature())->getNamePluralTranslated());
+			boost::algorithm::replace_first(tmp,"%s",getCreature()->getNamePluralTranslated());
 			ynd.text.appendRawString(tmp);
 			cb->showBlockingDialog(this, &ynd);
 			break;
@@ -205,11 +205,16 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 	}
 }
 
-CreatureID CGCreature::getCreature() const
+CreatureID CGCreature::getCreatureID() const
 {
 	return CreatureID(getObjTypeIndex().getNum());
 }
 
+const CCreature * CGCreature::getCreature() const
+{
+	return getCreatureID().toCreature();
+}
+
 void CGCreature::pickRandomObject(vstd::RNG & rand)
 {
 	switch(ID.toEnum())
@@ -279,7 +284,7 @@ void CGCreature::initObj(vstd::RNG & rand)
 
 	stacks[SlotID(0)]->setType(getCreature());
 	TQuantity &amount = stacks[SlotID(0)]->count;
-	const Creature * c = VLC->creatures()->getById(getCreature());
+	const Creature * c = getCreature();
 	if(amount == 0)
 	{
 		amount = rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax());
@@ -353,8 +358,8 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 
 	for(const auto & elem : h->Slots())
 	{
-		bool isOurUpgrade = vstd::contains(getCreature().toCreature()->upgrades, elem.second->getCreatureID());
-		bool isOurDowngrade = vstd::contains(elem.second->type->upgrades, getCreature());
+		bool isOurUpgrade = vstd::contains(getCreature()->upgrades, elem.second->getCreatureID());
+		bool isOurDowngrade = vstd::contains(elem.second->type->upgrades, getCreatureID());
 
 		if(isOurUpgrade || isOurDowngrade)
 			count += elem.second->count;
@@ -380,7 +385,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
 
 		if(diplomacy * 2 + sympathy + 1 >= character)
 		{
-			int32_t recruitCost = VLC->creatures()->getById(getCreature())->getRecruitCost(EGameResID::GOLD);
+			int32_t recruitCost = getCreature()->getRecruitCost(EGameResID::GOLD);
 			int32_t stackCount = getStackCount(SlotID(0));
 			return recruitCost * stackCount; //join for gold
 		}
@@ -493,7 +498,7 @@ void CGCreature::flee( const CGHeroInstance * h ) const
 	BlockingDialog ynd(true,false);
 	ynd.player = h->tempOwner;
 	ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91);
-	ynd.text.replaceName(getCreature(), getStackCount(SlotID(0)));
+	ynd.text.replaceName(getCreatureID(), getStackCount(SlotID(0)));
 	cb->showBlockingDialog(this, &ynd);
 }
 
@@ -513,7 +518,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
 	{
 		//merge stacks into one
 		TSlots::const_iterator i;
-		const CCreature * cre = getCreature().toCreature();
+		const CCreature * cre = getCreature();
 		for(i = stacks.begin(); i != stacks.end(); i++)
 		{
 			if(cre->isMyUpgrade(i->second->type))

+ 2 - 1
lib/mapObjects/CGCreature.h

@@ -49,7 +49,8 @@ public:
 	void newTurn(vstd::RNG & rand) const override;
 	void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
 	void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
-	CreatureID getCreature() const;
+	CreatureID getCreatureID() const;
+	const CCreature * getCreature() const;
 
 	//stack formation depends on position,
 	bool containsUpgradedStack() const;

+ 1 - 1
lib/mapObjects/CGDwelling.cpp

@@ -93,7 +93,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
 
 		assert(linkedTown->ID == Obj::TOWN);
 		if(linkedTown->ID==Obj::TOWN)
-			return linkedTown->getFaction();
+			return linkedTown->getFactionID();
 	}
 
 	if(!randomizationInfo->allowedFactions.empty())

+ 56 - 57
lib/mapObjects/CGHeroInstance.cpp

@@ -116,9 +116,9 @@ ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const Terrain
 	return static_cast<ui32>(ret);
 }
 
-FactionID CGHeroInstance::getFaction() const
+FactionID CGHeroInstance::getFactionID() const
 {
-	return FactionID(type->heroClass->faction);
+	return getHeroClass()->faction;
 }
 
 const IBonusBearer* CGHeroInstance::getBonusBearer() const
@@ -229,10 +229,10 @@ bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const
 	if (getSecSkillLevel(which) > 0)
 		return false;
 
-	if (type->heroClass->secSkillProbability.count(which) == 0)
+	if (getHeroClass()->secSkillProbability.count(which) == 0)
 		return false;
 
-	if (type->heroClass->secSkillProbability.at(which) == 0)
+	if (getHeroClass()->secSkillProbability.at(which) == 0)
 		return false;
 
 	return true;
@@ -282,7 +282,6 @@ int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti)
 
 CGHeroInstance::CGHeroInstance(IGameCallback * cb)
 	: CArmedInstance(cb),
-	type(nullptr),
 	tacticFormationEnabled(false),
 	inTownGarrison(false),
 	moveDir(4),
@@ -303,14 +302,30 @@ PlayerColor CGHeroInstance::getOwner() const
 	return tempOwner;
 }
 
-HeroTypeID CGHeroInstance::getHeroType() const
+const CHeroClass * CGHeroInstance::getHeroClass() const
 {
+	return getHeroType()->heroClass;
+}
+
+HeroClassID CGHeroInstance::getHeroClassID() const
+{
+	return getHeroType()->heroClass->getId();
+}
+
+const CHero * CGHeroInstance::getHeroType() const
+{
+	return getHeroTypeID().toHeroType();
+}
+
+HeroTypeID CGHeroInstance::getHeroTypeID() const
+{
+	if (ID == Obj::RANDOM_HERO)
+		return HeroTypeID::NONE;
 	return HeroTypeID(getObjTypeIndex().getNum());
 }
 
 void CGHeroInstance::setHeroType(HeroTypeID heroType)
 {
-	assert(type == nullptr);
 	subID = heroType;
 }
 
@@ -323,16 +338,14 @@ void CGHeroInstance::initHero(vstd::RNG & rand, const HeroTypeID & SUBID)
 void CGHeroInstance::initHero(vstd::RNG & rand)
 {
 	assert(validTypes(true));
-	if(!type)
-		type = getHeroType().toHeroType();
-
+	
 	if (ID == Obj::HERO)
-		appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
+		appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, getHeroClass()->getIndex())->getTemplates().front();
 
 	if(!vstd::contains(spells, SpellID::PRESET))
 	{
 		// hero starts with default spells
-		for(const auto & spellID : type->spells)
+		for(const auto & spellID : getHeroType()->spells)
 			spells.insert(spellID);
 	}
 	else //remove placeholder
@@ -341,7 +354,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
 	if(!vstd::contains(spells, SpellID::SPELLBOOK_PRESET))
 	{
 		// hero starts with default spellbook presence status
-		if(!getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook)
+		if(!getArt(ArtifactPosition::SPELLBOOK) && getHeroType()->haveSpellBook)
 		{
 			auto artifact = ArtifactUtils::createArtifact(ArtifactID::SPELLBOOK);
 			putArtifact(ArtifactPosition::SPELLBOOK, artifact);
@@ -360,14 +373,14 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
 	{
 		for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
 		{
-			pushPrimSkill(static_cast<PrimarySkill>(g), type->heroClass->primarySkillInitial[g]);
+			pushPrimSkill(static_cast<PrimarySkill>(g), getHeroClass()->primarySkillInitial[g]);
 		}
 	}
 	if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::NONE, -1)) //set secondary skills to default
-		secSkills = type->secSkillsInit;
+		secSkills = getHeroType()->secSkillsInit;
 
 	if (gender == EHeroGender::DEFAULT)
-		gender = type->gender;
+		gender = getHeroType()->gender;
 
 	setFormation(EArmyFormation::LOOSE);
 	if (!stacksCount()) //standard army//initial army
@@ -403,9 +416,9 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
 		addNewBonus(bonus);
 	}
 
-	if (cb->getSettings().getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && type->heroClass->commander.hasValue())
+	if (cb->getSettings().getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && getHeroClass()->commander.hasValue())
 	{
-		commander = new CCommanderInstance(type->heroClass->commander);
+		commander = new CCommanderInstance(getHeroClass()->commander);
 		commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
 		commander->giveStackExp (exp); //after our exp is set
 	}
@@ -413,7 +426,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
 	skillsInfo = SecondarySkillsInfo();
 
 	//copy active (probably growing) bonuses from hero prototype to hero object
-	for(const std::shared_ptr<Bonus> & b : type->specialty)
+	for(const std::shared_ptr<Bonus> & b : getHeroType()->specialty)
 		addNewBonus(b);
 
 	//initialize bonuses
@@ -433,14 +446,14 @@ void CGHeroInstance::initArmy(vstd::RNG & rand, IArmyDescriptor * dst)
 	auto stacksCountChances = cb->getSettings().getVector(EGameSettings::HEROES_STARTING_STACKS_CHANCES);
 	int stacksCountInitRandomNumber = rand.nextInt(1, 100);
 
-	size_t maxStacksCount = std::min(stacksCountChances.size(), type->initialArmy.size());
+	size_t maxStacksCount = std::min(stacksCountChances.size(), getHeroType()->initialArmy.size());
 
 	for(int stackNo=0; stackNo < maxStacksCount; stackNo++)
 	{
 		if (stacksCountInitRandomNumber > stacksCountChances[stackNo])
 			continue;
 
-		auto & stack = type->initialArmy[stackNo];
+		auto & stack = getHeroType()->initialArmy[stackNo];
 
 		int count = rand.nextInt(stack.minAmount, stack.maxAmount);
 
@@ -588,11 +601,11 @@ std::string CGHeroInstance::getMovementPointsTextIfOwner(PlayerColor player) con
 
 ui8 CGHeroInstance::maxlevelsToMagicSchool() const
 {
-	return type->heroClass->isMagicHero() ? 3 : 4;
+	return getHeroClass()->isMagicHero() ? 3 : 4;
 }
 ui8 CGHeroInstance::maxlevelsToWisdom() const
 {
-	return type->heroClass->isMagicHero() ? 3 : 6;
+	return getHeroClass()->isMagicHero() ? 3 : 6;
 }
 
 CGHeroInstance::SecondarySkillsInfo::SecondarySkillsInfo():
@@ -617,11 +630,8 @@ void CGHeroInstance::pickRandomObject(vstd::RNG & rand)
 	{
 		ID = Obj::HERO;
 		subID = cb->gameState()->pickNextHeroType(getOwner());
-		type = getHeroType().toHeroType();
-		randomizeArmy(type->heroClass->faction);
+		randomizeArmy(getHeroClass()->faction);
 	}
-	else
-		type = getHeroType().toHeroType();
 
 	auto oldSubID = subID;
 
@@ -629,7 +639,7 @@ void CGHeroInstance::pickRandomObject(vstd::RNG & rand)
 	// after setType subID used to store unique hero identify id. Check issue 2277 for details
 	// exclude prisons which should use appearance as set in map, via map editor or RMG
 	if (ID != Obj::PRISON)
-		setType(ID, type->heroClass->getIndex());
+		setType(ID, getHeroClass()->getIndex());
 
 	this->subID = oldSubID;
 }
@@ -1041,7 +1051,7 @@ si32 CGHeroInstance::getManaNewTurn() const
 
 BoatId CGHeroInstance::getBoatType() const
 {
-	return BoatId(VLC->townh->getById(type->heroClass->faction)->getBoatType());
+	return BoatId(VLC->townh->getById(getHeroClass()->faction)->getBoatType());
 }
 
 void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
@@ -1080,7 +1090,7 @@ void CGHeroInstance::pushPrimSkill( PrimarySkill which, int val )
 
 EAlignment CGHeroInstance::getAlignment() const
 {
-	return type->heroClass->getAlignment();
+	return getHeroClass()->getAlignment();
 }
 
 void CGHeroInstance::initExp(vstd::RNG & rand)
@@ -1104,12 +1114,12 @@ HeroTypeID CGHeroInstance::getPortraitSource() const
 	if (customPortraitSource.isValid())
 		return customPortraitSource;
 	else
-		return getHeroType();
+		return getHeroTypeID();
 }
 
 int32_t CGHeroInstance::getIconIndex() const
 {
-	return VLC->heroTypes()->getById(getPortraitSource())->getIconIndex();
+	return getPortraitSource().toEntity(VLC)->getIconIndex();
 }
 
 std::string CGHeroInstance::getNameTranslated() const
@@ -1126,15 +1136,15 @@ std::string CGHeroInstance::getClassNameTextID() const
 {
 	if (isCampaignGem())
 		return "core.genrltxt.735";
-	return type->heroClass->getNameTextID();
+	return getHeroClass()->getNameTextID();
 }
 
 std::string CGHeroInstance::getNameTextID() const
 {
 	if (!nameCustomTextId.empty())
 		return nameCustomTextId;
-	if (type)
-		return type->getNameTextID();
+	if (getHeroTypeID().hasValue())
+		return getHeroType()->getNameTextID();
 
 	// FIXME: called by logging from some specialties (mods?) before type is set on deserialization
 	// assert(0);
@@ -1150,8 +1160,8 @@ std::string CGHeroInstance::getBiographyTextID() const
 {
 	if (!biographyCustomTextId.empty())
 		return biographyCustomTextId;
-	if (type)
-		return type->getBiographyTextID();
+	if (getHeroTypeID().hasValue())
+		return getHeroType()->getBiographyTextID();
 	
 	return ""; //for random hero
 }
@@ -1349,11 +1359,11 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills(vs
 		SecondarySkill selection;
 
 		if (selectWisdom)
-			selection = type->heroClass->chooseSecSkill(intersect(options, wisdomList), rand);
+			selection = getHeroClass()->chooseSecSkill(intersect(options, wisdomList), rand);
 		else if (selectSchool)
-			selection = type->heroClass->chooseSecSkill(intersect(options, schoolList), rand);
+			selection = getHeroClass()->chooseSecSkill(intersect(options, schoolList), rand);
 		else
-			selection = type->heroClass->chooseSecSkill(options, rand);
+			selection = getHeroClass()->chooseSecSkill(options, rand);
 
 		skills.push_back(selection);
 		options.erase(selection);
@@ -1384,7 +1394,7 @@ PrimarySkill CGHeroInstance::nextPrimarySkill(vstd::RNG & rand) const
 {
 	assert(gainsLevel());
 	const auto isLowLevelHero = level < GameConstants::HERO_HIGH_LEVEL;
-	const auto & skillChances = isLowLevelHero ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel;
+	const auto & skillChances = isLowLevelHero ? getHeroClass()->primarySkillLowLevel : getHeroClass()->primarySkillHighLevel;
 
 	if (isCampaignYog())
 	{
@@ -1524,25 +1534,15 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, BonusSubtypeID
 std::string CGHeroInstance::getHeroTypeName() const
 {
 	if(ID == Obj::HERO || ID == Obj::PRISON)
-	{
-		if(type)
-		{
-			return type->getJsonKey();
-		}
-		else
-		{
-			return getHeroType().toEntity(VLC)->getJsonKey();
-		}
-	}
+		return getHeroType()->getJsonKey();
+
 	return "";
 }
 
 void CGHeroInstance::afterAddToMap(CMap * map)
 {
 	if(ID != Obj::PRISON)
-	{		
 		map->heroesOnMap.emplace_back(this);
-	}
 }
 void CGHeroInstance::afterRemoveFromMap(CMap* map)
 {
@@ -1729,8 +1729,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 		if(!appearance)
 		{
 			// crossoverDeserialize
-			type = getHeroType().toHeroType();
-			appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
+			appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, getHeroClassID())->getTemplates().front();
 		}
 	}
 
@@ -1817,7 +1816,7 @@ bool CGHeroInstance::isCampaignYog() const
 	if (!boost::starts_with(campaign, "DATA/YOG")) // "Birth of a Barbarian"
 		return false;
 
-	if (getHeroType() != HeroTypeID::SOLMYR) // Yog (based on Solmyr)
+	if (getHeroTypeID() != HeroTypeID::SOLMYR) // Yog (based on Solmyr)
 		return false;
 
 	return true;
@@ -1835,7 +1834,7 @@ bool CGHeroInstance::isCampaignGem() const
 	if (!boost::starts_with(campaign, "DATA/GEM") &&  !boost::starts_with(campaign, "DATA/FINAL")) // "New Beginning" and "Unholy Alliance"
 		return false;
 
-	if (getHeroType() != HeroTypeID::GEM) // Yog (based on Solmyr)
+	if (getHeroTypeID() != HeroTypeID::GEM) // Yog (based on Solmyr)
 		return false;
 
 	return true;

+ 11 - 4
lib/mapObjects/CGHeroInstance.h

@@ -72,7 +72,6 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
-	const CHero * type;
 	TExpType exp; //experience points
 	ui32 level; //current level of hero
 
@@ -171,7 +170,7 @@ public:
 	const IOwnableObject * asOwnable() const final;
 
 	//INativeTerrainProvider
-	FactionID getFaction() const override;
+	FactionID getFactionID() const override;
 	TerrainId getNativeTerrain() const override;
 	int getLowestCreatureSpeed() const;
 	si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
@@ -235,7 +234,11 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 
-	HeroTypeID getHeroType() const;
+	const CHeroClass * getHeroClass() const;
+	HeroClassID getHeroClassID() const;
+
+	const CHero * getHeroType() const;
+	HeroTypeID getHeroTypeID() const;
 	void setHeroType(HeroTypeID type);
 
 	void initHero(vstd::RNG & rand);
@@ -352,7 +355,11 @@ public:
 		h & skillsInfo;
 		h & visitedTown;
 		h & boat;
-		h & type;
+		if (h.version < Handler::Version::REMOVE_TOWN_PTR)
+		{
+			CHero * type = nullptr;
+			h & type;
+		}
 		h & commander;
 		h & visitedObjects;
 		BONUS_TREE_DESERIALIZATION_FIX

+ 58 - 62
lib/mapObjects/CGTownInstance.cpp

@@ -45,7 +45,7 @@ int CGTownInstance::getSightRadius() const //returns sight distance
 
 	for(const auto & bid : builtBuildings)
 	{
-		auto height = town->buildings.at(bid)->height;
+		auto height = getTown()->buildings.at(bid)->height;
 		if(ret < height)
 			ret = height;
 	}
@@ -115,7 +115,7 @@ int CGTownInstance::mageGuildLevel() const
 
 int CGTownInstance::getHordeLevel(const int & HID)  const//HID - 0 or 1; returns creature level or -1 if that horde structure is not present
 {
-	return town->hordeLvl.at(HID);
+	return getTown()->hordeLvl.at(HID);
 }
 
 int CGTownInstance::creatureGrowth(const int & level) const
@@ -127,7 +127,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
 {
 	GrowthInfo ret;
 
-	if (level<0 || level >=town->creatures.size())
+	if (level<0 || level >=getTown()->creatures.size())
 		return ret;
 	if (creatures[level].second.empty())
 		return ret; //no dwelling
@@ -151,11 +151,11 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
 	else if (hasBuilt(BuildingID::CITADEL))
 		ret.entries.emplace_back(subID, BuildingID::CITADEL, castleBonus = base / 2);
 
-	if(town->hordeLvl.at(0) == level)//horde 1
+	if(getTown()->hordeLvl.at(0) == level)//horde 1
 		if(hasBuilt(BuildingID::HORDE_1))
 			ret.entries.emplace_back(subID, BuildingID::HORDE_1, creature->getHorde());
 
-	if(town->hordeLvl.at(1) == level)//horde 2
+	if(getTown()->hordeLvl.at(1) == level)//horde 2
 		if(hasBuilt(BuildingID::HORDE_2))
 			ret.entries.emplace_back(subID, BuildingID::HORDE_2, creature->getHorde());
 
@@ -209,11 +209,11 @@ int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds,
 TResources CGTownInstance::dailyIncome() const
 {
 	TResources ret;
-	for(const auto & p : town->buildings)
+	for(const auto & p : getTown()->buildings)
 	{
 		BuildingID buildingUpgrade;
 
-		for(const auto & p2 : town->buildings)
+		for(const auto & p2 : getTown()->buildings)
 		{
 			if (p2.second->upgrade == p.first)
 			{
@@ -251,10 +251,10 @@ bool CGTownInstance::hasCapitol() const
 
 TownFortifications CGTownInstance::fortificationsLevel() const
 {
-	auto result = town->fortifications;
+	auto result = getTown()->fortifications;
 
 	for (auto const	& buildingID : builtBuildings)
-		result += town->buildings.at(buildingID)->fortifications;
+		result += getTown()->buildings.at(buildingID)->fortifications;
 
 	if (result.wallsHealth == 0)
 		return TownFortifications();
@@ -264,7 +264,6 @@ TownFortifications CGTownInstance::fortificationsLevel() const
 
 CGTownInstance::CGTownInstance(IGameCallback *cb):
 	CGDwelling(cb),
-	town(nullptr),
 	built(0),
 	destroyed(0),
 	identifier(0),
@@ -379,17 +378,17 @@ void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
 
 std::string CGTownInstance::getObjectName() const
 {
-	return getNameTranslated() + ", " + town->faction->getNameTranslated();
+	return getNameTranslated() + ", " + getTown()->faction->getNameTranslated();
 }
 
 bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId) const
 {
-	return town->getBuildingType(subId) != BuildingID::NONE;
+	return getTown()->getBuildingType(subId) != BuildingID::NONE;
 }
 
 void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
 {
-	for(const auto & kvp : town->buildings)
+	for(const auto & kvp : getTown()->buildings)
 	{
 		if(!kvp.second->rewardableObjectInfo.getParameters().isNull())
 			rewardableBuildings[kvp.first] = new TownRewardableBuildingInstance(this, kvp.second->bid, rand);
@@ -457,8 +456,7 @@ void CGTownInstance::pickRandomObject(vstd::RNG & rand)
 
 	assert(ID == Obj::TOWN); // just in case
 	setType(ID, subID);
-	town = (*VLC->townh)[getFaction()]->town;
-	randomizeArmy(getFaction());
+	randomizeArmy(getFactionID());
 	updateAppearance();
 }
 
@@ -467,19 +465,19 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
 	blockVisit = true;
 
 	if(townEnvisagesBuilding(BuildingSubID::PORTAL_OF_SUMMONING)) //Dungeon for example
-		creatures.resize(town->creatures.size() + 1);
+		creatures.resize(getTown()->creatures.size() + 1);
 	else
-		creatures.resize(town->creatures.size());
+		creatures.resize(getTown()->creatures.size());
 
-	for (int level = 0; level < town->creatures.size(); level++)
+	for (int level = 0; level < getTown()->creatures.size(); level++)
 	{
 		BuildingID buildID = BuildingID(BuildingID::getDwellingFromLevel(level, 0));
 		int upgradeNum = 0;
 
-		for (; town->buildings.count(buildID); upgradeNum++, BuildingID::advanceDwelling(buildID))
+		for (; getTown()->buildings.count(buildID); upgradeNum++, BuildingID::advanceDwelling(buildID))
 		{
-			if (hasBuilt(buildID) && town->creatures.at(level).size() > upgradeNum)
-				creatures[level].second.push_back(town->creatures[level][upgradeNum]);
+			if (hasBuilt(buildID) && getTown()->creatures.at(level).size() > upgradeNum)
+				creatures[level].second.push_back(getTown()->creatures[level][upgradeNum]);
 		}
 	}
 	initializeConfigurableBuildings(rand);
@@ -623,9 +621,9 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
 	if (hasCapitol()) // search if there's an older capitol
 	{
 		PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player
-		for (const auto & town : state->getTowns())
+		for (const auto & otherTown : state->getTowns())
 		{
-			if (town != this && town->hasCapitol())
+			if (otherTown != this && otherTown->hasCapitol())
 			{
 				RazeStructures rs;
 				rs.tid = id;
@@ -648,7 +646,7 @@ void CGTownInstance::clearArmy() const
 
 BoatId CGTownInstance::getBoatType() const
 {
-	return town->faction->boatType;
+	return getTown()->faction->boatType;
 }
 
 int CGTownInstance::getMarketEfficiency() const
@@ -703,7 +701,7 @@ void CGTownInstance::updateAppearance()
 
 std::string CGTownInstance::nodeName() const
 {
-	return "Town (" + (town ? town->faction->getNameTranslated() : "unknown") + ") of " + getNameTranslated();
+	return "Town (" + getTown()->faction->getNameTranslated() + ") of " + getNameTranslated();
 }
 
 void CGTownInstance::deserializationFix()
@@ -752,7 +750,7 @@ void CGTownInstance::recreateBuildingsBonuses()
 
 		for(const auto & upgradeID : builtBuildings)
 		{
-			const auto & upgrade = town->buildings.at(upgradeID);
+			const auto & upgrade = getTown()->buildings.at(upgradeID);
 			if (upgrade->getBase() == bid && upgrade->upgradeReplacesBonuses)
 				bonusesReplacedByUpgrade = true;
 		}
@@ -761,7 +759,7 @@ void CGTownInstance::recreateBuildingsBonuses()
 		if (bonusesReplacedByUpgrade)
 			continue;
 
-		auto building = town->buildings.at(bid);
+		auto building = getTown()->buildings.at(bid);
 
 		if(building->buildingBonuses.empty())
 			continue;
@@ -828,21 +826,6 @@ bool CGTownInstance::armedGarrison() const
 	return !stacks.empty() || garrisonHero;
 }
 
-const CTown * CGTownInstance::getTown() const
-{
-    if(ID == Obj::RANDOM_TOWN)
-		return VLC->townh->randomTown;
-	else
-	{
-		if(nullptr == town)
-		{
-			return (*VLC->townh)[getFaction()]->town;
-		}
-		else
-			return town;
-	}
-}
-
 int CGTownInstance::getTownLevel() const
 {
 	// count all buildings that are not upgrades
@@ -850,7 +833,7 @@ int CGTownInstance::getTownLevel() const
 
 	for(const auto & bid : builtBuildings)
 	{
-		if(town->buildings.at(bid)->upgrade == BuildingID::NONE)
+		if(getTown()->buildings.at(bid)->upgrade == BuildingID::NONE)
 			level++;
 	}
 	return level;
@@ -892,7 +875,7 @@ bool CGTownInstance::hasBuilt(BuildingSubID::EBuildingSubID buildingID) const
 {
 	for(const auto & bid : builtBuildings)
 	{
-		if(town->buildings.at(bid)->subId == buildingID)
+		if(getTown()->buildings.at(bid)->subId == buildingID)
 			return true;
 	}
 	return false;
@@ -905,7 +888,7 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID) const
 
 bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) const
 {
-	if (townID == town->faction->getId() || townID == FactionID::ANY)
+	if (townID == getTown()->faction->getId() || townID == FactionID::ANY)
 		return hasBuilt(buildingID);
 	return false;
 }
@@ -923,7 +906,7 @@ std::set<EMarketMode> CGTownInstance::availableModes() const
 	std::set<EMarketMode> result;
 	for (const auto & buildingID : builtBuildings)
 	{
-		const auto * buildingPtr = town->buildings.at(buildingID).get();
+		const auto * buildingPtr = getTown()->buildings.at(buildingID).get();
 		result.insert(buildingPtr->marketModes.begin(), buildingPtr->marketModes.end());
 	}
 
@@ -950,8 +933,8 @@ std::set<BuildingID> CGTownInstance::getBuildings() const
 
 TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
 {
-	if (vstd::contains(town->buildings, buildingID))
-		return town->buildings.at(buildingID)->resources;
+	if (vstd::contains(getTown()->buildings, buildingID))
+		return getTown()->buildings.at(buildingID)->resources;
 	else
 	{
 		logGlobal->error("Town %s at %s has no possible building %d!", getNameTranslated(), anchorPos().toString(), buildingID.toEnum());
@@ -962,7 +945,7 @@ TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
 
 CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID & buildID, bool deep) const
 {
-	const CBuilding * building = town->buildings.at(buildID);
+	const CBuilding * building = getTown()->buildings.at(buildID);
 
 	//TODO: find better solution to prevent infinite loops
 	std::set<BuildingID> processed;
@@ -970,13 +953,13 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
 	std::function<CBuilding::TRequired::Variant(const BuildingID &)> dependTest =
 	[&](const BuildingID & id) -> CBuilding::TRequired::Variant
 	{
-		if (town->buildings.count(id) == 0)
+		if (getTown()->buildings.count(id) == 0)
 		{
 			logMod->error("Invalid building ID %d in building dependencies!", id.getNum());
 			return CBuilding::TRequired::OperatorAll();
 		}
 
-		const CBuilding * build = town->buildings.at(id);
+		const CBuilding * build = getTown()->buildings.at(id);
 		CBuilding::TRequired::OperatorAll requirements;
 
 		if (!hasBuilt(id))
@@ -1001,7 +984,7 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
 	CBuilding::TRequired::OperatorAll requirements;
 	if (building->upgrade != BuildingID::NONE)
 	{
-		const CBuilding * upgr = town->buildings.at(building->upgrade);
+		const CBuilding * upgr = getTown()->buildings.at(building->upgrade);
 
 		requirements.expressions.push_back(dependTest(upgr->bid));
 		processed.clear();
@@ -1151,14 +1134,27 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 	}
 }
 
-FactionID CGTownInstance::getFaction() const
+const CFaction * CGTownInstance::getFaction() const
+{
+	return getFactionID().toFaction();
+}
+
+const CTown * CGTownInstance::getTown() const
+{
+	if(ID == Obj::RANDOM_TOWN)
+		return VLC->townh->randomTown;
+
+	return getFaction()->town;
+}
+
+FactionID CGTownInstance::getFactionID() const
 {
-	return  FactionID(subID.getNum());
+	return FactionID(subID.getNum());
 }
 
 TerrainId CGTownInstance::getNativeTerrain() const
 {
-	return town->faction->getNativeTerrain();
+	return getTown()->faction->getNativeTerrain();
 }
 
 ArtifactID CGTownInstance::getWarMachineInBuilding(BuildingID building) const
@@ -1166,21 +1162,21 @@ ArtifactID CGTownInstance::getWarMachineInBuilding(BuildingID building) const
 	if (builtBuildings.count(building) == 0)
 		return ArtifactID::NONE;
 
-	if (building == BuildingID::BLACKSMITH && town->warMachineDeprecated.hasValue())
-		return town->warMachineDeprecated.toCreature()->warMachine;
+	if (building == BuildingID::BLACKSMITH && getTown()->warMachineDeprecated.hasValue())
+		return getTown()->warMachineDeprecated.toCreature()->warMachine;
 
-	return town->buildings.at(building)->warMachine;
+	return getTown()->buildings.at(building)->warMachine;
 }
 
 bool CGTownInstance::isWarMachineAvailable(ArtifactID warMachine) const
 {
 	for (auto const & buildingID : builtBuildings)
-		if (town->buildings.at(buildingID)->warMachine == warMachine)
+		if (getTown()->buildings.at(buildingID)->warMachine == warMachine)
 			return true;
 
 	if (builtBuildings.count(BuildingID::BLACKSMITH) &&
-	   town->warMachineDeprecated.hasValue() &&
-	   town->warMachineDeprecated.toCreature()->warMachine == warMachine)
+	   getTown()->warMachineDeprecated.hasValue() &&
+	   getTown()->warMachineDeprecated.toCreature()->warMachine == warMachine)
 		return true;
 
 	return false;
@@ -1200,7 +1196,7 @@ GrowthInfo::Entry::Entry(int subID, const BuildingID & building, int _count): co
 {
 	MetaString formatter;
 	formatter.appendRawString("%s %+d");
-	formatter.replaceRawString((*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated());
+	formatter.replaceRawString(FactionID(subID).toFaction()->town->buildings.at(building)->getNameTranslated());
 	formatter.replacePositiveNumber(count);
 
 	description = formatter.toString();

+ 17 - 11
lib/mapObjects/CGTownInstance.h

@@ -50,6 +50,7 @@ struct DLL_LINKAGE GrowthInfo
 
 class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
 {
+	friend class CTownInstanceConstructor;
 	std::string nameTextId; // name of town
 
 	std::map<BuildingID, TownRewardableBuildingInstance*> convertOldBuildings(std::vector<TownRewardableBuildingInstance*> oldVector);
@@ -59,7 +60,6 @@ public:
 	enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
 
 	CTownAndVisitingHero townAndVis;
-	const CTown * town;
 	si32 built; //how many buildings has been built this turn
 	si32 destroyed; //how many buildings has been destroyed this turn
 	ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
@@ -112,16 +112,21 @@ public:
 			rewardableBuildings = convertOldBuildings(oldVector);
 		}
 
-		if (h.saving)
+		if (h.version < Handler::Version::REMOVE_TOWN_PTR)
 		{
-			CFaction * faction = town ? town->faction : nullptr;
-			h & faction;
-		}
-		else
-		{
-			CFaction * faction = nullptr;
-			h & faction;
-			town = faction ? faction->town : nullptr;
+			CTown * town = nullptr;
+
+			if (h.saving)
+			{
+				CFaction * faction = town ? town->faction : nullptr;
+				h & faction;
+			}
+			else
+			{
+				CFaction * faction = nullptr;
+				h & faction;
+				town = faction ? faction->town : nullptr;
+			}
 		}
 
 		h & townAndVis;
@@ -213,9 +218,10 @@ public:
 	DamageRange getKeepDamageRange() const;
 
 	const CTown * getTown() const;
+	const CFaction * getFaction() const;
 
 	/// INativeTerrainProvider
-	FactionID getFaction() const override;
+	FactionID getFactionID() const override;
 	TerrainId getNativeTerrain() const override;
 
 	/// Returns ID of war machine that is produced by specified building or NONE if this is not built or if building does not produce war machines

+ 1 - 1
lib/mapObjects/CQuest.cpp

@@ -431,7 +431,7 @@ void CGSeerHut::setObjToKill()
 	
 	if(getCreatureToKill(true))
 	{
-		quest->stackToKill = getCreatureToKill(false)->getCreature();
+		quest->stackToKill = getCreatureToKill(false)->getCreatureID();
 		assert(quest->stackToKill != CreatureID::NONE);
 		quest->stackDirection = checkDirection();
 	}

+ 2 - 2
lib/mapObjects/TownBuildingInstance.cpp

@@ -73,14 +73,14 @@ TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance *
 
 void TownRewardableBuildingInstance::initObj(vstd::RNG & rand)
 {
-	assert(town && town->town);
+	assert(town && town->getTown());
 	configuration = generateConfiguration(rand);
 }
 
 Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(vstd::RNG & rand) const
 {
 	Rewardable::Configuration result;
-	auto building = town->town->buildings.at(getBuildingType());
+	auto building = town->getTown()->buildings.at(getBuildingType());
 
 	building->rewardableObjectInfo.configureObject(result, rand, cb);
 	for(auto & rewardInfo : result.info)

+ 1 - 1
lib/mapping/CMap.cpp

@@ -302,7 +302,7 @@ void CMap::calculateGuardingGreaturePositions()
 CGHeroInstance * CMap::getHero(HeroTypeID heroID)
 {
 	for(auto & elem : heroesOnMap)
-		if(elem->getHeroType() == heroID)
+		if(elem->getHeroTypeID() == heroID)
 			return elem;
 	return nullptr;
 }

+ 6 - 6
lib/mapping/MapFormatH3M.cpp

@@ -896,7 +896,7 @@ void CMapLoaderH3M::readPredefinedHeroes()
 		}
 		map->predefinedHeroes.emplace_back(hero);
 
-		logGlobal->debug("Map '%s': Hero predefined in map: %s", mapName, VLC->heroh->getById(hero->getHeroType())->getJsonKey());
+		logGlobal->debug("Map '%s': Hero predefined in map: %s", mapName, hero->getHeroType()->getJsonKey());
 	}
 }
 
@@ -913,7 +913,7 @@ void CMapLoaderH3M::loadArtifactsOfHero(CGHeroInstance * hero)
 
 	if(!hero->artifactsWorn.empty() || !hero->artifactsInBackpack.empty())
 	{
-		logGlobal->debug("Hero %d at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set...", hero->getHeroType().getNum(), hero->anchorPos().toString());
+		logGlobal->debug("Hero %d at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set...", hero->getHeroTypeID().getNum(), hero->anchorPos().toString());
 
 		hero->artifactsInBackpack.clear();
 		while(!hero->artifactsWorn.empty())
@@ -1778,7 +1778,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
 
 	for(auto & elem : map->disposedHeroes)
 	{
-		if(elem.heroId == object->getHeroType())
+		if(elem.heroId == object->getHeroTypeID())
 		{
 			object->nameCustomTextId = elem.name;
 			object->customPortraitSource = elem.portrait;
@@ -1788,7 +1788,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
 
 	bool hasName = reader->readBool();
 	if(hasName)
-		object->nameCustomTextId = readLocalizedString(TextIdentifier("heroes", object->getHeroType().getNum(), "name"));
+		object->nameCustomTextId = readLocalizedString(TextIdentifier("heroes", object->getHeroTypeID().getNum(), "name"));
 
 	if(features.levelSOD)
 	{
@@ -1891,7 +1891,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
 			auto ps = object->getAllBonuses(Selector::type()(BonusType::PRIMARY_SKILL).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL)), nullptr);
 			if(ps->size())
 			{
-				logGlobal->debug("Hero %s has set primary skills twice (in map properties and on adventure map instance). Using the latter set...", object->getHeroType().getNum() );
+				logGlobal->debug("Hero %s has set primary skills twice (in map properties and on adventure map instance). Using the latter set...", object->getHeroTypeID().getNum() );
 				for(const auto & b : *ps)
 					object->removeBonus(b);
 			}
@@ -1904,7 +1904,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
 	}
 
 	if (object->subID != MapObjectSubID())
-		logGlobal->debug("Map '%s': Hero on map: %s at %s, owned by %s", mapName, VLC->heroh->getById(object->getHeroType())->getJsonKey(), mapPosition.toString(), object->getOwner().toString());
+		logGlobal->debug("Map '%s': Hero on map: %s at %s, owned by %s", mapName, object->getHeroType()->getJsonKey(), mapPosition.toString(), object->getOwner().toString());
 	else
 		logGlobal->debug("Map '%s': Hero on map: (random) at %s, owned by %s", mapName, mapPosition.toString(), object->getOwner().toString());
 

+ 4 - 6
lib/mapping/MapFormatJson.cpp

@@ -436,10 +436,8 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler)
 						if(hero->ID == Obj::HERO)
 						{
 							std::string temp;
-							if(hero->type)
-								temp = hero->type->getJsonKey();
-							else
-								temp = hero->getHeroType().toEntity(VLC)->getJsonKey();
+							if(hero->getHeroTypeID().hasValue())
+								temp = hero->getHeroType()->getJsonKey();
 
 							handler.serializeString("type", temp);
 						}
@@ -1154,10 +1152,10 @@ void CMapLoaderJson::readObjects()
 
 		auto * hero = dynamic_cast<const CGHeroInstance *>(object.get());
 
-		if (debugHeroesOnMap.count(hero->getHeroType()))
+		if (debugHeroesOnMap.count(hero->getHeroTypeID()))
 			logGlobal->error("Hero is already on the map at %s", hero->visitablePos().toString());
 
-		debugHeroesOnMap.insert(hero->getHeroType());
+		debugHeroesOnMap.insert(hero->getHeroTypeID());
 	}
 }
 

+ 2 - 2
lib/networkPacks/NetPacksLib.cpp

@@ -1355,7 +1355,7 @@ void NewStructures::applyGs(CGameState *gs)
 
 	for(const auto & id : bid)
 	{
-		assert(t->town->buildings.at(id) != nullptr);
+		assert(t->getTown()->buildings.at(id) != nullptr);
 		t->addBuilding(id);
 	}
 	t->updateAppearance();
@@ -1470,7 +1470,7 @@ void GiveHero::applyGs(CGameState *gs)
 
 	auto oldVisitablePos = h->visitablePos();
 	gs->map->removeBlockVisTiles(h,true);
-	h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front();
+	h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->getHeroClassID().getNum())->getTemplates().front();
 
 	h->setOwner(player);
 	h->setMovementPoints(h->movementPointsLimit(true));

+ 1 - 1
lib/pathfinder/CPathfinder.cpp

@@ -292,7 +292,7 @@ TeleporterTilesVector CPathfinderHelper::getTeleportExits(const PathNodeInfo & s
 	{
 		auto * town = dynamic_cast<const CGTownInstance *>(source.nodeObject);
 		assert(town);
-		if (town && town->getFaction() == FactionID::INFERNO)
+		if (town && town->getFactionID() == FactionID::INFERNO)
 		{
 			/// TODO: Find way to reuse CPlayerSpecificInfoCallback::getTownsInfo
 			/// This may be handy if we allow to use teleportation to friendly towns

+ 2 - 2
lib/rewardable/Limiter.cpp

@@ -173,10 +173,10 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
 	if(!players.empty() && !vstd::contains(players, hero->getOwner()))
 		return false;
 	
-	if(!heroes.empty() && !vstd::contains(heroes, hero->type->getId()))
+	if(!heroes.empty() && !vstd::contains(heroes, hero->getHeroTypeID()))
 		return false;
 	
-	if(!heroClasses.empty() && !vstd::contains(heroClasses, hero->type->heroClass->getId()))
+	if(!heroClasses.empty() && !vstd::contains(heroClasses, hero->getHeroClassID()))
 		return false;
 		
 	

+ 1 - 1
lib/rmg/modificators/ObjectManager.cpp

@@ -733,7 +733,7 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard)
 			continue;
 		if(!cre->getAIValue()) //bug #2681
 			continue;
-		if(!vstd::contains(zone.getMonsterTypes(), cre->getFaction()))
+		if(!vstd::contains(zone.getMonsterTypes(), cre->getFactionID()))
 			continue;
 		if((static_cast<si32>(cre->getAIValue() * (cre->ammMin + cre->ammMax) / 2) < strength) && (strength < static_cast<si32>(cre->getAIValue()) * 100)) //at least one full monster. size between average size of given stack and 100
 		{

+ 2 - 2
lib/rmg/modificators/TownPlacer.cpp

@@ -90,7 +90,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
 		
 		totalTowns++;
 		//register MAIN town of zone only
-		map.registerZone(town->getFaction());
+		map.registerZone(town->getFactionID());
 		
 		if(player.isValidPlayer()) //configure info for owning player
 		{
@@ -213,7 +213,7 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
 		{
 			//FIXME: discovered bug with small zones - getPos is close to map boarder and we have outOfMap exception
 			//register MAIN town of zone
-			map.registerZone(town->getFaction());
+			map.registerZone(town->getFactionID());
 			//first town in zone goes in the middle
 			placeMainTown(manager, *town);
 		}

+ 6 - 6
lib/rmg/modificators/TreasurePlacer.cpp

@@ -51,7 +51,7 @@ void TreasurePlacer::process()
 	// Add all native creatures
 	for(auto const & cre : VLC->creh->objects)
 	{
-		if(!cre->special && cre->getFaction() == zone.getTownType())
+		if(!cre->special && cre->getFactionID() == zone.getTownType())
 		{
 			creatures.push_back(cre.get());
 		}
@@ -192,7 +192,7 @@ void TreasurePlacer::addPrisons()
 			{
 				// Hero can be used again
 				auto* hero = dynamic_cast<CGHeroInstance*>(obj);
-				prisonHeroPlacer->restoreDrawnHero(hero->getHeroType());
+				prisonHeroPlacer->restoreDrawnHero(hero->getHeroTypeID());
 			};
 
 			oi.setTemplates(Obj::PRISON, 0, zone.getTerrainType());
@@ -236,9 +236,9 @@ void TreasurePlacer::addDwellings()
 				continue;
 
 			const auto * cre = creatures.front();
-			if(cre->getFaction() == zone.getTownType())
+			if(cre->getFactionID() == zone.getTownType())
 			{
-				auto nativeZonesCount = static_cast<float>(map.getZoneCount(cre->getFaction()));
+				auto nativeZonesCount = static_cast<float>(map.getZoneCount(cre->getFactionID()));
 				ObjectInfo oi(dwellingType, secondaryID);
 				setBasicProperties(oi, CompoundMapObjectID(dwellingType, secondaryID));
 
@@ -375,7 +375,7 @@ void TreasurePlacer::addPandoraBoxesWithCreatures()
 			return obj;
 		};
 		oi.setTemplates(Obj::PANDORAS_BOX, 0, zone.getTerrainType());
-		oi.value = static_cast<ui32>(creature->getAIValue() * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount()));
+		oi.value = static_cast<ui32>(creature->getAIValue() * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFactionID())) / map.getTotalZoneCount()));
 		oi.probability = 3;
 		if(!oi.templates.empty())
 			addObjectToRandomPool(oi);
@@ -555,7 +555,7 @@ void TreasurePlacer::addSeerHuts()
 			oi.destroyObject = destroyObject;
 			oi.probability = 3;
 			oi.setTemplates(Obj::SEER_HUT, randomAppearance, zone.getTerrainType());
-			oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFaction())) / map.getTotalZoneCount())) - 4000) / 3);
+			oi.value = static_cast<ui32>(((2 * (creature->getAIValue()) * creaturesAmount * (1 + static_cast<float>(map.getZoneCount(creature->getFactionID())) / map.getTotalZoneCount())) - 4000) / 3);
 			if (oi.value > zone.getMaxTreasureValue())
 			{
 				continue;

+ 1 - 1
lib/serializer/CSerializer.cpp

@@ -26,7 +26,7 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
 	registerVectoredType<CGObjectInstance, ObjectInstanceID>(&gs->map->objects,
 		[](const CGObjectInstance &obj){ return obj.id; });
 	registerVectoredType<CGHeroInstance, HeroTypeID>(&gs->map->allHeroes,
-		[](const CGHeroInstance &h){ return h.type->getId(); });
+		[](const CGHeroInstance &h){ return h.getHeroType()->getId(); });
 	registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances,
 		[](const CArtifactInstance &artInst){ return artInst.getId(); });
 	registerVectoredType<CQuest, si32>(&gs->map->quests,

+ 2 - 1
lib/serializer/ESerializationVersion.h

@@ -63,6 +63,7 @@ enum class ESerializationVersion : int32_t
 	REGION_LABEL, // 864 - labels for campaign regions
 	SPELL_RESEARCH, // 865 - spell research
 	LOCAL_PLAYER_STATE_DATA, // 866 - player state contains arbitrary client-side data
+	REMOVE_TOWN_PTR, // 867 - removed pointer to CTown from CGTownInstance
 
-	CURRENT = LOCAL_PLAYER_STATE_DATA
+	CURRENT = REMOVE_TOWN_PTR
 };

+ 1 - 1
lib/spells/effects/Moat.cpp

@@ -88,7 +88,7 @@ void Moat::convertBonus(const Mechanics * m, std::vector<Bonus> & converted) con
 
 		if(m->battle()->battleGetDefendedTown() && m->battle()->battleGetFortifications().hasMoat)
 		{
-			nb.sid = BonusSourceID(m->battle()->battleGetDefendedTown()->town->buildings.at(BuildingID::CITADEL)->getUniqueTypeID());
+			nb.sid = BonusSourceID(m->battle()->battleGetDefendedTown()->getTown()->buildings.at(BuildingID::CITADEL)->getUniqueTypeID());
 			nb.source = BonusSource::TOWN_STRUCTURE;
 		}
 		else

+ 1 - 1
lib/spells/effects/Summon.cpp

@@ -79,7 +79,7 @@ bool Summon::applicable(Problem & problem, const Mechanics * m) const
 
 				text.replaceNamePlural(elemental->creatureId());
 
-				if(caster->type->gender == EHeroGender::FEMALE)
+				if(caster->gender == EHeroGender::FEMALE)
 					text.replaceLocalString(EMetaText::GENERAL_TXT, 540);
 				else
 					text.replaceLocalString(EMetaText::GENERAL_TXT, 539);

+ 13 - 30
mapeditor/inspector/inspector.cpp

@@ -138,25 +138,11 @@ void Initializer::initialize(CGHeroInstance * o)
 		o->subID = 0;
 		o->tempOwner = PlayerColor::NEUTRAL;
 	}
-	
-	if(o->ID == Obj::HERO)
-	{
-		for(auto const & t : VLC->heroh->objects)
-		{
-			if(t->heroClass->getId() == HeroClassID(o->subID))
-			{
-				o->type = t.get();
-				break;
-			}
-		}
-	}
-	
-	if(o->type)
+
+	if(o->getHeroTypeID().hasValue())
 	{
-		//	o->type = VLC->heroh->objects.at(o->subID);
-		
-		o->gender = o->type->gender;
-		o->randomizeArmy(o->type->heroClass->faction);
+		o->gender = o->getHeroType()->gender;
+		o->randomizeArmy(o->getFactionID());
 	}
 }
 
@@ -304,7 +290,7 @@ void Inspector::updateProperties(CGHeroInstance * o)
 	auto isPrison = o->ID == Obj::PRISON;
 	addProperty("Owner", o->tempOwner, new OwnerDelegate(controller, isPrison), isPrison); //field is not editable for prison
 	addProperty<int>("Experience", o->exp, false);
-	addProperty("Hero class", o->type ? o->type->heroClass->getNameTranslated() : "", true);
+	addProperty("Hero class", o->getHeroClassID().hasValue() ? o->getHeroClass()->getNameTranslated() : "", true);
 	
 	{ //Gender
 		auto * delegate = new InspectorDelegate;
@@ -319,20 +305,20 @@ void Inspector::updateProperties(CGHeroInstance * o)
 	addProperty("Skills", PropertyEditorPlaceholder(), delegate, false);
 	addProperty("Spells", PropertyEditorPlaceholder(), new HeroSpellDelegate(*o), false);
 	
-	if(o->type || o->ID == Obj::PRISON)
+	if(o->getHeroTypeID().hasValue() || o->ID == Obj::PRISON)
 	{ //Hero type
 		auto * delegate = new InspectorDelegate;
-		for(int i = 0; i < VLC->heroh->objects.size(); ++i)
+		for(const auto & heroPtr : VLC->heroh->objects)
 		{
-			if(controller.map()->allowedHeroes.count(HeroTypeID(i)) != 0)
+			if(controller.map()->allowedHeroes.count(heroPtr->getId()) != 0)
 			{
-				if(o->ID == Obj::PRISON || (o->type && VLC->heroh->objects[i]->heroClass->getIndex() == o->type->heroClass->getIndex()))
+				if(o->ID == Obj::PRISON || heroPtr->heroClass->getIndex() == o->getHeroClassID())
 				{
-					delegate->options.push_back({QObject::tr(VLC->heroh->objects[i]->getNameTranslated().c_str()), QVariant::fromValue(VLC->heroh->objects[i]->getId().getNum())});
+					delegate->options.push_back({QObject::tr(heroPtr->getNameTranslated().c_str()), QVariant::fromValue(heroPtr->getIndex())});
 				}
 			}
 		}
-		addProperty("Hero type", o->type ? o->type->getNameTranslated() : "", delegate, false);
+		addProperty("Hero type", o->getHeroTypeID().hasValue() ? o->getHeroType()->getNameTranslated() : "", delegate, false);
 	}
 }
 
@@ -706,13 +692,10 @@ void Inspector::setProperty(CGHeroInstance * o, const QString & key, const QVari
 		for(auto const & t : VLC->heroh->objects)
 		{
 			if(t->getId() == value.toInt())
-			{
 				o->subID = value.toInt();
-				o->type = t.get();
-			}
 		}
-		o->gender = o->type->gender;
-		o->randomizeArmy(o->type->heroClass->faction);
+		o->gender = o->getHeroType()->gender;
+		o->randomizeArmy(o->getHeroType()->heroClass->faction);
 		updateProperties(); //updating other properties after change
 	}
 }

+ 1 - 1
mapeditor/inspector/townbuildingswidget.cpp

@@ -331,7 +331,7 @@ void TownBuildingsDelegate::setEditorData(QWidget *editor, const QModelIndex &in
 {
 	if(auto * ed = qobject_cast<TownBuildingsWidget *>(editor))
 	{
-		auto * ctown = town.town;
+		auto * ctown = town.getTown();
 		if(!ctown)
 			ctown = VLC->townh->randomTown;
 		if(!ctown)

+ 2 - 2
mapeditor/inspector/towneventdialog.cpp

@@ -100,7 +100,7 @@ void TownEventDialog::initResources()
 
 void TownEventDialog::initBuildings()
 {
-	auto * ctown = town.town;
+	auto * ctown = town.getTown();
 	if (!ctown)
 		ctown = VLC->townh->randomTown;
 	if (!ctown)
@@ -156,7 +156,7 @@ QStandardItem * TownEventDialog::addBuilding(const CTown& ctown, BuildingID buil
 void TownEventDialog::initCreatures()
 {
 	auto creatures = params.value("creatures").toList();
-	auto * ctown = town.town;
+	auto * ctown = town.getTown();
 	if (!ctown)
 		ui->creaturesTable->setRowCount(GameConstants::CREATURES_PER_TOWN);
 	else 

+ 1 - 5
mapeditor/mapcontroller.cpp

@@ -138,7 +138,7 @@ void MapController::repairMap(CMap * map) const
 
 			// FIXME: How about custom scenarios where defeated hero cannot be hired again?
 
-			map->allowedHeroes.insert(nih->getHeroType());
+			map->allowedHeroes.insert(nih->getHeroTypeID());
 
 			auto const & type = VLC->heroh->objects[nih->subID];
 			assert(type->heroClass);
@@ -154,10 +154,6 @@ void MapController::repairMap(CMap * map) const
 				nih->subTypeName = "prison";
 				nih->subID = 0;
 			}
-			
-			if(obj->ID != Obj::RANDOM_HERO)
-				nih->type = type.get();
-			
 			if(nih->ID == Obj::HERO) //not prison
 				nih->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
 			//fix spellbook

+ 2 - 4
mapeditor/playerparams.cpp

@@ -77,12 +77,10 @@ PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent)
 	{
 		if(auto town = dynamic_cast<CGTownInstance*>(controller.map()->objects[i].get()))
 		{
-			auto * ctown = town->town;
+			auto * ctown = town->getTown();
 			if(!ctown)
-			{
 				ctown = VLC->townh->randomTown;
-				town->town = ctown;
-			}
+
 			if(ctown && town->getOwner().getNum() == playerColor)
 			{
 				if(playerInfo.hasMainTown && playerInfo.posOfMainTown == town->pos)

+ 5 - 5
mapeditor/validator.cpp

@@ -122,13 +122,13 @@ std::set<Validator::Issue> Validator::validate(const CMap * map)
 
 					++amountOfHeroes[ins->getOwner()];
 				}
-				if(ins->type)
+				if(ins->getHeroTypeID().hasValue())
 				{
-					if(map->allowedHeroes.count(ins->getHeroType()) == 0)
-						issues.insert({ tr("Hero %1 is prohibited by map settings").arg(ins->type->getNameTranslated().c_str()), false });
+					if(map->allowedHeroes.count(ins->getHeroTypeID()) == 0)
+						issues.insert({ tr("Hero %1 is prohibited by map settings").arg(ins->getHeroType()->getNameTranslated().c_str()), false });
 					
-					if(!allHeroesOnMap.insert(ins->type).second)
-						issues.insert({ tr("Hero %1 has duplicate on map").arg(ins->type->getNameTranslated().c_str()), false });
+					if(!allHeroesOnMap.insert(ins->getHeroType()).second)
+						issues.insert({ tr("Hero %1 has duplicate on map").arg(ins->getHeroType()->getNameTranslated().c_str()), false });
 				}
 				else if(ins->ID != Obj::RANDOM_HERO)
 					issues.insert({ tr("Hero %1 has an empty type and must be removed").arg(ins->instanceName.c_str()), true });

+ 1 - 1
scripting/lua/api/Creature.cpp

@@ -46,7 +46,7 @@ const std::vector<CreatureProxy::CustomRegType> CreatureProxy::REGISTER_CUSTOM =
 	{"getLevel", LuaMethodWrapper<Creature, decltype(&Creature::getLevel), &Creature::getLevel>::invoke, false},
 	{"getGrowth", LuaMethodWrapper<Creature, decltype(&Creature::getGrowth), &Creature::getGrowth>::invoke, false},
 	{"getHorde", LuaMethodWrapper<Creature, decltype(&Creature::getHorde), &Creature::getHorde>::invoke, false},
-	{"getFaction", LuaMethodWrapper<Creature, decltype(&Creature::getFaction), &Creature::getFaction>::invoke, false},
+	{"getFactionID", LuaMethodWrapper<Creature, decltype(&Creature::getFactionID), &Creature::getFactionID>::invoke, false},
 
 	{"getBaseAttack", LuaMethodWrapper<Creature, decltype(&Creature::getBaseAttack), &Creature::getBaseAttack>::invoke, false},
 	{"getBaseDefense", LuaMethodWrapper<Creature, decltype(&Creature::getBaseDefense), &Creature::getBaseDefense>::invoke, false},

+ 22 - 22
server/CGameHandler.cpp

@@ -162,7 +162,7 @@ void CGameHandler::levelUpHero(const CGHeroInstance * hero)
 	hlu.player = hero->tempOwner;
 	hlu.heroId = hero->id;
 	hlu.primskill = primarySkill;
-	hlu.skills = hero->getLevelUpProposedSecondarySkills(heroPool->getHeroSkillsRandomGenerator(hero->getHeroType()));
+	hlu.skills = hero->getLevelUpProposedSecondarySkills(heroPool->getHeroSkillsRandomGenerator(hero->getHeroTypeID()));
 
 	if (hlu.skills.size() == 0)
 	{
@@ -553,7 +553,7 @@ void CGameHandler::init(StartInfo *si, Load::ProgressAccumulator & progressTrack
 	for (auto & elem : gs->map->allHeroes)
 	{
 		if(elem)
-			heroPool->getHeroSkillsRandomGenerator(elem->getHeroType()); // init RMG seed
+			heroPool->getHeroSkillsRandomGenerator(elem->getHeroTypeID()); // init RMG seed
 	}
 
 	reinitScripting();
@@ -569,12 +569,12 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa
 		return;
 	}
 
-	if (forced || town->creatures.at(town->town->creatures.size()).second.empty())//we need to change creature
+	if (forced || town->creatures.at(town->getTown()->creatures.size()).second.empty())//we need to change creature
 		{
 			SetAvailableCreatures ssi;
 			ssi.tid = town->id;
 			ssi.creatures = town->creatures;
-			ssi.creatures[town->town->creatures.size()].second.clear();//remove old one
+			ssi.creatures[town->getTown()->creatures.size()].second.clear();//remove old one
 
 			std::set<CreatureID> availableCreatures;
 			for (const auto & dwelling : p->getOwnedObjects())
@@ -590,13 +590,13 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa
 
 			if (clear)
 			{
-				ssi.creatures[town->town->creatures.size()].first = std::max(1, (creatureId.toEntity(VLC)->getGrowth())/2);
+				ssi.creatures[town->getTown()->creatures.size()].first = std::max(1, (creatureId.toEntity(VLC)->getGrowth())/2);
 			}
 			else
 			{
-				ssi.creatures[town->town->creatures.size()].first = creatureId.toEntity(VLC)->getGrowth();
+				ssi.creatures[town->getTown()->creatures.size()].first = creatureId.toEntity(VLC)->getGrowth();
 			}
-			ssi.creatures[town->town->creatures.size()].second.push_back(creatureId);
+			ssi.creatures[town->getTown()->creatures.size()].second.push_back(creatureId);
 			sendAndApply(ssi);
 		}
 }
@@ -657,7 +657,7 @@ void CGameHandler::onNewTurn()
 		PlayerColor player = t->tempOwner;
 
 		if(t->hasBuilt(BuildingID::GRAIL)
-			&& t->town->buildings.at(BuildingID::GRAIL)->height == CBuilding::HEIGHT_SKYSHIP)
+			&& t->getTown()->buildings.at(BuildingID::GRAIL)->height == CBuilding::HEIGHT_SKYSHIP)
 		{
 			// Skyship, probably easier to handle same as Veil of darkness
 			// do it every new day before veils
@@ -1048,7 +1048,7 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
 	if (((h->getOwner() != t->getOwner())
 		&& complain("Cannot teleport hero to another player"))
 
-	|| (from->town->faction->getId() != t->town->faction->getId()
+	|| (from->getFactionID() != t->getFactionID()
 		&& complain("Source town and destination town should belong to the same faction"))
 
 	|| ((!from || !from->hasBuilt(BuildingSubID::CASTLE_GATE))
@@ -1212,7 +1212,7 @@ void CGameHandler::visitCastleObjects(const CGTownInstance * t, std::vector<cons
 
 	for (auto & building : t->rewardableBuildings)
 	{
-		if (!t->town->buildings.at(building.first)->manualHeroVisit && t->hasBuilt(building.first))
+		if (!t->getTown()->buildings.at(building.first)->manualHeroVisit && t->hasBuilt(building.first))
 			buildingsToVisit.push_back(building.first);
 	}
 
@@ -2036,12 +2036,12 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	const CGTownInstance * t = getTown(tid);
 	if(!t)
 		COMPLAIN_RETF("No such town (ID=%s)!", tid);
-	if(!t->town->buildings.count(requestedID))
-		COMPLAIN_RETF("Town of faction %s does not have info about building ID=%s!", t->town->faction->getNameTranslated() % requestedID);
+	if(!t->getTown()->buildings.count(requestedID))
+		COMPLAIN_RETF("Town of faction %s does not have info about building ID=%s!", t->getFaction()->getNameTranslated() % requestedID);
 	if(t->hasBuilt(requestedID))
-		COMPLAIN_RETF("Building %s is already built in %s", t->town->buildings.at(requestedID)->getNameTranslated() % t->getNameTranslated());
+		COMPLAIN_RETF("Building %s is already built in %s", t->getTown()->buildings.at(requestedID)->getNameTranslated() % t->getNameTranslated());
 
-	const CBuilding * requestedBuilding = t->town->buildings.at(requestedID);
+	const CBuilding * requestedBuilding = t->getTown()->buildings.at(requestedID);
 
 	//Vector with future list of built building and buildings in auto-mode that are not yet built.
 	std::vector<const CBuilding*> remainingAutoBuildings;
@@ -2081,7 +2081,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 			int level = BuildingID::getLevelFromDwelling(buildingID);
 			int upgradeNumber = BuildingID::getUpgradedFromDwelling(buildingID);
 
-			if(upgradeNumber >= t->town->creatures.at(level).size())
+			if(upgradeNumber >= t->getTown()->creatures.at(level).size())
 			{
 				complain(boost::str(boost::format("Error encountered when building dwelling (bid=%s):"
 													"no creature found (upgrade number %d, level %d!")
@@ -2089,7 +2089,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 				return;
 			}
 
-			const CCreature * crea = t->town->creatures.at(level).at(upgradeNumber).toCreature();
+			const CCreature * crea = t->getTown()->creatures.at(level).at(upgradeNumber).toCreature();
 
 			SetAvailableCreatures ssi;
 			ssi.tid = t->id;
@@ -2099,7 +2099,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 			ssi.creatures[level].second.push_back(crea->getId());
 			sendAndApply(ssi);
 		}
-		if(t->town->buildings.at(buildingID)->subId == BuildingSubID::PORTAL_OF_SUMMONING)
+		if(t->getTown()->buildings.at(buildingID)->subId == BuildingSubID::PORTAL_OF_SUMMONING)
 		{
 			setPortalDwelling(t);
 		}
@@ -2110,9 +2110,9 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	{
 		auto isMageGuild = (buildingID <= BuildingID::MAGES_GUILD_5 && buildingID >= BuildingID::MAGES_GUILD_1);
 		auto isLibrary = isMageGuild ? false
-			: t->town->buildings.at(buildingID)->subId == BuildingSubID::EBuildingSubID::LIBRARY;
+			: t->getTown()->buildings.at(buildingID)->subId == BuildingSubID::EBuildingSubID::LIBRARY;
 
-		if(isMageGuild || isLibrary || (t->getFaction() == ETownType::CONFLUX && buildingID == BuildingID::GRAIL))
+		if(isMageGuild || isLibrary || (t->getFactionID() == ETownType::CONFLUX && buildingID == BuildingID::GRAIL))
 		{
 			if(t->visitingHero)
 				giveSpells(t,t->visitingHero);
@@ -2128,7 +2128,7 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	};
 
 	//Init the vectors
-	for(auto & build : t->town->buildings)
+	for(auto & build : t->getTown()->buildings)
 	{
 		if(t->hasBuilt(build.first))
 		{
@@ -2212,7 +2212,7 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
 	if(!t->hasBuilt(bid))
 		return false;
 
-	auto subID = t->town->buildings.at(bid)->subId;
+	auto subID = t->getTown()->buildings.at(bid)->subId;
 
 	if(subID == BuildingSubID::EBuildingSubID::BANK)
 	{
@@ -2224,7 +2224,7 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid)
 		return true;
 	}
 
-	if (t->rewardableBuildings.count(bid) && t->visitingHero && t->town->buildings.at(bid)->manualHeroVisit)
+	if (t->rewardableBuildings.count(bid) && t->visitingHero && t->getTown()->buildings.at(bid)->manualHeroVisit)
 	{
 		std::vector<BuildingID> buildingsToVisit;
 		std::vector<const CGHeroInstance*> visitors;

+ 14 - 14
server/processors/HeroPoolProcessor.cpp

@@ -47,8 +47,8 @@ TavernHeroSlot HeroPoolProcessor::selectSlotForRole(const PlayerColor & player,
 	// try to find "better" slot to overwrite
 	// we want to avoid overwriting retreated heroes when tavern still has slot with random hero
 	// as well as avoid overwriting surrendered heroes if we can overwrite retreated hero
-	auto roleLeft = heroesPool->getSlotRole(heroes[0]->getHeroType());
-	auto roleRight = heroesPool->getSlotRole(heroes[1]->getHeroType());
+	auto roleLeft = heroesPool->getSlotRole(heroes[0]->getHeroTypeID());
+	auto roleRight = heroesPool->getSlotRole(heroes[1]->getHeroTypeID());
 
 	if (roleLeft > roleRight)
 		return TavernHeroSlot::RANDOM;
@@ -70,7 +70,7 @@ void HeroPoolProcessor::onHeroSurrendered(const PlayerColor & color, const CGHer
 
 	sah.slotID = selectSlotForRole(color, sah.roleID);
 	sah.player = color;
-	sah.hid = hero->getHeroType();
+	sah.hid = hero->getHeroTypeID();
 	sah.replenishPoints = false;
 	gameHandler->sendAndApply(sah);
 }
@@ -82,9 +82,9 @@ void HeroPoolProcessor::onHeroEscaped(const PlayerColor & color, const CGHeroIns
 
 	sah.slotID = selectSlotForRole(color, sah.roleID);
 	sah.player = color;
-	sah.hid = hero->getHeroType();
+	sah.hid = hero->getHeroTypeID();
 	sah.army.clearSlots();
-	sah.army.setCreature(SlotID(0), hero->type->initialArmy.at(0).creature, 1);
+	sah.army.setCreature(SlotID(0), hero->getHeroType()->initialArmy.at(0).creature, 1);
 	sah.replenishPoints = false;
 
 	gameHandler->sendAndApply(sah);
@@ -112,7 +112,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
 
 	if (newHero)
 	{
-		sah.hid = newHero->getHeroType();
+		sah.hid = newHero->getHeroTypeID();
 
 		if (giveArmy)
 		{
@@ -123,7 +123,7 @@ void HeroPoolProcessor::selectNewHeroForSlot(const PlayerColor & color, TavernHe
 		{
 			sah.roleID = TavernSlotRole::SINGLE_UNIT;
 			sah.army.clearSlots();
-			sah.army.setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1);
+			sah.army.setCreature(SlotID(0), newHero->getHeroType()->initialArmy[0].creature, 1);
 		}
 	}
 	else
@@ -208,7 +208,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 
 	for(const auto & hero : recruitableHeroes)
 	{
-		if(hero->getHeroType() == heroToRecruit)
+		if(hero->getHeroTypeID() == heroToRecruit)
 			recruitedHero = hero;
 	}
 
@@ -221,7 +221,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
 
 	HeroRecruited hr;
 	hr.tid = mapObject->id;
-	hr.hid = recruitedHero->getHeroType();
+	hr.hid = recruitedHero->getHeroTypeID();
 	hr.player = player;
 	hr.tile = recruitedHero->convertFromVisitablePos(targetPos );
 	if(gameHandler->getTile(targetPos)->isWater() && !recruitedHero->boat)
@@ -258,14 +258,14 @@ std::vector<const CHeroClass *> HeroPoolProcessor::findAvailableClassesFor(const
 
 	for(const auto & elem : heroesPool->unusedHeroesFromPool())
 	{
-		if (vstd::contains(result, elem.second->type->heroClass))
+		if (vstd::contains(result, elem.second->getHeroClass()))
 			continue;
 
 		bool heroAvailable = heroesPool->isHeroAvailableFor(elem.first, player);
-		bool heroClassBanned = elem.second->type->heroClass->tavernProbability(factionID) == 0;
+		bool heroClassBanned = elem.second->getHeroClass()->tavernProbability(factionID) == 0;
 
 		if(heroAvailable && !heroClassBanned)
-			result.push_back(elem.second->type->heroClass);
+			result.push_back(elem.second->getHeroClass());
 	}
 
 	return result;
@@ -282,7 +282,7 @@ std::vector<CGHeroInstance *> HeroPoolProcessor::findAvailableHeroesFor(const Pl
 		assert(!vstd::contains(result, elem.second));
 
 		bool heroAvailable = heroesPool->isHeroAvailableFor(elem.first, player);
-		bool heroClassMatches = elem.second->type->heroClass == heroClass;
+		bool heroClassMatches = elem.second->getHeroClass() == heroClass;
 
 		if(heroAvailable && heroClassMatches)
 			result.push_back(elem.second);
@@ -318,7 +318,7 @@ const CHeroClass * HeroPoolProcessor::pickClassFor(bool isNative, const PlayerCo
 			continue;
 
 		bool hasSameClass = vstd::contains_if(currentTavern, [&](const CGHeroInstance * hero){
-			return hero->type->heroClass == heroClass;
+			return hero->getHeroClass() == heroClass;
 		});
 
 		if (hasSameClass)

+ 5 - 5
server/processors/NewTurnProcessor.cpp

@@ -95,10 +95,10 @@ void NewTurnProcessor::handleTownEvents(const CGTownInstance * town)
 			// 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress
 			// 2. Building was not built yet
 			// othervice, silently ignore / skip it
-			if (town->town->buildings.count(i) && !town->hasBuilt(i))
+			if (town->getTown()->buildings.count(i) && !town->hasBuilt(i))
 			{
 				gameHandler->buildStructure(town->id, i, true);
-				iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i));
+				iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFactionID(), i));
 			}
 		}
 
@@ -246,7 +246,7 @@ SetAvailableCreatures NewTurnProcessor::generateTownGrowth(const CGTownInstance
 	sac.tid = t->id;
 	sac.creatures = t->creatures;
 
-	for (int k=0; k < t->town->creatures.size(); k++)
+	for (int k=0; k < t->getTown()->creatures.size(); k++)
 	{
 		if (t->creatures.at(k).second.empty())
 			continue;
@@ -345,7 +345,7 @@ void NewTurnProcessor::updateNeutralTownGarrison(const CGTownInstance * t, int c
 	{
 		const auto * creature = slot.second->type;
 
-		if (creature->getFaction() != t->getFaction())
+		if (creature->getFactionID() != t->getFactionID())
 			continue;
 
 		if (creature->getLevel() != tierToGrow)
@@ -518,7 +518,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
 			{
 				newMonster.second = VLC->creh->pickRandomMonster(gameHandler->getRandomGenerator());
 			} while (VLC->creh->objects[newMonster.second] &&
-					(*VLC->townh)[VLC->creatures()->getById(newMonster.second)->getFaction()]->town == nullptr); // find first non neutral creature
+					(*VLC->townh)[VLC->creatures()->getById(newMonster.second)->getFactionID()]->town == nullptr); // find first non neutral creature
 
 			return { EWeekType::BONUS_GROWTH, newMonster.second};
 		}

+ 1 - 1
server/processors/PlayerMessageProcessor.cpp

@@ -370,7 +370,7 @@ void PlayerMessageProcessor::cheatBuildTown(PlayerColor player, const CGTownInst
 	if (!town)
 		return;
 
-	for (auto & build : town->town->buildings)
+	for (auto & build : town->getTown()->buildings)
 	{
 		if (!town->hasBuilt(build.first)
 			&& !build.second->getNameTranslated().empty()

+ 1 - 1
test/entity/CCreatureTest.cpp

@@ -100,7 +100,7 @@ TEST_F(CCreatureTest, DISABLED_JsonUpdate)
 
 	EXPECT_EQ(subject->getFightValue(), 2420);
 	EXPECT_EQ(subject->getLevel(), 6);
-	EXPECT_EQ(subject->getFaction().getNum(), 55);
+	EXPECT_EQ(subject->getFactionID().getNum(), 55);
 	EXPECT_TRUE(subject->isDoubleWide());
 }
 

+ 1 - 1
test/mock/mock_Creature.h

@@ -44,7 +44,7 @@ public:
 	MOCK_CONST_METHOD0(getLevel, int32_t());
 	MOCK_CONST_METHOD0(getGrowth, int32_t());
 	MOCK_CONST_METHOD0(getHorde, int32_t());
-	MOCK_CONST_METHOD0(getFaction, FactionID());
+	MOCK_CONST_METHOD0(getFactionID, FactionID());
 
 	MOCK_CONST_METHOD0(getBaseAttack, int32_t());
 	MOCK_CONST_METHOD0(getBaseDefense, int32_t());

+ 1 - 1
test/mock/mock_battle_Unit.h

@@ -82,7 +82,7 @@ public:
 	MOCK_CONST_METHOD1(willMove, bool(int));
 	MOCK_CONST_METHOD1(waited, bool(int));
 
-	MOCK_CONST_METHOD0(getFaction, FactionID());
+	MOCK_CONST_METHOD0(getFactionID, FactionID());
 
 	MOCK_CONST_METHOD1(battleQueuePhase, battle::BattlePhases::Type(int));