Browse Source

Identifiers that can be added by mods are now serialized as string

Ivan Savenko 2 years ago
parent
commit
843e97349a

+ 4 - 4
lib/bonuses/BonusCustomTypes.h

@@ -13,10 +13,10 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class DLL_LINKAGE BonusCustomSource : public Identifier<BonusCustomSource>
+class DLL_LINKAGE BonusCustomSource : public StaticIdentifier<BonusCustomSource>
 {
 public:
-	using Identifier<BonusCustomSource>::Identifier;
+	using StaticIdentifier<BonusCustomSource>::StaticIdentifier;
 
 	static std::string encode(int32_t index);
 	static si32 decode(const std::string & identifier);
@@ -24,10 +24,10 @@ public:
 	static const BonusCustomSource undeadMoraleDebuff; // -2
 };
 
-class DLL_LINKAGE BonusCustomSubtype : public Identifier<BonusCustomSubtype>
+class DLL_LINKAGE BonusCustomSubtype : public StaticIdentifier<BonusCustomSubtype>
 {
 public:
-	using Identifier<BonusCustomSubtype>::Identifier;
+	using StaticIdentifier<BonusCustomSubtype>::StaticIdentifier;
 
 	static std::string encode(int32_t index);
 	static si32 decode(const std::string & identifier);

+ 108 - 39
lib/constants/EntityIdentifiers.cpp

@@ -123,15 +123,16 @@ namespace GameConstants
 
 si32 HeroClassID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "heroClass", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "heroClass", identifier);
+	return rawId.value();
 }
 
 std::string HeroClassID::encode(const si32 index)
 {
+	if (index == -1)
+		return "";
 	return VLC->heroClasses()->getByIndex(index)->getJsonKey();
 }
 
@@ -162,29 +163,50 @@ std::string CampaignScenarioID::encode(const si32 index)
 
 std::string MapObjectID::encode(int32_t index)
 {
+	if (index == -1)
+		return "";
 	return VLC->objtypeh->getObjectHandlerName(MapObjectID(index));
 }
 
 si32 MapObjectID::decode(const std::string & identifier)
 {
+	if (identifier.empty())
+		return -1;
 	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "objects", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	return rawId.value();
+}
+
+std::string BoatId::encode(int32_t index)
+{
+	if (index == -1)
+		return "";
+	return VLC->objtypeh->getObjectHandlerName(MapObjectID(index));
+}
+
+si32 BoatId::decode(const std::string & identifier)
+{
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "objects", identifier);
+	return rawId.value();
 }
 
 si32 HeroTypeID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	if (identifier == "random")
+		return -2;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", identifier);
+	return rawId.value();
 }
 
 std::string HeroTypeID::encode(const si32 index)
 {
+	if (index == -1)
+		return "";
+	if (index == -2)
+		return "random";
 	return VLC->heroTypes()->getByIndex(index)->getJsonKey();
 }
 
@@ -205,15 +227,16 @@ const Artifact * ArtifactIDBase::toEntity(const Services * services) const
 
 si32 ArtifactID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "artifact", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "artifact", identifier);
+	return rawId.value();
 }
 
 std::string ArtifactID::encode(const si32 index)
 {
+	if (index == -1)
+		return "";
 	return VLC->artifacts()->getByIndex(index)->getJsonKey();
 }
 
@@ -224,15 +247,16 @@ std::string ArtifactID::entityType()
 
 si32 SecondarySkill::decode(const std::string& identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "secondarySkill", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "secondarySkill", identifier);
+	return rawId.value();
 }
 
 std::string SecondarySkill::encode(const si32 index)
 {
+	if (index == -1)
+		return "";
 	return VLC->skills()->getById(SecondarySkill(index))->getJsonKey();
 }
 
@@ -263,15 +287,16 @@ const Creature * CreatureIDBase::toEntity(const CreatureService * creatures) con
 
 si32 CreatureID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "creature", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "creature", identifier);
+	return rawId.value();
 }
 
 std::string CreatureID::encode(const si32 index)
 {
+	if (index == -1)
+		return "";
 	return VLC->creatures()->getById(CreatureID(index))->getJsonKey();
 }
 
@@ -312,11 +337,10 @@ const HeroType * HeroTypeID::toEntity(const Services * services) const
 
 si32 SpellID::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier);
+	return rawId.value();
 }
 
 std::string SpellID::encode(const si32 index)
