浏览代码

Merge pull request #3188 from IvanSavenko/remove_identifier_implicit_int_conversion2

Remove implicit conversion of identifier to integer
Ivan Savenko 1 年之前
父节点
当前提交
76956cfe3a
共有 100 个文件被更改,包括 528 次插入960 次删除
  1. 4 9
      AI/VCAI/Goals/Win.cpp
  2. 19 27
      client/lobby/CBonusSelection.cpp
  3. 1 4
      client/lobby/OptionsTab.cpp
  4. 2 2
      client/windows/CSpellWindow.cpp
  5. 5 3
      client/windows/CTradeWindow.cpp
  6. 3 3
      client/windows/GUIClasses.cpp
  7. 0 2
      cmake_modules/VCMI_lib.cmake
  8. 2 1
      include/vcmi/spells/Caster.h
  9. 1 1
      lib/ArtifactUtils.cpp
  10. 0 5
      lib/BattleFieldHandler.cpp
  11. 0 1
      lib/BattleFieldHandler.h
  12. 18 16
      lib/CArtHandler.cpp
  13. 3 3
      lib/CArtHandler.h
  14. 3 17
      lib/CCreatureHandler.cpp
  15. 0 1
      lib/CCreatureHandler.h
  16. 7 7
      lib/CCreatureSet.cpp
  17. 16 16
      lib/CGameInfoCallback.cpp
  18. 8 4
      lib/CGameInfoCallback.h
  19. 7 15
      lib/CHeroHandler.cpp
  20. 1 3
      lib/CHeroHandler.h
  21. 8 4
      lib/CSkillHandler.cpp
  22. 1 1
      lib/CSkillHandler.h
  23. 28 31
      lib/CTownHandler.cpp
  24. 1 1
      lib/CTownHandler.h
  25. 1 1
      lib/IGameCallback.cpp
  26. 0 7
      lib/IHandlerBase.h
  27. 8 8
      lib/JsonRandom.cpp
  28. 5 2
      lib/MetaString.cpp
  29. 2 1
      lib/MetaString.h
  30. 0 5
      lib/ObstacleHandler.cpp
  31. 0 1
      lib/ObstacleHandler.h
  32. 0 5
      lib/RiverHandler.cpp
  33. 0 1
      lib/RiverHandler.h
  34. 0 5
      lib/RoadHandler.cpp
  35. 0 1
      lib/RoadHandler.h
  36. 0 5
      lib/ScriptHandler.cpp
  37. 0 1
      lib/ScriptHandler.h
  38. 0 5
      lib/TerrainHandler.cpp
  39. 0 1
      lib/TerrainHandler.h
  40. 1 1
      lib/battle/CUnitState.cpp
  41. 1 1
      lib/battle/CUnitState.h
  42. 23 3
      lib/constants/EntityIdentifiers.cpp
  43. 38 1
      lib/constants/EntityIdentifiers.h
  44. 3 2
      lib/constants/VariantIdentifier.h
  45. 18 38
      lib/gameState/CGameState.cpp
  46. 2 2
      lib/gameState/CGameStateCampaign.cpp
  47. 1 1
      lib/gameState/SThievesGuildInfo.h
  48. 16 23
      lib/mapObjectConstructors/CObjectClassesHandler.cpp
  49. 1 3
      lib/mapObjectConstructors/CObjectClassesHandler.h
  50. 1 1
      lib/mapObjectConstructors/CommonConstructors.cpp
  51. 1 1
      lib/mapObjects/CArmedInstance.cpp
  52. 1 1
      lib/mapObjects/CArmedInstance.h
  53. 1 1
      lib/mapObjects/CBank.cpp
  54. 2 2
      lib/mapObjects/CGDwelling.cpp
  55. 15 19
      lib/mapObjects/CGHeroInstance.cpp
  56. 1 1
      lib/mapObjects/CGHeroInstance.h
  57. 8 22
      lib/mapObjects/CGMarket.cpp
  58. 4 5
      lib/mapObjects/CGMarket.h
  59. 14 42
      lib/mapObjects/CGTownInstance.cpp
  60. 2 2
      lib/mapObjects/CGTownInstance.h
  61. 2 2
      lib/mapObjects/CQuest.cpp
  62. 4 4
      lib/mapObjects/IMarket.cpp
  63. 3 2
      lib/mapObjects/IMarket.h
  64. 3 3
      lib/mapObjects/MiscObjects.cpp
  65. 1 1
      lib/mapObjects/MiscObjects.h
  66. 2 2
      lib/mapObjects/ObjectTemplate.h
  67. 26 56
      lib/mapping/CMap.cpp
  68. 3 3
      lib/mapping/CMap.h
  69. 1 6
      lib/mapping/CMapHeader.cpp
  70. 7 15
      lib/mapping/CMapHeader.h
  71. 26 26
      lib/mapping/MapFormatH3M.cpp
  72. 31 154
      lib/mapping/MapFormatJson.cpp
  73. 25 30
      lib/mapping/MapReaderH3M.cpp
  74. 6 9
      lib/mapping/MapReaderH3M.h
  75. 2 2
      lib/modding/IdentifierStorage.cpp
  76. 4 4
      lib/networkPacks/NetPacksLib.cpp
  77. 3 2
      lib/pathfinder/TurnInfo.cpp
  78. 1 1
      lib/pathfinder/TurnInfo.h
  79. 1 1
      lib/rewardable/Interface.cpp
  80. 1 1
      lib/rmg/CMapGenOptions.cpp
  81. 12 20
      lib/rmg/CMapGenerator.cpp
  82. 1 7
      lib/rmg/CRmgTemplate.cpp
  83. 0 6
      lib/rmg/CRmgTemplateStorage.cpp
  84. 0 1
      lib/rmg/CRmgTemplateStorage.h
  85. 1 1
      lib/rmg/RmgMap.cpp
  86. 2 2
      lib/rmg/Zone.cpp
  87. 2 2
      lib/rmg/Zone.h
  88. 3 3
      lib/rmg/modificators/ObjectManager.cpp
  89. 2 2
      lib/rmg/modificators/TownPlacer.cpp
  90. 1 1
      lib/rmg/modificators/TownPlacer.h
  91. 3 3
      lib/rmg/modificators/TreasurePlacer.cpp
  92. 1 1
      lib/rmg/modificators/TreasurePlacer.h
  93. 0 9
      lib/serializer/BinaryDeserializer.h
  94. 0 8
      lib/serializer/BinarySerializer.h
  95. 0 49
      lib/serializer/ILICReader.cpp
  96. 0 23
      lib/serializer/ILICReader.h
  97. 4 68
      lib/serializer/JsonDeserializer.cpp
  98. 2 4
      lib/serializer/JsonDeserializer.h
  99. 12 11
      lib/serializer/JsonSerializeFormat.cpp
  100. 22 17
      lib/serializer/JsonSerializeFormat.h

+ 4 - 9
AI/VCAI/Goals/Win.cpp

@@ -51,7 +51,7 @@ TSubgoal Win::whatToDoToAchieve()
 		switch(goal.condition)
 		{
 		case EventCondition::HAVE_ARTIFACT:
-			return sptr(GetArtOfType(goal.objectType));
+			return sptr(GetArtOfType(goal.objectType.as<ArtifactID>()));
 		case EventCondition::DESTROY:
 		{
 			if(goal.object)
@@ -78,7 +78,7 @@ TSubgoal Win::whatToDoToAchieve()
 			// goal.object = optional, town in which building should be built
 			// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
 
-			if(goal.objectType == BuildingID::GRAIL)
+			if(goal.objectType.as<BuildingID>() == BuildingID::GRAIL)
 			{
 				if(auto h = ai->getHeroWithGrail())
 				{
@@ -149,9 +149,9 @@ TSubgoal Win::whatToDoToAchieve()
 		case EventCondition::HAVE_RESOURCES:
 			//TODO mines? piles? marketplace?
 			//save?
-			return sptr(CollectRes(static_cast<EGameResID>(goal.objectType), goal.value));
+			return sptr(CollectRes(goal.objectType.as<GameResID>(), goal.value));
 		case EventCondition::HAVE_CREATURES:
-			return sptr(GatherTroops(goal.objectType, goal.value));
+			return sptr(GatherTroops(goal.objectType.as<CreatureID>(), 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
@@ -173,11 +173,6 @@ TSubgoal Win::whatToDoToAchieve()
 		case EventCondition::CONST_VALUE:
 			break;
 
-		case EventCondition::HAVE_0:
-		case EventCondition::HAVE_BUILDING_0:
-		case EventCondition::DESTROY_0:
-			//TODO: support new condition format
-			return sptr(Conquer());
 		default:
 			assert(0);
 		}

+ 19 - 27
client/lobby/CBonusSelection.cpp

@@ -235,38 +235,30 @@ void CBonusSelection::createBonusesIcons()
 			break;
 		case CampaignBonusType::RESOURCE:
 		{
-			int serialResID = 0;
+			desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
+
 			switch(bonDescs[i].info1)
 			{
-			case 0:
-			case 1:
-			case 2:
-			case 3:
-			case 4:
-			case 5:
-			case 6:
-				serialResID = bonDescs[i].info1;
-				break;
-			case 0xFD: //wood + ore
-				serialResID = 7;
-				break;
-			case 0xFE: //rare resources
-				serialResID = 8;
-				break;
+				case 0xFD: //wood + ore
+				{
+					desc.replaceLocalString(EMetaText::GENERAL_TXT, 721);
+					picNumber = 7;
+					break;
+				}
+				case 0xFE: //wood + ore
+				{
+					desc.replaceLocalString(EMetaText::GENERAL_TXT, 722);
+					picNumber = 8;
+					break;
+				}
+				default:
+				{
+					desc.replaceName(GameResID(bonDescs[i].info1));
+					picNumber = bonDescs[i].info1;
+				}
 			}
-			picNumber = serialResID;
 
-			desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
 			desc.replaceNumber(bonDescs[i].info2);
-			
-			if(serialResID <= 6)
-			{
-				desc.replaceLocalString(EMetaText::RES_NAMES, serialResID);
-			}
-			else
-			{
-				desc.replaceLocalString(EMetaText::GENERAL_TXT, 714 + serialResID);
-			}
 			break;
 		}
 		case CampaignBonusType::HEROES_FROM_PREVIOUS_SCENARIO:

+ 1 - 4
client/lobby/OptionsTab.cpp

@@ -407,10 +407,7 @@ OptionsTab::SelectionWindow::SelectionWindow(PlayerColor _color, SelType _type)
 	selectedHero = initialHero;
 	selectedBonus = initialBonus;
 	allowedFactions = SEL->getPlayerInfo(color).allowedFactions;
-	std::vector<bool> allowedHeroesFlag = SEL->getMapInfo()->mapHeader->allowedHeroes;
-	for(int i = 0; i < allowedHeroesFlag.size(); i++)
-		if(allowedHeroesFlag[i])
-			allowedHeroes.insert(HeroTypeID(i));
+	allowedHeroes = SEL->getMapInfo()->mapHeader->allowedHeroes;
 
 	for(auto & player : SEL->getStartInfo()->playerInfos)
 	{

+ 2 - 2
client/windows/CSpellWindow.cpp

@@ -639,7 +639,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
 	mySpell = spell;
 	if(mySpell)
 	{
-		int32_t whichSchool = 0; //0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
+		SpellSchool whichSchool; //0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
 		schoolLevel = owner->myHero->getSpellSchoolLevel(mySpell, &whichSchool);
 		auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero);
 
@@ -648,7 +648,7 @@ void CSpellWindow::SpellArea::setSpell(const CSpell * spell)
 
 		{
 			OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
-			schoolBorder = std::make_shared<CAnimImage>(owner->schoolBorders[owner->selectedTab >= 4 ? whichSchool : owner->selectedTab], schoolLevel);
+			schoolBorder = std::make_shared<CAnimImage>(owner->schoolBorders[owner->selectedTab >= 4 ? whichSchool.getNum() : owner->selectedTab], schoolLevel);
 		}
 
 		ColorRGBA firstLineColor, secondLineColor;

+ 5 - 3
client/windows/CTradeWindow.cpp

@@ -149,7 +149,9 @@ std::vector<int> *CTradeWindow::getItemsIds(bool Left)
 			break;
 
 		case ARTIFACT_TYPE:
-			ids = new std::vector<int>(market->availableItemsIds(mode));
+			ids = new std::vector<int>;
+			for (auto const & item : market->availableItemsIds(mode))
+				ids->push_back(item.getNum());
 			break;
 		}
 	}
@@ -632,10 +634,10 @@ void CMarketplaceWindow::artifactsChanged(bool Left)
 	if(mode != EMarketMode::RESOURCE_ARTIFACT)
 		return;
 
-	std::vector<int> available = market->availableItemsIds(mode);
+	std::vector<TradeItemBuy> available = market->availableItemsIds(mode);
 	std::set<std::shared_ptr<CTradeableItem>> toRemove;
 	for(auto item : items[0])
-		if(!vstd::contains(available, item->id))
+		if(!vstd::contains(available, ArtifactID(item->id)))
 			toRemove.insert(item);
 
 	removeItems(toRemove);

+ 3 - 3
client/windows/GUIClasses.cpp

@@ -1156,10 +1156,10 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket
 	clerkSpeech = std::make_shared<CTextBox>(speechStr, Rect(24, 129, 413, 70), 0, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE);
 	title = std::make_shared<CLabel>(231, 26, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, titleStr);
 
-	std::vector<int> goods = market->availableItemsIds(EMarketMode::RESOURCE_SKILL);
+	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], 54+i*104, 234));
+		items.push_back(std::make_shared<CItem>(this, goods[i].as<SecondarySkill>(), 54+i*104, 234));
 
 	cancel = std::make_shared<CButton>(Point(200, 313), AnimationPath::builtin("IOKAY.DEF"), CGI->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));
@@ -1579,7 +1579,7 @@ CThievesGuildWindow::CThievesGuildWindow(const CGObjectInstance * _owner):
 	counter = 0;
 	for(auto & it : tgi.bestCreature)
 	{
-		if(it.second >= 0)
+		if(it.second != CreatureID::NONE)
 			bestCreatures.push_back(std::make_shared<CAnimImage>(AnimationPath::builtin("TWCRPORT"), it.second+2, 0, 255 + 66 * counter, 479));
 		counter++;
 	}

+ 0 - 2
cmake_modules/VCMI_lib.cmake

@@ -182,7 +182,6 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/serializer/JsonSerializeFormat.cpp
 		${MAIN_LIB_DIR}/serializer/JsonSerializer.cpp
 		${MAIN_LIB_DIR}/serializer/JsonUpdater.cpp
-		${MAIN_LIB_DIR}/serializer/ILICReader.cpp
 
 		${MAIN_LIB_DIR}/spells/AbilityCaster.cpp
 		${MAIN_LIB_DIR}/spells/AdventureSpellMechanics.cpp
@@ -555,7 +554,6 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/serializer/JsonSerializeFormat.h
 		${MAIN_LIB_DIR}/serializer/JsonSerializer.h
 		${MAIN_LIB_DIR}/serializer/JsonUpdater.h
-		${MAIN_LIB_DIR}/serializer/ILICReader.h
 		${MAIN_LIB_DIR}/serializer/Cast.h
 
 		${MAIN_LIB_DIR}/spells/AbilityCaster.h

+ 2 - 1
include/vcmi/spells/Caster.h

@@ -17,6 +17,7 @@ class MetaString;
 class ServerCallback;
 class CGHeroInstance;
 class Spell;
+class SpellSchool;
 
 namespace battle
 {
@@ -38,7 +39,7 @@ public:
 	/// returns level on which given spell would be cast by this(0 - none, 1 - basic etc);
 	/// caster may not know this spell at all
 	/// optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic
-	virtual int32_t getSpellSchoolLevel(const Spell * spell, int32_t * outSelectedSchool = nullptr) const = 0;
+	virtual int32_t getSpellSchoolLevel(const Spell * spell, SpellSchool * outSelectedSchool = nullptr) const = 0;
 
 	///default spell school level for effect calculation
 	virtual int32_t getEffectLevel(const Spell * spell) const = 0;

+ 1 - 1
lib/ArtifactUtils.cpp

@@ -216,7 +216,7 @@ DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(CArtifa
 
 DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(const ArtifactID & aid)
 {
-	return ArtifactUtils::createNewArtifactInstance(VLC->arth->objects[aid]);
+	return ArtifactUtils::createNewArtifactInstance((*VLC->arth)[aid]);
 }
 
 DLL_LINKAGE CArtifactInstance * ArtifactUtils::createArtifact(CMap * map, const ArtifactID & aid, SpellID spellID)

+ 0 - 5
lib/BattleFieldHandler.cpp

@@ -54,11 +54,6 @@ const std::vector<std::string> & BattleFieldHandler::getTypeNames() const
 	return types;
 }
 
-std::vector<bool> BattleFieldHandler::getDefaultAllowed() const
-{
-	return std::vector<bool>();
-}
-
 int32_t BattleFieldInfo::getIndex() const
 {
 	return battlefield.getNum();

+ 0 - 1
lib/BattleFieldHandler.h

@@ -71,7 +71,6 @@ public:
 
 	virtual const std::vector<std::string> & getTypeNames() const override;
 	virtual std::vector<JsonNode> loadLegacyData() override;
-	virtual std::vector<bool> getDefaultAllowed() const override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 18 - 16
lib/CArtHandler.cpp

@@ -371,7 +371,7 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
 
 	objects.emplace_back(object);
 
-	registerObject(scope, "artifact", name, object->id);
+	registerObject(scope, "artifact", name, object->id.getNum());
 }
 
 void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
@@ -383,7 +383,7 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
 	assert(objects[index] == nullptr); // ensure that this id was not loaded before
 	objects[index] = object;
 
-	registerObject(scope, "artifact", name, object->id);
+	registerObject(scope, "artifact", name, object->id.getNum());
 }
 
 const std::vector<std::string> & CArtHandler::getTypeNames() const
@@ -630,7 +630,7 @@ void CArtHandler::makeItCommanderArt(CArtifact * a, bool onlyCommander)
 
 bool CArtHandler::legalArtifact(const ArtifactID & id)
 {
-	auto art = objects[id];
+	auto art = id.toArtifact();
 	//assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components
 
 	if(art->isCombined())
@@ -639,36 +639,39 @@ bool CArtHandler::legalArtifact(const ArtifactID & id)
 	if(art->aClass < CArtifact::ART_TREASURE || art->aClass > CArtifact::ART_RELIC)
 		return false; // invalid class
 
-	if(!art->possibleSlots[ArtBearer::HERO].empty())
+	if(art->possibleSlots.count(ArtBearer::HERO) && !art->possibleSlots.at(ArtBearer::HERO).empty())
 		return true;
 
-	if(!art->possibleSlots[ArtBearer::CREATURE].empty() && VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_ARTIFACT))
+	if(art->possibleSlots.count(ArtBearer::CREATURE) && !art->possibleSlots.at(ArtBearer::CREATURE).empty() && VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_ARTIFACT))
 		return true;
 
