Browse Source

Reduce usage of implicit conversions to int

Ivan Savenko 6 months ago
parent
commit
829739da24
64 changed files with 185 additions and 165 deletions
  1. 2 2
      AI/Nullkiller/AIGateway.cpp
  2. 1 1
      AI/Nullkiller/Analyzers/BuildAnalyzer.cpp
  3. 1 1
      AI/Nullkiller/Analyzers/BuildAnalyzer.h
  4. 3 3
      AI/Nullkiller/Engine/Nullkiller.cpp
  5. 5 5
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  6. 2 2
      AI/Nullkiller/Engine/PriorityEvaluator.h
  7. 1 1
      AI/Nullkiller/Goals/BuildThis.cpp
  8. 1 1
      AI/Nullkiller/Goals/BuildThis.h
  9. 1 1
      AI/Nullkiller/Goals/CGoal.h
  10. 2 2
      AI/Nullkiller/Goals/CompleteQuest.cpp
  11. 1 1
      AI/Nullkiller/Goals/ExecuteHeroChain.cpp
  12. 1 1
      AI/Nullkiller/Markers/UnlockCluster.h
  13. 1 1
      AI/Nullkiller/Pathfinding/AINodeStorage.cpp
  14. 2 2
      AI/Nullkiller/Pathfinding/AINodeStorage.h
  15. 1 1
      AI/Nullkiller/Pathfinding/Actors.cpp
  16. 1 1
      AI/Nullkiller/Pathfinding/GraphPaths.cpp
  17. 1 1
      AI/VCAI/AIUtility.cpp
  18. 1 1
      AI/VCAI/Goals/Build.cpp
  19. 2 2
      AI/VCAI/Goals/BuildThis.h
  20. 4 4
      AI/VCAI/Goals/CollectRes.cpp
  21. 2 2
      AI/VCAI/Goals/CompleteQuest.cpp
  22. 2 2
      AI/VCAI/Goals/Win.cpp
  23. 1 1
      AI/VCAI/MapObjectsEvaluator.cpp
  24. 3 3
      AI/VCAI/Pathfinding/AINodeStorage.cpp
  25. 2 2
      AI/VCAI/VCAI.cpp
  26. 1 1
      client/NetPacksClient.cpp
  27. 3 3
      client/PlayerLocalState.cpp
  28. 1 1
      client/adventureMap/AdventureMapInterface.cpp
  29. 4 4
      client/adventureMap/TurnTimerWidget.cpp
  30. 3 3
      client/battle/BattleInterfaceClasses.cpp
  31. 1 1
      client/battle/BattleStacksController.cpp
  32. 4 4
      client/lobby/CBonusSelection.cpp
  33. 2 2
      client/lobby/OptionsTab.cpp
  34. 1 1
      client/lobby/RandomMapTab.cpp
  35. 4 4
      client/mapView/MapRenderer.cpp
  36. 1 1
      client/render/Graphics.cpp
  37. 1 1
      client/widgets/CComponent.cpp
  38. 1 1
      client/widgets/CreatureCostBox.cpp
  39. 1 1
      client/widgets/MiscWidgets.cpp
  40. 1 1
      client/widgets/markets/CAltarArtifacts.cpp
  41. 1 1
      client/widgets/markets/CArtifactsSelling.cpp
  42. 6 6
      client/windows/CCastleInterface.cpp
  43. 1 1
      client/windows/CCreatureWindow.cpp
  44. 1 1
      client/windows/CHeroWindow.cpp
  45. 2 2
      client/windows/CKingdomInterface.cpp
  46. 2 2
      client/windows/CPuzzleWindow.cpp
  47. 1 1
      client/windows/CQuestLog.cpp
  48. 3 3
      client/windows/CSpellWindow.cpp
  49. 14 13
      client/windows/GUIClasses.cpp
  50. 4 0
      lib/bonuses/BonusCache.h
  51. 1 1
      lib/campaign/CampaignHandler.cpp
  52. 49 36
      lib/constants/EntityIdentifiers.h
  53. 3 3
      lib/gameState/CGameStateCampaign.cpp
  54. 1 1
      lib/logging/VisualLogger.cpp
  55. 4 4
      lib/mapObjects/CGHeroInstance.cpp
  56. 2 2
      lib/mapObjects/TownBuildingInstance.cpp
  57. 1 1
      lib/mapping/CMap.cpp
  58. 2 2
      lib/rmg/CZonePlacer.cpp
  59. 1 1
      lib/rmg/modificators/TreasurePlacer.cpp
  60. 3 3
      server/CVCMIServer.cpp
  61. 1 1
      server/battles/BattleResultProcessor.cpp
  62. 2 3
      server/processors/NewTurnProcessor.cpp
  63. 6 3
      server/queries/MapQueries.cpp
  64. 1 1
      test/spells/effects/HealTest.cpp

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -325,7 +325,7 @@ void AIGateway::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID her
 
 void AIGateway::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val)
 {
-	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", static_cast<int>(which) % val);
+	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val);
 	NET_EVENT_HANDLER;
 }
 
@@ -1542,7 +1542,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
 
 				int toGive;
 				int toGet;
-				m->getOffer(res, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
+				m->getOffer(res.getNum(), g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
 				toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down
 				//TODO trade only as much as needed
 				if (toGive) //don't try to sell 0 resources

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

@@ -351,7 +351,7 @@ void BuildAnalyzer::updateDailyIncome()
 	}
 }
 
-bool BuildAnalyzer::hasAnyBuilding(int32_t alignment, BuildingID bid) const
+bool BuildAnalyzer::hasAnyBuilding(FactionID alignment, BuildingID bid) const
 {
 	for(auto tdi : developmentInfos)
 	{

+ 1 - 1
AI/Nullkiller/Analyzers/BuildAnalyzer.h

@@ -97,7 +97,7 @@ public:
 	TResources getDailyIncome() const { return dailyIncome; }
 	float getGoldPressure() const { return goldPressure; }
 	bool isGoldPressureHigh() const;
-	bool hasAnyBuilding(int32_t alignment, BuildingID bid) const;
+	bool hasAnyBuilding(FactionID alignment, BuildingID bid) const;
 
 private:
 	BuildingInfo getBuildingOrPrerequisite(

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

@@ -642,7 +642,7 @@ bool Nullkiller::handleTrading()
 {
 	bool haveTraded = false;
 	bool shouldTryToTrade = true;
-	int marketId = -1;
+	ObjectInstanceID marketId;
 	for (auto town : cb->getTownsInfo())
 	{
 		if (town->hasBuiltSomeTradeBuilding())
@@ -650,9 +650,9 @@ bool Nullkiller::handleTrading()
 			marketId = town->id;
 		}
 	}
-	if (marketId == -1)
+	if (!marketId.hasValue())
 		return false;
-	if (const CGObjectInstance* obj = cb->getObj(ObjectInstanceID(marketId), false))
+	if (const CGObjectInstance* obj = cb->getObj(marketId, false))
 	{
 		if (const auto* m = dynamic_cast<const IMarket*>(obj))
 		{

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

@@ -380,7 +380,7 @@ float RewardEvaluator::getEnemyHeroStrategicalValue(const CGHeroInstance * enemy
 	return std::min(1.5f, objectValue * 0.9f + (1.5f - (1.5f / (1 + enemy->level))));
 }
 
-float RewardEvaluator::getResourceRequirementStrength(int resType) const
+float RewardEvaluator::getResourceRequirementStrength(GameResID resType) const
 {
 	TResources requiredResources = ai->buildAnalyzer->getResourcesRequiredNow();
 	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
@@ -396,7 +396,7 @@ float RewardEvaluator::getResourceRequirementStrength(int resType) const
 	return std::min(ratio, 1.0f);
 }
 
-float RewardEvaluator::getTotalResourceRequirementStrength(int resType) const
+float RewardEvaluator::getTotalResourceRequirementStrength(GameResID resType) const
 {
 	TResources requiredResources = ai->buildAnalyzer->getTotalResourcesRequired();
 	TResources dailyIncome = ai->buildAnalyzer->getDailyIncome();
@@ -987,12 +987,12 @@ public:
 		float totalPower = 0;
 
 		// Map to store the aggregated power of creatures by CreatureID
-		std::map<int, float> totalPowerByCreatureID;
+		std::map<CreatureID, float> totalPowerByCreatureID;
 
 		// Calculate hero power and total power by CreatureID
 		for (const auto & slot : hero->Slots())
 		{
-			int creatureID = slot.second->getCreatureID();
+			CreatureID creatureID = slot.second->getCreatureID();
 			float slotPower = slot.second->getPower();
 
 			// Add the power of this slot to the heroPower
@@ -1220,7 +1220,7 @@ public:
 		}
 		else if(bi.id >= BuildingID::MAGES_GUILD_1 && bi.id <= BuildingID::MAGES_GUILD_5)
 		{
-			evaluationContext.skillReward += 2 * (bi.id - BuildingID::MAGES_GUILD_1);
+			evaluationContext.skillReward += 2 * buildThis.town->spellsAtLevel(bi.id.getMagesGuildLevel(), false);
 			if (!alreadyOwn && evaluationContext.evaluator.ai->cb->canBuildStructure(buildThis.town, highestMageGuildPossible) != EBuildingState::FORBIDDEN)
 			{
 				for (auto hero : evaluationContext.evaluator.ai->cb->getHeroesInfo())

+ 2 - 2
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -38,11 +38,11 @@ public:
 	uint64_t getArmyGrowth(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
 	int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
 	float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const;
-	float getResourceRequirementStrength(int resType) const;
+	float getResourceRequirementStrength(GameResID resType) const;
 	float getResourceRequirementStrength(const TResources & res) const;
 	float getStrategicalValue(const CGObjectInstance * target, const CGHeroInstance * hero = nullptr) const;
 	float getConquestValue(const CGObjectInstance* target) const;
-	float getTotalResourceRequirementStrength(int resType) const;
+	float getTotalResourceRequirementStrength(GameResID resType) const;
 	float evaluateWitchHutSkillScore(const CGObjectInstance * hut, const CGHeroInstance * hero, HeroRole role) const;
 	float getSkillReward(const CGObjectInstance * target, const CGHeroInstance * hero, HeroRole role) const;
 	int32_t getGoldReward(const CGObjectInstance * target, const CGHeroInstance * hero) const;

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

@@ -29,7 +29,7 @@ BuildThis::BuildThis(BuildingID Bid, const CGTownInstance * tid)
 		tid,
 		nullptr);
 
-	bid = Bid;
+	bid = Bid.getNum();
 	town = tid;
 }
 

+ 1 - 1
AI/Nullkiller/Goals/BuildThis.h

@@ -34,7 +34,7 @@ namespace Goals
 		BuildThis(const BuildingInfo & buildingInfo, const TownDevelopmentInfo & townInfo) //should be private, but unit test uses it
 			: ElementarGoal(Goals::BUILD_STRUCTURE), buildingInfo(buildingInfo), townInfo(townInfo)
 		{
-			bid = buildingInfo.id;
+			bid = buildingInfo.id.getNum();
 			town = townInfo.town;
 		}
 		BuildThis(BuildingID Bid, const CGTownInstance * tid);

+ 1 - 1
AI/Nullkiller/Goals/CGoal.h

@@ -94,7 +94,7 @@ namespace Goals
 		bool isObjectAffected(ObjectInstanceID id) const override
 		{
 			return (AbstractGoal::hero && AbstractGoal::hero->id == id)
-				|| AbstractGoal::objid == id
+				|| AbstractGoal::objid == id.getNum()
 				|| (AbstractGoal::town && AbstractGoal::town->id == id);
 		}
 

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

@@ -87,7 +87,7 @@ uint64_t CompleteQuest::getHash() const
 		return q.getObject(cb)->subID;
 	}
 
-	return q.getObject(cb)->id;
+	return q.getObject(cb)->id.getNum();
 }
 
 std::string CompleteQuest::questToString() const
@@ -129,7 +129,7 @@ TGoalVec CompleteQuest::missionArt(const Nullkiller * ai) const
 
 	for(auto art : q.getQuest(cb)->mission.artifacts)
 	{
-		solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art)));
+		solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art.getNum())));
 	}
 
 	return solutions;

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