@@ -326,11 +350,10 @@ std::string SpellID::encode(const si32 index)
 
 si32 BattleField::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return -1;
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), "spell", identifier);
+	return rawId.value();
 }
 
 std::string BattleField::encode(const si32 index)
@@ -399,15 +422,17 @@ std::string PrimarySkill::entityType()
 
 si32 FactionID::decode(const std::string & identifier)
 {
+	if (identifier.empty())
+		return -1;
+
 	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
-	if(rawId)
-		return rawId.value();
-	else
-		return FactionID::DEFAULT.getNum();
+	return rawId.value();
 }
 
 std::string FactionID::encode(const si32 index)
 {
+	if (index == -1)
+		return "";
 	return VLC->factions()->getByIndex(index)->getJsonKey();
 }
 
@@ -423,15 +448,17 @@ const Faction * FactionID::toEntity(const Services * service) const
 
 si32 TerrainId::decode(const std::string & identifier)
 {
-	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
-	if(rawId)
-		return rawId.value();
-	else
+	if (identifier.empty())
 		return static_cast<si32>(TerrainId::NONE);
+
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
+	return rawId.value();
 }
 
 std::string TerrainId::encode(const si32 index)
 {
+	if (index == TerrainId::NONE)
+		return "";
 	return VLC->terrainTypeHandler->getByIndex(index)->getJsonKey();
 }
 
@@ -440,6 +467,48 @@ std::string TerrainId::entityType()
 	return "terrain";
 }
 