-	if(!art->possibleSlots[ArtBearer::COMMANDER].empty() && VLC->settings()->getBoolean(EGameSettings::MODULE_COMMANDERS))
+	if(art->possibleSlots.count(ArtBearer::COMMANDER) && !art->possibleSlots.at(ArtBearer::COMMANDER).empty() && VLC->settings()->getBoolean(EGameSettings::MODULE_COMMANDERS))
 		return true;
 
 	return false;
 }
 
-void CArtHandler::initAllowedArtifactsList(const std::vector<bool> &allowed)
+void CArtHandler::initAllowedArtifactsList(const std::set<ArtifactID> & allowed)
 {
 	allowedArtifacts.clear();
 
-	for (ArtifactID i=ArtifactID::SPELLBOOK; i < ArtifactID(static_cast<si32>(objects.size())); i.advance(1))
+	for (ArtifactID i : allowed)
 	{
-		if (allowed[i] && legalArtifact(ArtifactID(i)))
-			allowedArtifacts.push_back(objects[i]);
+		if (legalArtifact(ArtifactID(i)))
+			allowedArtifacts.push_back(i.toArtifact());
 			//keep im mind that artifact can be worn by more than one type of bearer
 	}
 }
 
-std::vector<bool> CArtHandler::getDefaultAllowed() const
+std::set<ArtifactID> CArtHandler::getDefaultAllowed() const
 {
-	std::vector<bool> allowedArtifacts;
-	allowedArtifacts.resize(127, true);
-	allowedArtifacts.resize(141, false);
-	allowedArtifacts.resize(size(), true);
+	std::set<ArtifactID> allowedArtifacts;
+
+	for (auto artifact : objects)
+	{
+		if (!artifact->isCombined())
+			allowedArtifacts.insert(artifact->getId());
+	}
 	return allowedArtifacts;
 }
 
@@ -679,7 +682,6 @@ void CArtHandler::afterLoadFinalization()
 	{
 		for(auto &bonus : art->getExportedBonusList())
 		{
-			assert(art == objects[art->id]);
 			assert(bonus->source == BonusSource::ARTIFACT);
 			bonus->sid = BonusSourceID(art->id);
 		}

+ 3 - 3
lib/CArtHandler.h

@@ -142,14 +142,14 @@ class DLL_LINKAGE CArtHandler : public CHandlerBase<ArtifactID, Artifact, CArtif
 {
 public:
 	/// List of artifacts allowed on the map
-	std::vector<CArtifact *> allowedArtifacts;
+	std::vector<const CArtifact *> allowedArtifacts;
 
 	void addBonuses(CArtifact *art, const JsonNode &bonusList);
 
 	static CArtifact::EartClass stringToClass(const std::string & className); //TODO: rework EartClass to make this a constructor
 
 	bool legalArtifact(const ArtifactID & id);
-	void initAllowedArtifactsList(const std::vector<bool> &allowed); //allowed[art_id] -> 0 if not allowed, 1 if allowed
+	void initAllowedArtifactsList(const std::set<ArtifactID> & allowed);
 	static void makeItCreatureArt(CArtifact * a, bool onlyCreature = true);
 	static void makeItCommanderArt(CArtifact * a, bool onlyCommander = true);
 
@@ -161,7 +161,7 @@ public:
 	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
 	void afterLoadFinalization() override;
 
-	std::vector<bool> getDefaultAllowed() const override;
+	std::set<ArtifactID> getDefaultAllowed() const;
 
 protected:
 	const std::vector<std::string> & getTypeNames() const override;

+ 3 - 17
lib/CCreatureHandler.cpp

@@ -324,7 +324,7 @@ bool CCreature::isMyUpgrade(const CCreature *anotherCre) const
 
 bool CCreature::valid() const
 {
-	return this == VLC->creh->objects[idNumber];
+	return this == (*VLC->creh)[idNumber];
 }
 
 std::string CCreature::nodeName() const
@@ -666,18 +666,6 @@ const std::vector<std::string> & CCreatureHandler::getTypeNames() const
 	return typeNames;
 }
 
-std::vector<bool> CCreatureHandler::getDefaultAllowed() const
-{
-	std::vector<bool> ret;
-
-	ret.reserve(objects.size());
-	for(const CCreature * crea : objects)
-	{
-		ret.push_back(crea ? !crea->special : false);
-	}
-	return ret;
-}
-
 void CCreatureHandler::loadCrExpMod()
 {
 	if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) 	//reading default stack experience values
@@ -790,15 +778,13 @@ void CCreatureHandler::loadCrExpBon(CBonusSystemNode & globalEffects)
 		}
 		do //parse everything that's left
 		{
-			CreatureID sid = static_cast<ui32>(parser.readNumber()); //id = this particular creature ID
+			CreatureID sid = parser.readNumber(); //id = this particular creature ID
 
 			b.sid = BonusSourceID(sid);
 			bl.clear();
 			loadStackExp(b, bl, parser);
 			for(const auto & b : bl)
-			{
-				objects[sid]->addNewBonus(b); //add directly to CCreature Node
-			}
+				(*this)[sid]->addNewBonus(b); //add directly to CCreature Node
 		}
 		while (parser.endLine());
 

+ 0 - 1
lib/CCreatureHandler.h

@@ -238,7 +238,6 @@ public:
 
 	std::vector<JsonNode> loadLegacyData() override;
 
-	std::vector<bool> getDefaultAllowed() const override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 7 - 7
lib/CCreatureSet.cpp

@@ -79,7 +79,7 @@ bool CCreatureSet::setCreature(SlotID slot, CreatureID type, TQuantity quantity)
 
 SlotID CCreatureSet::getSlotFor(const CreatureID & creature, ui32 slotsAmount) const /*returns -1 if no slot available */
 {
-	return getSlotFor(VLC->creh->objects[creature], slotsAmount);
+	return getSlotFor(creature.toCreature(), slotsAmount);
 }
 
 SlotID CCreatureSet::getSlotFor(const CCreature *c, ui32 slotsAmount) const
@@ -304,7 +304,7 @@ void CCreatureSet::sweep()
 
 void CCreatureSet::addToSlot(const SlotID & slot, const CreatureID & cre, TQuantity count, bool allowMerging)
 {
-	const CCreature *c = VLC->creh->objects[cre];
+	const CCreature *c = cre.toCreature();
 
 	if(!hasStackAtSlot(slot))
 	{
@@ -749,10 +749,10 @@ void CStackInstance::giveStackExp(TExpType exp)
 
 void CStackInstance::setType(const CreatureID & creID)
 {
-	if(creID.getNum() >= 0 && creID.getNum() < VLC->creh->objects.size())
-		setType(VLC->creh->objects[creID]);
+	if (creID == CreatureID::NONE)
+		setType(nullptr);//FIXME: unused branch?
 	else
-		setType((const CCreature*)nullptr);
+		setType(creID.toCreature());
 }
 
 void CStackInstance::setType(const CCreature *c)
@@ -809,7 +809,7 @@ bool CStackInstance::valid(bool allowUnrandomized) const
 {
 	if(!randomStack)
 	{
-		return (type  &&  type == VLC->creh->objects[type->getId()]);
+		return (type && type == type->getId().toEntity(VLC));
 	}
 	else
 		return allowUnrandomized;
@@ -999,7 +999,7 @@ bool CCommanderInstance::gainsLevel() const
 CStackBasicDescriptor::CStackBasicDescriptor() = default;
 
 CStackBasicDescriptor::CStackBasicDescriptor(const CreatureID & id, TQuantity Count):
-	type(VLC->creh->objects[id]), 
+	type(id.toCreature()),
 	count(Count)
 {
 }

+ 16 - 16
lib/CGameInfoCallback.cpp

@@ -53,19 +53,19 @@ const PlayerSettings * CGameInfoCallback::getPlayerSettings(PlayerColor color) c
 	return &gs->scenarioOps->getIthPlayersSettings(color);
 }
 
-bool CGameInfoCallback::isAllowed(int32_t type, int32_t id) const
+bool CGameInfoCallback::isAllowed(SpellID id) const
 {
-	switch(type)
-	{
-	case 0:
-		return gs->map->allowedSpells[id];
-	case 1:
-		return gs->map->allowedArtifact[id];
-	case 2:
-		return gs->map->allowedAbilities[id];
-	default:
-		ERROR_RET_VAL_IF(1, "Wrong type!", false);
-	}
+	return gs->map->allowedSpells.count(id) != 0;
+}
+
+bool CGameInfoCallback::isAllowed(ArtifactID id) const
+{
+	return gs->map->allowedArtifact.count(id) != 0;
+}
+
+bool CGameInfoCallback::isAllowed(SecondarySkill id) const
+{
+	return gs->map->allowedAbilities.count(id) != 0;
 }
 
 std::optional<PlayerColor> CGameInfoCallback::getPlayerID() const
@@ -122,17 +122,17 @@ TurnTimerInfo CGameInfoCallback::getPlayerTurnTime(PlayerColor color) const
 	return TurnTimerInfo{};
 }
 
-const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(int identifier) const
+const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(ObjectInstanceID identifier) const
 {
 	if(gs->map->questIdentifierToId.empty())
 	{
 		//assume that it is VCMI map and quest identifier equals instance identifier
-		return getObj(ObjectInstanceID(identifier), true);
+		return getObj(identifier, true);
 	}
 	else
 	{
-		ERROR_RET_VAL_IF(!vstd::contains(gs->map->questIdentifierToId, identifier), "There is no object with such quest identifier!", nullptr);
-		return getObj(gs->map->questIdentifierToId[identifier]);
+		ERROR_RET_VAL_IF(!vstd::contains(gs->map->questIdentifierToId, identifier.getNum()), "There is no object with such quest identifier!", nullptr);
+		return getObj(gs->map->questIdentifierToId[identifier.getNum()]);
 	}
 }
 

+ 8 - 4
lib/CGameInfoCallback.h

@@ -57,7 +57,9 @@ public:
 //	//various
 	virtual int getDate(Date mode=Date::DAY) const = 0; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 //	const StartInfo * getStartInfo(bool beforeRandomization = false)const;
-	virtual bool isAllowed(int32_t type, int32_t id) const = 0; //type: 0 - spell; 1- artifact; 2 - secondary skill
+	virtual bool isAllowed(SpellID id) const = 0;
+	virtual bool isAllowed(ArtifactID id) const = 0;
+	virtual bool isAllowed(SecondarySkill id) const = 0;
 
 	//player
 	virtual std::optional<PlayerColor> getPlayerID() const = 0;
@@ -91,7 +93,7 @@ public:
 //	std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
 //	const CGObjectInstance * getTopObj (int3 pos) const;
 //	PlayerColor getOwner(ObjectInstanceID heroID) const;
-//	const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
+//	const CGObjectInstance *getObjByQuestIdentifier(ObjectInstanceID identifier) const; //nullptr if object has been removed (eg. killed)
 
 	//map
 //	int3 guardingCreaturePosition (int3 pos) const;
@@ -143,7 +145,9 @@ public:
 	//various
 	int getDate(Date mode=Date::DAY)const override; //mode=0 - total days in game, mode=1 - day of week, mode=2 - current week, mode=3 - current month
 	virtual const StartInfo * getStartInfo(bool beforeRandomization = false)const;
-	bool isAllowed(int32_t type, int32_t id) const override; //type: 0 - spell; 1- artifact; 2 - secondary skill
+	bool isAllowed(SpellID id) const override;
+	bool isAllowed(ArtifactID id) const override;
+	bool isAllowed(SecondarySkill id) const override;
 
 	//player
 	std::optional<PlayerColor> getPlayerID() const override;
@@ -186,7 +190,7 @@ public:
 	virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
 	virtual const CGObjectInstance * getTopObj (int3 pos) const;
 	virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
-	virtual const CGObjectInstance *getObjByQuestIdentifier(int identifier) const; //nullptr if object has been removed (eg. killed)
+	virtual const CGObjectInstance *getObjByQuestIdentifier(ObjectInstanceID identifier) const; //nullptr if object has been removed (eg. killed)
 
 	//map
 	virtual int3 guardingCreaturePosition (int3 pos) const;

+ 7 - 15
lib/CHeroHandler.cpp

@@ -393,11 +393,6 @@ void CHeroClassHandler::afterLoadFinalization()
 	}
 }
 
-std::vector<bool> CHeroClassHandler::getDefaultAllowed() const
-{
-	return std::vector<bool>(size(), true);
-}
-
 CHeroClassHandler::~CHeroClassHandler() = default;
 
 CHeroHandler::~CHeroHandler() = default;
@@ -523,7 +518,7 @@ static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID ba
 
 		for (auto const & upgradeSourceID : oldTargets)
 		{
-			const CCreature * upgradeSource = VLC->creh->objects[upgradeSourceID];
+			const CCreature * upgradeSource = upgradeSourceID.toCreature();
 			targets.insert(upgradeSource->upgrades.begin(), upgradeSource->upgrades.end());
 		}
 
@@ -533,7 +528,7 @@ static std::vector<std::shared_ptr<Bonus>> createCreatureSpecialty(CreatureID ba
 
 	for(CreatureID cid : targets)
 	{
-		const CCreature &specCreature = *VLC->creh->objects[cid];
+		auto const & specCreature = *cid.toCreature();
 		int stepSize = specCreature.getLevel() ? specCreature.getLevel() : 5;
 
 		{
@@ -763,18 +758,15 @@ ui64 CHeroHandler::reqExp (ui32 level) const
 	}
 }
 
-std::vector<bool> CHeroHandler::getDefaultAllowed() const
+std::set<HeroTypeID> CHeroHandler::getDefaultAllowed() const
 {
-	// Look Data/HOTRAITS.txt for reference
-	std::vector<bool> allowedHeroes;
-	allowedHeroes.reserve(size());
+	std::set<HeroTypeID> result;
 
 	for(const CHero * hero : objects)
-	{
-		allowedHeroes.push_back(hero && !hero->special);
-	}
+		if (hero && !hero->special)
+			result.insert(hero->getId());
 
-	return allowedHeroes;
+	return result;
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 3
lib/CHeroHandler.h

@@ -165,8 +165,6 @@ public:
 
 	void afterLoadFinalization() override;
 
-	std::vector<bool> getDefaultAllowed() const override;
-
 	~CHeroClassHandler();
 
 protected:
@@ -206,7 +204,7 @@ public:
 	CHeroHandler();
 	~CHeroHandler();
 
-	std::vector<bool> getDefaultAllowed() const override;
+	std::set<HeroTypeID> getDefaultAllowed() const;
 
 protected:
 	const std::vector<std::string> & getTypeNames() const override;

+ 8 - 4
lib/CSkillHandler.cpp

@@ -77,7 +77,7 @@ void CSkill::registerIcons(const IconRegistar & cb) const
 {
 	for(int level = 1; level <= 3; level++)
 	{
-		int frame = 2 + level + 3 * id;
+		int frame = 2 + level + 3 * id.getNum();
 		const LevelInfo & skillAtLevel = at(level);
 		cb(frame, 0, "SECSK32", skillAtLevel.iconSmall);
 		cb(frame, 0, "SECSKILL", skillAtLevel.iconMedium);
@@ -256,10 +256,14 @@ void CSkillHandler::beforeValidate(JsonNode & object)
 	inheritNode("expert");
 }
 
-std::vector<bool> CSkillHandler::getDefaultAllowed() const
+std::set<SecondarySkill> CSkillHandler::getDefaultAllowed() const
 {
-	std::vector<bool> allowedSkills(objects.size(), true);
-	return allowedSkills;
+	std::set<SecondarySkill> result;
+
+	for (auto const & skill : objects)
+		result.insert(skill->getId());
+
+	return result;
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/CSkillHandler.h

@@ -90,7 +90,7 @@ public:
 	void afterLoadFinalization() override;
 	void beforeValidate(JsonNode & object) override;
 
-	std::vector<bool> getDefaultAllowed() const override;
+	std::set<SecondarySkill> getDefaultAllowed() const;
 
 protected:
 	const std::vector<std::string> & getTypeNames() const override;

+ 28 - 31
lib/CTownHandler.cpp

@@ -144,12 +144,12 @@ CFaction::~CFaction()
 
 int32_t CFaction::getIndex() const
 {
-	return index;
+	return index.getNum();
 }
 
 int32_t CFaction::getIconIndex() const
 {
-	return index; //???
+	return index.getNum(); //???
 }
 
 std::string CFaction::getJsonKey() const
@@ -172,8 +172,8 @@ void CFaction::registerIcons(const IconRegistar & cb) const
 		cb(info.icons[1][0] + 2, 0, "ITPA", info.iconSmall[1][0]);
 		cb(info.icons[1][1] + 2, 0, "ITPA", info.iconSmall[1][1]);
 
-		cb(index, 1, "CPRSMALL", info.towerIconSmall);
-		cb(index, 1, "TWCRPORT", info.towerIconLarge);
+		cb(index.getNum(), 1, "CPRSMALL", info.towerIconSmall);
+		cb(index.getNum(), 1, "TWCRPORT", info.towerIconLarge);
 
 	}
 }
@@ -734,7 +734,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 
 	ret->town->buildings[ret->bid] = ret;
 
-	registerObject(source.meta, ret->town->getBuildingScope(), ret->identifier, ret->bid);
+	registerObject(source.meta, ret->town->getBuildingScope(), ret->identifier, ret->bid.getNum());
 }
 
 void CTownHandler::loadBuildings(CTown * town, const JsonNode & source)
@@ -1115,10 +1115,10 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 	if (object->town)
 	{
 		auto & info = object->town->clientInfo;
-		info.icons[0][0] = 8 + object->index * 4 + 0;
-		info.icons[0][1] = 8 + object->index * 4 + 1;
-		info.icons[1][0] = 8 + object->index * 4 + 2;
-		info.icons[1][1] = 8 + object->index * 4 + 3;
+		info.icons[0][0] = 8 + object->index.getNum() * 4 + 0;
+		info.icons[0][1] = 8 + object->index.getNum() * 4 + 1;
+		info.icons[1][0] = 8 + object->index.getNum() * 4 + 2;
+		info.icons[1][1] = 8 + object->index.getNum() * 4 + 3;
 
 		VLC->identifiers()->requestIdentifier(scope, "object", "town", [=](si32 index)
 		{
@@ -1142,7 +1142,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 		});
 	}
 
-	registerObject(scope, "faction", name, object->index);
+	registerObject(scope, "faction", name, object->index.getNum());
 }
 
 void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