@@ -68,7 +68,7 @@ std::vector<ObjectInstanceID> ExecuteHeroChain::getAffectedObjects() const
 
 bool ExecuteHeroChain::isObjectAffected(ObjectInstanceID id) const
 {
-	if(chainPath.targetHero->id == id || objid == id)
+	if(chainPath.targetHero->id == id || objid == id.getNum())
 		return true;
 
 	for(auto & node : chainPath.nodes)

+ 1 - 1
AI/Nullkiller/Markers/UnlockCluster.h

@@ -34,7 +34,7 @@ namespace Goals
 		{
 			tile = cluster->blocker->visitablePos();
 			hero = pathToCenter.targetHero;
-			objid = cluster->blocker->id;
+			objid = cluster->blocker->id.getNum();
 		}
 
 		bool operator==(const UnlockCluster & other) const override;

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

@@ -180,7 +180,7 @@ std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
 	const EPathfindingLayer layer, 
 	const ChainActor * actor)
 {
-	int bucketIndex = ((uintptr_t)actor + static_cast<uint32_t>(layer)) % ai->settings->getPathfinderBucketsCount();
+	int bucketIndex = ((uintptr_t)actor + layer.getNum()) % ai->settings->getPathfinderBucketsCount();
 	int bucketOffset = bucketIndex * ai->settings->getPathfinderBucketSize();
 	auto chains = nodes.get(pos);
 

+ 2 - 2
AI/Nullkiller/Pathfinding/AINodeStorage.h

@@ -286,12 +286,12 @@ public:
 
 	inline EPathAccessibility getAccessibility(const int3 & tile, EPathfindingLayer layer) const
 	{
-		return (*this->accessibility)[tile.z][tile.x][tile.y][layer];
+		return (*this->accessibility)[tile.z][tile.x][tile.y][layer.getNum()];
 	}
 
 	inline void resetTile(const int3 & tile, EPathfindingLayer layer, EPathAccessibility tileAccessibility)
 	{
-		(*this->accessibility)[tile.z][tile.x][tile.y][layer] = tileAccessibility;
+		(*this->accessibility)[tile.z][tile.x][tile.y][layer.getNum()] = tileAccessibility;
 	}
 
 	inline int getBucket(const ChainActor * actor) const

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

@@ -75,7 +75,7 @@ int ChainActor::maxMovePoints(CGPathNode::ELayer layer)
 		throw std::logic_error("Asking movement points for static actor");
 #endif
 
-	return hero->movementPointsLimit(layer);
+	return hero->movementPointsLimit(layer != EPathfindingLayer::SAIL);
 }
 
 std::string ChainActor::toString() const

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

@@ -59,7 +59,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
 	graph.copyFrom(*ai->baseGraph);
 	graph.connectHeroes(ai);
 
-	visualKey = std::to_string(ai->playerID) + ":" + targetHero->getNameTranslated();
+	visualKey = std::to_string(ai->playerID.getNum()) + ":" + targetHero->getNameTranslated();
 	pathNodes.clear();
 
 	GraphNodeComparer cmp(pathNodes);

+ 1 - 1
AI/VCAI/AIUtility.cpp

@@ -204,7 +204,7 @@ bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should
 	if(!object)
 		return false;
 
-	if(object->id != Obj::BORDER_GATE)
+	if(object->ID != Obj::BORDER_GATE)
 		return false;
 
 	auto gate = dynamic_cast<const CGKeys *>(object);

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