+si32 RoadId::decode(const std::string & identifier)
+{
+	if (identifier.empty())
+		return RoadId::NO_ROAD.getNum();
+
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
+	return rawId.value();
+}
+
+std::string RoadId::encode(const si32 index)
+{
+	if (index == RoadId::NO_ROAD.getNum())
+		return "";
+	return VLC->roadTypeHandler->getByIndex(index)->getJsonKey();
+}
+
+std::string RoadId::entityType()
+{
+	return "road";
+}
+
+si32 RiverId::decode(const std::string & identifier)
+{
+	if (identifier.empty())
+		return RiverId::NO_RIVER.getNum();
+
+	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
+	return rawId.value();
+}
+
+std::string RiverId::encode(const si32 index)
+{
+	if (index == RiverId::NO_RIVER.getNum())
+		return "";
+	return VLC->riverTypeHandler->getByIndex(index)->getJsonKey();
+}
+
+std::string RiverId::entityType()
+{
+	return "road";
+}
+
 const TerrainType * TerrainId::toEntity(const Services * service) const
 {
 	return VLC->terrainTypeHandler->getByIndex(num);

+ 79 - 168
lib/constants/EntityIdentifiers.h

@@ -43,153 +43,50 @@ class CSkill;
 class CGameInfoCallback;
 class CNonConstInfoCallback;
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Note: use template to force different type, blocking any Identifier<A> <=> Identifier<B> comparisons
-template<typename FinalClass>
-class Identifier : public IdentifierBase
+class ArtifactInstanceID : public StaticIdentifier<ArtifactInstanceID>
 {
-	using BaseClass = IdentifierBase;
 public:
-	constexpr Identifier()
-	{}
-
-	explicit constexpr Identifier(int32_t value):
-		IdentifierBase(value)
-	{}
-
-	constexpr bool operator == (const Identifier & b) const { return BaseClass::num == b.num; }
-	constexpr bool operator <= (const Identifier & b) const { return BaseClass::num <= b.num; }
-	constexpr bool operator >= (const Identifier & b) const { return BaseClass::num >= b.num; }
-	constexpr bool operator != (const Identifier & b) const { return BaseClass::num != b.num; }
-	constexpr bool operator <  (const Identifier & b) const { return BaseClass::num <  b.num; }
-	constexpr bool operator >  (const Identifier & b) const { return BaseClass::num >  b.num; }
-
-	constexpr FinalClass & operator++()
-	{
-		++BaseClass::num;
-		return static_cast<FinalClass&>(*this);
-	}
-
-	constexpr FinalClass & operator--()
-	{
-		--BaseClass::num;
-		return static_cast<FinalClass&>(*this);
-	}
-
-	constexpr FinalClass operator--(int)
-	{
-		FinalClass ret(num);
-		--BaseClass::num;
-		return ret;
-	}
-
-	constexpr FinalClass operator++(int)
-	{
-		FinalClass ret(num);
-		++BaseClass::num;
-		return ret;
-	}
+	using StaticIdentifier<ArtifactInstanceID>::StaticIdentifier;
 };
 
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-template<typename FinalClass, typename BaseClass>
-class IdentifierWithEnum : public BaseClass
+class QueryID : public StaticIdentifier<QueryID>
 {
-	using EnumType = typename BaseClass::Type;
-
-	static_assert(std::is_same_v<std::underlying_type_t<EnumType>, int32_t>, "Entity Identifier must use int32_t");
 public:
-	constexpr EnumType toEnum() const
-	{
-		return static_cast<EnumType>(BaseClass::num);
-	}
-
-	constexpr IdentifierWithEnum(const EnumType & enumValue)
-	{
-		BaseClass::num = static_cast<int32_t>(enumValue);
-	}
-
-	constexpr IdentifierWithEnum(int32_t _num = -1)
-	{
-		BaseClass::num = _num;
-	}
-
-	constexpr bool operator == (const EnumType & b) const { return BaseClass::num == static_cast<int32_t>(b); }
-	constexpr bool operator <= (const EnumType & b) const { return BaseClass::num <= static_cast<int32_t>(b); }
-	constexpr bool operator >= (const EnumType & b) const { return BaseClass::num >= static_cast<int32_t>(b); }
-	constexpr bool operator != (const EnumType & b) const { return BaseClass::num != static_cast<int32_t>(b); }
-	constexpr bool operator <  (const EnumType & b) const { return BaseClass::num <  static_cast<int32_t>(b); }
-	constexpr bool operator >  (const EnumType & b) const { return BaseClass::num >  static_cast<int32_t>(b); }
-
-	constexpr bool operator == (const IdentifierWithEnum & b) const { return BaseClass::num == b.num; }
-	constexpr bool operator <= (const IdentifierWithEnum & b) const { return BaseClass::num <= b.num; }
-	constexpr bool operator >= (const IdentifierWithEnum & b) const { return BaseClass::num >= b.num; }
-	constexpr bool operator != (const IdentifierWithEnum & b) const { return BaseClass::num != b.num; }
-	constexpr bool operator <  (const IdentifierWithEnum & b) const { return BaseClass::num <  b.num; }
-	constexpr bool operator >  (const IdentifierWithEnum & b) const { return BaseClass::num >  b.num; }
-
-	constexpr FinalClass & operator++()
-	{
-		++BaseClass::num;
-		return static_cast<FinalClass&>(*this);
-	}
-
-	constexpr FinalClass operator++(int)
-	{
-		FinalClass ret(BaseClass::num);
-		++BaseClass::num;
-		return ret;
-	}
-};
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-class ArtifactInstanceID : public Identifier<ArtifactInstanceID>
-{
-public:
-	using Identifier<ArtifactInstanceID>::Identifier;
-};
-
-class QueryID : public Identifier<QueryID>
-{
-public:
-	using Identifier<QueryID>::Identifier;
+	using StaticIdentifier<QueryID>::StaticIdentifier;
 	DLL_LINKAGE static const QueryID NONE;
 	DLL_LINKAGE static const QueryID CLIENT;
 };
 
-class BattleID : public Identifier<BattleID>
+class BattleID : public StaticIdentifier<BattleID>
 {
 public:
-	using Identifier<BattleID>::Identifier;
+	using StaticIdentifier<BattleID>::StaticIdentifier;
 	DLL_LINKAGE static const BattleID NONE;
 };
-class DLL_LINKAGE ObjectInstanceID : public Identifier<ObjectInstanceID>
+class DLL_LINKAGE ObjectInstanceID : public StaticIdentifier<ObjectInstanceID>
 {
 public:
-	using Identifier<ObjectInstanceID>::Identifier;
+	using StaticIdentifier<ObjectInstanceID>::StaticIdentifier;
 	static const ObjectInstanceID NONE;
 
 	static si32 decode(const std::string & identifier);
 	static std::string encode(const si32 index);
 };
 
-class HeroClassID : public Identifier<HeroClassID>
+class HeroClassID : public EntityIdentifier<HeroClassID>
 {
 public:
-	using Identifier<HeroClassID>::Identifier;
+	using EntityIdentifier<HeroClassID>::EntityIdentifier;
 	///json serialization helpers
 	DLL_LINKAGE static si32 decode(const std::string & identifier);
 	DLL_LINKAGE static std::string encode(const si32 index);
 	static std::string entityType();
 };
 
-class DLL_LINKAGE HeroTypeID : public Identifier<HeroTypeID>
+class DLL_LINKAGE HeroTypeID : public EntityIdentifier<HeroTypeID>
 {
 public:
-	using Identifier<HeroTypeID>::Identifier;
+	using EntityIdentifier<HeroTypeID>::EntityIdentifier;
 	///json serialization helpers
 	static si32 decode(const std::string & identifier);
 	static std::string encode(const si32 index);
@@ -207,10 +104,10 @@ public:
 	}
 };
 