@@ -1158,10 +1158,10 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 	if (object->town)
 	{
 		auto & info = object->town->clientInfo;
-		info.icons[0][0] = (GameConstants::F_NUMBER + object->index) * 2 + 0;
-		info.icons[0][1] = (GameConstants::F_NUMBER + object->index) * 2 + 1;
-		info.icons[1][0] = object->index * 2 + 0;
-		info.icons[1][1] = object->index * 2 + 1;
+		info.icons[0][0] = (GameConstants::F_NUMBER + object->index.getNum()) * 2 + 0;
+		info.icons[0][1] = (GameConstants::F_NUMBER + object->index.getNum()) * 2 + 1;
+		info.icons[1][0] = object->index.getNum() * 2 + 0;
+		info.icons[1][1] = object->index.getNum() * 2 + 1;
 
 		VLC->identifiers()->requestIdentifier(scope, "object", "town", [=](si32 index)
 		{
@@ -1173,7 +1173,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
 		});
 	}
 
-	registerObject(scope, "faction", name, object->index);
+	registerObject(scope, "faction", name, object->index.getNum());
 }
 
 void CTownHandler::loadRandomFaction()
@@ -1259,31 +1259,28 @@ void CTownHandler::initializeWarMachines()
 	warMachinesToLoad.clear();
 }
 
-std::vector<bool> CTownHandler::getDefaultAllowed() const
+std::set<FactionID> CTownHandler::getDefaultAllowed() const
 {
-	std::vector<bool> allowedFactions;
-	allowedFactions.reserve(objects.size());
+	std::set<FactionID> allowedFactions;
+
 	for(auto town : objects)
-	{
-		allowedFactions.push_back(town->town != nullptr);
-	}
+		if (town->town != nullptr)
+			allowedFactions.insert(town->getId());
+
 	return allowedFactions;
 }
 
 std::set<FactionID> CTownHandler::getAllowedFactions(bool withTown) const
 {
-	std::set<FactionID> allowedFactions;
-	std::vector<bool> allowed;
-	if (withTown)
-		allowed = getDefaultAllowed();
-	else
-		allowed.resize( objects.size(), true);
+	if (!withTown)
+		return getDefaultAllowed();
 
-	for (size_t i=0; i<allowed.size(); i++)
-		if (allowed[i])
-			allowedFactions.insert(static_cast<FactionID>(i));
+	std::set<FactionID> result;
+	for(auto town : objects)
+		result.insert(town->getId());
+
+	return result;
 
-	return allowedFactions;
 }
 
 const std::vector<std::string> & CTownHandler::getTypeNames() const

+ 1 - 1
lib/CTownHandler.h

@@ -350,7 +350,7 @@ public:
 	void loadCustom() override;
 	void afterLoadFinalization() override;
 
-	std::vector<bool> getDefaultAllowed() const override;
+	std::set<FactionID> getDefaultAllowed() const;
 	std::set<FactionID> getAllowedFactions(bool withTown = true) const;
 
 	static void loadSpecialBuildingBonuses(const JsonNode & source, BonusList & bonusList, CBuilding * building);

+ 1 - 1
lib/IGameCallback.cpp

@@ -160,7 +160,7 @@ void CPrivilegedInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::
 	{
 		const spells::Spell * spell = SpellID(i).toSpell();
 
-		if (!isAllowed(0, spell->getIndex()))
+		if (!isAllowed(spell->getId()))
 			continue;
 
 		if (level.has_value() && spell->getLevel() != level)

+ 0 - 7
lib/IHandlerBase.h

@@ -44,13 +44,6 @@ public:
 	/// allows handler to do post-loading step for validation or integration of loaded data
 	virtual void afterLoadFinalization(){};
 
-	/**
-	 * Gets a list of objects that are allowed by default on maps
-	 *
-	 * @return a list of allowed objects, the index is the object id
-	 */
-	virtual std::vector<bool> getDefaultAllowed() const = 0;
-
 	virtual ~IHandlerBase(){}
 };
 

+ 8 - 8
lib/JsonRandom.cpp

@@ -156,7 +156,7 @@ namespace JsonRandom
 
 		for (auto const & artID : valuesSet)
 		{
-			CArtifact * art = VLC->arth->objects[artID];
+			const CArtifact * art = artID.toArtifact();
 
 			if(!vstd::iswithin(art->getPrice(), minValue, maxValue))
 				continue;
@@ -164,7 +164,7 @@ namespace JsonRandom
 			if(!allowedClasses.empty() && !allowedClasses.count(art->aClass))
 				continue;
 
-			if(!IObjectInterface::cb->isAllowed(1, art->getIndex()))
+			if(!IObjectInterface::cb->isAllowed(art->getId()))
 				continue;
 
 			if(!allowedPositions.empty())
@@ -344,7 +344,7 @@ namespace JsonRandom
 	{
 		std::set<SecondarySkill> defaultSkills;
 		for(const auto & skill : VLC->skillh->objects)
-			if (IObjectInterface::cb->isAllowed(2, skill->getIndex()))
+			if (IObjectInterface::cb->isAllowed(skill->getId()))
 				defaultSkills.insert(skill->getId());
 
 		std::set<SecondarySkill> potentialPicks = filterKeys(value, defaultSkills, variables);
@@ -366,7 +366,7 @@ namespace JsonRandom
 		{
 			std::set<SecondarySkill> defaultSkills;
 			for(const auto & skill : VLC->skillh->objects)
-				if (IObjectInterface::cb->isAllowed(2, skill->getIndex()))
+				if (IObjectInterface::cb->isAllowed(skill->getId()))
 					defaultSkills.insert(skill->getId());
 
 			for(const auto & element : value.Vector())
@@ -406,7 +406,7 @@ namespace JsonRandom
 	{
 		std::set<SpellID> defaultSpells;
 		for(const auto & spell : VLC->spellh->objects)
-			if (IObjectInterface::cb->isAllowed(0, spell->getIndex()))
+			if (IObjectInterface::cb->isAllowed(spell->getId()))
 				defaultSpells.insert(spell->getId());
 
 		std::set<SpellID> potentialPicks = filterKeys(value, defaultSpells, variables);
@@ -482,13 +482,13 @@ namespace JsonRandom
 		else
 			logMod->warn("Failed to select suitable random creature!");
 
-		stack.type = VLC->creh->objects[pickedCreature];
+		stack.type = pickedCreature.toCreature();
 		stack.count = loadValue(value, rng, variables);
 		if (!value["upgradeChance"].isNull() && !stack.type->upgrades.empty())
 		{
 			if (int(value["upgradeChance"].Float()) > rng.nextInt(99)) // select random upgrade
 			{
-				stack.type = VLC->creh->objects[*RandomGeneratorUtil::nextItem(stack.type->upgrades, rng)];
+				stack.type = RandomGeneratorUtil::nextItem(stack.type->upgrades, rng)->toCreature();
 			}
 		}
 		return stack;
@@ -523,7 +523,7 @@ namespace JsonRandom
 			if (node["upgradeChance"].Float() > 0)
 			{
 				for(const auto & creaID : crea->upgrades)
-					info.allowedCreatures.push_back(VLC->creh->objects[creaID]);
+					info.allowedCreatures.push_back(creaID.toCreature());
 			}
 			ret.push_back(info);
 		}

+ 5 - 2
lib/MetaString.cpp

@@ -114,8 +114,6 @@ std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) c
 	{
 		case EMetaText::GENERAL_TXT:
 			return VLC->generaltexth->translate("core.genrltxt", ser);
-		case EMetaText::RES_NAMES:
-			return VLC->generaltexth->translate("core.restypes", ser);
 		case EMetaText::ARRAY_TXT:
 			return VLC->generaltexth->translate("core.arraytxt", ser);
 		case EMetaText::ADVOB_TXT:
@@ -374,6 +372,11 @@ void MetaString::replaceName(const SpellID & id)
 	replaceTextID(id.toEntity(VLC)->getNameTextID());
 }
 
+void MetaString::replaceName(const GameResID& id)
+{
+	replaceTextID(TextIdentifier("core.restypes", id.getNum()).get());
+}
+
 void MetaString::replaceNameSingular(const CreatureID & id)
 {
 	replaceTextID(id.toEntity(VLC)->getNameSingularTextID());

+ 2 - 1
lib/MetaString.h

@@ -21,13 +21,13 @@ class MapObjectSubID;
 class PlayerColor;
 class SecondarySkill;
 class SpellID;
+class GameResID;
 using TQuantity = si32;
 
 /// Strings classes that can be used as replacement in MetaString
 enum class EMetaText : uint8_t
 {
 	GENERAL_TXT = 1,
-	RES_NAMES,
 	ARRAY_TXT,
 	ADVOB_TXT,
 	JK_TXT
@@ -97,6 +97,7 @@ public:
 	void replaceName(const PlayerColor& id);
 	void replaceName(const SecondarySkill& id);
 	void replaceName(const SpellID& id);
+	void replaceName(const GameResID& id);
 
 	/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
 	void replaceName(const CreatureID & id, TQuantity count);

+ 0 - 5
lib/ObstacleHandler.cpp

@@ -114,11 +114,6 @@ std::vector<JsonNode> ObstacleHandler::loadLegacyData()
 	return {};
 }
 
-std::vector<bool> ObstacleHandler::getDefaultAllowed() const
-{
-	return {};
-}
-
 const std::vector<std::string> & ObstacleHandler::getTypeNames() const
 {
 	static const std::vector<std::string> types = { "obstacle" };

+ 0 - 1
lib/ObstacleHandler.h

@@ -71,7 +71,6 @@ public:
 	
 	const std::vector<std::string> & getTypeNames() const override;
 	std::vector<JsonNode> loadLegacyData() override;
-	std::vector<bool> getDefaultAllowed() const override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 0 - 5
lib/RiverHandler.cpp

@@ -68,11 +68,6 @@ std::vector<JsonNode> RiverTypeHandler::loadLegacyData()
 	return {};
 }
 
-std::vector<bool> RiverTypeHandler::getDefaultAllowed() const
-{
-	return {};
-}
-
 std::string RiverType::getJsonKey() const
 {
 	return modScope + ":" + identifier;

+ 0 - 1
lib/RiverHandler.h

@@ -71,7 +71,6 @@ public:
 
 	virtual const std::vector<std::string> & getTypeNames() const override;
 	virtual std::vector<JsonNode> loadLegacyData() override;
-	virtual std::vector<bool> getDefaultAllowed() const override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 0 - 5
lib/RoadHandler.cpp

@@ -59,11 +59,6 @@ std::vector<JsonNode> RoadTypeHandler::loadLegacyData()
 	return {};
 }
 
-std::vector<bool> RoadTypeHandler::getDefaultAllowed() const
-{
-	return {};
-}
-
 std::string RoadType::getJsonKey() const
 {
 	return modScope + ":" + identifier;

+ 0 - 1
lib/RoadHandler.h

@@ -61,7 +61,6 @@ public:
 
 	virtual const std::vector<std::string> & getTypeNames() const override;
 	virtual std::vector<JsonNode> loadLegacyData() override;
-	virtual std::vector<bool> getDefaultAllowed() const override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 0 - 5
lib/ScriptHandler.cpp

@@ -217,11 +217,6 @@ const Script * ScriptHandler::resolveScript(const std::string & name) const
 	}
 }
 
-std::vector<bool> ScriptHandler::getDefaultAllowed() const
-{
-	return std::vector<bool>();
-}
-
 std::vector<JsonNode> ScriptHandler::loadLegacyData()
 {
 	return std::vector<JsonNode>();

+ 0 - 1
lib/ScriptHandler.h

@@ -97,7 +97,6 @@ public:
 
 	const Script * resolveScript(const std::string & name) const;
 
-	std::vector<bool> getDefaultAllowed() const override;
 	std::vector<JsonNode> loadLegacyData() override;
 
 	ScriptPtr loadFromJson(vstd::CLoggerBase * logger, const std::string & scope, const JsonNode & json, const std::string & identifier) const;

+ 0 - 5
lib/TerrainHandler.cpp

@@ -140,11 +140,6 @@ std::vector<JsonNode> TerrainTypeHandler::loadLegacyData()
 	return result;
 }
 
-std::vector<bool> TerrainTypeHandler::getDefaultAllowed() const
-{
-	return {};
-}
-
 bool TerrainType::isLand() const
 {
 	return !isWater();

+ 0 - 1
lib/TerrainHandler.h

@@ -109,7 +109,6 @@ public:
 
 	virtual const std::vector<std::string> & getTypeNames() const override;
 	virtual std::vector<JsonNode> loadLegacyData() override;
-	virtual std::vector<bool> getDefaultAllowed() const override;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/battle/CUnitState.cpp

@@ -428,7 +428,7 @@ const CGHeroInstance * CUnitState::getHeroCaster() const
 	return nullptr;
 }
 
-int32_t CUnitState::getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool) const
+int32_t CUnitState::getSpellSchoolLevel(const spells::Spell * spell, SpellSchool * outSelectedSchool) const
 {
 	int32_t skill = valOfBonuses(Selector::typeSubtype(BonusType::SPELLCASTER, BonusSubtypeID(spell->getId())));
 	vstd::abetween(skill, 0, 3);

+ 1 - 1
lib/battle/CUnitState.h

@@ -182,7 +182,7 @@ public:
 
 	int32_t getCasterUnitId() const override;
 
-	int32_t getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool = nullptr) const override;
+	int32_t getSpellSchoolLevel(const spells::Spell * spell, SpellSchool * outSelectedSchool = nullptr) const override;
 	int32_t getEffectLevel(const spells::Spell * spell) const override;
 
 	int64_t getSpellBonus(const spells::Spell * spell, int64_t base, const Unit * affectedStack) const override;

+ 23 - 3
lib/constants/EntityIdentifiers.cpp

@@ -160,12 +160,12 @@ std::string CampaignScenarioID::encode(const si32 index)
 	return std::to_string(index);
 }
 
-std::string Obj::encode(int32_t index)
+std::string MapObjectID::encode(int32_t index)
 {
-	return VLC->objtypeh->getObjectHandlerName(index);
+	return VLC->objtypeh->getObjectHandlerName(MapObjectID(index));
 }
 
-si32 Obj::decode(const std::string & identifier)
+si32 MapObjectID::decode(const std::string & identifier)
 {
 	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "objects", identifier);
 	if(rawId)
@@ -236,6 +236,16 @@ std::string SecondarySkill::encode(const si32 index)
 	return VLC->skills()->getById(SecondarySkill(index))->getJsonKey();
 }
 
+const CSkill * SecondarySkill::toSkill() const
+{
+	return dynamic_cast<const CSkill *>(toEntity(VLC));
+}
+
+const Skill * SecondarySkill::toEntity(const Services * services) const
+{
+	return services->skills()->getByIndex(num);
+}
+
 const CCreature * CreatureIDBase::toCreature() const
 {
 	return VLC->creh->objects.at(num);
@@ -522,4 +532,14 @@ std::string SecondarySkill::entityType()
 	return "secondarySkill";
 }
 
+std::string BuildingID::encode(int32_t index)
+{
+	return std::to_string(index);
+}
+
+si32 BuildingID::decode(const std::string & identifier)
+{
+	return std::stoi(identifier);
+}
+
 VCMI_LIB_NAMESPACE_END

+ 38 - 1
lib/constants/EntityIdentifiers.h

@@ -23,6 +23,7 @@ class HeroType;
 class CHero;
 class HeroTypeService;
 class Faction;
+class Skill;
 class RoadType;
 class RiverType;
 class TerrainType;
@@ -308,6 +309,9 @@ public:
 	static std::string entityType();
 	static si32 decode(const std::string& identifier);
 	static std::string encode(const si32 index);
+
+	const CSkill * toSkill() const;
+	const Skill * toEntity(const Services * services) const;
 };
 
 class DLL_LINKAGE PrimarySkill : public Identifier<PrimarySkill>
@@ -409,10 +413,24 @@ public:
 	}
 };
 
-class BuildingID : public IdentifierWithEnum<BuildingID, BuildingIDBase>
+class DLL_LINKAGE BuildingID : public IdentifierWithEnum<BuildingID, BuildingIDBase>
 {
 public:
 	using IdentifierWithEnum<BuildingID, BuildingIDBase>::IdentifierWithEnum;
+
+	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::TOWN_HALL + level);
+	}
+
+	static std::string encode(int32_t index);
+	static si32 decode(const std::string & identifier);
 };
 
 class MapObjectBaseID : public IdentifierBase
@@ -567,6 +585,12 @@ public:
 
 	static std::string encode(int32_t index);
 	static si32 decode(const std::string & identifier);
+
+	// TODO: Remove
+	constexpr operator int32_t () const
+	{
+		return num;
+	}
 };
 
 class MapObjectSubID : public Identifier<MapObjectSubID>
@@ -590,6 +614,12 @@ public:
 		this->num = value.getNum();
 		return *this;
 	}
+
+	// TODO: Remove
+	constexpr operator int32_t () const
+	{
+		return num;
+	}
 };
 
 class DLL_LINKAGE RoadId : public Identifier<RoadId>
@@ -668,6 +698,12 @@ class ArtifactPosition : public IdentifierWithEnum<ArtifactPosition, ArtifactPos
 {
 public:
 	using IdentifierWithEnum<ArtifactPosition, ArtifactPositionBase>::IdentifierWithEnum;
+
+	// TODO: Remove
+	constexpr operator int32_t () const
+	{
+		return num;
+	}
 };
 
 class ArtifactIDBase : public IdentifierBase
@@ -724,6 +760,7 @@ public:
 		FIRE_ELEMENTAL = 114, // for tests
 		PSYCHIC_ELEMENTAL = 120, // for hardcoded ability
 		MAGIC_ELEMENTAL = 121, // for hardcoded ability
+		AZURE_DRAGON = 132,
 		CATAPULT = 145,
 		BALLISTA = 146,
 		FIRST_AID_TENT = 147,

+ 3 - 2
lib/constants/VariantIdentifier.h