@@ -36,7 +36,7 @@ TGoalVec Build::getAllPossibleSubgoals()
 		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.has_value())
 		{
 			auto potentialBuilding = expensiveBuilding.value();
-			switch(expensiveBuilding.value().bid)
+			switch(expensiveBuilding.value().bid.toEnum())
 			{
 			case BuildingID::TOWN_HALL:
 			case BuildingID::CITY_HALL:

+ 2 - 2
AI/VCAI/Goals/BuildThis.h

@@ -27,14 +27,14 @@ namespace Goals
 		BuildThis(BuildingID Bid, const CGTownInstance * tid)
 			: CGoal(Goals::BUILD_STRUCTURE)
 		{
-			bid = Bid;
+			bid = Bid.getNum();
 			town = tid;
 			priority = 1;
 		}
 		BuildThis(BuildingID Bid)
 			: CGoal(Goals::BUILD_STRUCTURE)
 		{
-			bid = Bid;
+			bid = Bid.getNum();
 			priority = 1;
 		}
 		TGoalVec getAllPossibleSubgoals() override

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

@@ -41,7 +41,7 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
 		switch (obj->ID.num)
 		{
 		case Obj::TREASURE_CHEST:
-			return resID == GameResID(EGameResID::GOLD);
+			return resID == GameResID(EGameResID::GOLD).getNum();
 			break;
 		case Obj::RESOURCE:
 			return dynamic_cast<const CGResource*>(obj)->resourceID() == GameResID(resID);
@@ -62,13 +62,13 @@ TGoalVec CollectRes::getAllPossibleSubgoals()
 			}
 			break;
 		case Obj::MYSTICAL_GARDEN:
-			if ((resID != GameResID(EGameResID::GOLD)) && (resID != GameResID(EGameResID::GEMS)))
+			if (resID != GameResID(EGameResID::GOLD).getNum() && resID != GameResID(EGameResID::GEMS).getNum())
 				return false;
 			break;
 		case Obj::WATER_WHEEL:
 		case Obj::LEAN_TO:
 		case Obj::WAGON:
-			if (resID != GameResID(EGameResID::GOLD))
+			if (resID != GameResID(EGameResID::GOLD).getNum())
 				return false;
 			break;
 		default:
@@ -177,7 +177,7 @@ TSubgoal CollectRes::whatToDoToTrade()
 				continue;
 			int toGive = -1;
 			int toReceive = -1;
-			m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
+			m->getOffer(i.getNum(), resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
 			assert(toGive > 0 && toReceive > 0);
 			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
 		}

+ 2 - 2
AI/VCAI/Goals/CompleteQuest.cpp

@@ -146,7 +146,7 @@ TGoalVec CompleteQuest::missionArt() const
 
 	for(auto art : q.getQuest(cb)->mission.artifacts)
 	{
-		solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport?
+		solutions.push_back(sptr(GetArtOfType(art.getNum()))); //TODO: transport?
 	}
 
 	return solutions;
@@ -174,7 +174,7 @@ TGoalVec CompleteQuest::missionArmy() const
 
 	for(auto creature : q.getQuest(cb)->mission.creatures)
 	{
-		solutions.push_back(sptr(GatherTroops(creature.getId(), creature.count)));
+		solutions.push_back(sptr(GatherTroops(creature.getId().getNum(), creature.count)));
 	}
 
 	return solutions;

+ 2 - 2
AI/VCAI/Goals/Win.cpp

@@ -51,7 +51,7 @@ TSubgoal Win::whatToDoToAchieve()
 		switch(goal.condition)
 		{
 		case EventCondition::HAVE_ARTIFACT:
-			return sptr(GetArtOfType(goal.objectType.as<ArtifactID>()));
+			return sptr(GetArtOfType(goal.objectType.as<ArtifactID>().getNum()));
 		case EventCondition::DESTROY:
 		{
 			if(goal.objectID != ObjectInstanceID::NONE)
@@ -151,7 +151,7 @@ TSubgoal Win::whatToDoToAchieve()
 			//save?
 			return sptr(CollectRes(goal.objectType.as<GameResID>(), goal.value));
 		case EventCondition::HAVE_CREATURES:
-			return sptr(GatherTroops(goal.objectType.as<CreatureID>(), goal.value));
+			return sptr(GatherTroops(goal.objectType.as<CreatureID>().getNum(), goal.value));
 		case EventCondition::TRANSPORT:
 		{
 			//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it

+ 1 - 1
AI/VCAI/MapObjectsEvaluator.cpp

@@ -68,7 +68,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->getHeroClassID());
+		return getObjectValue(obj->ID.getNum(), hero->getHeroClassID().getNum());
 	}
 	else if(obj->ID == Obj::PRISON)
 	{

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

@@ -88,7 +88,7 @@ bool AINodeStorage::isBattleNode(const CGPathNode * node) const
 
 std::optional<AIPathNode *> AINodeStorage::getOrCreateNode(const int3 & pos, const EPathfindingLayer layer, int chainNumber)
 {
-	auto chains = nodes[layer][pos.z][pos.x][pos.y];
+	auto chains = nodes[layer.getNum()][pos.z][pos.x][pos.y];
 
 	for(AIPathNode & node : chains)
 	{
@@ -125,7 +125,7 @@ void AINodeStorage::resetTile(const int3 & coord, EPathfindingLayer layer, EPath
 {
 	for(int i = 0; i < NUM_CHAINS; i++)
 	{
-		AIPathNode & heroNode = nodes[layer][coord.z][coord.x][coord.y][i];
+		AIPathNode & heroNode = nodes[layer.getNum()][coord.z][coord.x][coord.y][i];
 
 		heroNode.chainMask = 0;
 		heroNode.danger = 0;
@@ -326,7 +326,7 @@ bool AINodeStorage::hasBetterChain(const PathNodeInfo & source, CDestinationNode
 
 bool AINodeStorage::isTileAccessible(const int3 & pos, const EPathfindingLayer layer) const
 {
-	return nodes[layer][pos.z][pos.x][pos.y][0].action != EPathNodeAction::UNKNOWN;
+	return nodes[layer.getNum()][pos.z][pos.x][pos.y][0].action != EPathNodeAction::UNKNOWN;
 }
 
 std::vector<AIPath> AINodeStorage::getChainInfo(const int3 & pos, bool isOnLand) const

+ 2 - 2
AI/VCAI/VCAI.cpp

@@ -364,7 +364,7 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
 
 void VCAI::heroPrimarySkillChanged(const CGHeroInstance * hero, PrimarySkill which, si64 val)
 {
-	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", static_cast<int>(which) % val);
+	LOG_TRACE_PARAMS(logAi, "which '%i', val '%i'", which.getNum() % val);
 	NET_EVENT_HANDLER;
 }
 
@@ -2156,7 +2156,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
 
 				int toGive;
 				int toGet;
-				m->getOffer(res, g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
+				m->getOffer(res.getNum(), g.resID, toGive, toGet, EMarketMode::RESOURCE_RESOURCE);
 				toGive = static_cast<int>(toGive * (it->resVal / toGive)); //round down
 				//TODO trade only as much as needed
 				if (toGive) //don't try to sell 0 resources

+ 1 - 1
client/NetPacksClient.cpp

@@ -1072,7 +1072,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
 
 void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack)
 {
-	if(pack.id < 0) //artifact merchants globally
+	if(!pack.id.hasValue()) //artifact merchants globally
 	{
 		callAllInterfaces(cl, &IGameEventsReceiver::availableArtifactsChanged, nullptr);
 	}

+ 3 - 3
client/PlayerLocalState.cpp

@@ -315,14 +315,14 @@ void PlayerLocalState::serialize(JsonNode & dest) const
 	for (auto const * town : ownedTowns)
 	{
 		JsonNode record;
-		record["id"].Integer() = town->id;
+		record["id"].Integer() = town->id.getNum();
 		dest["towns"].Vector().push_back(record);
 	}
 
 	for (auto const * hero : wanderingHeroes)
 	{
 		JsonNode record;
-		record["id"].Integer() = hero->id;
+		record["id"].Integer() = hero->id.getNum();
 		if (vstd::contains(sleepingHeroes, hero))
 			record["sleeping"].Bool() = true;
 
@@ -340,7 +340,7 @@ void PlayerLocalState::serialize(JsonNode & dest) const
 	dest["spellbook"]["tabAdvmap"].Integer() = spellbookSettings.spellbookLastTabAdvmap;
 
 	if (currentSelection)
-		dest["currentSelection"].Integer() = currentSelection->id;
+		dest["currentSelection"].Integer() = currentSelection->id.getNum();
 }
 
 void PlayerLocalState::deserialize(const JsonNode & source)

+ 1 - 1
client/adventureMap/AdventureMapInterface.cpp

@@ -606,7 +606,7 @@ void AdventureMapInterface::onTileHovered(const int3 &targetPosition)
 
 	if(spellBeingCasted)
 	{
-		switch(spellBeingCasted->id)
+		switch(spellBeingCasted->id.toEnum())
 		{
 		case SpellID::SCUTTLE_BOAT:
 			if(isValidAdventureSpellTarget(targetPosition))

+ 4 - 4
client/adventureMap/TurnTimerWidget.cpp

@@ -55,18 +55,18 @@ TurnTimerWidget::TurnTimerWidget(const Point & position, PlayerColor player)
 		pos.w = 76;
 
 		pos.h += 20;
-		playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+		playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 
 		if (timers.battleTimer != 0)
 		{
 			pos.h += 20;
-			playerLabelsBattle[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+			playerLabelsBattle[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 		}
 
 		if (!timers.accumulatingUnitTimer && timers.unitTimer != 0)
 		{
 			pos.h += 20;
-			playerLabelsUnit[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+			playerLabelsUnit[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 		}
 
 		updateTextLabel(player, GAME->interface()->cb->getPlayerTurnTime(player));
@@ -87,7 +87,7 @@ TurnTimerWidget::TurnTimerWidget(const Point & position, PlayerColor player)
 				continue;
 
 			pos.h += 20;
-			playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player], "");
+			playerLabelsMain[player] = std::make_shared<CLabel>(pos.w / 2, pos.h - 10, FONT_BIG, ETextAlignment::CENTER, graphics->playerColors[player.getNum()], "");
 
 			updateTextLabel(player, GAME->interface()->cb->getPlayerTurnTime(player));
 		}

+ 3 - 3
client/battle/BattleInterfaceClasses.cpp

@@ -631,7 +631,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
 {
 	OBJECT_CONSTRUCTION;
 
-	icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), stack->creatureId() + 2, 0, 10, 6));
+	icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), stack->creatureId().getNum() + 2, 0, 10, 6));
 	labels.push_back(std::make_shared<CLabel>(10 + 58, 6 + 64, FONT_MEDIUM, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, TextOperations::formatMetric(stack->getCount(), 4)));
 
 	int damageMultiplier = 1;
@@ -705,7 +705,7 @@ void StackInfoBasicPanel::initializeData(const CStack * stack)
 
 			int duration = spellBonuses->front()->turnsRemain;
 
-			icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
+			icons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SpellInt"), effect.getNum() + 1, 0, firstPos.x + offset.x * printed, firstPos.y + offset.y * printed));
 			if(settings["general"]["enableUiEnhancements"].Bool())
 				labels.push_back(std::make_shared<CLabel>(firstPos.x + offset.x * printed + 46, firstPos.y + offset.y * printed + 36, EFonts::FONT_TINY, ETextAlignment::BOTTOMRIGHT, Colors::WHITE, std::to_string(duration)));
 			if(++printed >= 3 || (printed == 2 && spells.size() > 3)) // interface limit reached
@@ -837,7 +837,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
 			int yPos = 344 + static_cast<int>(step) * 97;
 			for(auto & elem : br.casualties[step])
 			{
-				auto creature = LIBRARY->creatures()->getByIndex(elem.first);
+				auto creature = elem.first.toEntity(LIBRARY);
 				if (creature->getId() == CreatureID::ARROW_TOWERS )
 					continue; // do not show destroyed towers in battle results
 

+ 1 - 1
client/battle/BattleStacksController.cpp

@@ -264,7 +264,7 @@ std::shared_ptr<IImage> BattleStacksController::getStackAmountBox(const CStack *
 
 	for(const auto & spellID : activeSpells)
 	{
-		auto positiveness = LIBRARY->spells()->getByIndex(spellID)->getPositiveness();
+		auto positiveness = spellID.toEntity(LIBRARY)->getPositiveness();
 		if(!boost::logic::indeterminate(positiveness))
 		{
 			if(positiveness)

+ 4 - 4
client/lobby/CBonusSelection.cpp

@@ -202,14 +202,14 @@ void CBonusSelection::createBonusesIcons()
 				}
 
 			}
-			assert(faction != -1);
+			assert(faction.hasValue());
 
 			BuildingID buildID;
 			if(getCampaign()->formatVCMI())
 				buildID = BuildingID(bonDescs[i].info1);
 			else
 				buildID = CBuildingHandler::campToERMU(bonDescs[i].info1, faction, std::set<BuildingID>());
-			picName = graphics->ERMUtoPicture[faction][buildID];
+			picName = graphics->ERMUtoPicture[faction.getNum()][buildID.getNum()];
 			picNumber = -1;
 
 			if(vstd::contains((*LIBRARY->townh)[faction]->town->buildings, buildID))
@@ -304,7 +304,7 @@ void CBonusSelection::createBonusesIcons()
 		}
 
 		case CampaignBonusType::HERO:
-			if(bonDescs[i].info2 == HeroTypeID::CAMP_RANDOM)
+			if(bonDescs[i].info2 == HeroTypeID::CAMP_RANDOM.getNum())
 			{
 				desc.appendLocalString(EMetaText::GENERAL_TXT, 720); // Start with random hero
 				picNumber = -1;
@@ -425,7 +425,7 @@ void CBonusSelection::startMap()
 	{
 		auto exitCb = []()
 		{
-			logGlobal->info("Starting scenario %d", static_cast<int>(GAME->server().campaignMap));
+			logGlobal->info("Starting scenario %d", GAME->server().campaignMap.getNum());
 			GAME->server().sendStartGame();
 		};
 

+ 2 - 2
client/lobby/OptionsTab.cpp

@@ -822,7 +822,7 @@ OptionsTab::HandicapWindow::HandicapWindow()
 		INCOME = 1000,
 		GROWTH = 2000,
 	};
-	auto columns = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS, Columns::INCOME, Columns::GROWTH};
+	auto columns = std::vector<int>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS, Columns::INCOME, Columns::GROWTH};
 
 	int i = 0;
 	for(auto & pInfo : SEL->getStartInfo()->playerInfos)
@@ -847,7 +847,7 @@ OptionsTab::HandicapWindow::HandicapWindow()
 				else if(isGrowth)
 					labels.push_back(std::make_shared<CLabel>(xPos, 38, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, LIBRARY->generaltexth->translate("core.genrltxt.194")));
 				else
-					anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
+					anim.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), GameResID(resource).getNum(), 0, 15 + xPos + (j == 0 ? 10 : 0), 35));
 			}
 
 			auto area = Rect(xPos, 60 + i * 30, j == 0 ? 60 : 50, 16);

+ 1 - 1
client/lobby/RandomMapTab.cpp

@@ -229,7 +229,7 @@ void RandomMapTab::updateMapInfoByHost()
 		playerInfo.team = team;
 		playerInfo.hasMainTown = true;
 		playerInfo.generateHeroAtMainTown = true;
-		mapInfo->mapHeader->players[player.first] = playerInfo;
+		mapInfo->mapHeader->players[player.first.getNum()] = playerInfo;
 		vstd::erase(availableColors, player.first);
 	}
 

+ 4 - 4
client/mapView/MapRenderer.cpp

@@ -152,7 +152,7 @@ void MapRendererTerrain::renderTile(IMapRendererContext & context, Canvas & targ
 {
 	const TerrainTile & mapTile = context.getMapTile(coordinates);
 
-	int32_t terrainIndex = mapTile.getTerrainID();
+	int32_t terrainIndex = mapTile.getTerrainID().getNum();
 	int32_t imageIndex = mapTile.terView;
 	int32_t rotationIndex = mapTile.extTileFlags % 4;
 
@@ -194,7 +194,7 @@ void MapRendererRiver::renderTile(IMapRendererContext & context, Canvas & target
 	if(!mapTile.hasRiver())
 		return;
 
-	int32_t terrainIndex = mapTile.getRiverID();
+	int32_t terrainIndex = mapTile.getRiverID().getNum();
 	int32_t imageIndex = mapTile.riverDir;
 	int32_t rotationIndex = (mapTile.extTileFlags >> 2) % 4;
 
@@ -231,7 +231,7 @@ void MapRendererRoad::renderTile(IMapRendererContext & context, Canvas & target,
 		const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove);
 		if(mapTileAbove.hasRoad())
 		{
-			int32_t terrainIndex = mapTileAbove.getRoadID();
+			int32_t terrainIndex = mapTileAbove.getRoadID().getNum();
 			int32_t imageIndex = mapTileAbove.roadDir;
 			int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4;
 
@@ -243,7 +243,7 @@ void MapRendererRoad::renderTile(IMapRendererContext & context, Canvas & target,
 	const TerrainTile & mapTile = context.getMapTile(coordinates);
 	if(mapTile.hasRoad())
 	{
-		int32_t terrainIndex = mapTile.getRoadID();
+		int32_t terrainIndex = mapTile.getRoadID().getNum();
 		int32_t imageIndex = mapTile.roadDir;
 		int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4;
 

+ 1 - 1
client/render/Graphics.cpp

@@ -129,7 +129,7 @@ void Graphics::setPlayerPalette(SDL_Palette * targetPalette, PlayerColor player)
 	if(player.isValidPlayer())
 	{
 		for(int i=0; i<32; ++i)
-			palette[i] = CSDL_Ext::toSDL(playerColorPalette[player][i]);
+			palette[i] = CSDL_Ext::toSDL(playerColorPalette[player.getNum()][i]);
 	}
 	else
 	{

+ 1 - 1
client/widgets/CComponent.cpp

@@ -199,7 +199,7 @@ size_t CComponent::getIndex() const
 		case ComponentType::LUCK:
 			return std::clamp(data.value.value_or(0) + 3, 0, 6);
 		case ComponentType::BUILDING:
-			return data.subType.as<BuildingTypeUniqueID>().getBuilding();
+			return data.subType.as<BuildingTypeUniqueID>().getBuilding().getNum();
 		case ComponentType::HERO_PORTRAIT:
 			return LIBRARY->heroTypes()->getById(data.subType.as<HeroTypeID>())->getIconIndex();
 		case ComponentType::FLAG:

+ 1 - 1
client/widgets/CreatureCostBox.cpp

@@ -39,7 +39,7 @@ void CreatureCostBox::createItems(TResources res)
 	TResources::nziterator iter(res);
 	while(iter.valid())
 	{
-		auto image = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), iter->resType);
+		auto image = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), iter->resType.getNum());
 		auto text = std::make_shared<CLabel>(15, 43, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, "0");
 
 		resources.insert(std::make_pair(iter->resType, std::make_pair(text, image)));

+ 1 - 1
client/widgets/MiscWidgets.cpp

@@ -449,7 +449,7 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
 	OBJECT_CONSTRUCTION;
 
 	const InfoAboutTown townInfo = InfoAboutTown(town, true);
-	int townId = town->id;
+	ObjectInstanceID townId = town->id;
 
 	//order of icons in def: fort, citadel, castle, no fort
 	size_t fortIndex = townInfo.fortLevel ? townInfo.fortLevel - 1 : 3;

+ 1 - 1
client/widgets/markets/CAltarArtifacts.cpp

@@ -189,7 +189,7 @@ CMarketBase::MarketShowcasesParams CAltarArtifacts::getShowcasesParams() const
 		return MarketShowcasesParams
 		{
 			std::nullopt,
-			ShowcaseParams {std::to_string(offerQty), LIBRARY->artifacts()->getByIndex(art->getTypeId())->getIconIndex()}
+			ShowcaseParams {std::to_string(offerQty), art->getType()->getIconIndex()}
 		};
 	return MarketShowcasesParams {std::nullopt, std::nullopt};
 }

+ 1 - 1
client/widgets/markets/CArtifactsSelling.cpp

@@ -91,7 +91,7 @@ void CArtifactsSelling::updateShowcases()
 	{
 		bidSelectedSlot->image->enable();
 		bidSelectedSlot->setID(art->getTypeId().num);
-		bidSelectedSlot->image->setFrame(LIBRARY->artifacts()->getByIndex(art->getTypeId())->getIconIndex());
+		bidSelectedSlot->image->setFrame(art->getTypeId().toEntity(LIBRARY)->getIconIndex());
 		bidSelectedSlot->subtitle->setText(std::to_string(bidQty));
 	}
 	else

+ 6 - 6
client/windows/CCastleInterface.cpp

@@ -894,7 +894,7 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu
 	}
 	else
 	{
-		switch(buildingToTest)
+		switch(buildingToTest.toEnum())
 		{
 		case BuildingID::MAGES_GUILD_1:
 		case BuildingID::MAGES_GUILD_2:
@@ -1065,7 +1065,7 @@ void CCastleBuildings::enterCastleGate(BuildingID building)
 		}
 	}
 
-	auto gateIcon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building);//will be deleted by selection window
+	auto gateIcon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building.getNum());//will be deleted by selection window
 	auto wnd = std::make_shared<CObjectListWindow>(availableTowns, gateIcon, LIBRARY->generaltexth->jktexts[40],
 		LIBRARY->generaltexth->jktexts[41], std::bind (&CCastleInterface::castleTeleport, GAME->interface()->castleInt, _1), 0, images);
 	wnd->onPopup = [availableTowns](int index) { CRClickPopup::createAndPush(GAME->interface()->cb->getObjInstance(ObjectInstanceID(availableTowns[index])), ENGINE->getCursorPosition()); };
@@ -1667,7 +1667,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->getTown()->clientInfo.buildingsIcons, building->bid, 0, 2, 2);
+	icon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building->bid.getNum(), 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);
@@ -1769,7 +1769,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 {
 	OBJECT_CONSTRUCTION;
 
-	icon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building->bid, 0, 125, 50);
+	icon = std::make_shared<CAnimImage>(town->getTown()->clientInfo.buildingsIcons, building->bid.getNum(), 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);
 
@@ -2170,7 +2170,7 @@ CMageGuildScreen::Scroll::Scroll(Point position, const CSpell *Spell, ObjectInst
 
 	addUsedEvents(LCLICK | SHOW_POPUP | HOVER);
 	pos += position;
-	image = std::make_shared<CAnimImage>(AnimationPath::builtin("SPELLSCR"), spell->id);
+	image = std::make_shared<CAnimImage>(AnimationPath::builtin("SPELLSCR"), spell->id.getNum());
 	pos = image->pos;
 }
 
@@ -2292,5 +2292,5 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
 	else
 		buy->block(true);
 
-	costIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::GOLD), 0, 148, 244);
+	costIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::GOLD).getNum(), 0, 148, 244);
 }