-class SlotID : public Identifier<SlotID>
+class SlotID : public StaticIdentifier<SlotID>
 {
 public:
-	using Identifier<SlotID>::Identifier;
+	using StaticIdentifier<SlotID>::StaticIdentifier;
 
 	DLL_LINKAGE static const SlotID COMMANDER_SLOT_PLACEHOLDER;
 	DLL_LINKAGE static const SlotID SUMMONED_SLOT_PLACEHOLDER; ///<for all summoned creatures, only during battle
@@ -223,10 +120,10 @@ public:
 	}
 };
 
-class DLL_LINKAGE PlayerColor : public Identifier<PlayerColor>
+class DLL_LINKAGE PlayerColor : public StaticIdentifier<PlayerColor>
 {
 public:
-	using Identifier<PlayerColor>::Identifier;
+	using StaticIdentifier<PlayerColor>::StaticIdentifier;
 
 	enum EPlayerColor
 	{
@@ -249,18 +146,18 @@ public:
 	static std::string entityType();
 };
 
-class TeamID : public Identifier<TeamID>
+class TeamID : public StaticIdentifier<TeamID>
 {
 public:
-	using Identifier<TeamID>::Identifier;
+	using StaticIdentifier<TeamID>::StaticIdentifier;
 
 	DLL_LINKAGE static const TeamID NO_TEAM;
 };
 
-class TeleportChannelID : public Identifier<TeleportChannelID>
+class TeleportChannelID : public StaticIdentifier<TeleportChannelID>
 {
 public:
-	using Identifier<TeleportChannelID>::Identifier;
+	using StaticIdentifier<TeleportChannelID>::StaticIdentifier;
 };
 
 class SecondarySkillBase : public IdentifierBase
@@ -302,10 +199,10 @@ public:
 	static_assert(GameConstants::SKILL_QUANTITY == SKILL_SIZE, "Incorrect number of skills");
 };
 
-class SecondarySkill : public IdentifierWithEnum<SecondarySkill, SecondarySkillBase>
+class DLL_LINKAGE SecondarySkill : public EntityIdentifierWithEnum<SecondarySkill, SecondarySkillBase>
 {
 public:
-	using IdentifierWithEnum<SecondarySkill, SecondarySkillBase>::IdentifierWithEnum;
+	using EntityIdentifierWithEnum<SecondarySkill, SecondarySkillBase>::EntityIdentifierWithEnum;
 	static std::string entityType();
 	static si32 decode(const std::string& identifier);
 	static std::string encode(const si32 index);
@@ -314,10 +211,10 @@ public:
 	const Skill * toEntity(const Services * services) const;
 };
 
-class DLL_LINKAGE PrimarySkill : public Identifier<PrimarySkill>
+class DLL_LINKAGE PrimarySkill : public StaticIdentifier<PrimarySkill>
 {
 public:
-	using Identifier<PrimarySkill>::Identifier;
+	using StaticIdentifier<PrimarySkill>::StaticIdentifier;
 
 	static const PrimarySkill NONE;
 	static const PrimarySkill ATTACK;
@@ -335,10 +232,10 @@ public:
 	static std::string entityType();
 };
 
-class DLL_LINKAGE FactionID : public Identifier<FactionID>
+class DLL_LINKAGE FactionID : public EntityIdentifier<FactionID>
 {
 public:
-	using Identifier<FactionID>::Identifier;
+	using EntityIdentifier<FactionID>::EntityIdentifier;
 
 	static const FactionID NONE;
 	static const FactionID DEFAULT;
@@ -413,10 +310,10 @@ public:
 	}
 };
 
-class DLL_LINKAGE BuildingID : public IdentifierWithEnum<BuildingID, BuildingIDBase>
+class DLL_LINKAGE BuildingID : public StaticIdentifierWithEnum<BuildingID, BuildingIDBase>
 {
 public:
-	using IdentifierWithEnum<BuildingID, BuildingIDBase>::IdentifierWithEnum;
+	using StaticIdentifierWithEnum<BuildingID, BuildingIDBase>::StaticIdentifierWithEnum;
 
 	static BuildingID HALL_LEVEL(unsigned int level)
 	{
@@ -578,10 +475,10 @@ public:
 	};
 };
 