@@ -17,9 +17,10 @@ VCMI_LIB_NAMESPACE_BEGIN
 template<typename... Types>
 class VariantIdentifier
 {
-	std::variant<Types...> value;
-public:
+	using Type = std::variant<Types...>;
+	Type value;
 
+public:
 	VariantIdentifier()
 	{}
 

+ 18 - 38
lib/gameState/CGameState.cpp

@@ -780,8 +780,10 @@ void CGameState::initTowns()
 		campaign->initTowns();
 
 	CGTownInstance::universitySkills.clear();
-	for ( int i=0; i<4; i++)
-		CGTownInstance::universitySkills.push_back(14+i);//skills for university
+	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::FIRE_MAGIC));
+	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::AIR_MAGIC));
+	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::WATER_MAGIC));
+	CGTownInstance::universitySkills.push_back(SecondarySkill(SecondarySkill::EARTH_MAGIC));
 
 	for (auto & elem : map->towns)
 	{
@@ -1382,7 +1384,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 		case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
 		{
 			for(const auto & elem : p->heroes)
-				if(elem->hasArt(ArtifactID(condition.objectType)))
+				if(elem->hasArt(condition.objectType.as<ArtifactID>()))
 					return true;
 			return false;
 		}
@@ -1398,7 +1400,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 					&& (ai = dynamic_cast<const CArmedInstance *>(object.get()))) //contains army
 				{
 					for(const auto & elem : ai->Slots()) //iterate through army
-						if(elem.second->type->getIndex() == condition.objectType) //it's searched creature
+						if(elem.second->getId() == condition.objectType.as<CreatureID>()) //it's searched creature
 							total += elem.second->count;
 				}
 			}