+ 1 - 1
client/windows/CCreatureWindow.cpp

@@ -879,7 +879,7 @@ void CStackWindow::initBonusesList()
 		bonusInfo.imagePath = info->stackNode->bonusToGraphics(b);
 		bonusInfo.bonusSource = b->source;
 
-		if(b->sid.getNum() != info->stackNode->getId() && b->propagator && b->propagator->getPropagatorType() == CBonusSystemNode::HERO) // Shows bonus with "propagator":"HERO" only at creature with bonus
+		if(b->sid.as<CreatureID>() != info->stackNode->getId() && b->propagator && b->propagator->getPropagatorType() == CBonusSystemNode::HERO) // Shows bonus with "propagator":"HERO" only at creature with bonus
 			continue;
 
 		//if it's possible to give any description or image for this kind of bonus

+ 1 - 1
client/windows/CHeroWindow.cpp

@@ -239,7 +239,7 @@ void CHeroWindow::update()
 	{
 		SecondarySkill skill = curHero->secSkills[g].first;
 		int	level = curHero->getSecSkillLevel(skill);
-		std::string skillName = LIBRARY->skillh->getByIndex(skill)->getNameTranslated();
+		std::string skillName = skill.toEntity(LIBRARY)->getNameTranslated();
 		std::string skillValue = LIBRARY->generaltexth->levels[level-1];
 
 		secSkillNames[g]->setText(skillName);

+ 2 - 2
client/windows/CKingdomInterface.cpp

@@ -297,7 +297,7 @@ int InfoBoxHeroData::getSubID()
 		return index;
 	case HERO_SECONDARY_SKILL:
 		if(hero->secSkills.size() > index)
-			return hero->secSkills[index].first;
+			return hero->secSkills[index].first.getNum();
 		else
 			return 0;
 	case HERO_SPECIAL:
@@ -353,7 +353,7 @@ std::string InfoBoxHeroData::getHoverText()
 		if (hero->secSkills.size() > index)
 		{
 			std::string level = LIBRARY->generaltexth->levels[hero->secSkills[index].second-1];
-			std::string skill = LIBRARY->skillh->getByIndex(hero->secSkills[index].first)->getNameTranslated();
+			std::string skill = hero->secSkills[index].first.toEntity(LIBRARY)->getNameTranslated();
 			return boost::str(boost::format(LIBRARY->generaltexth->heroscrn[21]) % level % skill);
 		}
 		else

+ 2 - 2
client/windows/CPuzzleWindow.cpp

@@ -48,9 +48,9 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio)
 	title = std::make_shared<CLabel>(700, 95, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, LIBRARY->generaltexth->allTexts[463]);
 	resDataBar = std::make_shared<CResDataBar>(ImagePath::builtin("ARESBAR.bmp"), 3, 575, 32, 2, 85, 85);
 