-class DLL_LINKAGE MapObjectID : public IdentifierWithEnum<MapObjectID, MapObjectBaseID>
+class DLL_LINKAGE MapObjectID : public EntityIdentifierWithEnum<MapObjectID, MapObjectBaseID>
 {
 public:
-	using IdentifierWithEnum<MapObjectID, MapObjectBaseID>::IdentifierWithEnum;
+	using EntityIdentifierWithEnum<MapObjectID, MapObjectBaseID>::EntityIdentifierWithEnum;
 
 	static std::string encode(int32_t index);
 	static si32 decode(const std::string & identifier);
@@ -593,14 +490,16 @@ public:
 	}
 };
 
-class MapObjectSubID : public Identifier<MapObjectSubID>
+class MapObjectSubID : public StaticIdentifier<MapObjectSubID>
 {
 public:
+	MapObjectID primaryIdentifier;
+
 	constexpr MapObjectSubID(const IdentifierBase & value):
-		Identifier<MapObjectSubID>(value.getNum())
+		StaticIdentifier<MapObjectSubID>(value.getNum())
 	{}
 	constexpr MapObjectSubID(int32_t value = -1):
-		Identifier<MapObjectSubID>(value)
+		StaticIdentifier<MapObjectSubID>(value)
 	{}
 
 	MapObjectSubID & operator =(int32_t value)
@@ -615,6 +514,9 @@ public:
 		return *this;
 	}
 
+	static si32 decode(const std::string & identifier);
+	static std::string encode(const si32 index);
+
 	// TODO: Remove
 	constexpr operator int32_t () const
 	{
@@ -622,10 +524,13 @@ public:
 	}
 };
 
-class DLL_LINKAGE RoadId : public Identifier<RoadId>
+class DLL_LINKAGE RoadId : public EntityIdentifier<RoadId>
 {
 public:
-	using Identifier<RoadId>::Identifier;
+	using EntityIdentifier<RoadId>::EntityIdentifier;
+	static si32 decode(const std::string & identifier);
+	static std::string encode(const si32 index);
+	static std::string entityType();
 
 	static const RoadId NO_ROAD;
 	static const RoadId DIRT_ROAD;
@@ -635,10 +540,13 @@ public:
 	const RoadType * toEntity(const Services * service) const;
 };
 
-class DLL_LINKAGE RiverId : public Identifier<RiverId>
+class DLL_LINKAGE RiverId : public EntityIdentifier<RiverId>
 {
 public:
-	using Identifier<RiverId>::Identifier;
+	using EntityIdentifier<RiverId>::EntityIdentifier;
+	static si32 decode(const std::string & identifier);
+	static std::string encode(const si32 index);
+	static std::string entityType();
 
 	static const RiverId NO_RIVER;
 	static const RiverId WATER_RIVER;
@@ -658,10 +566,10 @@ public:
 	};
 };
 
-class EPathfindingLayer : public IdentifierWithEnum<EPathfindingLayer, EPathfindingLayerBase>
+class EPathfindingLayer : public StaticIdentifierWithEnum<EPathfindingLayer, EPathfindingLayerBase>
 {
 public:
-	using IdentifierWithEnum<EPathfindingLayer, EPathfindingLayerBase>::IdentifierWithEnum;
+	using StaticIdentifierWithEnum<EPathfindingLayer, EPathfindingLayerBase>::StaticIdentifierWithEnum;
 };
 
 class ArtifactPositionBase : public IdentifierBase
@@ -694,10 +602,10 @@ public:
 	DLL_LINKAGE static std::string encode(const si32 index);
 };
 
-class ArtifactPosition : public IdentifierWithEnum<ArtifactPosition, ArtifactPositionBase>
+class ArtifactPosition : public StaticIdentifierWithEnum<ArtifactPosition, ArtifactPositionBase>
 {
 public:
-	using IdentifierWithEnum<ArtifactPosition, ArtifactPositionBase>::IdentifierWithEnum;
+	using StaticIdentifierWithEnum<ArtifactPosition, ArtifactPositionBase>::StaticIdentifierWithEnum;
 
 	// TODO: Remove
 	constexpr operator int32_t () const
@@ -730,10 +638,10 @@ public:
 	DLL_LINKAGE const Artifact * toEntity(const Services * service) const;
 };
 