@@ -1406,20 +1408,20 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 		}
 		case EventCondition::HAVE_RESOURCES:
 		{
-			return p->resources[condition.objectType] >= condition.value;
+			return p->resources[condition.objectType.as<GameResID>()] >= condition.value;
 		}
 		case EventCondition::HAVE_BUILDING:
 		{
 			if (condition.object) // specific town
 			{
 				const auto * t = dynamic_cast<const CGTownInstance *>(condition.object);
-				return (t->tempOwner == player && t->hasBuilt(BuildingID(condition.objectType)));
+				return (t->tempOwner == player && t->hasBuilt(condition.objectType.as<BuildingID>()));
 			}
 			else // any town
 			{
 				for (const CGTownInstance * t : p->towns)
 				{
-					if (t->hasBuilt(BuildingID(condition.objectType)))
+					if (t->hasBuilt(condition.objectType.as<BuildingID>()))
 						return true;
 				}
 				return false;
@@ -1438,7 +1440,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 			{
 				for(const auto & elem : map->objects) // mode B - destroy all objects of this type
 				{
-					if(elem && elem->ID.getNum() == condition.objectType)
+					if(elem && elem->ID == condition.objectType.as<MapObjectID>())
 						return false;
 				}
 				return true;
@@ -1459,7 +1461,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 				for(const auto & elem : map->objects) // mode B - flag all objects of this type
 				{
 					 //check not flagged objs
-					if ( elem && elem->ID.getNum() == condition.objectType && team.count(elem->tempOwner) == 0 )
+					if ( elem && elem->ID == condition.objectType.as<MapObjectID>() && team.count(elem->tempOwner) == 0 )
 						return false;
 				}
 				return true;
@@ -1468,8 +1470,8 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 		case EventCondition::TRANSPORT:
 		{
 			const auto * t = dynamic_cast<const CGTownInstance *>(condition.object);
-			return (t->visitingHero && t->visitingHero->hasArt(ArtifactID(condition.objectType))) ||
-				   (t->garrisonHero && t->garrisonHero->hasArt(ArtifactID(condition.objectType)));
+			return (t->visitingHero && t->visitingHero->hasArt(condition.objectType.as<ArtifactID>())) ||
+				   (t->garrisonHero && t->garrisonHero->hasArt(condition.objectType.as<ArtifactID>()));
 		}
 		case EventCondition::DAYS_PASSED:
 		{
@@ -1490,24 +1492,6 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
 		{
 			return condition.value; // just convert to bool
 		}
-		case EventCondition::HAVE_0:
-		{
-			logGlobal->debug("Not implemented event condition type: %d", (int)condition.condition);
-			//TODO: support new condition format
-			return false;
-		}
-		case EventCondition::HAVE_BUILDING_0:
-		{
-			logGlobal->debug("Not implemented event condition type: %d", (int)condition.condition);
-			//TODO: support new condition format
-			return false;
-		}
-		case EventCondition::DESTROY_0:
-		{
-			logGlobal->debug("Not implemented event condition type: %d", (int)condition.condition);
-			//TODO: support new condition format
-			return false;
-		}
 		default:
 			logGlobal->error("Invalid event condition type: %d", (int)condition.condition);
 			return false;
@@ -1791,13 +1775,13 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
 		{
 			if(playerInactive(player.second.color)) //do nothing for neutral player
 				continue;
-			int bestCre = -1; //best creature's ID
+			CreatureID bestCre; //best creature's ID
 			for(const auto & elem : player.second.heroes)
 			{
 				for(const auto & it : elem->Slots())
 				{
-					int toCmp = it.second->type->getId(); //ID of creature we should compare with the best one
-					if(bestCre == -1 || VLC->creh->objects[bestCre]->getAIValue() < VLC->creh->objects[toCmp]->getAIValue())
+					CreatureID toCmp = it.second->type->getId(); //ID of creature we should compare with the best one
+					if(bestCre == CreatureID::NONE || bestCre.toEntity(VLC)->getAIValue() < toCmp.toEntity(VLC)->getAIValue())
 					{
 						bestCre = toCmp;
 					}
@@ -1862,8 +1846,7 @@ void CGameState::attachArmedObjects()
 
 bool CGameState::giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid)
 {
-	 CArtifact * const artifact = VLC->arth->objects[aid]; //pointer to constant object
-	 CArtifactInstance * ai = ArtifactUtils::createNewArtifactInstance(artifact);
+	 CArtifactInstance * ai = ArtifactUtils::createNewArtifactInstance(aid);
 	 map->addNewArtifactInstance(ai);
 	 auto slot = ArtifactUtils::getArtAnyPosition(h, aid);
 	 if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
@@ -1879,10 +1862,7 @@ bool CGameState::giveHeroArtifact(CGHeroInstance * h, const ArtifactID & aid)
 
 std::set<HeroTypeID> CGameState::getUnusedAllowedHeroes(bool alsoIncludeNotAllowed) const
 {
-	std::set<HeroTypeID> ret;
-	for(int i = 0; i < map->allowedHeroes.size(); i++)
-		if(map->allowedHeroes[i] || alsoIncludeNotAllowed)
-			ret.insert(HeroTypeID(i));
+	std::set<HeroTypeID> ret = map->allowedHeroes;
 
 	for(const auto & playerSettingPair : scenarioOps->playerInfos) //remove uninitialized yet heroes picked for start by other players
 	{

+ 2 - 2
lib/gameState/CGameStateCampaign.cpp

@@ -189,8 +189,8 @@ void CGameStateCampaign::placeCampaignHeroes()
 		auto it = gameState->scenarioOps->playerInfos.find(playerColor);
 		if(it != gameState->scenarioOps->playerInfos.end())
 		{
-			auto heroTypeId = campaignBonus->info2;
-			if(heroTypeId == 0xffff) // random bonus hero
+			HeroTypeID heroTypeId = HeroTypeID(campaignBonus->info2);
+			if(heroTypeId.getNum() == 0xffff) // random bonus hero
 			{
 				heroTypeId = gameState->pickUnusedHeroTypeRandomly(playerColor);
 			}

+ 1 - 1
lib/gameState/SThievesGuildInfo.h

@@ -23,7 +23,7 @@ struct DLL_LINKAGE SThievesGuildInfo
 	std::map<PlayerColor, InfoAboutHero> colorToBestHero; //maps player's color to his best heros'
 
 	std::map<PlayerColor, EAiTactic> personality; // color to personality // ai tactic
-	std::map<PlayerColor, si32> bestCreature; // color to ID // id or -1 if not known
+	std::map<PlayerColor, CreatureID> bestCreature; // color to ID // id or -1 if not known
 
 //	template <typename Handler> void serialize(Handler &h, const int version)
 //	{

+ 16 - 23
lib/mapObjectConstructors/CObjectClassesHandler.cpp

@@ -117,7 +117,7 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData()
 		tmpl->readTxt(parser);
 		parser.endLine();
 
-		std::pair<si32, si32> key(tmpl->id.num, tmpl->subid);
+		std::pair key(tmpl->id, tmpl->subid);
 		legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(tmpl)));
 	}
 
@@ -288,32 +288,26 @@ void CObjectClassesHandler::loadObject(std::string scope, std::string name, cons
 void CObjectClassesHandler::loadSubObject(const std::string & identifier, JsonNode config, MapObjectID ID, MapObjectSubID subID)
 {
 	config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not NULL
-	assert(objects[ID]);
+	assert(objects[ID.getNum()]);
 
-	if ( subID >= objects[ID]->objects.size())
-		objects[ID]->objects.resize(subID+1);
+	if ( subID.getNum() >= objects[ID.getNum()]->objects.size())
+		objects[ID.getNum()]->objects.resize(subID.getNum()+1);
 
-	JsonUtils::inherit(config, objects.at(ID)->base);
-	loadSubObject(config.meta, identifier, config, objects[ID], subID);
+	JsonUtils::inherit(config, objects.at(ID.getNum())->base);
+	loadSubObject(config.meta, identifier, config, objects[ID.getNum()], subID.getNum());
 }
 
 void CObjectClassesHandler::removeSubObject(MapObjectID ID, MapObjectSubID subID)
 {
-	assert(objects[ID]);
-	assert(subID < objects[ID]->objects.size());
-	objects[ID]->objects[subID] = nullptr;
-}
-
-std::vector<bool> CObjectClassesHandler::getDefaultAllowed() const
-{
-	return std::vector<bool>(); //TODO?
+	assert(objects[ID.getNum()]);
+	objects[ID.getNum()]->objects[subID.getNum()] = nullptr;
 }
 
 TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObjectSubID subtype) const
 {
 	try
 	{
-		auto result = objects.at(type)->objects.at(subtype);
+		auto result = objects.at(type.getNum())->objects.at(subtype.getNum());
 
 		if (result != nullptr)
 			return result;
@@ -323,7 +317,7 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(MapObjectID type, MapObj
 		// Leave catch block silently
 	}
 
-	std::string errorString = "Failed to find object of type " + std::to_string(type) + "::" + std::to_string(subtype);
+	std::string errorString = "Failed to find object of type " + std::to_string(type.getNum()) + "::" + std::to_string(subtype.getNum());
 	logGlobal->error(errorString);
 	throw std::runtime_error(errorString);
 }
@@ -365,13 +359,13 @@ std::set<MapObjectSubID> CObjectClassesHandler::knownSubObjects(MapObjectID prim
 {
 	std::set<MapObjectSubID> ret;
 
-	if (!objects.at(primaryID))
+	if (!objects.at(primaryID.getNum()))
 	{
 		logGlobal->error("Failed to find object %d", primaryID);
 		return ret;
 	}
 
-	for(const auto & entry : objects.at(primaryID)->objects)
+	for(const auto & entry : objects.at(primaryID.getNum())->objects)
 		if (entry)
 			ret.insert(entry->subtype);
 
@@ -465,7 +459,7 @@ std::string CObjectClassesHandler::getObjectName(MapObjectID type, MapObjectSubI
 	if (handler && handler->hasNameTextID())
 		return handler->getNameTranslated();
 	else
-		return objects[type]->getNameTranslated();
+		return objects[type.getNum()]->getNameTranslated();
 }
 
 SObjectSounds CObjectClassesHandler::getObjectSounds(MapObjectID type, MapObjectSubID subtype) const
@@ -477,20 +471,19 @@ SObjectSounds CObjectClassesHandler::getObjectSounds(MapObjectID type, MapObject
 	if(type == Obj::PRISON || type == Obj::HERO || type == Obj::SPELL_SCROLL)
 		subtype = 0;
 
-	assert(objects[type]);
-	assert(subtype < objects[type]->objects.size());
+	assert(objects[type.getNum()]);
 
 	return getHandlerFor(type, subtype)->getSounds();
 }
 
 std::string CObjectClassesHandler::getObjectHandlerName(MapObjectID type) const
 {
-	return objects.at(type)->handlerName;
+	return objects.at(type.getNum())->handlerName;
 }
 
 std::string CObjectClassesHandler::getJsonKey(MapObjectID type) const
 {
-	return objects.at(type)->getJsonKey();
+	return objects.at(type.getNum())->getJsonKey();
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 3
lib/mapObjectConstructors/CObjectClassesHandler.h

@@ -74,7 +74,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
 	std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
 
 	/// container with H3 templates, used only during loading, no need to serialize it
-	using TTemplatesContainer = std::multimap<std::pair<si32, si32>, std::shared_ptr<const ObjectTemplate>>;
+	using TTemplatesContainer = std::multimap<std::pair<MapObjectID, MapObjectSubID>, std::shared_ptr<const ObjectTemplate>>;
 	TTemplatesContainer legacyTemplates;
 
 	TObjectTypeHandler loadSubObjectFromJson(const std::string & scope, const std::string & identifier, const JsonNode & entry, ObjectClass * obj, size_t index);
@@ -101,8 +101,6 @@ public:
 	void beforeValidate(JsonNode & object) override;
 	void afterLoadFinalization() override;
 
-	std::vector<bool> getDefaultAllowed() const override;
-
 	/// Queries to detect loaded objects
 	std::set<MapObjectID> knownObjects() const;
 	std::set<MapObjectSubID> knownSubObjects(MapObjectID primaryID) const;

+ 1 - 1
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -261,7 +261,7 @@ void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenera
 	if(auto * university = dynamic_cast<CGUniversity *>(object))
 	{
 		for(auto skill : JsonRandom::loadSecondaries(predefinedOffer, rng, emptyVariables))
-			university->skills.push_back(skill.first.getNum());
+			university->skills.push_back(skill.first);
 	}
 }
 

+ 1 - 1
lib/mapObjects/CArmedInstance.cpp

@@ -19,7 +19,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-void CArmedInstance::randomizeArmy(int type)
+void CArmedInstance::randomizeArmy(FactionID type)
 {
 	for (auto & elem : stacks)
 	{

+ 1 - 1
lib/mapObjects/CArmedInstance.h

@@ -29,7 +29,7 @@ private:
 public:
 	BattleInfo *battle; //set to the current battle, if engaged
 
-	void randomizeArmy(int type);
+	void randomizeArmy(FactionID type);
 	virtual void updateMoraleBonusFromArmy();
 
 	void armyChanged() override;

+ 1 - 1
lib/mapObjects/CBank.cpp

@@ -273,7 +273,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 				iw.components.emplace_back(ComponentType::RESOURCE, it, bc->resources[it]);
 				loot.appendRawString("%d %s");
 				loot.replaceNumber(bc->resources[it]);
-				loot.replaceLocalString(EMetaText::RES_NAMES, it);
+				loot.replaceName(it);
 				cb->giveResource(hero->getOwner(), it, bc->resources[it]);
 			}
 		}

+ 2 - 2
lib/mapObjects/CGDwelling.cpp

@@ -49,7 +49,7 @@ CGDwelling::~CGDwelling() = default;
 FactionID CGDwelling::randomizeFaction(CRandomGenerator & rand)
 {
 	if (ID == Obj::RANDOM_DWELLING_FACTION)
-		return FactionID(subID);
+		return FactionID(subID.getNum());
 
 	assert(ID == Obj::RANDOM_DWELLING || ID == Obj::RANDOM_DWELLING_LVL);
 	assert(randomizationInfo.has_value());
@@ -138,7 +138,7 @@ void CGDwelling::pickRandomObject(CRandomGenerator & rand)
 		auto testID = [&](const Obj & primaryID) -> MapObjectSubID
 		{
 			auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID);
-			for (si32 entry : dwellingIDs)
+			for (MapObjectSubID entry : dwellingIDs)
 			{
 				const auto * handler = dynamic_cast<const DwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
 

+ 15 - 19
lib/mapObjects/CGHeroInstance.cpp

@@ -212,7 +212,7 @@ bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const
 	if ( !canLearnSkill())
 		return false;
 
-	if (!cb->isAllowed(2, which))
+	if (!cb->isAllowed(which))
 		return false;
 
 	if (getSecSkillLevel(which) > 0)
@@ -590,11 +590,11 @@ void CGHeroInstance::pickRandomObject(CRandomGenerator & rand)
 	{
 		ID = Obj::HERO;
 		subID = cb->gameState()->pickNextHeroType(getOwner());
-		type = VLC->heroh->objects[subID];
+		type = VLC->heroh->objects[getHeroType().getNum()];
 		randomizeArmy(type->heroClass->faction);
 	}
 	else
-		type = VLC->heroh->objects[subID];
+		type = VLC->heroh->objects[getHeroType().getNum()];
 
 	auto oldSubID = subID;
 
@@ -669,7 +669,7 @@ int32_t CGHeroInstance::getCasterUnitId() const
 	return id.getNum();
 }
 
-int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool) const
+int32_t CGHeroInstance::getSpellSchoolLevel(const spells::Spell * spell, SpellSchool * outSelectedSchool) const
 {
 	int32_t skill = -1; //skill level
 
@@ -789,7 +789,7 @@ void CGHeroInstance::spendMana(ServerCallback * server, const int spellCost) con
 
 bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
 {
-	const bool isAllowed = IObjectInterface::cb->isAllowed(0, spell->getIndex());
+	const bool isAllowed = IObjectInterface::cb->isAllowed(spell->getId());
 
 	const bool inSpellBook = vstd::contains(spells, spell->getId()) && hasSpellbook();
 	const bool specificBonus = hasBonusOfType(BonusType::SPELL, BonusSubtypeID(spell->getId()));
@@ -853,7 +853,7 @@ bool CGHeroInstance::canLearnSpell(const spells::Spell * spell, bool allowBanned
 		return false;//creature abilities can not be learned
 	}
 
-	if(!allowBanned && !IObjectInterface::cb->isAllowed(0, spell->getIndex()))
+	if(!allowBanned && !IObjectInterface::cb->isAllowed(spell->getId()))
 	{
 		logGlobal->warn("Hero %s try to learn banned spell %s", nodeName(), spell->getNameTranslated());
 		return false;//banned spells should not be learned
@@ -921,7 +921,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
 		// raise upgraded creature (at 2/3 rate) if no space available otherwise
 		if(getSlotFor(creatureTypeRaised) == SlotID())
 		{
-			for(const CreatureID & upgraded : VLC->creh->objects[creatureTypeRaised]->upgrades)
+			for(const CreatureID & upgraded : creatureTypeRaised.toCreature()->upgrades)
 			{
 				if(getSlotFor(upgraded) != SlotID())
 				{
@@ -932,7 +932,7 @@ CStackBasicDescriptor CGHeroInstance::calculateNecromancy (const BattleResult &b
 			}
 		}
 		// calculate number of creatures raised - low level units contribute at 50% rate
-		const double raisedUnitHealth = VLC->creh->objects[creatureTypeRaised]->getMaxHealth();
+		const double raisedUnitHealth = creatureTypeRaised.toCreature()->getMaxHealth();
 		double raisedUnits = 0;
 		for(const auto & casualty : casualties)
 		{
@@ -1535,7 +1535,7 @@ std::string CGHeroInstance::getHeroTypeName() const
 		}
 		else
 		{
-			return VLC->heroh->objects[getHeroType()]->getJsonKey();
+			return getHeroType().toEntity(VLC)->getJsonKey();
 		}
 	}
 	return "";
@@ -1669,15 +1669,11 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
 			for(size_t skillIndex = 0; skillIndex < secondarySkills.size(); ++skillIndex)
 			{
 				JsonArraySerializer inner = secondarySkills.enterArray(skillIndex);
-				const si32 rawId = secSkills.at(skillIndex).first;
-
-				if(rawId < 0 || rawId >= VLC->skillh->size())
-					logGlobal->error("Invalid secondary skill %d", rawId);
+				SecondarySkill skillId = secSkills.at(skillIndex).first;
 
-				auto value = (*VLC->skillh)[SecondarySkill(rawId)]->getJsonKey();
-				handler.serializeString("skill", value);
-				value = NSecondarySkill::levels.at(secSkills.at(skillIndex).second);
-				handler.serializeString("level", value);
+				handler.serializeId("skill", skillId);
+				std::string skillLevel = NSecondarySkill::levels.at(secSkills.at(skillIndex).second);
+				handler.serializeString("level", skillLevel);
 			}
 		}
 	}
@@ -1769,7 +1765,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 			if(!appearance)
 			{
 				// crossoverDeserialize
-				type = VLC->heroh->objects[getHeroType()];
+				type = VLC->heroh->objects[getHeroType().getNum()];
 				appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
 			}
 
@@ -1794,7 +1790,7 @@ bool CGHeroInstance::isMissionCritical() const
 
 		auto const & testFunctor = [&](const EventCondition & condition)
 		{
-			if ((condition.condition == EventCondition::CONTROL || condition.condition == EventCondition::HAVE_0) && condition.object)
+			if ((condition.condition == EventCondition::CONTROL) && condition.object)
 			{
 				const auto * hero = dynamic_cast<const CGHeroInstance *>(condition.object);
 				return (hero != this);

+ 1 - 1
lib/mapObjects/CGHeroInstance.h

@@ -274,7 +274,7 @@ public:
 
 	///spells::Caster
 	int32_t getCasterUnitId() const override;
-	int32_t getSpellSchoolLevel(const spells::Spell * spell, int32_t * outSelectedSchool = nullptr) const override;
+	int32_t getSpellSchoolLevel(const spells::Spell * spell, SpellSchool * outSelectedSchool = nullptr) const override;
 	int64_t getSpellBonus(const spells::Spell * spell, int64_t base, const battle::Unit * affectedStack) const override;
 	int64_t getSpecificSpellBonus(const spells::Spell * spell, int64_t base) const override;
 

+ 8 - 22
lib/mapObjects/CGMarket.cpp

@@ -48,18 +48,18 @@ int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
 	return -1;
 }
 
-std::vector<int> CGMarket::availableItemsIds(EMarketMode mode) const
+std::vector<TradeItemBuy> CGMarket::availableItemsIds(EMarketMode mode) const
 {
 	if(allowsTrade(mode))
 		return IMarket::availableItemsIds(mode);
-	return std::vector<int>();
+	return std::vector<TradeItemBuy>();
 }
 
 CGMarket::CGMarket()
 {
 }
 
-std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode mode) const
+std::vector<TradeItemBuy> CGBlackMarket::availableItemsIds(EMarketMode mode) const
 {
 	switch(mode)
 	{
@@ -67,16 +67,16 @@ std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode mode) const
 		return IMarket::availableItemsIds(mode);
 	case EMarketMode::RESOURCE_ARTIFACT:
 		{
-			std::vector<int> ret;
+			std::vector<TradeItemBuy> ret;
 			for(const CArtifact *a : artifacts)
 				if(a)
 					ret.push_back(a->getId());
 				else
-					ret.push_back(-1);
+					ret.push_back(ArtifactID{});
 			return ret;
 		}
 	default:
-		return std::vector<int>();
+		return std::vector<TradeItemBuy>();
 	}
 }
 
@@ -96,21 +96,7 @@ void CGBlackMarket::newTurn(CRandomGenerator & rand) const
 	cb->sendAndApply(&saa);
 }
 
-void CGUniversity::initObj(CRandomGenerator & rand)
-{
-	CGMarket::initObj(rand);
-	
-	std::vector<int> toChoose;
-	for(int i = 0; i < VLC->skillh->size(); ++i)
-	{
-		if(!vstd::contains(skills, i) && cb->isAllowed(2, i))
-		{
-			toChoose.push_back(i);
-		}
-	}
-}
-
-std::vector<int> CGUniversity::availableItemsIds(EMarketMode mode) const
+std::vector<TradeItemBuy> CGUniversity::availableItemsIds(EMarketMode mode) const
 {
 	switch (mode)
 	{
@@ -118,7 +104,7 @@ std::vector<int> CGUniversity::availableItemsIds(EMarketMode mode) const
 			return skills;
 
 		default:
-			return std::vector<int>();
+			return std::vector<TradeItemBuy>();
 	}
 }
 

+ 4 - 5
lib/mapObjects/CGMarket.h

@@ -34,7 +34,7 @@ public:
 	int getMarketEfficiency() const override;
 	bool allowsTrade(EMarketMode mode) const override;
 	int availableUnits(EMarketMode mode, int marketItemSerial) const override; //-1 if unlimited
-	std::vector<int> availableItemsIds(EMarketMode mode) const override;
+	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -52,7 +52,7 @@ public:
 	std::vector<const CArtifact *> artifacts; //available artifacts
 
 	void newTurn(CRandomGenerator & rand) const override; //reset artifacts for black market every month
-	std::vector<int> availableItemsIds(EMarketMode mode) const override;
+	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -64,10 +64,9 @@ public:
 class DLL_LINKAGE CGUniversity : public CGMarket
 {
 public:
-	std::vector<int> skills; //available skills
+	std::vector<TradeItemBuy> skills; //available skills
 
-	std::vector<int> availableItemsIds(EMarketMode mode) const override;
-	void initObj(CRandomGenerator & rand) override;//set skills for trade
+	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
 	void onHeroVisit(const CGHeroInstance * h) const override; //open window
 
 	template <typename Handler> void serialize(Handler &h, const int version)

+ 14 - 42
lib/mapObjects/CGTownInstance.cpp

@@ -34,7 +34,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 std::vector<const CArtifact *> CGTownInstance::merchantArtifacts;
-std::vector<int> CGTownInstance::universitySkills;
+std::vector<TradeItemBuy> CGTownInstance::universitySkills;
 
 
 int CGTownInstance::getSightRadius() const //returns sight distance
@@ -136,7 +136,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
 	if (creatures[level].second.empty())
 		return ret; //no dwelling
 
-	const CCreature *creature = VLC->creh->objects[creatures[level].second.back()];
+	const Creature *creature = creatures[level].second.back().toEntity(VLC);
 	const int base = creature->getGrowth();
 	int castleBonus = 0;
 
@@ -489,8 +489,8 @@ void CGTownInstance::pickRandomObject(CRandomGenerator & rand)
 
 	assert(ID == Obj::TOWN); // just in case
 	setType(ID, subID);
-	town = (*VLC->townh)[subID]->town;
-	randomizeArmy(subID);
+	town = (*VLC->townh)[getFaction()]->town;
+	randomizeArmy(getFaction());
 	updateAppearance();
 }
 
@@ -571,7 +571,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
 				}
 				else //upgrade
 				{
-					cb->changeStackType(sl, VLC->creh->objects[*c->upgrades.begin()]);
+					cb->changeStackType(sl, c->upgrades.begin()->toCreature());
 				}
 			}
 			if ((stacksCount() < GameConstants::ARMY_SIZE && rand.nextInt(99) < 25) || Slots().empty()) //add new stack
@@ -592,7 +592,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
 						{
 							StackLocation sl(this, n);
 							if (slotEmpty(n))
-								cb->insertNewStack(sl, VLC->creh->objects[c], count);
+								cb->insertNewStack(sl, c.toCreature(), count);
 							else //add to existing
 								cb->changeStackCount(sl, count);
 						}
@@ -765,16 +765,16 @@ bool CGTownInstance::allowsTrade(EMarketMode mode) const
 	}
 }
 
-std::vector<int> CGTownInstance::availableItemsIds(EMarketMode mode) const
+std::vector<TradeItemBuy> CGTownInstance::availableItemsIds(EMarketMode mode) const
 {
 	if(mode == EMarketMode::RESOURCE_ARTIFACT)
 	{
-		std::vector<int> ret;
+		std::vector<TradeItemBuy> ret;
 		for(const CArtifact *a : merchantArtifacts)
 			if(a)
 				ret.push_back(a->getId());
 			else
-				ret.push_back(-1);
+				ret.push_back(ArtifactID{});
 		return ret;
 	}
 	else if ( mode == EMarketMode::RESOURCE_SKILL )
@@ -1149,7 +1149,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 
 			for(const BuildingID & id : forbiddenBuildings)
 			{
-				buildingsLIC.none.insert(id);
+				buildingsLIC.none.insert(id.getNum());
 				customBuildings = true;
 			}
 
@@ -1166,7 +1166,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 				if(id == BuildingID::FORT)
 					hasFort = true;
 
-				buildingsLIC.all.insert(id);
+				buildingsLIC.all.insert(id.getNum());
 				customBuildings = true;
 			}
 
@@ -1201,42 +1201,14 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 	}
 
 	{
-		std::vector<bool> standard = VLC->spellh->getDefaultAllowed();
-		JsonSerializeFormat::LIC spellsLIC(standard, SpellID::decode, SpellID::encode);
-
-		if(handler.saving)
-		{
-			for(const SpellID & id : possibleSpells)
-				spellsLIC.any[id.num] = true;
-
-			for(const SpellID & id : obligatorySpells)
-				spellsLIC.all[id.num] = true;
-		}
-
-		handler.serializeLIC("spells", spellsLIC);
-
-		if(!handler.saving)
-		{
-			possibleSpells.clear();
-			for(si32 idx = 0; idx < spellsLIC.any.size(); idx++)
-			{
-				if(spellsLIC.any[idx])
-					possibleSpells.emplace_back(idx);
-			}
-
-			obligatorySpells.clear();
-			for(si32 idx = 0; idx < spellsLIC.all.size(); idx++)
-			{
-				if(spellsLIC.all[idx])
-					obligatorySpells.emplace_back(idx);
-			}
-		}
+		handler.serializeIdArray( "possibleSpells", possibleSpells);
+		handler.serializeIdArray( "obligatorySpells", obligatorySpells);
 	}
 }
 
 FactionID CGTownInstance::getFaction() const
 {
-	return town->faction->getId();
+	return  FactionID(subID.getNum());
 }
 
 TerrainId CGTownInstance::getNativeTerrain() const

+ 2 - 2
lib/mapObjects/CGTownInstance.h

@@ -68,7 +68,7 @@ public:
 
 	//////////////////////////////////////////////////////////////////////////
 	static std::vector<const CArtifact *> merchantArtifacts; //vector of artifacts available at Artifact merchant, NULLs possible (for making empty space when artifact is bought)
-	static std::vector<int> universitySkills;//skills for university of magic
+	static std::vector<TradeItemBuy> universitySkills;//skills for university of magic
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -150,7 +150,7 @@ public:
 	const IObjectInterface * getObject() const override;
 	int getMarketEfficiency() const override; //=market count
 	bool allowsTrade(EMarketMode mode) const override;
-	std::vector<int> availableItemsIds(EMarketMode mode) const override;
+	std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
 
 	void updateAppearance();
 

+ 2 - 2
lib/mapObjects/CQuest.cpp

@@ -242,13 +242,13 @@ void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & com
 	if(mission.resources.nonZero())
 	{
 		MetaString loot;
-		for(int i = 0; i < 7; ++i)
+		for(auto i : GameResID::ALL_RESOURCES())
 		{
 			if(mission.resources[i])
 			{
 				loot.appendRawString("%d %s");
 				loot.replaceNumber(mission.resources[i]);
-				loot.replaceLocalString(EMetaText::RES_NAMES, i);
+				loot.replaceName(i);
 			}
 		}
 		text.replaceRawString(loot.buildList());

+ 4 - 4
lib/mapObjects/IMarket.cpp

@@ -140,16 +140,16 @@ int IMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
 	}
 }
 
-std::vector<int> IMarket::availableItemsIds(EMarketMode mode) const
+std::vector<TradeItemBuy> IMarket::availableItemsIds(EMarketMode mode) const
 {
-	std::vector<int> ret;
+	std::vector<TradeItemBuy> ret;
 	switch(mode)
 	{
 	case EMarketMode::RESOURCE_RESOURCE:
 	case EMarketMode::ARTIFACT_RESOURCE:
 	case EMarketMode::CREATURE_RESOURCE:
-		for (int i = 0; i < 7; i++)
-			ret.push_back(i);
+		for (auto res : GameResID::ALL_RESOURCES())
+			ret.push_back(res);
 	}
 	return ret;
 }

+ 3 - 2
lib/mapObjects/IMarket.h

@@ -9,7 +9,8 @@
  */
 #pragma once
 
-#include "../GameConstants.h"
+#include "../networkPacks/TradeItem.h"
+#include "../constants/Enumerations.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -24,7 +25,7 @@ public:
 	virtual int getMarketEfficiency() const = 0;
 	virtual bool allowsTrade(EMarketMode mode) const;
 	virtual int availableUnits(EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
-	virtual std::vector<int> availableItemsIds(EMarketMode mode) const;
+	virtual std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const;
 
 	bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units
 	std::vector<EMarketMode> availableModes() const;

+ 3 - 3
lib/mapObjects/MiscObjects.cpp

@@ -33,7 +33,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
+std::map <MapObjectSubID, std::vector<ObjectInstanceID> > CGMagi::eyelist;
 ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map
 std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
 
@@ -219,7 +219,7 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler)
 			for(const auto & resID : abandonedMineResources)
 			{
 				JsonNode one(JsonNode::JsonType::DATA_STRING);
-				one.String() = GameConstants::RESOURCE_NAMES[resID];
+				one.String() = GameConstants::RESOURCE_NAMES[resID.getNum()];
 				node.Vector().push_back(one);
 			}
 			handler.serializeRaw("possibleResources", node, std::nullopt);
@@ -320,7 +320,7 @@ void CGResource::collectRes(const PlayerColor & player) const
 	{
 		sii.type = EInfoWindowMode::INFO;
 		sii.text.appendLocalString(EMetaText::ADVOB_TXT,113);
-		sii.text.replaceLocalString(EMetaText::RES_NAMES, resourceID());
+		sii.text.replaceName(resourceID());
 	}
 	sii.components.emplace_back(ComponentType::RESOURCE, resourceID(), amount);
 	sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6);

+ 1 - 1
lib/mapObjects/MiscObjects.h

@@ -338,7 +338,7 @@ protected:
 class DLL_LINKAGE CGMagi : public CGObjectInstance
 {
 public:
-	static std::map <si32, std::vector<ObjectInstanceID> > eyelist; //[subID][id], supports multiple sets as in H5
+	static std::map <MapObjectSubID, std::vector<ObjectInstanceID> > eyelist; //[subID][id], supports multiple sets as in H5
 
 	static void reset();
 

+ 2 - 2
lib/mapObjects/ObjectTemplate.h

@@ -43,8 +43,8 @@ class DLL_LINKAGE ObjectTemplate
 
 public:
 	/// H3 ID/subID of this object
-	Obj id;
-	si32 subid;
+	MapObjectID id;
+	MapObjectSubID subid;
 	/// print priority, objects with higher priority will be print first, below everything else
 	si32 printPriority;
 	/// animation file that should be used to display object

+ 26 - 56
lib/mapping/CMap.cpp

@@ -79,8 +79,9 @@ void CCastleEvent::serializeJson(JsonSerializeFormat & handler)
 		a.syncSize(temp);
 		for(int i = 0; i < temp.size(); ++i)
 		{
-			a.serializeInt(i, temp[i]);
-			buildings.insert(temp[i]);
+			int buildingID = temp[i].getNum();
+			a.serializeInt(i, buildingID);
+			buildings.insert(buildingID);
 		}
 	}
 
@@ -432,16 +433,16 @@ void CMap::checkForObjectives()
 			switch (cond.condition)
 			{
 				case EventCondition::HAVE_ARTIFACT:
-					event.onFulfill.replaceTextID(VLC->artifacts()->getByIndex(cond.objectType)->getNameTextID());
+					event.onFulfill.replaceTextID(cond.objectType.as<ArtifactID>().toEntity(VLC)->getNameTextID());
 					break;
 
 				case EventCondition::HAVE_CREATURES:
-					event.onFulfill.replaceTextID(VLC->creatures()->getByIndex(cond.objectType)->getNameSingularTextID());
+					event.onFulfill.replaceTextID(cond.objectType.as<CreatureID>().toEntity(VLC)->getNameSingularTextID());
 					event.onFulfill.replaceNumber(cond.value);
 					break;
 
 				case EventCondition::HAVE_RESOURCES:
-					event.onFulfill.replaceLocalString(EMetaText::RES_NAMES, cond.objectType);
+					event.onFulfill.replaceName(cond.objectType.as<GameResID>());
 					event.onFulfill.replaceNumber(cond.value);
 					break;
 
@@ -452,7 +453,7 @@ void CMap::checkForObjectives()
 
 				case EventCondition::CONTROL:
 					if (isInTheMap(cond.position))
-						cond.object = getObjectiveObjectFrom(cond.position, static_cast<Obj>(cond.objectType));
+						cond.object = getObjectiveObjectFrom(cond.position, cond.objectType.as<MapObjectID>());
 
 					if (cond.object)
 					{
@@ -467,7 +468,7 @@ void CMap::checkForObjectives()
 
 				case EventCondition::DESTROY:
 					if (isInTheMap(cond.position))
-						cond.object = getObjectiveObjectFrom(cond.position, static_cast<Obj>(cond.objectType));
+						cond.object = getObjectiveObjectFrom(cond.position, cond.objectType.as<MapObjectID>());
 
 					if (cond.object)
 					{
@@ -483,14 +484,6 @@ void CMap::checkForObjectives()
 				//break; case EventCondition::IS_HUMAN:
 				//break; case EventCondition::DAYS_WITHOUT_TOWN:
 				//break; case EventCondition::STANDARD_WIN:
-
-				//TODO: support new condition format
-				case EventCondition::HAVE_0:
-					break;
-				case EventCondition::DESTROY_0:
-					break;
-				case EventCondition::HAVE_BUILDING_0:
-					break;
 			}
 			return cond;
 		};
@@ -624,67 +617,44 @@ void CMap::banWaterContent()
 
 void CMap::banWaterSpells()
 {
-	for (int j = 0; j < allowedSpells.size(); j++)
+	vstd::erase_if(allowedSpells, [&](SpellID spell)
 	{
-		if (allowedSpells[j])
-		{
-			auto* spell = dynamic_cast<const CSpell*>(VLC->spells()->getByIndex(j));
-			if (spell->onlyOnWaterMap && !isWaterMap())
-			{
-				allowedSpells[j] = false;
-			}
-		}
-	}
+		return spell.toSpell()->onlyOnWaterMap && !isWaterMap();
+	});
 }
 
 void CMap::banWaterArtifacts()
 {
-	for (int j = 0; j < allowedArtifact.size(); j++)
+	vstd::erase_if(allowedArtifact, [&](ArtifactID artifact)
 	{
-		if (allowedArtifact[j])
-		{
-			auto* art = dynamic_cast<const CArtifact*>(VLC->artifacts()->getByIndex(j));
-			if (art->onlyOnWaterMap && !isWaterMap())
-			{
-				allowedArtifact[j] = false;
-			}
-		}
-	}
+		return artifact.toArtifact()->onlyOnWaterMap && !isWaterMap();
+	});
 }
 
 void CMap::banWaterSkills()
 {
-	for (int j = 0; j < allowedAbilities.size(); j++)
+	vstd::erase_if(allowedAbilities, [&](SecondarySkill skill)
 	{
-		if (allowedAbilities[j])
-		{
-			auto* skill = dynamic_cast<const CSkill*>(VLC->skills()->getByIndex(j));
-			if (skill->onlyOnWaterMap && !isWaterMap())
-			{
-				allowedAbilities[j] = false;
-			}
-		}
-	}
+		return skill.toSkill()->onlyOnWaterMap && !isWaterMap();
+	});
 }
 
 void CMap::banWaterHeroes()
 {
-	for (int j = 0; j < allowedHeroes.size(); j++)
+	vstd::erase_if(allowedHeroes, [&](HeroTypeID hero)
 	{
-		if (allowedHeroes[j])
-		{
-			auto* h = dynamic_cast<const CHero*>(VLC->heroTypes()->getByIndex(j));
-			if ((h->onlyOnWaterMap && !isWaterMap()) || (h->onlyOnMapWithoutWater && isWaterMap()))
-			{
-				banHero(HeroTypeID(j));
-			}
-		}
-	}
+		return hero.toHeroType()->onlyOnWaterMap && !isWaterMap();
+	});
+
+	vstd::erase_if(allowedHeroes, [&](HeroTypeID hero)
+	{
+		return hero.toHeroType()->onlyOnMapWithoutWater && isWaterMap();
+	});
 }
 
 void CMap::banHero(const HeroTypeID & id)
 {
-	allowedHeroes.at(id) = false;
+	allowedHeroes.erase(id);
 }
 
 void CMap::initTerrain()