-	int faction = GAME->interface()->cb->getStartInfo()->playerInfos.find(GAME->interface()->playerID)->second.castle;
+	FactionID faction = GAME->interface()->cb->getStartInfo()->playerInfos.find(GAME->interface()->playerID)->second.castle;
 
-	auto & puzzleMap = (*LIBRARY->townh)[faction]->puzzleMap;
+	auto & puzzleMap = faction.toFaction()->puzzleMap;
 
 	for(auto & elem : puzzleMap)
 	{

+ 1 - 1
client/windows/CQuestLog.cpp

@@ -101,7 +101,7 @@ void CQuestMinimap::update()
 
 void CQuestMinimap::iconClicked()
 {
-	if(currentQuest->obj)
+	if(currentQuest->obj.hasValue())
 		adventureInt->centerOnTile(currentQuest->getObject(GAME->interface()->cb.get())->visitablePos());
 	//moveAdvMapSelection();
 }

+ 3 - 3
client/windows/CSpellWindow.cpp

@@ -278,7 +278,7 @@ void CSpellWindow::processSpells()
 
 		spell->forEachSchool([&sitesPerOurTab](const SpellSchool & school, bool & stop)
 		{
-			++sitesPerOurTab[school];
+			++sitesPerOurTab[school.getNum()];
 		});
 	}
 	if(sitesPerTabAdv[4] % spellsPerPage == 0)
@@ -729,7 +729,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
 		schoolLevel = owner->myHero->getSpellSchoolLevel(mySpell, &whichSchool);
 		auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero);
 
-		image->setFrame(mySpell->id);
+		image->setFrame(mySpell->id.getNum());
 		image->visible = true;
 
 		{
@@ -745,7 +745,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
 			schoolBorder.reset();
 			if (owner->selectedTab >= 4)
 			{
-				if (whichSchool.getNum() != SpellSchool())
+				if (whichSchool.hasValue())
 					schoolBorder = std::make_shared<CAnimImage>(schoolBorders.at(whichSchool.getNum()), schoolLevel);
 			}
 			else

+ 14 - 13
client/windows/GUIClasses.cpp

@@ -439,9 +439,9 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std
 
 	levelTitle = std::make_shared<CLabel>(192, 162, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, levelTitleText);
 
-	skillIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("PSKIL42"), static_cast<int>(pskill), 0, 174, 190);
+	skillIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("PSKIL42"), pskill.getNum(), 0, 174, 190);
 
-	skillValue = std::make_shared<CLabel>(192, 253, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->primarySkillNames[static_cast<int>(pskill)] + " +1");
+	skillValue = std::make_shared<CLabel>(192, 253, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->primarySkillNames[pskill.getNum()] + " +1");
 }
 
 void CLevelWindow::close()
@@ -765,8 +765,8 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat
 	goldCost = std::make_shared<CLabel>(118, 294, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, goldValue);
 	woodCost = std::make_shared<CLabel>(212, 294, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, woodValue);
 