-class ArtifactID : public IdentifierWithEnum<ArtifactID, ArtifactIDBase>
+class ArtifactID : public EntityIdentifierWithEnum<ArtifactID, ArtifactIDBase>
 {
 public:
-	using IdentifierWithEnum<ArtifactID, ArtifactIDBase>::IdentifierWithEnum;
+	using EntityIdentifierWithEnum<ArtifactID, ArtifactIDBase>::EntityIdentifierWithEnum;
 
 	///json serialization helpers
 	DLL_LINKAGE static si32 decode(const std::string & identifier);
@@ -773,10 +681,10 @@ public:
 	DLL_LINKAGE const Creature * toEntity(const CreatureService * creatures) const;
 };
 
-class DLL_LINKAGE CreatureID : public IdentifierWithEnum<CreatureID, CreatureIDBase>
+class DLL_LINKAGE CreatureID : public EntityIdentifierWithEnum<CreatureID, CreatureIDBase>
 {
 public:
-	using IdentifierWithEnum<CreatureID, CreatureIDBase>::IdentifierWithEnum;
+	using EntityIdentifierWithEnum<CreatureID, CreatureIDBase>::EntityIdentifierWithEnum;
 
 	///json serialization helpers
 	static si32 decode(const std::string & identifier);
@@ -892,10 +800,10 @@ public:
 	const spells::Spell * toEntity(const spells::Service * service) const;
 };
 
-class DLL_LINKAGE SpellID : public IdentifierWithEnum<SpellID, SpellIDBase>
+class DLL_LINKAGE SpellID : public EntityIdentifierWithEnum<SpellID, SpellIDBase>
 {
 public:
-	using IdentifierWithEnum<SpellID, SpellIDBase>::IdentifierWithEnum;
+	using EntityIdentifierWithEnum<SpellID, SpellIDBase>::EntityIdentifierWithEnum;
 
 	///json serialization helpers
 	static si32 decode(const std::string & identifier);
@@ -904,10 +812,10 @@ public:
 };
 
 class BattleFieldInfo;
-class DLL_LINKAGE BattleField : public Identifier<BattleField>
+class DLL_LINKAGE BattleField : public EntityIdentifier<BattleField>
 {
 public:
-	using Identifier<BattleField>::Identifier;
+	using EntityIdentifier<BattleField>::EntityIdentifier;
 
 	static const BattleField NONE;
 	const BattleFieldInfo * getInfo() const;
@@ -916,10 +824,13 @@ public:
 	static std::string encode(const si32 index);
 };
 
-class DLL_LINKAGE BoatId : public Identifier<BoatId>
+class DLL_LINKAGE BoatId : public EntityIdentifier<BoatId>
 {
 public:
-	using Identifier<BoatId>::Identifier;
+	using EntityIdentifier<BoatId>::EntityIdentifier;
+
+	static si32 decode(const std::string & identifier);
+	static std::string encode(const si32 index);
 
 	static const BoatId NONE;
 	static const BoatId NECROPOLIS;
@@ -950,29 +861,29 @@ public:
 	};
 };
 
-class TerrainId : public IdentifierWithEnum<TerrainId, TerrainIdBase>
+class DLL_LINKAGE TerrainId : public EntityIdentifierWithEnum<TerrainId, TerrainIdBase>
 {
 public:
-	using IdentifierWithEnum<TerrainId, TerrainIdBase>::IdentifierWithEnum;
+	using EntityIdentifierWithEnum<TerrainId, TerrainIdBase>::EntityIdentifierWithEnum;
 
-	DLL_LINKAGE static si32 decode(const std::string & identifier);
-	DLL_LINKAGE static std::string encode(const si32 index);
+	static si32 decode(const std::string & identifier);
+	static std::string encode(const si32 index);
 	static std::string entityType();
 	const TerrainType * toEntity(const Services * service) const;
 };
 
 class ObstacleInfo;
-class Obstacle : public Identifier<Obstacle>
+class Obstacle : public EntityIdentifier<Obstacle>
 {
 public:
-	using Identifier<Obstacle>::Identifier;
+	using EntityIdentifier<Obstacle>::EntityIdentifier;
 	DLL_LINKAGE const ObstacleInfo * getInfo() const;
 };
 