+ 3 - 3
lib/mapping/CMap.h

@@ -129,9 +129,9 @@ public:
 	std::vector<Rumor> rumors;
 	std::vector<DisposedHero> disposedHeroes;
 	std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
-	std::vector<bool> allowedSpells;
-	std::vector<bool> allowedArtifact;
-	std::vector<bool> allowedAbilities;
+	std::set<SpellID> allowedSpells;
+	std::set<ArtifactID> allowedArtifact;
+	std::set<SecondarySkill> allowedAbilities;
 	std::list<CMapEvent> events;
 	int3 grailPos;
 	int grailRadius;

+ 1 - 6
lib/mapping/CMapHeader.cpp

@@ -69,21 +69,16 @@ bool PlayerInfo::hasCustomMainHero() const
 
 EventCondition::EventCondition(EWinLoseType condition):
 	object(nullptr),
-	metaType(EMetaclass::INVALID),
 	value(-1),
-	objectType(-1),
-	objectSubtype(-1),
 	position(-1, -1, -1),
 	condition(condition)
 {
 }
 
-EventCondition::EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position):
+EventCondition::EventCondition(EWinLoseType condition, si32 value, TargetTypeID objectType, const int3 & position):
 	object(nullptr),
-	metaType(EMetaclass::INVALID),
 	value(value),
 	objectType(objectType),
-	objectSubtype(-1),
 	position(position),
 	condition(condition)
 {}

+ 7 - 15
lib/mapping/CMapHeader.h

@@ -10,6 +10,7 @@
 
 #pragma once
 
+#include "../constants/VariantIdentifier.h"
 #include "../modding/CModInfo.h"
 #include "../LogicalExpression.h"
 #include "../int3.h"
@@ -99,7 +100,6 @@ struct DLL_LINKAGE PlayerInfo
 struct DLL_LINKAGE EventCondition
 {
 	enum EWinLoseType {
-		//internal use, deprecated
 		HAVE_ARTIFACT,     // type - required artifact
 		HAVE_CREATURES,    // type - creatures to collect, value - amount to collect
 		HAVE_RESOURCES,    // type - resource ID, value - amount to collect
@@ -108,27 +108,21 @@ struct DLL_LINKAGE EventCondition
 		DESTROY,           // position - position of object, optional, type - type of object
 		TRANSPORT,         // position - where artifact should be transported, type - type of artifact
 
-		//map format version pre 1.0
 		DAYS_PASSED,       // value - number of days from start of the game
 		IS_HUMAN,          // value - 0 = player is AI, 1 = player is human
 		DAYS_WITHOUT_TOWN, // value - how long player can live without town, 0=instakill
 		STANDARD_WIN,      // normal defeat all enemies condition
 		CONST_VALUE,        // condition that always evaluates to "value" (0 = false, 1 = true)
-
-		//map format version 1.0+
-		HAVE_0,
-		HAVE_BUILDING_0,
-		DESTROY_0
 	};
 
+	using TargetTypeID = VariantIdentifier<ArtifactID, CreatureID, GameResID, BuildingID, MapObjectID>;
+
 	EventCondition(EWinLoseType condition = STANDARD_WIN);
-	EventCondition(EWinLoseType condition, si32 value, si32 objectType, const int3 & position = int3(-1, -1, -1));
+	EventCondition(EWinLoseType condition, si32 value, TargetTypeID objectType, const int3 & position = int3(-1, -1, -1));
 
 	const CGObjectInstance * object; // object that was at specified position or with instance name on start
-	EMetaclass metaType;
 	si32 value;
-	si32 objectType;
-	si32 objectSubtype;
+	TargetTypeID objectType;
 	std::string objectInstanceName;
 	int3 position;
 	EWinLoseType condition;
@@ -141,9 +135,7 @@ struct DLL_LINKAGE EventCondition
 		h & objectType;
 		h & position;
 		h & condition;
-		h & objectSubtype;
 		h & objectInstanceName;
-		h & metaType;
 	}
 };
 
@@ -238,8 +230,8 @@ public:
 
 	std::vector<PlayerInfo> players; /// The default size of the vector is PlayerColor::PLAYER_LIMIT.
 	ui8 howManyTeams;
-	std::vector<bool> allowedHeroes;
-	std::vector<HeroTypeID> reservedCampaignHeroes; /// Heroes that have placeholders in this map and are reserverd for campaign
+	std::set<HeroTypeID> allowedHeroes;
+	std::set<HeroTypeID> reservedCampaignHeroes; /// Heroes that have placeholders in this map and are reserverd for campaign
 
 	bool areAnyPlayers; /// Unused. True if there are any playable players on the map.
 

+ 26 - 26
lib/mapping/MapFormatH3M.cpp

@@ -268,7 +268,7 @@ void CMapLoaderH3M::readPlayerInfo()
 			{
 				SHeroName vv;
 				vv.heroId = reader->readHero();
-				vv.heroName = readLocalizedString(TextIdentifier("header", "heroNames", vv.heroId));
+				vv.heroName = readLocalizedString(TextIdentifier("header", "heroNames", vv.heroId.getNum()));
 
 				playerInfo.heroesNames.push_back(vv);
 			}
@@ -381,7 +381,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			case EVictoryConditionType::GATHERRESOURCE:
 			{
 				EventCondition cond(EventCondition::HAVE_RESOURCES);
-				cond.objectType = reader->readUInt8();
+				cond.objectType = reader->readGameResID();
 				cond.value = reader->readInt32();
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279");
@@ -397,9 +397,9 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				EventExpression::OperatorAll oper;
 				EventCondition cond(EventCondition::HAVE_BUILDING);
 				cond.position = reader->readInt3();
-				cond.objectType = BuildingID::TOWN_HALL + reader->readUInt8();
+				cond.objectType = BuildingID::HALL_LEVEL(reader->readUInt8() + 1);
 				oper.expressions.emplace_back(cond);
-				cond.objectType = BuildingID::FORT + reader->readUInt8();
+				cond.objectType = BuildingID::FORT_LEVEL(reader->readUInt8());
 				oper.expressions.emplace_back(cond);
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283");
@@ -414,7 +414,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				assert(allowNormalVictory == true); // not selectable in editor
 				assert(appliesToAI == true); // not selectable in editor
 				EventCondition cond(EventCondition::HAVE_BUILDING);
-				cond.objectType = BuildingID::GRAIL;
+				cond.objectType = BuildingID(BuildingID::GRAIL);
 				cond.position = reader->readInt3();
 				if(cond.position.z > 2)
 					cond.position = int3(-1, -1, -1);
@@ -433,7 +433,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				allowNormalVictory = true; // H3 behavior
 				assert(appliesToAI == false); // not selectable in editor
 				EventCondition cond(EventCondition::DESTROY);
-				cond.objectType = Obj::HERO;
+				cond.objectType = MapObjectID(MapObjectID::HERO);
 				cond.position = reader->readInt3();
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253");
@@ -446,7 +446,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			case EVictoryConditionType::CAPTURECITY:
 			{
 				EventCondition cond(EventCondition::CONTROL);
-				cond.objectType = Obj::TOWN;
+				cond.objectType = MapObjectID(MapObjectID::TOWN);
 				cond.position = reader->readInt3();
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250");
@@ -460,7 +460,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			{
 				assert(appliesToAI == true); // not selectable in editor
 				EventCondition cond(EventCondition::DESTROY);
-				cond.objectType = Obj::MONSTER;
+				cond.objectType = MapObjectID(MapObjectID::MONSTER);
 				cond.position = reader->readInt3();
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.287");
@@ -473,8 +473,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			case EVictoryConditionType::TAKEDWELLINGS:
 			{
 				EventExpression::OperatorAll oper;
-				oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR1));
-				oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR4));
+				oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj(Obj::CREATURE_GENERATOR1)));
+				oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj(Obj::CREATURE_GENERATOR4)));
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.289");
 				specialVictory.onFulfill.appendTextID("core.genrltxt.288");
@@ -486,7 +486,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			case EVictoryConditionType::TAKEMINES:
 			{
 				EventCondition cond(EventCondition::CONTROL);
-				cond.objectType = Obj::MINE;
+				cond.objectType = MapObjectID(MapObjectID::MINE);
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.291");
 				specialVictory.onFulfill.appendTextID("core.genrltxt.290");
@@ -499,7 +499,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			{
 				assert(allowNormalVictory == true); // not selectable in editor
 				EventCondition cond(EventCondition::TRANSPORT);
-				cond.objectType = reader->readUInt8();
+				cond.objectType = reader->readArtifact8();
 				cond.position = reader->readInt3();
 
 				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293");
@@ -513,7 +513,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			{
 				assert(appliesToAI == false); // not selectable in editor
 				EventCondition cond(EventCondition::DESTROY);
-				cond.objectType = Obj::MONSTER;
+				cond.objectType = MapObjectID(MapObjectID::MONSTER);
 
 				specialVictory.effect.toOtherMessage.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toOthers");
 				specialVictory.onFulfill.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toSelf");
@@ -602,7 +602,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			{
 				EventExpression::OperatorNone noneOf;
 				EventCondition cond(EventCondition::CONTROL);
-				cond.objectType = Obj::TOWN;
+				cond.objectType = Obj(Obj::TOWN);
 				cond.position = reader->readInt3();
 
 				noneOf.expressions.emplace_back(cond);
@@ -616,7 +616,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 			{
 				EventExpression::OperatorNone noneOf;
 				EventCondition cond(EventCondition::CONTROL);
-				cond.objectType = Obj::HERO;
+				cond.objectType = Obj(Obj::HERO);
 				cond.position = reader->readInt3();
 
 				noneOf.expressions.emplace_back(cond);
@@ -692,7 +692,7 @@ void CMapLoaderH3M::readAllowedHeroes()
 		for (uint32_t i = 0; i < placeholdersQty; ++i)
 		{
 			auto heroID = reader->readHero();
-			mapHeader->reservedCampaignHeroes.push_back(heroID);
+			mapHeader->reservedCampaignHeroes.insert(heroID);
 		}
 	}
 }
@@ -707,8 +707,8 @@ void CMapLoaderH3M::readDisposedHeroes()
 		for(int g = 0; g < disp; ++g)
 		{
 			map->disposedHeroes[g].heroId = reader->readHero();
-			map->disposedHeroes[g].portrait.setNum(reader->readHeroPortrait());
-			map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId));
+			map->disposedHeroes[g].portrait = reader->readHeroPortrait();
+			map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId.getNum()));
 			reader->readBitmaskPlayers(map->disposedHeroes[g].players, false);
 		}
 	}
@@ -764,13 +764,13 @@ void CMapLoaderH3M::readAllowedArtifacts()
 	{
 		for(CArtifact * artifact : VLC->arth->objects)
 			if(artifact->isCombined())
-				map->allowedArtifact[artifact->getId()] = false;
+				map->allowedArtifact.erase(artifact->getId());
 	}
 
 	if(!features.levelAB)
 	{
-		map->allowedArtifact[ArtifactID::VIAL_OF_DRAGON_BLOOD] = false;
-		map->allowedArtifact[ArtifactID::ARMAGEDDONS_BLADE] = false;
+		map->allowedArtifact.erase(ArtifactID::VIAL_OF_DRAGON_BLOOD);
+		map->allowedArtifact.erase(ArtifactID::ARMAGEDDONS_BLADE);
 	}
 
 	// Messy, but needed
@@ -780,7 +780,7 @@ void CMapLoaderH3M::readAllowedArtifacts()
 		{
 			if(cond.condition == EventCondition::HAVE_ARTIFACT || cond.condition == EventCondition::TRANSPORT)
 			{
-				map->allowedArtifact[cond.objectType] = false;
+				map->allowedArtifact.erase(cond.objectType.as<ArtifactID>());
 			}
 			return cond;
 		};
@@ -1160,7 +1160,7 @@ CGObjectInstance * CMapLoaderH3M::readWitchHut(const int3 & position, std::share
 			auto defaultAllowed = VLC->skillh->getDefaultAllowed();
 
 			for(int skillID = features.skillsCount; skillID < defaultAllowed.size(); ++skillID)
-				if(defaultAllowed[skillID])
+				if(defaultAllowed.count(skillID))
 					allowedAbilities.insert(SecondarySkill(skillID));
 		}
 
@@ -2085,7 +2085,7 @@ int CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
 			{
 				auto artid = reader->readArtifact();
 				guard->quest->mission.artifacts.push_back(artid);
-				map->allowedArtifact[artid] = false; //these are unavailable for random generation
+				map->allowedArtifact.erase(artid); //these are unavailable for random generation
 			}
 			break;
 		}
@@ -2095,7 +2095,7 @@ int CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
 			guard->quest->mission.creatures.resize(typeNumber);
 			for(int hh = 0; hh < typeNumber; ++hh)
 			{
-				guard->quest->mission.creatures[hh].type = VLC->creh->objects[reader->readCreature()];
+				guard->quest->mission.creatures[hh].type = reader->readCreature().toCreature();
 				guard->quest->mission.creatures[hh].count = reader->readUInt16();
 			}
 			break;
@@ -2212,7 +2212,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
 
 		//add all spells from mods
 		for(int i = features.spellsCount; i < defaultAllowed.size(); ++i)
-			if(defaultAllowed[i])
+			if(defaultAllowed.count(i))
 				object->possibleSpells.emplace_back(i);
 	}
 

+ 31 - 154
lib/mapping/MapFormatJson.cpp

@@ -139,63 +139,6 @@ namespace TriggeredEventsDetail
 
 	static const std::array<std::string, 2> typeNames = { "victory", "defeat" };
 
-	static EMetaclass decodeMetaclass(const std::string & source)
-	{
-		if(source.empty())
-			return EMetaclass::INVALID;
-		auto rawId = vstd::find_pos(NMetaclass::names, source);
-
-		if(rawId >= 0)
-			return static_cast<EMetaclass>(rawId);
-		else
-			return EMetaclass::INVALID;
-	}
-
-	static std::string encodeIdentifier(EMetaclass metaType, si32 type)
-	{
-		std::string metaclassName = NMetaclass::names[static_cast<int>(metaType)];
-		std::string identifier;
-
-		switch(metaType)
-		{
-		case EMetaclass::ARTIFACT:
-			{
-				identifier = ArtifactID::encode(type);
-			}
-			break;
-		case EMetaclass::CREATURE:
-			{
-				identifier = CreatureID::encode(type);
-			}
-			break;
-		case EMetaclass::OBJECT:
-			{
-				//TODO
-				auto subtypes = VLC->objtypeh->knownSubObjects(type);
-				if(!subtypes.empty())
-				{
-					auto subtype = *subtypes.begin();
-					auto handler = VLC->objtypeh->getHandlerFor(type, subtype);
-					identifier = handler->getTypeName();
-				}
-			}
-			break;
-		case EMetaclass::RESOURCE:
-			{
-				identifier = GameConstants::RESOURCE_NAMES[type];
-			}
-			break;
-		default:
-			{
-				logGlobal->error("Unsupported metaclass %s for event condition", metaclassName);
-				return "";
-			}
-			break;
-		}
-
-		return ModUtility::makeFullIdentifier("", metaclassName, identifier);
-	}
-
 	static EventCondition JsonToCondition(const JsonNode & node)
 	{
 		EventCondition event;
@@ -210,54 +153,28 @@ namespace TriggeredEventsDetail
 		{
 			const JsonNode & data = node.Vector()[1];
 
+			event.objectInstanceName = data["object"].String();
+			event.value = data["value"].Integer();
+
 			switch (event.condition)
 			{
-			case EventCondition::HAVE_0:
-			case EventCondition::DESTROY_0:
-				{
-					//todo: support subtypes
-
-					std::string fullIdentifier = data["type"].String();
-					std::string metaTypeName;
-					std::string scope;
-					std::string identifier;
-					ModUtility::parseIdentifier(fullIdentifier, scope, metaTypeName, identifier);
-
-					event.metaType = decodeMetaclass(metaTypeName);
-
-					auto type = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), fullIdentifier, false);
-
-					if(type)
-						event.objectType = type.value();
-					event.objectInstanceName = data["object"].String();
-					if(data["value"].isNumber())
-						event.value = static_cast<si32>(data["value"].Integer());
-				}
-				break;
-			case EventCondition::HAVE_BUILDING_0:
-				{
-					//todo: support of new condition format HAVE_BUILDING_0
-				}
-				break;
-			default:
-				{
-					//old format
-					if (data["type"].getType() == JsonNode::JsonType::DATA_STRING)
-					{
-						auto identifier = VLC->identifiers()->getIdentifier(data["type"]);
-						if(identifier)
-							event.objectType = identifier.value();
-						else
-							throw std::runtime_error("Identifier resolution failed in event condition");
-					}
-
-					if (data["type"].isNumber())
-						event.objectType = static_cast<si32>(data["type"].Float());
-
-					if (!data["value"].isNull())
-						event.value = static_cast<si32>(data["value"].Float());
-				}
-				break;
+				case EventCondition::HAVE_ARTIFACT:
+				case EventCondition::TRANSPORT:
+					event.objectType = ArtifactID(ArtifactID::decode(data["type"].String()));
+					break;
+				case EventCondition::HAVE_CREATURES:
+					event.objectType = CreatureID(CreatureID::decode(data["type"].String()));
+					break;
+				case EventCondition::HAVE_RESOURCES:
+					event.objectType = GameResID(GameResID::decode(data["type"].String()));
+					break;
+				case EventCondition::HAVE_BUILDING:
+					event.objectType = BuildingID(BuildingID::decode(data["type"].String()));
+					break;
+				case EventCondition::CONTROL:
+				case EventCondition::DESTROY:
+					event.objectType = MapObjectID(MapObjectID::decode(data["type"].String()));
+					break;
 			}
 
 			if (!data["position"].isNull())