-	goldPic = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::GOLD), 0, 100, 244);
-	woodPic = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::WOOD), 0, 196, 244);
+	goldPic = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::GOLD).getNum(), 0, 100, 244);
+	woodPic = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::WOOD).getNum(), 0, 196, 244);
 
 	quit = std::make_shared<CButton>(Point(224, 312), AnimationPath::builtin("ICANCEL"), CButton::tooltip(LIBRARY->generaltexth->allTexts[599]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_CANCEL);
 	build = std::make_shared<CButton>(Point(42, 312), AnimationPath::builtin("IBUY30"), CButton::tooltip(LIBRARY->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_ACCEPT);
@@ -953,7 +953,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, BuildingID bu
 	if(auto town = dynamic_cast<const CGTownInstance *>(_market))
 	{
 		auto faction = town->getTown()->faction->getId();
-		titlePic = std::make_shared<CAnimImage>((*LIBRARY->townh)[faction]->town->clientInfo.buildingsIcons, building);
+		titlePic = std::make_shared<CAnimImage>(faction.toFaction()->town->clientInfo.buildingsIcons, building.getNum());
 	}
 	else if(auto uni = dynamic_cast<const CGUniversity *>(_market); uni->appearance)
 	{
@@ -974,7 +974,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, BuildingID bu
 	std::vector<TradeItemBuy> goods = market->availableItemsIds(EMarketMode::RESOURCE_SKILL);
 
 	for(int i=0; i<goods.size(); i++)//prepare clickable items
-		items.push_back(std::make_shared<CItem>(this, goods[i].as<SecondarySkill>(), 54+i*104, 234));
+		items.push_back(std::make_shared<CItem>(this, goods[i].as<SecondarySkill>().getNum(), 54+i*104, 234));
 
 	cancel = std::make_shared<CButton>(Point(200, 313), AnimationPath::builtin("IOKAY.DEF"), LIBRARY->generaltexth->zelp[632], [&](){ close(); }, EShortcut::GLOBAL_ACCEPT);
 	statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
@@ -1007,24 +1007,25 @@ CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, SecondarySkil
 
 	std::string text = LIBRARY->generaltexth->allTexts[608];
 	boost::replace_first(text, "%s", LIBRARY->generaltexth->levels[0]);
-	boost::replace_first(text, "%s", LIBRARY->skillh->getByIndex(SKILL)->getNameTranslated());
+	boost::replace_first(text, "%s", SKILL.toEntity(LIBRARY)->getNameTranslated());
+
 	boost::replace_first(text, "%d", "2000");
 
 	clerkSpeech = std::make_shared<CTextBox>(text, Rect(24, 129, 413, 70), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
 
-	name = std::make_shared<CLabel>(230, 37,  FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->skillh->getByIndex(SKILL)->getNameTranslated());
-	icon = std::make_shared<CAnimImage>(AnimationPath::builtin("SECSKILL"), SKILL*3+3, 0, 211, 51);
+	name = std::make_shared<CLabel>(230, 37,  FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, SKILL.toEntity(LIBRARY)->getNameTranslated());
+	icon = std::make_shared<CAnimImage>(AnimationPath::builtin("SECSKILL"), SKILL.getNum()*3+3, 0, 211, 51);
 	level = std::make_shared<CLabel>(230, 107, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->levels[1]);
 
 	costIcon = std::make_shared<CAnimImage>(AnimationPath::builtin("RESOURCE"), GameResID(EGameResID::GOLD), 0, 210, 210);
 	cost = std::make_shared<CLabel>(230, 267, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, "2000");
 
 	std::string hoverText = LIBRARY->generaltexth->allTexts[609];
-	boost::replace_first(hoverText, "%s", LIBRARY->generaltexth->levels[0]+ " " + LIBRARY->skillh->getByIndex(SKILL)->getNameTranslated());
+	boost::replace_first(hoverText, "%s", LIBRARY->generaltexth->levels[0]+ " " + SKILL.toEntity(LIBRARY)->getNameTranslated());
 
 	text = LIBRARY->generaltexth->zelp[633].second;
 	boost::replace_first(text, "%s", LIBRARY->generaltexth->levels[0]);
-	boost::replace_first(text, "%s", LIBRARY->skillh->getByIndex(SKILL)->getNameTranslated());
+	boost::replace_first(text, "%s", SKILL.toEntity(LIBRARY)->getNameTranslated());
 	boost::replace_first(text, "%d", "2000");
 
 	confirm = std::make_shared<CButton>(Point(148, 299), AnimationPath::builtin("IBY6432.DEF"), CButton::tooltip(hoverText, text), [this, SKILL](){makeDeal(SKILL);}, EShortcut::GLOBAL_ACCEPT);
@@ -1230,7 +1231,7 @@ void CHillFortWindow::updateGarrisons()
 			else//free upgrade - print gold image and "Free" text
 			{
 				slotIcons[i][0]->visible = true;
-				slotIcons[i][0]->setFrame(GameResID(EGameResID::GOLD));
+				slotIcons[i][0]->setFrame(GameResID(EGameResID::GOLD).getNum());
 				slotLabels[i][0]->setText(LIBRARY->generaltexth->allTexts[344]);
 			}
 		}
@@ -1357,7 +1358,7 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner):
 		boost::algorithm::trim_if(text,boost::algorithm::is_any_of("\""));
 		if(settings["general"]["enableUiEnhancements"].Bool() && g >= 2 && g <= 4) // add icons instead of text (text is OH3 behavior)
 		{
-			auto addicon = [this, y](GameResID res, int x){ columnHeaderIcons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), res, 0, x, y - 10)); };
+			auto addicon = [this, y](GameResID res, int x){ columnHeaderIcons.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("SMALRES"), res.getNum(), 0, x, y - 10)); };
 			if(g == 2) // gold
 				addicon(GameResID::GOLD, 125);
 			else if(g == 3) // wood, ore

+ 4 - 0
lib/bonuses/BonusCache.h

@@ -160,6 +160,10 @@ public:
 	PrimarySkillsCache(const IBonusBearer * target);
 
 	const std::array<std::atomic<int32_t>, 4> & getSkills() const;
+	const std::atomic<int32_t> & getSkill(PrimarySkill id) const
+	{
+		return getSkills()[id.getNum()];
+	}
 };
 
 /// Cache that tracks values of spell school mastery in bonus system

+ 1 - 1
lib/campaign/CampaignHandler.cpp

@@ -241,7 +241,7 @@ JsonNode CampaignHandler::writeScenarioToJson(const CampaignScenario & scenario)
 	JsonNode node;
 	node["map"].String() = scenario.mapName;
 	for(auto & g : scenario.preconditionRegions)
-		node["preconditions"].Vector().push_back(JsonNode(static_cast<ui32>(g)));
+		node["preconditions"].Vector().push_back(JsonNode(g.getNum()));
 	node["color"].Integer() = scenario.regionColor;
 	node["difficulty"].Integer() = scenario.difficulty;
 	node["regionText"].String() = scenario.regionText.toString();

+ 49 - 36
lib/constants/EntityIdentifiers.h

@@ -323,24 +323,57 @@ public:
 		//150-155 reserved for 8. creature with potential upgrades
 		DWELL_LVL_8=150, DWELL_LVL_8_UP=151, DWELL_LVL_8_UP2 = 152, DWELL_LVL_8_UP3 = 153, DWELL_LVL_8_UP4 = 154, DWELL_LVL_8_UP5 = 155,
 	};
+};
 