-class DLL_LINKAGE SpellSchool : public Identifier<SpellSchool>
+class DLL_LINKAGE SpellSchool : public StaticIdentifier<SpellSchool>
 {
 public:
-	using Identifier<SpellSchool>::Identifier;
+	using StaticIdentifier<SpellSchool>::StaticIdentifier;
 
 	static const SpellSchool ANY;
 	static const SpellSchool AIR;
@@ -1005,10 +916,10 @@ public:
 	};
 };
 
-class DLL_LINKAGE GameResID : public IdentifierWithEnum<GameResID, GameResIDBase>
+class DLL_LINKAGE GameResID : public StaticIdentifierWithEnum<GameResID, GameResIDBase>
 {
 public:
-	using IdentifierWithEnum<GameResID, GameResIDBase>::IdentifierWithEnum;
+	using StaticIdentifierWithEnum<GameResID, GameResIDBase>::StaticIdentifierWithEnum;
 
 	static si32 decode(const std::string & identifier);
 	static std::string encode(const si32 index);
@@ -1017,7 +928,7 @@ public:
 	static const std::array<GameResID, 7> & ALL_RESOURCES();
 };
 
-class DLL_LINKAGE BuildingTypeUniqueID : public Identifier<BuildingTypeUniqueID>
+class DLL_LINKAGE BuildingTypeUniqueID : public StaticIdentifier<BuildingTypeUniqueID>
 {
 public:
 	BuildingTypeUniqueID(FactionID faction, BuildingID building );
@@ -1028,13 +939,13 @@ public:
 	BuildingID getBuilding() const;
 	FactionID getFaction() const;
 
-	using Identifier<BuildingTypeUniqueID>::Identifier;
+	using StaticIdentifier<BuildingTypeUniqueID>::StaticIdentifier;
 };
 
-class DLL_LINKAGE CampaignScenarioID : public Identifier<CampaignScenarioID>
+class DLL_LINKAGE CampaignScenarioID : public StaticIdentifier<CampaignScenarioID>
 {
 public:
-	using Identifier<CampaignScenarioID>::Identifier;
+	using StaticIdentifier<CampaignScenarioID>::StaticIdentifier;
 
 	static si32 decode(const std::string & identifier);
 	static std::string encode(int32_t index);

+ 177 - 5
lib/constants/IdentifierBase.h

@@ -42,11 +42,6 @@ public:
 		}
 	};
 
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & num;
-	}
-
 	constexpr void advance(int change)
 	{
 		num += change;
@@ -62,3 +57,180 @@ public:
 		return os << dt.num;
 	}
 };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Note: use template to force different type, blocking any Identifier<A> <=> Identifier<B> comparisons