@@ -283,39 +200,11 @@ namespace TriggeredEventsDetail
 
 		JsonNode data;
 
-		switch (event.condition)
-		{
-		case EventCondition::HAVE_0:
-		case EventCondition::DESTROY_0:
-			{
-				//todo: support subtypes
-
-				if(event.metaType != EMetaclass::INVALID)
-					data["type"].String() = encodeIdentifier(event.metaType, event.objectType);
-
-				if(event.value > 0)
-					data["value"].Integer() = event.value;
-
-				if(!event.objectInstanceName.empty())
-					data["object"].String() = event.objectInstanceName;
-			}
-			break;
-		case EventCondition::HAVE_BUILDING_0:
-			{
-			//todo: support of new condition format HAVE_BUILDING_0
-			}
-			break;
-		default:
-			{
-				//old format
-				if(event.objectType != -1)
-					data["type"].Integer() = event.objectType;
+		if(!event.objectInstanceName.empty())
+			data["object"].String() = event.objectInstanceName;
 
-				if(event.value != -1)
-					data["value"].Integer() = event.value;
-			}
-			break;
-		}
+		data["type"].String() = event.objectType.toString();
+		data["value"].Integer() = event.value;
 
 		if(event.position != int3(-1, -1, -1))
 		{
@@ -389,28 +278,19 @@ RoadType * CMapFormatJson::getRoadByCode(const std::string & code)
 
 void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std::set<FactionID> & value) const
 {
-	//TODO: unify allowed factions with others - make them std::vector<bool>
-
-	std::vector<bool> temp;
-	temp.resize(VLC->townh->size(), false);
-	auto standard = VLC->townh->getDefaultAllowed();
+	std::set<FactionID> temp;
 
 	if(handler.saving)
 	{
 		for(auto faction : VLC->townh->objects)
 			if(faction->town && vstd::contains(value, faction->getId()))
-				temp[static_cast<std::size_t>(faction->getIndex())] = true;
+				temp.insert(faction->getId());
 	}
 
-	handler.serializeLIC("allowedFactions", &FactionID::decode, &FactionID::encode, standard, temp);
+	handler.serializeLIC("allowedFactions", &FactionID::decode, &FactionID::encode, VLC->townh->getDefaultAllowed(), temp);
 
 	if(!handler.saving)
-	{
-		value.clear();
-		for (std::size_t i=0; i<temp.size(); i++)
-			if(temp[i])
-				value.insert(static_cast<FactionID>(i));
-	}
+		value = temp;
 }
 
 void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
@@ -537,13 +417,10 @@ void CMapFormatJson::serializePlayerInfo(JsonSerializeFormat & handler)
 						{
 							std::string temp;
 							if(hero->type)
-							{
 								temp = hero->type->getJsonKey();
-							}
 							else
-							{
-								temp = VLC->heroh->objects[hero->subID]->getJsonKey();
-							}
+								temp = hero->getHeroType().toEntity(VLC)->getJsonKey();
+
 							handler.serializeString("type", temp);
 						}
 					}
@@ -762,7 +639,7 @@ void CMapFormatJson::writeDisposedHeroes(JsonSerializeFormat & handler)
 
 	for(DisposedHero & hero : map->disposedHeroes)
 	{
-		std::string type = HeroTypeID::encode(hero.heroId);
+		std::string type = HeroTypeID::encode(hero.heroId.getNum());
 
 		auto definition = definitions->enterStruct(type);
 

+ 25 - 30
lib/mapping/MapReaderH3M.cpp

@@ -81,6 +81,17 @@ ArtifactID MapReaderH3M::readArtifact()
 	return ArtifactID::NONE;
 }
 
+ArtifactID MapReaderH3M::readArtifact8()
+{
+	ArtifactID result(reader->readInt8());
+
+	if (result.getNum() < features.artifactsCount)
+		return remapIdentifier(result);
+
+	logGlobal->warn("Map contains invalid artifact %d. Will be removed!", result.getNum());
+	return ArtifactID::NONE;
+}
+
 ArtifactID MapReaderH3M::readArtifact32()
 {
 	ArtifactID result(reader->readInt32());
@@ -197,6 +208,13 @@ SpellID MapReaderH3M::readSpell32()
 	return result;
 }
 