-private:
-	static std::array<std::array<Type, 8>, 6> getDwellings()
+class DLL_LINKAGE BuildingID : public StaticIdentifierWithEnum<BuildingID, BuildingIDBase>
+{
+	static std::array<std::array<BuildingID, 8>, 6> getDwellings()
 	{
-		static const std::array<std::array<Type, 8>, 6> allDwellings = {{
-			{ DWELL_LVL_1, DWELL_LVL_2, DWELL_LVL_3, DWELL_LVL_4, DWELL_LVL_5, DWELL_LVL_6, DWELL_LVL_7, DWELL_LVL_8 },
-			{ DWELL_LVL_1_UP, DWELL_LVL_2_UP, DWELL_LVL_3_UP, DWELL_LVL_4_UP, DWELL_LVL_5_UP, DWELL_LVL_6_UP, DWELL_LVL_7_UP, DWELL_LVL_8_UP },
-			{ DWELL_LVL_1_UP2, DWELL_LVL_2_UP2, DWELL_LVL_3_UP2, DWELL_LVL_4_UP2, DWELL_LVL_5_UP2, DWELL_LVL_6_UP2, DWELL_LVL_7_UP2, DWELL_LVL_8_UP2 },
-			{ DWELL_LVL_1_UP3, DWELL_LVL_2_UP3, DWELL_LVL_3_UP3, DWELL_LVL_4_UP3, DWELL_LVL_5_UP3, DWELL_LVL_6_UP3, DWELL_LVL_7_UP3, DWELL_LVL_8_UP3 },
-			{ DWELL_LVL_1_UP4, DWELL_LVL_2_UP4, DWELL_LVL_3_UP4, DWELL_LVL_4_UP4, DWELL_LVL_5_UP4, DWELL_LVL_6_UP4, DWELL_LVL_7_UP4, DWELL_LVL_8_UP4 },
-			{ DWELL_LVL_1_UP5, DWELL_LVL_2_UP5, DWELL_LVL_3_UP5, DWELL_LVL_4_UP5, DWELL_LVL_5_UP5, DWELL_LVL_6_UP5, DWELL_LVL_7_UP5, DWELL_LVL_8_UP5 }
-		}};
+		static const std::array<std::array<BuildingID, 8>, 6> allDwellings = {{
+				{ DWELL_LVL_1, DWELL_LVL_2, DWELL_LVL_3, DWELL_LVL_4, DWELL_LVL_5, DWELL_LVL_6, DWELL_LVL_7, DWELL_LVL_8 },
+				{ DWELL_LVL_1_UP, DWELL_LVL_2_UP, DWELL_LVL_3_UP, DWELL_LVL_4_UP, DWELL_LVL_5_UP, DWELL_LVL_6_UP, DWELL_LVL_7_UP, DWELL_LVL_8_UP },
+				{ DWELL_LVL_1_UP2, DWELL_LVL_2_UP2, DWELL_LVL_3_UP2, DWELL_LVL_4_UP2, DWELL_LVL_5_UP2, DWELL_LVL_6_UP2, DWELL_LVL_7_UP2, DWELL_LVL_8_UP2 },
+				{ DWELL_LVL_1_UP3, DWELL_LVL_2_UP3, DWELL_LVL_3_UP3, DWELL_LVL_4_UP3, DWELL_LVL_5_UP3, DWELL_LVL_6_UP3, DWELL_LVL_7_UP3, DWELL_LVL_8_UP3 },
+				{ DWELL_LVL_1_UP4, DWELL_LVL_2_UP4, DWELL_LVL_3_UP4, DWELL_LVL_4_UP4, DWELL_LVL_5_UP4, DWELL_LVL_6_UP4, DWELL_LVL_7_UP4, DWELL_LVL_8_UP4 },
+				{ DWELL_LVL_1_UP5, DWELL_LVL_2_UP5, DWELL_LVL_3_UP5, DWELL_LVL_4_UP5, DWELL_LVL_5_UP5, DWELL_LVL_6_UP5, DWELL_LVL_7_UP5, DWELL_LVL_8_UP5 }
+			}};
 
 		return allDwellings;
 	}
 
 public:
-	static Type getDwellingFromLevel(int level, int upgradeIndex)
+	using StaticIdentifierWithEnum<BuildingID, BuildingIDBase>::StaticIdentifierWithEnum;
+
+	static BuildingID HALL_LEVEL(unsigned int level)
+	{
+		assert(level < 4);
+		return BuildingID(Type::VILLAGE_HALL + level);
+	}
+	static BuildingID FORT_LEVEL(unsigned int level)
+	{
+		assert(level < 3);
+		return BuildingID(Type::FORT + level);
+	}
+
+	static std::string encode(int32_t index);
+	static si32 decode(const std::string & identifier);
+
+public:
+
+	int getMagesGuildLevel()
+	{
+		switch (toEnum())
+		{
+			case Type::MAGES_GUILD_1: return 1;
+			case Type::MAGES_GUILD_2: return 2;
+			case Type::MAGES_GUILD_3: return 3;
+			case Type::MAGES_GUILD_4: return 4;
+			case Type::MAGES_GUILD_5: return 5;
+		}
+		throw std::runtime_error("Call to getMageGuildLevel with building '" + std::to_string(getNum()) +"' that is not mages guild!");
+	}
+
+	static BuildingID getDwellingFromLevel(int level, int upgradeIndex)
 	{
 		try
 		{
@@ -352,7 +385,7 @@ public:
 		}
 	}
 
-	static int getLevelFromDwelling(BuildingIDBase dwelling)
+	static int getLevelFromDwelling(BuildingID dwelling)
 	{
 		for (const auto & level : getDwellings())
 		{
@@ -364,7 +397,7 @@ public:
 		throw std::runtime_error("Call to getLevelFromDwelling with building '" + std::to_string(dwelling.num) +"' that is not dwelling!");
 	}
 
-	static int getUpgradedFromDwelling(BuildingIDBase dwelling)
+	static int getUpgradedFromDwelling(BuildingID dwelling)
 	{
 		const auto & dwellings = getDwellings();
 
@@ -377,45 +410,25 @@ public:
 		throw std::runtime_error("Call to getUpgradedFromDwelling with building '" + std::to_string(dwelling.num) +"' that is not dwelling!");
 	}
 
-	static void advanceDwelling(BuildingIDBase & dwelling)
+	static void advanceDwelling(BuildingID & dwelling)
 	{
 		int level =	getLevelFromDwelling(dwelling);
 		int upgrade = getUpgradedFromDwelling(dwelling);
 
-		dwelling.setNum(getDwellingFromLevel(level, upgrade + 1));
+		dwelling = getDwellingFromLevel(level, upgrade + 1);
 	}
 
 	bool isDwelling() const
 	{
 		for (const auto & level : getDwellings())
 		{
-			if (vstd::contains(level, num))
+			if (vstd::contains(level, BuildingID(num)))
 				return true;
 		}
 		return false;
 	}
 };
 
-class DLL_LINKAGE BuildingID : public StaticIdentifierWithEnum<BuildingID, BuildingIDBase>
-{
-public:
-	using StaticIdentifierWithEnum<BuildingID, BuildingIDBase>::StaticIdentifierWithEnum;
-
-	static BuildingID HALL_LEVEL(unsigned int level)
-	{
-		assert(level < 4);
-		return BuildingID(Type::VILLAGE_HALL + level);
-	}
-	static BuildingID FORT_LEVEL(unsigned int level)
-	{
-		assert(level < 3);
-		return BuildingID(Type::FORT + level);
-	}
-
-	static std::string encode(int32_t index);
-	static si32 decode(const std::string & identifier);
-};
-
 class MapObjectBaseID : public IdentifierBase
 {
 public:

+ 3 - 3
lib/gameState/CGameStateCampaign.cpp

@@ -221,7 +221,7 @@ void CGameStateCampaign::placeCampaignHeroes()
 		if(it != gameState->scenarioOps->playerInfos.end())
 		{
 			HeroTypeID heroTypeId = HeroTypeID(campaignBonus->info2);
-			if(heroTypeId.getNum() == HeroTypeID::CAMP_RANDOM) // random bonus hero
+			if(heroTypeId == HeroTypeID::CAMP_RANDOM) // random bonus hero
 			{
 				heroTypeId = gameState->pickUnusedHeroTypeRandomly(playerColor);
 			}
@@ -519,7 +519,7 @@ void CGameStateCampaign::generateCampaignHeroesToReplace()
 void CGameStateCampaign::initHeroes()
 {
 	auto chosenBonus = currentBonus();
-	if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != HeroTypeID::CAMP_GENERATED) //exclude generated heroes
+	if (chosenBonus && chosenBonus->isBonusForHero() && chosenBonus->info1 != HeroTypeID::CAMP_GENERATED.getNum()) //exclude generated heroes
 	{
 		//find human player
 		PlayerColor humanPlayer=PlayerColor::NEUTRAL;
@@ -535,7 +535,7 @@ void CGameStateCampaign::initHeroes()
 
 		const auto & heroes = gameState->players.at(humanPlayer).getHeroes();
 
-		if (chosenBonus->info1 == HeroTypeID::CAMP_STRONGEST) //most powerful
+		if (chosenBonus->info1 == HeroTypeID::CAMP_STRONGEST.getNum()) //most powerful
 		{
 			int maxB = -1;
 			for (int b=0; b<heroes.size(); ++b)

+ 1 - 1
lib/logging/VisualLogger.cpp

@@ -81,7 +81,7 @@ void IVisualLogBuilder::addText(int3 tile, const std::string & text, PlayerColor
 {
 	std::optional<ColorRGBA> rgbColor;
 
-	switch(background)
+	switch(background.getNum())
 	{
 	case 0:
 		rgbColor = ColorRGBA(255, 0, 0);

+ 4 - 4
lib/mapObjects/CGHeroInstance.cpp

@@ -721,13 +721,13 @@ void CGHeroInstance::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
 
 int CGHeroInstance::getPrimSkillLevel(PrimarySkill id) const
 {
-	return primarySkills.getSkills()[id];
+	return primarySkills.getSkill(id);
 }
 
 double CGHeroInstance::getFightingStrength() const
 {
 	const auto & skillValues = primarySkills.getSkills();
-	return sqrt((1.0 + 0.05*skillValues[PrimarySkill::ATTACK]) * (1.0 + 0.05*skillValues[PrimarySkill::DEFENSE]));
+	return sqrt((1.0 + 0.05*skillValues[PrimarySkill::ATTACK.getNum()]) * (1.0 + 0.05*skillValues[PrimarySkill::DEFENSE.getNum()]));
 }
 
 double CGHeroInstance::getMagicStrength() const
@@ -746,7 +746,7 @@ double CGHeroInstance::getMagicStrength() const
 	}
 	if (!atLeastOneCombatSpell)
 		return 1;
-	return sqrt((1.0 + 0.05*skillValues[PrimarySkill::KNOWLEDGE] * mana / manaLimit()) * (1.0 + 0.05*skillValues[PrimarySkill::SPELL_POWER] * mana / manaLimit()));
+	return sqrt((1.0 + 0.05*skillValues[PrimarySkill::KNOWLEDGE.getNum()] * mana / manaLimit()) * (1.0 + 0.05*skillValues[PrimarySkill::SPELL_POWER.getNum()] * mana / manaLimit()));
 }
 
 double CGHeroInstance::getHeroStrength() const
@@ -1976,7 +1976,7 @@ const IOwnableObject * CGHeroInstance::asOwnable() const
 
 int CGHeroInstance::getBasePrimarySkillValue(PrimarySkill which) const
 {
-	std::string cachingStr = "CGHeroInstance::getBasePrimarySkillValue" + std::to_string(static_cast<int>(which));
+	std::string cachingStr = "CGHeroInstance::getBasePrimarySkillValue" + std::to_string(which.getNum());
 	auto selector = Selector::typeSubtype(BonusType::PRIMARY_SKILL, BonusSubtypeID(which)).And(Selector::sourceType()(BonusSource::HERO_BASE_SKILL));
 	auto minSkillValue = LIBRARY->engineSettings()->getVectorValue(EGameSettings::HEROES_MINIMAL_PRIMARY_SKILLS, which.getNum());
 	return std::max(valOfBonuses(selector, cachingStr), minSkillValue);

+ 2 - 2
lib/mapObjects/TownBuildingInstance.cpp

@@ -106,7 +106,7 @@ void TownRewardableBuildingInstance::newTurn(vstd::RNG & rand) const
 
 		if(configuration.resetParameters.visitors)
 		{
-			cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, getBuildingType());
+			cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, getBuildingType().getNum());
 		}
 	}
 }
@@ -212,7 +212,7 @@ bool TownRewardableBuildingInstance::wasVisited(PlayerColor player) const
 
 void TownRewardableBuildingInstance::markAsVisited(const CGHeroInstance * hero) const
 {
-	town->addHeroToStructureVisitors(hero, getBuildingType());
+	town->addHeroToStructureVisitors(hero, getBuildingType().getNum());
 }
 
 void TownRewardableBuildingInstance::markAsScouted(const CGHeroInstance * hero) const

+ 1 - 1
lib/mapping/CMap.cpp

@@ -164,7 +164,7 @@ EDiggingStatus TerrainTile::getDiggingStatus(const bool excludeTop) const
 		return EDiggingStatus::WRONG_TERRAIN;
 
 	int allowedBlocked = excludeTop ? 1 : 0;
-	if(blockingObjects.size() > allowedBlocked || topVisitableObj(excludeTop))
+	if(blockingObjects.size() > allowedBlocked || topVisitableObj(excludeTop).hasValue())
 		return EDiggingStatus::TILE_OCCUPIED;
 	else
 		return EDiggingStatus::CAN_DIG;

+ 2 - 2
lib/rmg/CZonePlacer.cpp

@@ -464,9 +464,9 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
 				auto player = PlayerColor(*owner - 1);
 				auto playerSettings = map.getMapGenOptions().getPlayersSettings();
 				FactionID faction = FactionID::RANDOM;
-				if (playerSettings.size() > player)
+				if (playerSettings.size() > player.getNum())
 				{
-					faction = std::next(playerSettings.begin(), player)->second.getStartingTown();
+					faction = std::next(playerSettings.begin(), player.getNum())->second.getStartingTown();
 				}
 				else
 				{

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

@@ -767,7 +767,7 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector<ObjectInfo*>
 
 		if (templates.empty())
 		{
-			throw rmgException(boost::str(boost::format("Did not find template for object (%d,%d) at %s") % object->ID % object->subID % zone.getTerrainType().encode(zone.getTerrainType())));
+			throw rmgException(boost::str(boost::format("Did not find template for object (%d,%d) at %s") % object->ID.getNum() % object->subID.getNum() % zone.getTerrainType().encode(zone.getTerrainType().getNum())));
 		}
 
 		object->appearance = *RandomGeneratorUtil::nextItem(templates, zone.getRand());

+ 3 - 3
server/CVCMIServer.cpp

@@ -953,10 +953,10 @@ bool CVCMIServer::canUseThisHero(PlayerColor player, HeroTypeID ID)
 	if (!ID.hasValue())
 		return false;
 
-	if (ID >= LIBRARY->heroh->size())
+	if (ID.getNum() >= LIBRARY->heroh->size())
 		return false;
 
-	if (si->playerInfos[player].castle != LIBRARY->heroh->objects[ID]->heroClass->faction)
+	if (si->playerInfos[player].castle != ID.toHeroType()->heroClass->faction)
 		return false;
 
 	if (vstd::contains(getUsedHeroes(), ID))
@@ -979,7 +979,7 @@ std::vector<HeroTypeID> CVCMIServer::getUsedHeroes()
 	{
 		const auto & heroes = getPlayerInfo(p.first).heroesNames;
 		for(auto & hero : heroes)
-			if(hero.heroId >= 0) //in VCMI map format heroId = -1 means random hero
+			if(hero.heroId.hasValue())
 				heroIds.push_back(hero.heroId);
 
 		if(p.second.hero != HeroTypeID::RANDOM)

+ 1 - 1
server/battles/BattleResultProcessor.cpp

@@ -217,7 +217,7 @@ void BattleResultProcessor::endBattle(const CBattleInfoCallback & battle)
 		r.exp[BattleSide::DEFENDER] = 0;
 		for (auto i = r.casualties[battle.otherSide(r.winner)].begin(); i!=r.casualties[battle.otherSide(r.winner)].end(); i++)
 		{
-			r.exp[r.winner] += LIBRARY->creh->objects.at(i->first)->valOfBonuses(BonusType::STACK_HEALTH) * i->second;
+			r.exp[r.winner] += i->first.toCreature()->valOfBonuses(BonusType::STACK_HEALTH) * i->second;
 		}
 	};
 

+ 2 - 3
server/processors/NewTurnProcessor.cpp

@@ -257,7 +257,7 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
 		// Distribute weekly bonuses over 7 days, depending on the current day of the week
 		for (GameResID i : GameResID::ALL_RESOURCES())
 		{
-			const std::string & name = GameConstants::RESOURCE_NAMES[i];
+			const std::string & name = GameConstants::RESOURCE_NAMES[i.getNum()];
 			int64_t weeklyBonus = difficultyConfig[name].Integer();
 			int64_t dayOfWeek = gameHandler->gameState().getDate(Date::DAY_OF_WEEK);
 			int64_t dailyIncome = incomeHandicapped[i];
@@ -552,8 +552,7 @@ std::tuple<EWeekType, CreatureID> NewTurnProcessor::pickWeekType(bool newMonth)
 			do
 			{
 				newMonster.second = LIBRARY->creh->pickRandomMonster(gameHandler->getRandomGenerator());
-			} while (LIBRARY->creh->objects[newMonster.second] &&
-					(*LIBRARY->townh)[LIBRARY->creatures()->getById(newMonster.second)->getFactionID()]->town == nullptr); // find first non neutral creature
+			} while (newMonster.second.toEntity(LIBRARY)->getFactionID().toFaction()->town == nullptr); // find first non neutral creature
 
 			return { EWeekType::BONUS_GROWTH, newMonster.second};
 		}

+ 6 - 3
server/queries/MapQueries.cpp

@@ -81,11 +81,13 @@ bool CGarrisonDialogQuery::blocksPack(const CPackForServer * pack) const
 
 	if(auto arts = dynamic_cast<const ExchangeArtifacts*>(pack))
 	{
-		if(auto id1 = arts->src.artHolder)
+		auto id1 = arts->src.artHolder;
+		if(id1.hasValue())
 			if(!vstd::contains(ourIds, id1))
 				return true;
 
-		if(auto id2 = arts->dst.artHolder)
+		auto id2 = arts->dst.artHolder;
+		if(id2.hasValue())
 			if(!vstd::contains(ourIds, id2))
 				return true;
 		return false;
@@ -101,7 +103,8 @@ bool CGarrisonDialogQuery::blocksPack(const CPackForServer * pack) const
 
 	if(auto art = dynamic_cast<const EraseArtifactByClient*>(pack))
 	{
-		if(auto id = art->al.artHolder)
+		auto id = art->al.artHolder;
+		if(id.hasValue())
 			return !vstd::contains(ourIds, id);
 	}
 

+ 1 - 1
test/spells/effects/HealTest.cpp

@@ -363,7 +363,7 @@ TEST_P(HealApplyTest, DISABLED_Heals)
 	mechanicsMock.caster = &actualCaster;
 	EXPECT_CALL(mechanicsMock, getEffectValue()).WillRepeatedly(Return(effectValue));
 	EXPECT_CALL(mechanicsMock, applySpellBonus(Eq(effectValue), Eq(&targetUnit))).WillRepeatedly(Return(effectValue));
-	EXPECT_CALL(actualCaster, creatureIndex()).WillRepeatedly(Return(CreatureID(unitId)));
+	EXPECT_CALL(actualCaster, creatureId()).WillRepeatedly(Return(CreatureID(unitId)));
 	EXPECT_CALL(actualCaster, getHeroCaster()).WillRepeatedly(Return(nullptr));
 
 	GTEST_ASSERT_EQ(targetUnitState->getAvailableHealth(), unitAmount * unitHP / 2 + 1);