+template<typename FinalClass>
+class Identifier : public IdentifierBase
+{
+	using BaseClass = IdentifierBase;
+public:
+	constexpr Identifier()
+	{}
+
+	explicit constexpr Identifier(int32_t value):
+		IdentifierBase(value)
+	{}
+
+	constexpr bool operator == (const Identifier & b) const { return BaseClass::num == b.num; }
+	constexpr bool operator <= (const Identifier & b) const { return BaseClass::num <= b.num; }
+	constexpr bool operator >= (const Identifier & b) const { return BaseClass::num >= b.num; }
+	constexpr bool operator != (const Identifier & b) const { return BaseClass::num != b.num; }
+	constexpr bool operator <  (const Identifier & b) const { return BaseClass::num <  b.num; }
+	constexpr bool operator >  (const Identifier & b) const { return BaseClass::num >  b.num; }
+
+	constexpr FinalClass & operator++()
+	{
+		++BaseClass::num;
+		return static_cast<FinalClass&>(*this);
+	}
+
+	constexpr FinalClass & operator--()
+	{
+		--BaseClass::num;
+		return static_cast<FinalClass&>(*this);
+	}
+
+	constexpr FinalClass operator--(int)
+	{
+		FinalClass ret(num);
+		--BaseClass::num;
+		return ret;
+	}
+
+	constexpr FinalClass operator++(int)
+	{
+		FinalClass ret(num);
+		++BaseClass::num;
+		return ret;
+	}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename FinalClass, typename BaseClass>
+class IdentifierWithEnum : public BaseClass
+{
+	using EnumType = typename BaseClass::Type;
+
+	static_assert(std::is_same_v<std::underlying_type_t<EnumType>, int32_t>, "Entity Identifier must use int32_t");
+public:
+	constexpr EnumType toEnum() const
+	{
+		return static_cast<EnumType>(BaseClass::num);
+	}
+
+	constexpr IdentifierWithEnum(const EnumType & enumValue)
+	{
+		BaseClass::num = static_cast<int32_t>(enumValue);
+	}
+
+	constexpr IdentifierWithEnum(int32_t _num = -1)
+	{
+		BaseClass::num = _num;
+	}
+
+	constexpr bool operator == (const EnumType & b) const { return BaseClass::num == static_cast<int32_t>(b); }
+	constexpr bool operator <= (const EnumType & b) const { return BaseClass::num <= static_cast<int32_t>(b); }
+	constexpr bool operator >= (const EnumType & b) const { return BaseClass::num >= static_cast<int32_t>(b); }
+	constexpr bool operator != (const EnumType & b) const { return BaseClass::num != static_cast<int32_t>(b); }
+	constexpr bool operator <  (const EnumType & b) const { return BaseClass::num <  static_cast<int32_t>(b); }
+	constexpr bool operator >  (const EnumType & b) const { return BaseClass::num >  static_cast<int32_t>(b); }
+
+	constexpr bool operator == (const IdentifierWithEnum & b) const { return BaseClass::num == b.num; }
+	constexpr bool operator <= (const IdentifierWithEnum & b) const { return BaseClass::num <= b.num; }
+	constexpr bool operator >= (const IdentifierWithEnum & b) const { return BaseClass::num >= b.num; }
+	constexpr bool operator != (const IdentifierWithEnum & b) const { return BaseClass::num != b.num; }
+	constexpr bool operator <  (const IdentifierWithEnum & b) const { return BaseClass::num <  b.num; }
+	constexpr bool operator >  (const IdentifierWithEnum & b) const { return BaseClass::num >  b.num; }
+
+	constexpr FinalClass & operator++()
+	{
+		++BaseClass::num;
+		return static_cast<FinalClass&>(*this);
+	}
+
+	constexpr FinalClass operator++(int)
+	{
+		FinalClass ret(BaseClass::num);
+		++BaseClass::num;
+		return ret;
+	}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+template<typename FinalClass>
+class EntityIdentifier : public Identifier<FinalClass>
+{
+public:
+	using Identifier<FinalClass>::Identifier;
+
+	template <typename Handler>
+	void serialize(Handler &h, const int version)
+	{
+		auto * finalClass = static_cast<FinalClass*>(this);
+		std::string value;
+
+		if (h.saving)
+			value = FinalClass::encode(finalClass->num);
+
+		h & value;
+
+		if (!h.saving)
+			finalClass->num = FinalClass::decode(value);
+	}
+};
+
+template<typename FinalClass, typename BaseClass>
+class EntityIdentifierWithEnum : public IdentifierWithEnum<FinalClass, BaseClass>
+{
+public:
+	using IdentifierWithEnum<FinalClass, BaseClass>::IdentifierWithEnum;
+
+	template <typename Handler>
+	void serialize(Handler &h, const int version)
+	{
+		auto * finalClass = static_cast<FinalClass*>(this);
+		std::string value;
+
+		if (h.saving)
+			value = FinalClass::encode(finalClass->num);
+
+		h & value;
+
+		if (!h.saving)
+			finalClass->num = FinalClass::decode(value);
+	}
+};
+
+template<typename FinalClass>
+class StaticIdentifier : public Identifier<FinalClass>
+{
+public:
+	using Identifier<FinalClass>::Identifier;
+
+	template <typename Handler>
+	void serialize(Handler &h, const int version)
+	{
+		auto * finalClass = static_cast<FinalClass*>(this);
+		h & finalClass->num;
+	}
+};
+
+template<typename FinalClass, typename BaseClass>
+class StaticIdentifierWithEnum : public IdentifierWithEnum<FinalClass, BaseClass>
+{
+public:
+	using IdentifierWithEnum<FinalClass, BaseClass>::IdentifierWithEnum;
+
+	template <typename Handler>
+	void serialize(Handler &h, const int version)
+	{
+		auto * finalClass = static_cast<FinalClass*>(this);
+		h & finalClass->num;
+	}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

+ 2 - 2
lib/networkPacks/ObjProperty.h

@@ -53,10 +53,10 @@ enum class ObjProperty : int8_t
 	REWARD_CLEARED
 };
 
-class NumericID : public Identifier<NumericID>
+class NumericID : public StaticIdentifier<NumericID>
 {
 public:
-	using Identifier<NumericID>::Identifier;
+	using StaticIdentifier<NumericID>::StaticIdentifier;
 
 	static si32 decode(const std::string & identifier)
 	{