+GameResID MapReaderH3M::readGameResID()
+{
+	GameResID result(readInt8());
+	assert(result.getNum() < features.resourcesCount);
+	return result;
+}
+
 PlayerColor MapReaderH3M::readPlayer()
 {
 	uint8_t value = readUInt8();
@@ -266,12 +284,12 @@ void MapReaderH3M::readBitmaskHeroClassesSized(std::set<HeroClassID> & dest, boo
 	readBitmask(dest, classesBytes, classesCount, invert);
 }
 
-void MapReaderH3M::readBitmaskHeroes(std::vector<bool> & dest, bool invert)
+void MapReaderH3M::readBitmaskHeroes(std::set<HeroTypeID> & dest, bool invert)
 {
 	readBitmask<HeroTypeID>(dest, features.heroesBytes, features.heroesCount, invert);
 }
 
-void MapReaderH3M::readBitmaskHeroesSized(std::vector<bool> & dest, bool invert)
+void MapReaderH3M::readBitmaskHeroesSized(std::set<HeroTypeID> & dest, bool invert)
 {
 	uint32_t heroesCount = readUInt32();
 	uint32_t heroesBytes = (heroesCount + 7) / 8;
@@ -280,12 +298,12 @@ void MapReaderH3M::readBitmaskHeroesSized(std::vector<bool> & dest, bool invert)
 	readBitmask<HeroTypeID>(dest, heroesBytes, heroesCount, invert);
 }
 
-void MapReaderH3M::readBitmaskArtifacts(std::vector<bool> &dest, bool invert)
+void MapReaderH3M::readBitmaskArtifacts(std::set<ArtifactID> &dest, bool invert)
 {
 	readBitmask<ArtifactID>(dest, features.artifactsBytes, features.artifactsCount, invert);
 }
 
-void MapReaderH3M::readBitmaskArtifactsSized(std::vector<bool> &dest, bool invert)
+void MapReaderH3M::readBitmaskArtifactsSized(std::set<ArtifactID> &dest, bool invert)
 {
 	uint32_t artifactsCount = reader->readUInt32();
 	uint32_t artifactsBytes = (artifactsCount + 7) / 8;
@@ -294,28 +312,18 @@ void MapReaderH3M::readBitmaskArtifactsSized(std::vector<bool> &dest, bool inver
 	readBitmask<ArtifactID>(dest, artifactsBytes, artifactsCount, invert);
 }
 
-void MapReaderH3M::readBitmaskSpells(std::vector<bool> & dest, bool invert)
-{
-	readBitmask<SpellID>(dest, features.spellsBytes, features.spellsCount, invert);
-}
-
 void MapReaderH3M::readBitmaskSpells(std::set<SpellID> & dest, bool invert)
 {
 	readBitmask(dest, features.spellsBytes, features.spellsCount, invert);
 }
 
-void MapReaderH3M::readBitmaskSkills(std::vector<bool> & dest, bool invert)
-{
-	readBitmask<SecondarySkill>(dest, features.skillsBytes, features.skillsCount, invert);
-}
-
 void MapReaderH3M::readBitmaskSkills(std::set<SecondarySkill> & dest, bool invert)
 {
 	readBitmask(dest, features.skillsBytes, features.skillsCount, invert);
 }
 
 template<class Identifier>
-void MapReaderH3M::readBitmask(std::vector<bool> & dest, const int bytesToRead, const int objectsToRead, bool invert)
+void MapReaderH3M::readBitmask(std::set<Identifier> & dest, int bytesToRead, int objectsToRead, bool invert)
 {
 	for(int byte = 0; byte < bytesToRead; ++byte)
 	{
@@ -331,26 +339,13 @@ void MapReaderH3M::readBitmask(std::vector<bool> & dest, const int bytesToRead,
 				Identifier h3mID(index);
 				Identifier vcmiID = remapIdentifier(h3mID);
 
-				if (vcmiID.getNum() >= dest.size())
-					dest.resize(vcmiID.getNum() + 1);
-				dest[vcmiID.getNum()] = result;
+				if (result)
+					dest.insert(vcmiID);
 			}
 		}
 	}
 }
 
-template<class Identifier>
-void MapReaderH3M::readBitmask(std::set<Identifier> & dest, int bytesToRead, int objectsToRead, bool invert)
-{
-	std::vector<bool> bitmap;
-	bitmap.resize(objectsToRead, false);
-	readBitmask<Identifier>(bitmap, bytesToRead, objectsToRead, invert);
-
-	for(int i = 0; i < bitmap.size(); i++)
-		if(bitmap[i])
-			dest.insert(static_cast<Identifier>(i));
-}
-
 int3 MapReaderH3M::readInt3()
 {
 	int3 p;

+ 6 - 9
lib/mapping/MapReaderH3M.h

@@ -32,6 +32,7 @@ public:
 	void setIdentifierRemapper(const MapIdentifiersH3M & remapper);
 
 	ArtifactID readArtifact();
+	ArtifactID readArtifact8();
 	ArtifactID readArtifact32();
 	CreatureID readCreature();
 	HeroTypeID readHero();
@@ -42,6 +43,7 @@ public:
 	SecondarySkill readSkill();
 	SpellID readSpell();
 	SpellID readSpell32();
+	GameResID readGameResID();
 	PlayerColor readPlayer();
 	PlayerColor readPlayer32();
 
@@ -50,13 +52,11 @@ public:
 	void readBitmaskPlayers(std::set<PlayerColor> & dest, bool invert);
 	void readBitmaskResources(std::set<GameResID> & dest, bool invert);
 	void readBitmaskHeroClassesSized(std::set<HeroClassID> & dest, bool invert);
-	void readBitmaskHeroes(std::vector<bool> & dest, bool invert);
-	void readBitmaskHeroesSized(std::vector<bool> & dest, bool invert);
-	void readBitmaskArtifacts(std::vector<bool> & dest, bool invert);
-	void readBitmaskArtifactsSized(std::vector<bool> & dest, bool invert);
-	void readBitmaskSpells(std::vector<bool> & dest, bool invert);
+	void readBitmaskHeroes(std::set<HeroTypeID> & dest, bool invert);
+	void readBitmaskHeroesSized(std::set<HeroTypeID> & dest, bool invert);
+	void readBitmaskArtifacts(std::set<ArtifactID> & dest, bool invert);
+	void readBitmaskArtifactsSized(std::set<ArtifactID> & dest, bool invert);
 	void readBitmaskSpells(std::set<SpellID> & dest, bool invert);
-	void readBitmaskSkills(std::vector<bool> & dest, bool invert);
 	void readBitmaskSkills(std::set<SecondarySkill> & dest, bool invert);
 
 	int3 readInt3();
@@ -88,9 +88,6 @@ private:
 	template<class Identifier>
 	void readBitmask(std::set<Identifier> & dest, int bytesToRead, int objectsToRead, bool invert);
 
-	template<class Identifier>
-	void readBitmask(std::vector<bool> & dest, int bytesToRead, int objectsToRead, bool invert);
-
 	MapFormatFeaturesH3M features;
 	MapIdentifiersH3M remapper;
 

+ 2 - 2
lib/modding/IdentifierStorage.cpp

@@ -26,9 +26,9 @@ CIdentifierStorage::CIdentifierStorage()
 {
 	//TODO: moddable spell schools
 	for (auto i = 0; i < GameConstants::DEFAULT_SCHOOLS; ++i)
-		registerObject(ModScope::scopeBuiltin(), "spellSchool", SpellConfig::SCHOOL[i].jsonName, SpellConfig::SCHOOL[i].id);
+		registerObject(ModScope::scopeBuiltin(), "spellSchool", SpellConfig::SCHOOL[i].jsonName, SpellConfig::SCHOOL[i].id.getNum());
 
-	registerObject(ModScope::scopeBuiltin(), "spellSchool", "any", SpellSchool(SpellSchool::ANY));
+	registerObject(ModScope::scopeBuiltin(), "spellSchool", "any", SpellSchool::ANY.getNum());
 
 	for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
 		registerObject(ModScope::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i);

+ 4 - 4
lib/networkPacks/NetPacksLib.cpp

@@ -1218,12 +1218,12 @@ void RemoveObject::applyGs(CGameState *gs)
 		{
 			if (cond.object == obj)
 			{
-				if (cond.condition == EventCondition::DESTROY || cond.condition == EventCondition::DESTROY_0)
+				if (cond.condition == EventCondition::DESTROY)
 				{
 					cond.condition = EventCondition::CONST_VALUE;
 					cond.value = 1; // destroyed object, from now on always fulfilled
 				}
-				else if (cond.condition == EventCondition::CONTROL || cond.condition == EventCondition::HAVE_0)
+				else if (cond.condition == EventCondition::CONTROL)
 				{
 					cond.condition = EventCondition::CONST_VALUE;
 					cond.value = 0; // destroyed object, from now on can not be fulfilled
@@ -1971,9 +1971,9 @@ void HeroVisit::applyGs(CGameState *gs)
 
 void SetAvailableArtifacts::applyGs(CGameState * gs) const
 {
-	if(id >= 0)
+	if(id != ObjectInstanceID::NONE)
 	{
-		if(auto * bm = dynamic_cast<CGBlackMarket *>(gs->map->objects[id].get()))
+		if(auto * bm = dynamic_cast<CGBlackMarket *>(gs->getObjInstance(id)))
 		{
 			bm->artifacts = arts;
 		}

+ 3 - 2
lib/pathfinder/TurnInfo.cpp

@@ -23,7 +23,8 @@ TurnInfo::BonusCache::BonusCache(const TConstBonusListPtr & bl)
 	for(const auto & terrain : VLC->terrainTypeHandler->objects)
 	{
 		auto selector = Selector::typeSubtype(BonusType::NO_TERRAIN_PENALTY, BonusSubtypeID(terrain->getId()));
-		noTerrainPenalty.push_back(static_cast<bool>(bl->getFirst(selector)));
+		if (bl->getFirst(selector))
+			noTerrainPenalty.insert(terrain->getId());
 	}
 
 	freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(BonusType::FREE_SHIP_BOARDING)));
@@ -87,7 +88,7 @@ bool TurnInfo::hasBonusOfType(BonusType type, BonusSubtypeID subtype) const
 	case BonusType::WATER_WALKING:
 		return bonusCache->waterWalking;
 	case BonusType::NO_TERRAIN_PENALTY:
-		return bonusCache->noTerrainPenalty[subtype.getNum()];
+		return bonusCache->noTerrainPenalty.count(subtype.as<TerrainId>());
 	}
 
 	return static_cast<bool>(

+ 1 - 1
lib/pathfinder/TurnInfo.h

@@ -21,7 +21,7 @@ struct DLL_LINKAGE TurnInfo
 	/// This is certainly not the best design ever and certainly can be improved
 	/// Unfortunately for pathfinder that do hundreds of thousands calls onus system add too big overhead
 	struct BonusCache {
-		std::vector<bool> noTerrainPenalty;
+		std::set<TerrainId> noTerrainPenalty;
 		bool freeShipBoarding;
 		bool flyingMovement;
 		int flyingMovementVal;

+ 1 - 1
lib/rewardable/Interface.cpp

@@ -157,7 +157,7 @@ void Rewardable::Interface::grantRewardAfterLevelup(IGameCallback * cb, const Re
 	}
 
 	for(const ArtifactID & art : info.reward.artifacts)
-		cb->giveHeroNewArtifact(hero, VLC->arth->objects[art],ArtifactPosition::FIRST_AVAILABLE);
+		cb->giveHeroNewArtifact(hero, art.toArtifact(), ArtifactPosition::FIRST_AVAILABLE);
 
 	if(!info.reward.spells.empty())
 	{

+ 1 - 1
lib/rmg/CMapGenOptions.cpp

@@ -325,7 +325,7 @@ void CMapGenOptions::resetPlayersMap()
 		}
 		else
 		{
-			logGlobal->warn("Adding settings for player %s", color.encode(color));
+			logGlobal->warn("Adding settings for player %s", color);
 			// Usually, all players should be initialized in initPlayersMap()
 			CPlayerSettings settings;
 			players[color] = settings;

+ 12 - 20
lib/rmg/CMapGenerator.cpp

@@ -98,12 +98,7 @@ const CMapGenOptions& CMapGenerator::getMapGenOptions() const
 
 void CMapGenerator::initPrisonsRemaining()
 {
-	allowedPrisons = 0;
-	for (auto isAllowed : map->getMap(this).allowedHeroes)
-	{
-		if (isAllowed)
-			allowedPrisons++;
-	}
+	allowedPrisons = map->getMap(this).allowedHeroes.size();
 	allowedPrisons = std::max<int> (0, allowedPrisons - 16 * mapGenOptions.getHumanOrCpuPlayerCount()); //so at least 16 heroes will be available for every player
 }
 
@@ -187,7 +182,7 @@ void CMapGenerator::addPlayerInfo()
 {
 	// Teams are already configured in CMapGenOptions. However, it's not the case when it comes to map editor
 
-	std::set<int> teamsTotal;
+	std::set<TeamID> teamsTotal;
 
 	if (mapGenOptions.arePlayersCustomized())
 	{
@@ -289,7 +284,7 @@ void CMapGenerator::addPlayerInfo()
 				player.team = TeamID(*itTeam);
 				teamNumbers[j].erase(itTeam);
 			}
-			teamsTotal.insert(player.team.getNum());
+			teamsTotal.insert(player.team);
 			map->getMap(this).players[pSettings.getColor().getNum()] = player;
 		}
 
@@ -493,19 +488,16 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
 	auto isWaterMap = map->getMap(this).isWaterMap();
 	//Skip heroes that were banned, including the ones placed in prisons
 	std::vector<HeroTypeID> ret;
-	for (int j = 0; j < map->getMap(this).allowedHeroes.size(); j++)
+	for (HeroTypeID hero : map->getMap(this).allowedHeroes)
 	{
-		if (map->getMap(this).allowedHeroes[j])
+		auto * h = dynamic_cast<const CHero*>(VLC->heroTypes()->getById(hero));
+		if ((h->onlyOnWaterMap && !isWaterMap) || (h->onlyOnMapWithoutWater && isWaterMap))
 		{
-			auto * h = dynamic_cast<const CHero*>(VLC->heroTypes()->getByIndex(j));
-			if ((h->onlyOnWaterMap && !isWaterMap) || (h->onlyOnMapWithoutWater && isWaterMap))
-			{
-				continue;
-			}
-			else
-			{
-				ret.push_back(HeroTypeID(j));
-			}
+			continue;
+		}
+		else
+		{
+			ret.push_back(hero);
 		}
 	}
 	return ret;
@@ -514,7 +506,7 @@ const std::vector<HeroTypeID> CMapGenerator::getAllPossibleHeroes() const
 void CMapGenerator::banQuestArt(const ArtifactID & id)
 {
 	//TODO: Protect with mutex
-	map->getMap(this).allowedArtifact[id] = false;
+	map->getMap(this).allowedArtifact.erase(id);
 }
 
 void CMapGenerator::banHero(const HeroTypeID & id)

+ 1 - 7
lib/rmg/CRmgTemplate.cpp

@@ -188,13 +188,7 @@ std::set<TerrainId> ZoneOptions::getDefaultTerrainTypes() const
 
 std::set<FactionID> ZoneOptions::getDefaultTownTypes() const
 {
-	std::set<FactionID> defaultTowns;
-	auto towns = VLC->townh->getDefaultAllowed();
-	for(int i = 0; i < towns.size(); ++i)
-	{
-		if(towns[i]) defaultTowns.insert(FactionID(i));
-	}
-	return defaultTowns;
+	return VLC->townh->getDefaultAllowed();
 }
 
 const std::set<FactionID> ZoneOptions::getTownTypes() const

+ 0 - 6
lib/rmg/CRmgTemplateStorage.cpp

@@ -51,12 +51,6 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const
 	}
 }
 
-std::vector<bool> CRmgTemplateStorage::getDefaultAllowed() const
-{
-	//all templates are allowed
-	return std::vector<bool>();
-}
-
 std::vector<JsonNode> CRmgTemplateStorage::loadLegacyData()
 {
 	return std::vector<JsonNode>();

+ 0 - 1
lib/rmg/CRmgTemplateStorage.h

@@ -24,7 +24,6 @@ class DLL_LINKAGE CRmgTemplateStorage : public IHandlerBase
 public:
 	CRmgTemplateStorage() = default;
 	
-	std::vector<bool> getDefaultAllowed() const override;
 	std::vector<JsonNode> loadLegacyData() override;
 
 	/// loads single object into game. Scope is namespace of this object, same as name of source mod

+ 1 - 1
lib/rmg/RmgMap.cpp

@@ -345,7 +345,7 @@ bool RmgMap::isAllowedSpell(const SpellID & sid) const
 	assert(sid.getNum() >= 0);
 	if (sid.getNum() < mapInstance->allowedSpells.size())
 	{
-		return mapInstance->allowedSpells[sid];
+		return mapInstance->allowedSpells.count(sid);
 	}
 	else
 		return false;

+ 2 - 2
lib/rmg/Zone.cpp

@@ -127,10 +127,10 @@ rmg::Area & Zone::freePaths()
 
 FactionID Zone::getTownType() const
 {
-	return FactionID(townType);
+	return townType;
 }
 
-void Zone::setTownType(si32 town)
+void Zone::setTownType(FactionID town)
 {
 	townType = town;
 }

+ 2 - 2
lib/rmg/Zone.h

@@ -59,7 +59,7 @@ public:
 	void fractalize();
 	
 	FactionID getTownType() const;
-	void setTownType(si32 town);
+	void setTownType(FactionID town);
 	TerrainId getTerrainType() const;
 	void setTerrainType(TerrainId terrain);
 		
@@ -108,7 +108,7 @@ protected:
 	std::vector<int3> possibleQuestArtifactPos;
 	
 	//template info
-	si32 townType;
+	FactionID townType;
 	TerrainId terrainType;
 };
 

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

@@ -638,14 +638,14 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard)
 	if(!possibleCreatures.empty())
 	{
 		creId = *RandomGeneratorUtil::nextItem(possibleCreatures, zone.getRand());
-		amount = strength / VLC->creh->objects[creId]->getAIValue();
+		amount = strength / creId.toEntity(VLC)->getAIValue();
 		if (amount >= 4)
 			amount = static_cast<int>(amount * zone.getRand().nextDouble(0.75, 1.25));
 	}
 	else //just pick any available creature
 	{
-		creId = CreatureID(132); //Azure Dragon
-		amount = strength / VLC->creh->objects[creId]->getAIValue();
+		creId = CreatureID::AZURE_DRAGON; //Azure Dragon
+		amount = strength / creId.toEntity(VLC)->getAIValue();
 	}
 	
 	auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId);

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

@@ -179,7 +179,7 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
 {
 	for(int i = 0; i < count; i++)
 	{
-		si32 subType = zone.getTownType();
+		FactionID subType = zone.getTownType();
 		
 		if(totalTowns>0)
 		{
@@ -223,7 +223,7 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
 	}
 }
 
-si32 TownPlacer::getRandomTownType(bool matchUndergroundType)
+FactionID TownPlacer::getRandomTownType(bool matchUndergroundType)
 {
 	auto townTypesAllowed = (!zone.getTownTypes().empty() ? zone.getTownTypes() : zone.getDefaultTownTypes());
 	if(matchUndergroundType)

+ 1 - 1
lib/rmg/modificators/TownPlacer.h

@@ -28,7 +28,7 @@ public:
 protected:
 	void cleanupBoundaries(const rmg::Object & rmgObject);
 	void addNewTowns(int count, bool hasFort, const PlayerColor & player, ObjectManager & manager);
-	si32 getRandomTownType(bool matchUndergroundType = false);
+	FactionID getRandomTownType(bool matchUndergroundType = false);
 	void placeTowns(ObjectManager & manager);
 	bool placeMines(ObjectManager & manager);
 	int3 placeMainTown(ObjectManager & manager, CGTownInstance & town);

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

@@ -155,8 +155,8 @@ void TreasurePlacer::addAllPossibleObjects()
 		if(dwellingType == Obj::CREATURE_GENERATOR1)
 		{
 			//don't spawn original "neutral" dwellings that got replaced by Conflux dwellings in AB
-			static int elementalConfluxROE[] = {7, 13, 16, 47};
-			for(int & i : elementalConfluxROE)
+			static MapObjectSubID elementalConfluxROE[] = {7, 13, 16, 47};
+			for(auto const & i : elementalConfluxROE)
 				vstd::erase_if_present(subObjects, i);
 		}
 		
@@ -918,7 +918,7 @@ char TreasurePlacer::dump(const int3 & t)
 	return Modificator::dump(t);
 }
 
-void ObjectInfo::setTemplates(si32 type, si32 subtype, TerrainId terrainType)
+void ObjectInfo::setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrainType)
 {
 	auto templHandler = VLC->objtypeh->getHandlerFor(type, subtype);
 	if(!templHandler)

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

@@ -29,7 +29,7 @@ struct ObjectInfo
 	//ui32 maxPerMap; //unused
 	std::function<CGObjectInstance *()> generateObject;
 	
-	void setTemplates(si32 type, si32 subtype, TerrainId terrain);
+	void setTemplates(MapObjectID type, MapObjectSubID subtype, TerrainId terrain);
 };
 
 class TreasurePlacer: public Modificator

+ 0 - 9
lib/serializer/BinaryDeserializer.h

@@ -196,15 +196,6 @@ public:
 		data = static_cast<bool>(read);
 	}
 
-	template < typename T, typename std::enable_if < std::is_same<T, std::vector<bool> >::value, int  >::type = 0 >
-	void load(T & data)
-	{
-		std::vector<ui8> convData;
-		load(convData);
-		convData.resize(data.size());
-		range::copy(convData, data.begin());
-	}
-
 	template <typename T, typename std::enable_if < !std::is_same<T, bool >::value, int  >::type = 0>
 	void load(std::vector<T> &data)
 	{

+ 0 - 8
lib/serializer/BinarySerializer.h

@@ -137,14 +137,6 @@ public:
 		save(writ);
 	}
 
-	template < typename T, typename std::enable_if < std::is_same<T, std::vector<bool> >::value, int  >::type = 0 >
-	void save(const T &data)
-	{
-		std::vector<ui8> convData;
-		std::copy(data.begin(), data.end(), std::back_inserter(convData));
-		save(convData);
-	}
-
 	template < class T, typename std::enable_if < std::is_fundamental<T>::value && !std::is_same<T, bool>::value, int  >::type = 0 >
 	void save(const T &data)
 	{

+ 0 - 49
lib/serializer/ILICReader.cpp

@@ -1,49 +0,0 @@
-/*
- * JsonTreeSerializer.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-#include "StdInc.h"
-#include "ILICReader.h"
-
-#include "../JsonNode.h"
-
-#include <vstd/StringUtils.h>
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-void ILICReader::readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, bool val, std::vector<bool> & value) const
-{
-	for(const auto & index : part.Vector())
-	{
-		const std::string & identifier = index.String();
-		const std::string type = typeid(decltype(this)).name();
-
-		const si32 rawId = decoder(identifier);
-		if(rawId >= 0)
-		{
-			if(rawId < value.size())
-				value[rawId] = val;
-			else
-				logGlobal->error("%s::serializeLIC: id out of bounds %d", type, rawId);
-		}
-	}
-}
-
-void ILICReader::readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, std::set<si32> & value) const
-{
-	for(const auto & index : part.Vector())
-	{
-		const std::string & identifier = index.String();
-
-		const si32 rawId = decoder(identifier);
-		if(rawId != -1)
-			value.insert(rawId);
-	}
-}
-
-VCMI_LIB_NAMESPACE_END

+ 0 - 23
lib/serializer/ILICReader.h

@@ -1,23 +0,0 @@
-/*
- * ILICReader.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-#pragma once
-
-#include "JsonTreeSerializer.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-class DLL_LINKAGE ILICReader
-{
-protected:
-	void readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, bool val, std::vector<bool> & value) const;
-	void readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, std::set<si32> & value) const;
-};
-
-VCMI_LIB_NAMESPACE_END

+ 4 - 68
lib/serializer/JsonDeserializer.cpp

@@ -131,75 +131,11 @@ void JsonDeserializer::serializeInternal(int64_t & value)
 	value = currentObject->Integer();
 }
 
-void JsonDeserializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value)
+void JsonDeserializer::serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::set<int32_t> & standard, std::set<int32_t> & value)
 {
-	const JsonNode & field = currentObject->operator[](fieldName);
-
-	const JsonNode & anyOf = field["anyOf"];
-	const JsonNode & allOf = field["allOf"];
-	const JsonNode & noneOf = field["noneOf"];
-
-	if(anyOf.Vector().empty() && allOf.Vector().empty())
-	{
-		//permissive mode
-		value = standard;
-	}
-	else
-	{
-		//restrictive mode
-		value.clear();
-		value.resize(standard.size(), false);
-
-		readLICPart(anyOf, decoder, true, value);
-		readLICPart(allOf, decoder, true, value);
-	}
-
-	readLICPart(noneOf, decoder, false, value);
-}
-
-void JsonDeserializer::serializeLIC(const std::string & fieldName, LIC & value)
-{
-	const JsonNode & field = currentObject->operator[](fieldName);
-
-	const JsonNode & anyOf = field["anyOf"];
-	const JsonNode & allOf = field["allOf"];
-	const JsonNode & noneOf = field["noneOf"];
-
-	if(anyOf.Vector().empty())
-	{
-		//permissive mode
-		value.any = value.standard;
-	}
-	else
-	{
-		//restrictive mode
-		value.any.clear();
-		value.any.resize(value.standard.size(), false);
-
-		readLICPart(anyOf, value.decoder, true, value.any);
-	}
-
-	readLICPart(allOf, value.decoder, true, value.all);
-	readLICPart(noneOf, value.decoder, true, value.none);
-
-	//remove any banned from allowed and required
-	for(si32 idx = 0; idx < value.none.size(); idx++)
-	{
-		if(value.none[idx])
-		{
-			value.all[idx] = false;
-			value.any[idx] = false;
-		}
-	}
-
-	//add all required to allowed
-	for(si32 idx = 0; idx < value.all.size(); idx++)
-	{
-		if(value.all[idx])
-		{
-			value.any[idx] = true;
-		}
-	}
+	LICSet lic(standard, decoder, encoder);
+	serializeLIC(fieldName, lic);
+	value = lic.any;
 }
 
 void JsonDeserializer::serializeLIC(const std::string & fieldName, LICSet & value)

+ 2 - 4
lib/serializer/JsonDeserializer.h

@@ -9,18 +9,16 @@
  */
 #pragma once
 
-#include "ILICReader.h"
 #include "JsonTreeSerializer.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class DLL_LINKAGE JsonDeserializer: public JsonTreeSerializer<const JsonNode *>, public ILICReader
+class DLL_LINKAGE JsonDeserializer: public JsonTreeSerializer<const JsonNode *>
 {
 public:
 	JsonDeserializer(const IInstanceResolver * instanceResolver_, const JsonNode & root_);
 
-	void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) override;
-	void serializeLIC(const std::string & fieldName, LIC & value) override;
+	void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::set<int32_t> & standard, std::set<int32_t> & value) override;
 	void serializeLIC(const std::string & fieldName, LICSet & value) override;
 	void serializeString(const std::string & fieldName, std::string & value) override;
 

+ 12 - 11
lib/serializer/JsonSerializeFormat.cpp

@@ -91,17 +91,6 @@ size_t JsonArraySerializer::size() const
     return thisNode->Vector().size();
 }
 
-//JsonSerializeFormat::LIC
-JsonSerializeFormat::LIC::LIC(const std::vector<bool> & Standard, TDecoder Decoder, TEncoder Encoder):
-	standard(Standard),
-	decoder(std::move(Decoder)),
-	encoder(std::move(Encoder))
-{
-	any.resize(standard.size(), false);
-	all.resize(standard.size(), false);
-	none.resize(standard.size(), false);
-}
-
 JsonSerializeFormat::LICSet::LICSet(const std::set<si32> & Standard, TDecoder Decoder, TEncoder Encoder):
 	standard(Standard),
 	decoder(std::move(Decoder)),
@@ -140,4 +129,16 @@ void JsonSerializeFormat::serializeBool(const std::string & fieldName, bool & va
 	serializeBool<bool>(fieldName, value, true, false, defaultValue);
 }
 
+void JsonSerializeFormat::readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, std::set<si32> & value) const
+{
+	for(const auto & index : part.Vector())
+	{
+		const std::string & identifier = index.String();
+
+		const si32 rawId = decoder(identifier);
+		if(rawId != -1)
+			value.insert(rawId);
+	}
+}
+
 VCMI_LIB_NAMESPACE_END

+ 22 - 17
lib/serializer/JsonSerializeFormat.h

@@ -137,18 +137,6 @@ public:
 	///may assume that object index is valid
 	using TEncoder = std::function<std::string(si32)>;
 
-	using TSerialize = std::function<void(JsonSerializeFormat &)>;
-
-	struct LIC
-	{
-		LIC(const std::vector<bool> & Standard, TDecoder Decoder, TEncoder Encoder);
-
-		const std::vector<bool> & standard;
-		const TDecoder decoder;
-		const TEncoder encoder;
-		std::vector<bool> all, any, none;
-	};
-
 	struct LICSet
 	{
 		LICSet(const std::set<si32> & Standard, TDecoder Decoder, TEncoder Encoder);
@@ -211,13 +199,28 @@ public:
 	 * @param value target value, must be resized properly
 	 *
 	 */
-	virtual void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::vector<bool> & standard, std::vector<bool> & value) = 0;
+	virtual void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::set<int32_t> & standard, std::set<int32_t> & value) = 0;
 
-	/** @brief Complete serialization of Logical identifier condition
-	 */
-	virtual void serializeLIC(const std::string & fieldName, LIC & value) = 0;
+	template<typename T>
+	void serializeLIC(const std::string & fieldName, const TDecoder & decoder, const TEncoder & encoder, const std::set<T> & standard, std::set<T> & value)
+	{
+		std::set<int32_t> standardInt;
+		std::set<int32_t> valueInt;
+
+		for (auto entry : standard)
+			standardInt.insert(entry.getNum());
+
+		for (auto entry : value)
+			valueInt.insert(entry.getNum());
 
-	/** @brief Complete serialization of Logical identifier condition. (Special version)
+		serializeLIC(fieldName, decoder, encoder, standardInt, valueInt);
+
+		value.clear();
+		for (auto entry : valueInt)
+			value.insert(T(entry));
+	}
+
+	/** @brief Complete serialization of Logical identifier condition.
 	 * Assumes that all values are allowed by default, and standard contains them
 	 */
 	virtual void serializeLIC(const std::string & fieldName, LICSet & value) = 0;
@@ -454,6 +457,8 @@ protected:
 	virtual void serializeInternal(std::string & value) = 0;
 	virtual void serializeInternal(int64_t & value) = 0;
 
+	void readLICPart(const JsonNode & part, const JsonSerializeFormat::TDecoder & decoder, std::set<si32> & value) const;
+
 private:
 	const IInstanceResolver * instanceResolver;
 

部分文件因为文件数量过多而无法显示