Selaa lähdekoodia

First step at unifying game identifiers code

Ivan Savenko 2 vuotta sitten
vanhempi
sitoutus
ec8d31bbfc

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

@@ -170,10 +170,10 @@ TSubgoal CollectRes::whatToDoToTrade()
 		int howManyCanWeBuy = 0;
 		for (GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i)
 		{
-			if (GameResID(i) == resID)
+			if (i.getNum() == resID)
 				continue;
 			int toGive = -1, toReceive = -1;
-			m->getOffer(GameResID(i), resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
+			m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
 			assert(toGive > 0 && toReceive > 0);
 			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
 		}

+ 0 - 5
client/adventureMap/CResDataBar.h

@@ -11,11 +11,6 @@
 
 #include "../gui/CIntObject.h"
 
-VCMI_LIB_NAMESPACE_BEGIN
-enum class EGameResID : int8_t;
-using GameResID = Identifier<EGameResID>;
-VCMI_LIB_NAMESPACE_END
-
 /// Resources bar which shows information about how many gold, crystals,... you have
 /// Current date is displayed too
 class CResDataBar : public CIntObject

+ 8 - 8
client/lobby/OptionsTab.cpp

@@ -94,7 +94,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex(bool big)
 		TOWN_RANDOM = 38,  TOWN_NONE = 39, // Special frames in ITPA
 		HERO_RANDOM = 163, HERO_NONE = 164 // Special frames in PortraitsSmall
 	};
-	auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle;
+	auto factionIndex = settings.castle.getNum() >= CGI->townh->size() ? 0 : settings.castle.getNum();
 
 	switch(type)
 	{
@@ -191,7 +191,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getName()
 			return CGI->generaltexth->allTexts[522];
 		default:
 		{
-			auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle;
+			auto factionIndex = settings.castle.getNum() >= CGI->townh->size() ? 0 : settings.castle.getNum();
 			return (*CGI->townh)[factionIndex]->getNameTranslated();
 		}
 	}
@@ -233,7 +233,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getTitle()
 	switch(type)
 	{
 	case OptionsTab::TOWN:
-		return (settings.castle < 0) ? CGI->generaltexth->allTexts[103] : CGI->generaltexth->allTexts[80];
+		return (settings.castle.getNum() < 0) ? CGI->generaltexth->allTexts[103] : CGI->generaltexth->allTexts[80];
 	case OptionsTab::HERO:
 		return (settings.hero < 0) ? CGI->generaltexth->allTexts[101] : CGI->generaltexth->allTexts[77];
 	case OptionsTab::BONUS:
@@ -255,7 +255,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getTitle()
 }
 std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
 {
-	auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle;
+	auto factionIndex = settings.castle.getNum() >= CGI->townh->size() ? 0 : settings.castle.getNum();
 	auto heroIndex = settings.hero >= CGI->heroh->size() ? 0 : settings.hero;
 
 	switch(type)
@@ -299,7 +299,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getSubtitle()
 
 std::string OptionsTab::CPlayerSettingsHelper::getDescription()
 {
-	auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle;
+	auto factionIndex = settings.castle.getNum() >= CGI->townh->size() ? 0 : settings.castle.getNum();
 
 	switch(type)
 	{
@@ -386,7 +386,7 @@ void OptionsTab::CPlayerOptionTooltipBox::genTownWindow()
 	pos = Rect(0, 0, 228, 290);
 	genHeader();
 	labelAssociatedCreatures = std::make_shared<CLabel>(pos.w / 2 + 8, 122, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[79]);
-	auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle;
+	auto factionIndex = settings.castle.getNum() >= CGI->townh->size() ? 0 : settings.castle.getNum();
 	std::vector<std::shared_ptr<CComponent>> components;
 	const CTown * town = (*CGI->townh)[factionIndex]->town;
 
@@ -784,7 +784,7 @@ void OptionsTab::SelectedBox::update()
 void OptionsTab::SelectedBox::showPopupWindow(const Point & cursorPosition)
 {
 	// cases when we do not need to display a message
-	if(settings.castle == PlayerSettings::NONE && CPlayerSettingsHelper::type == TOWN)
+	if(settings.castle.getNum() == PlayerSettings::NONE && CPlayerSettingsHelper::type == TOWN)
 		return;
 	if(settings.hero == PlayerSettings::NONE && !SEL->getPlayerInfo(settings.color.getNum()).hasCustomMainHero() && CPlayerSettingsHelper::type == HERO)
 		return;
@@ -932,7 +932,7 @@ void OptionsTab::PlayerOptionsEntry::hideUnavailableButtons()
 		buttonTownRight->enable();
 	}
 
-	if((pi->defaultHero() != -1 || s->castle < 0) //fixed hero
+	if((pi->defaultHero() != -1 || s->castle.getNum() < 0) //fixed hero
 		|| foreignPlayer) //or not our player
 	{
 		buttonHeroLeft->disable();

+ 2 - 2
include/vcmi/Creature.h

@@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 class CreatureID;
 class ResourceSet;
-enum class EGameResID : int8_t;
+class GameResID;
 
 /// Base class for creatures and battle stacks
 class DLL_LINKAGE ACreature: public AFactionMember
@@ -63,7 +63,7 @@ public:
 	virtual int32_t getBaseSpeed() const = 0;
 	virtual int32_t getBaseShots() const = 0;
 
-	virtual int32_t getRecruitCost(Identifier<EGameResID> resIndex) const = 0;
+	virtual int32_t getRecruitCost(GameResID resIndex) const = 0;
 	virtual ResourceSet getFullRecruitCost() const = 0;
 	
 	virtual bool hasUpgrades() const = 0;

+ 3 - 4
include/vcmi/Entity.h

@@ -14,8 +14,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 class IBonusBearer;
 class FactionID;
-enum class ETerrainId;
-template<typename T> class Identifier;
+class TerrainId;
 
 class DLL_LINKAGE IConstBonusProvider
 {
@@ -26,9 +25,9 @@ public:
 class DLL_LINKAGE INativeTerrainProvider
 {
 public:
-	virtual Identifier<ETerrainId> getNativeTerrain() const = 0;
+	virtual TerrainId getNativeTerrain() const = 0;
 	virtual FactionID getFaction() const = 0;
-	virtual bool isNativeTerrain(Identifier<ETerrainId> terrain) const;
+	virtual bool isNativeTerrain(TerrainId terrain) const;
 };
 
 class DLL_LINKAGE Entity

+ 2 - 2
include/vcmi/Faction.h

@@ -16,14 +16,14 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 class FactionID;
 enum class EAlignment : uint8_t;
-enum class EBoatId : int32_t;
+class BoatId;
 
 class DLL_LINKAGE Faction : public EntityT<FactionID>, public INativeTerrainProvider
 {
 public:
 	virtual bool hasTown() const = 0;
 	virtual EAlignment getAlignment() const = 0;
-	virtual EBoatId getBoatType() const = 0;
+	virtual BoatId getBoatType() const = 0;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 2
include/vcmi/FactionMember.h

@@ -27,7 +27,7 @@ public:
 	/**
 	 Returns native terrain considering some terrain bonuses.
 	*/
-	virtual Identifier<ETerrainId> getNativeTerrain() const;
+	virtual TerrainId getNativeTerrain() const;
 	/**
 	 Returns magic resistance considering some bonuses.
 	*/
@@ -71,4 +71,4 @@ public:
 	int luckValAndBonusList(std::shared_ptr<const BonusList> & bonusList) const;
 };
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 3 - 3
include/vcmi/spells/Spell.h

@@ -15,7 +15,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 class SpellID;
-enum class ESpellSchool: int8_t;
+class SpellSchool;
 
 namespace spells
 {
@@ -24,7 +24,7 @@ class Caster;
 class DLL_LINKAGE Spell: public EntityT<SpellID>
 {
 public:
-	using SchoolCallback = std::function<void(const Identifier<ESpellSchool> &, bool &)>;
+	using SchoolCallback = std::function<void(const SpellSchool &, bool &)>;
 
 	///calculate spell damage on stack taking caster`s secondary skills into account
 	virtual int64_t calculateDamage(const Caster * caster) const = 0;
@@ -43,7 +43,7 @@ public:
 	virtual bool isSpecial() const = 0;
 	virtual bool isMagical() const = 0; //Should this spell considered as magical effect or as ability (like dendroid's bind)
 
-	virtual bool hasSchool(Identifier<ESpellSchool> school) const = 0;
+	virtual bool hasSchool(SpellSchool school) const = 0;
 	virtual void forEachSchool(const SchoolCallback & cb) const = 0;
 	virtual const std::string & getCastSound() const = 0;
 	virtual int32_t getCost(const int32_t skillLevel) const = 0;

+ 1 - 1
lib/CCreatureHandler.cpp

@@ -161,7 +161,7 @@ int32_t CCreature::getBaseShots() const
 
 int32_t CCreature::getRecruitCost(GameResID resIndex) const
 {
-	if(resIndex >= 0 && resIndex < cost.size())
+	if(resIndex.getNum() >= 0 && resIndex.getNum() < cost.size())
 		return cost[resIndex];
 	else
 		return 0;

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -42,7 +42,7 @@ int CGameInfoCallback::getResource(PlayerColor Player, GameResID which) const
 {
 	const PlayerState *p = getPlayerState(Player);
 	ERROR_RET_VAL_IF(!p, "No player info!", -1);
-	ERROR_RET_VAL_IF(p->resources.size() <= which || which < 0, "No such resource!", -1);
+	ERROR_RET_VAL_IF(p->resources.size() <= which.getNum() || which.getNum() < 0, "No such resource!", -1);
 	return p->resources[which];
 }
 

+ 1 - 1
lib/CHeroHandler.cpp

@@ -345,7 +345,7 @@ std::vector<JsonNode> CHeroClassHandler::loadLegacyData()
 		for(const auto & name : NSecondarySkill::names)
 			entry["secondarySkills"][name].Float() = parser.readNumber();
 
-		for(const auto & name : ETownType::names)
+		for(const auto & name : NFaction::names)
 			entry["tavern"][name].Float() = parser.readNumber();
 
 		parser.endLine();

+ 2 - 2
lib/CTownHandler.cpp

@@ -184,7 +184,7 @@ EAlignment CFaction::getAlignment() const
 	return alignment;
 }
 
-EBoatId CFaction::getBoatType() const
+BoatId CFaction::getBoatType() const
 {
 	return boatType.toEnum();
 }
@@ -1034,7 +1034,7 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
 	faction->creatureBg120 = source["creatureBackground"]["120px"].String();
 	faction->creatureBg130 = source["creatureBackground"]["130px"].String();
 
-	faction->boatType = EBoatId::CASTLE; //Do not crash
+	faction->boatType = BoatId::CASTLE; //Do not crash
 	if (!source["boat"].isNull())
 	{
 		VLC->identifiers()->requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID)

+ 2 - 2
lib/CTownHandler.h

@@ -207,7 +207,7 @@ public:
 
 	/// Boat that will be used by town shipyard (if any)
 	/// and for placing heroes directly on boat (in map editor, water prisons & taverns)
-	BoatId boatType = BoatId(EBoatId::CASTLE);
+	BoatId boatType = BoatId::CASTLE;
 
 
 	CTown * town = nullptr; //NOTE: can be null
@@ -232,7 +232,7 @@ public:
 	bool hasTown() const override;
 	TerrainId getNativeTerrain() const override;
 	EAlignment getAlignment() const override;
-	EBoatId getBoatType() const override;
+	BoatId getBoatType() const override;
 
 	void updateFrom(const JsonNode & data);
 	void serializeJson(JsonSerializeFormat & handler);

+ 7 - 20
lib/GameConstants.cpp

@@ -191,20 +191,7 @@ std::string PlayerColor::getStrCap(bool L10n) const
 	return ret;
 }
 
-const FactionID FactionID::NONE = FactionID(-2);
-const FactionID FactionID::DEFAULT = FactionID(-1);
-const FactionID FactionID::CASTLE = FactionID(0);
-const FactionID FactionID::RAMPART = FactionID(1);
-const FactionID FactionID::TOWER = FactionID(2);
-const FactionID FactionID::INFERNO = FactionID(3);
-const FactionID FactionID::NECROPOLIS = FactionID(4);
-const FactionID FactionID::DUNGEON = FactionID(5);
-const FactionID FactionID::STRONGHOLD = FactionID(6);
-const FactionID FactionID::FORTRESS = FactionID(7);
-const FactionID FactionID::CONFLUX = FactionID(8);
-const FactionID FactionID::NEUTRAL = FactionID(9);
-
-si32 FactionID::decode(const std::string & identifier)
+si32 FactionIDBase::decode(const std::string & identifier)
 {
 	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
 	if(rawId)
@@ -213,32 +200,32 @@ si32 FactionID::decode(const std::string & identifier)
 		return FactionID::DEFAULT;
 }
 
-std::string FactionID::encode(const si32 index)
+std::string FactionIDBase::encode(const si32 index)
 {
 	return VLC->factions()->getByIndex(index)->getJsonKey();
 }
 
-std::string FactionID::entityType()
+std::string FactionIDBase::entityType()
 {
 	return "faction";
 }
 
 
-si32 TerrainID::decode(const std::string & identifier)
+si32 TerrainIdBase::decode(const std::string & identifier)
 {
 	auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier);
 	if(rawId)
 		return rawId.value();
 	else
-		return static_cast<si32>(ETerrainId::NONE);
+		return static_cast<si32>(TerrainId::NONE);
 }
 
-std::string TerrainID::encode(const si32 index)
+std::string TerrainIdBase::encode(const si32 index)
 {
 	return VLC->terrainTypeHandler->getByIndex(index)->getJsonKey();
 }
 
-std::string TerrainID::entityType()
+std::string TerrainIdBase::entityType()
 {
 	return "terrain";
 }

+ 202 - 137
lib/GameConstants.h

@@ -33,6 +33,7 @@ class CSkill;
 class CGameInfoCallback;
 class CNonConstInfoCallback;
 
+
 struct IdTag
 {};
 
@@ -203,92 +204,96 @@ public:
 	}
 };
 
-template < typename T>
-class Identifier : public IdTag
+template<typename Der, typename Num>
+std::ostream & operator << (std::ostream & os, BaseForID<Der, Num> id);
+
+template<typename Der, typename Num>
+std::ostream & operator << (std::ostream & os, BaseForID<Der, Num> id)
+{
+	//We use common type with short to force char and unsigned char to be promoted and formatted as numbers.
+	typedef typename std::common_type<short, Num>::type Number;
+	return os << static_cast<Number>(id.getNum());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class EntityBase
 {
 public:
-	using EnumType    = T;
-	using NumericType = typename std::underlying_type<EnumType>::type;
+	int32_t num;
+};
 
-private:
-	NumericType num;
+template<typename T>
+class EntityIdentifier : public T
+{
+	using EnumType = typename T::Type;
 
+	static_assert(std::is_same_v<std::underlying_type_t<EnumType>, int32_t>, "Entity Identifier must use int32_t");
 public:
-	constexpr NumericType getNum() const
+	constexpr int32_t getNum() const
 	{
-		return num;
+		return T::num;
 	}
 
 	constexpr EnumType toEnum() const
 	{
-		return static_cast<EnumType>(num);
+		return static_cast<EnumType>(T::num);
 	}
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & num;
+		h & T::num;
 	}
 
-	constexpr explicit Identifier(NumericType _num = -1)
+	constexpr EntityIdentifier(const EnumType & enumValue)
 	{
-		num = _num;
+		T::num = static_cast<int32_t>(enumValue);
 	}
 
-	/* implicit */constexpr Identifier(EnumType _num):
-		num(static_cast<NumericType>(_num))
+	constexpr EntityIdentifier(int32_t _num = -1)
 	{
+		T::num = _num;
 	}
 
 	constexpr void advance(int change)
 	{
-		num += change;
+		T::num += change;
 	}
 
-	constexpr bool operator == (const Identifier & b) const { return num == b.num; }
-	constexpr bool operator <= (const Identifier & b) const { return num <= b.num; }
-	constexpr bool operator >= (const Identifier & b) const { return num >= b.num; }
-	constexpr bool operator != (const Identifier & b) const { return num != b.num; }
-	constexpr bool operator <  (const Identifier & b) const { return num <  b.num; }
-	constexpr bool operator >  (const Identifier & b) const { return num > b.num; }
-
-	constexpr Identifier & operator++()
+	constexpr bool operator == (const EnumType & b) const { return T::num == static_cast<int32_t>(b); }
+	constexpr bool operator <= (const EnumType & b) const { return T::num <= static_cast<int32_t>(b); }
+	constexpr bool operator >= (const EnumType & b) const { return T::num >= static_cast<int32_t>(b); }
+	constexpr bool operator != (const EnumType & b) const { return T::num != static_cast<int32_t>(b); }
+	constexpr bool operator <  (const EnumType & b) const { return T::num <  static_cast<int32_t>(b); }
+	constexpr bool operator >  (const EnumType & b) const { return T::num >  static_cast<int32_t>(b); }
+
+	constexpr bool operator == (const EntityIdentifier & b) const { return T::num == b.num; }
+	constexpr bool operator <= (const EntityIdentifier & b) const { return T::num <= b.num; }
+	constexpr bool operator >= (const EntityIdentifier & b) const { return T::num >= b.num; }
+	constexpr bool operator != (const EntityIdentifier & b) const { return T::num != b.num; }
+	constexpr bool operator <  (const EntityIdentifier & b) const { return T::num <  b.num; }
+	constexpr bool operator >  (const EntityIdentifier & b) const { return T::num >  b.num; }
+
+	constexpr EntityIdentifier & operator++()
 	{
-		++num;
+		++T::num;
 		return *this;
 	}
 
-	constexpr Identifier operator++(int)
+	constexpr EntityIdentifier operator++(int)
 	{
-		Identifier ret(*this);
-		++num;
+		EntityIdentifier ret(*this);
+		++T::num;
 		return ret;
 	}
 
-	constexpr operator NumericType() const
+	constexpr operator int32_t () const
 	{
-		return num;
+		return T::num;
 	}
 };
 
-
-template<typename Der, typename Num>
-std::ostream & operator << (std::ostream & os, BaseForID<Der, Num> id);
-
-template<typename Der, typename Num>
-std::ostream & operator << (std::ostream & os, BaseForID<Der, Num> id)
-{
-	//We use common type with short to force char and unsigned char to be promoted and formatted as numbers.
-	typedef typename std::common_type<short, Num>::type Number;
-	return os << static_cast<Number>(id.getNum());
-}
-
-template<typename EnumType>
-std::ostream & operator << (std::ostream & os, Identifier<EnumType> id)
-{
-	//We use common type with short to force char and unsigned char to be promoted and formatted as numbers.
-	typedef typename std::common_type<short, typename Identifier<EnumType>::NumericType>::type Number;
-	return os << static_cast<Number>(id.getNum());
-}
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 class ArtifactInstanceID : public BaseForID<ArtifactInstanceID, si32>
 {
@@ -398,11 +403,6 @@ class TeleportChannelID : public BaseForID<TeleportChannelID, si32>
 	friend class CNonConstInfoCallback;
 };
 
-// #ifndef INSTANTIATE_BASE_FOR_ID_HERE
-// extern template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
-// extern template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
-// #endif
-
 // Enum declarations
 namespace PrimarySkill
 {
@@ -410,10 +410,10 @@ namespace PrimarySkill
 				EXPERIENCE = 4}; //for some reason changePrimSkill uses it
 }
 
-class SecondarySkill
+class SecondarySkillBase : public EntityBase
 {
 public:
-	enum ESecondarySkill
+	enum Type : int32_t
 	{
 		WRONG = -2,
 		DEFAULT = -1,
@@ -422,61 +422,51 @@ public:
 		SCHOLAR, TACTICS, ARTILLERY, LEARNING, OFFENCE, ARMORER, INTELLIGENCE, SORCERY, RESISTANCE,
 		FIRST_AID, SKILL_SIZE
 	};
-
 	static_assert(GameConstants::SKILL_QUANTITY == SKILL_SIZE, "Incorrect number of skills");
-
-	SecondarySkill(ESecondarySkill _num = WRONG) : num(_num)
-	{}
-
-	ID_LIKE_CLASS_COMMON(SecondarySkill, ESecondarySkill)
-
-	ESecondarySkill num;
 };
 
-ID_LIKE_OPERATORS(SecondarySkill, SecondarySkill::ESecondarySkill)
+class SecondarySkill : public EntityIdentifier<SecondarySkillBase>
+{
+public:
+	using EntityIdentifier<SecondarySkillBase>::EntityIdentifier;
+};
 
 enum class EAlignment : uint8_t { GOOD, EVIL, NEUTRAL };
 
-namespace ETownType//deprecated
+class FactionIDBase : public EntityBase
 {
-	enum ETownType
+public:
+	enum Type : int32_t
 	{
+		NONE = -2,
+		DEFAULT = -1,
+		RANDOM = -1,
 		ANY = -1,
-		CASTLE, RAMPART, TOWER, INFERNO, NECROPOLIS, DUNGEON, STRONGHOLD, FORTRESS, CONFLUX, NEUTRAL
+		CASTLE,
+		RAMPART,
+		TOWER,
+		INFERNO,
+		NECROPOLIS,
+		DUNGEON,
+		STRONGHOLD,
+		FORTRESS,
+		CONFLUX,
+		NEUTRAL
 	};
-}
-
-class FactionID : public BaseForID<FactionID, int32_t>
-{
-	INSTID_LIKE_CLASS_COMMON(FactionID, si32)
-
-	DLL_LINKAGE static const FactionID NONE;
-	DLL_LINKAGE static const FactionID DEFAULT;
-	DLL_LINKAGE static const FactionID CASTLE;
-	DLL_LINKAGE static const FactionID RAMPART;
-	DLL_LINKAGE static const FactionID TOWER;
-	DLL_LINKAGE static const FactionID INFERNO;
-	DLL_LINKAGE static const FactionID NECROPOLIS;
-	DLL_LINKAGE static const FactionID DUNGEON;
-	DLL_LINKAGE static const FactionID STRONGHOLD;
-	DLL_LINKAGE static const FactionID FORTRESS;
-	DLL_LINKAGE static const FactionID CONFLUX;
-	DLL_LINKAGE static const FactionID NEUTRAL;
 
 	static si32 decode(const std::string& identifier);
 	static std::string encode(const si32 index);
 	static std::string entityType();
 };
 
-class TerrainID
+class FactionID : public EntityIdentifier<FactionIDBase>
 {
-	//Dummy class used only for serialization
 public:
-	static si32 decode(const std::string & identifier);
-	static std::string encode(const si32 index);
-	static std::string entityType();
+	using EntityIdentifier<FactionIDBase>::EntityIdentifier;
 };
 
+using ETownType = FactionID;
+
 class BuildingID
 {
 public:
@@ -953,27 +943,50 @@ public:
 
 ID_LIKE_OPERATORS(Obj, Obj::EObj)
 
-enum class Road : int8_t
+class RoadIsBase : public EntityBase
 {
-	NO_ROAD = 0,
-	FIRST_REGULAR_ROAD = 1,
-	DIRT_ROAD = 1,
-	GRAVEL_ROAD = 2,
-	COBBLESTONE_ROAD = 3,
-	ORIGINAL_ROAD_COUNT //+1
+public:
+	enum Type : int32_t
+	{
+		NO_ROAD = 0,
+		FIRST_REGULAR_ROAD = 1,
+		DIRT_ROAD = 1,
+		GRAVEL_ROAD = 2,
+		COBBLESTONE_ROAD = 3,
+		ORIGINAL_ROAD_COUNT //+1
+	};
 };
 
-enum class River : int8_t
+class RoadId : public EntityIdentifier<RoadIsBase>
 {
-	NO_RIVER = 0,
-	FIRST_REGULAR_RIVER = 1,
-	WATER_RIVER = 1,
-	ICY_RIVER = 2,
-	MUD_RIVER = 3,
-	LAVA_RIVER = 4,
-	ORIGINAL_RIVER_COUNT //+1
+public:
+	using EntityIdentifier<RoadIsBase>::EntityIdentifier;
 };
 
+class RiverIdBase : public EntityBase
+{
+public:
+	enum Type : int32_t
+	{
+		NO_RIVER = 0,
+		FIRST_REGULAR_RIVER = 1,
+		WATER_RIVER = 1,
+		ICY_RIVER = 2,
+		MUD_RIVER = 3,
+		LAVA_RIVER = 4,
+		ORIGINAL_RIVER_COUNT //+1
+	};
+};
+
+class RiverId : public EntityIdentifier<RiverIdBase>
+{
+public:
+	using EntityIdentifier<RiverIdBase>::EntityIdentifier;
+};
+
+using River = RiverId;
+using Road = RoadId;
+
 namespace SecSkillLevel
 {
 	enum SecSkillLevel
@@ -1296,37 +1309,58 @@ class BattleField : public BaseForID<BattleField, si32>
 	DLL_LINKAGE const BattleFieldInfo * getInfo() const;
 };
 
-enum class EBoatId : int32_t
+class BoatIdBase : public EntityBase
 {
-	NONE = -1,
-	NECROPOLIS = 0,
-	CASTLE,
-	FORTRESS
+public:
+	enum Type : int32_t
+	{
+		NONE = -1,
+		NECROPOLIS = 0,
+		CASTLE,
+		FORTRESS
+	};
 };
 
-using BoatId = Identifier<EBoatId>;
+class BoatId : public EntityIdentifier<BoatIdBase>
+{
+public:
+	using EntityIdentifier<BoatIdBase>::EntityIdentifier;
+};
 
-enum class ETerrainId {
-	NATIVE_TERRAIN = -4,
-	ANY_TERRAIN = -3,
-	NONE = -1,
-	FIRST_REGULAR_TERRAIN = 0,
-	DIRT = 0,
-	SAND,
-	GRASS,
-	SNOW,
-	SWAMP,
-	ROUGH,
-	SUBTERRANEAN,
-	LAVA,
-	WATER,
-	ROCK,
-	ORIGINAL_REGULAR_TERRAIN_COUNT = ROCK
+class TerrainIdBase : public EntityBase
+{
+public:
+	enum Type : int32_t
+	{
+		NATIVE_TERRAIN = -4,
+		ANY_TERRAIN = -3,
+		NONE = -1,
+		FIRST_REGULAR_TERRAIN = 0,
+		DIRT = 0,
+		SAND,
+		GRASS,
+		SNOW,
+		SWAMP,
+		ROUGH,
+		SUBTERRANEAN,
+		LAVA,
+		WATER,
+		ROCK,
+		ORIGINAL_REGULAR_TERRAIN_COUNT = ROCK
+	};
+
+	static si32 decode(const std::string & identifier);
+	static std::string encode(const si32 index);
+	static std::string entityType();
 };
 
-using TerrainId = Identifier<ETerrainId>;
-using RoadId = Identifier<Road>;
-using RiverId = Identifier<River>;
+class TerrainId : public EntityIdentifier<TerrainIdBase>
+{
+public:
+	using EntityIdentifier<TerrainIdBase>::EntityIdentifier;
+};
+
+using ETerrainId = TerrainId;
 
 class ObstacleInfo;
 class Obstacle : public BaseForID<Obstacle, si32>
@@ -1336,16 +1370,26 @@ class Obstacle : public BaseForID<Obstacle, si32>
 	DLL_LINKAGE const ObstacleInfo * getInfo() const;
 };
 
-enum class ESpellSchool: int8_t
+class SpellSchoolBase : public EntityBase
 {
-	ANY 	= -1,
-	AIR 	= 0,
-	FIRE 	= 1,
-	WATER 	= 2,
-	EARTH 	= 3,
+public:
+	enum Type : int32_t
+	{
+		ANY 	= -1,
+		AIR 	= 0,
+		FIRE 	= 1,
+		WATER 	= 2,
+		EARTH 	= 3,
+	};
 };
 
-using SpellSchool = Identifier<ESpellSchool>;
+class SpellSchool : public EntityIdentifier<SpellSchoolBase>
+{
+public:
+	using EntityIdentifier<SpellSchoolBase>::EntityIdentifier;
+};
+
+using ESpellSchool = SpellSchool;
 
 enum class EMetaclass: ui8
 {
@@ -1387,6 +1431,27 @@ enum class EBattleResult : int8_t
 	SURRENDER = 2
 };
 
+class GameResIDBase : public EntityBase
+{
+public:
+	enum Type : int32_t
+	{
+		WOOD = 0, MERCURY, ORE, SULFUR, CRYSTAL, GEMS, GOLD, MITHRIL,
+		COUNT,
+
+		WOOD_AND_ORE = 127,  // special case for town bonus resource
+		INVALID = -1
+	};
+};
+
+class GameResID : public EntityIdentifier<GameResIDBase>
+{
+public:
+	using EntityIdentifier<GameResIDBase>::EntityIdentifier;
+};
+
+using EGameResID = GameResID;
+
 // Typedef declarations
 using TExpType = si64;
 using TQuantity = si32;

+ 3 - 3
lib/ResourceSet.cpp

@@ -119,7 +119,7 @@ std::string ResourceSet::toString() const
 
 bool ResourceSet::nziterator::valid() const
 {
-	return cur.resType < GameConstants::RESOURCE_QUANTITY && cur.resVal;
+	return cur.resType < GameResID::COUNT && cur.resVal;
 }
 
 ResourceSet::nziterator ResourceSet::nziterator::operator++()
@@ -150,9 +150,9 @@ void ResourceSet::nziterator::advance()
 	do
 	{
 		++cur.resType;
-	} while(cur.resType < GameConstants::RESOURCE_QUANTITY && !(cur.resVal=rs[cur.resType]));
+	} while(cur.resType < GameResID::COUNT && !(cur.resVal=rs[cur.resType]));
 
-	if(cur.resType >= GameConstants::RESOURCE_QUANTITY)
+	if(cur.resType >= GameResID::COUNT)
 		cur.resVal = -1;
 }
 

+ 0 - 10
lib/ResourceSet.h

@@ -22,16 +22,6 @@ class JsonSerializeFormat;
 
 class ResourceSet;
 
-enum class EGameResID : int8_t
-{
-	WOOD = 0, MERCURY, ORE, SULFUR, CRYSTAL, GEMS, GOLD, MITHRIL,
-
-	WOOD_AND_ORE = 127,  // special case for town bonus resource
-	INVALID = -1
-};
-
-using GameResID = Identifier<EGameResID>;
-
 //class to be representing a vector of resource
 class ResourceSet
 {

+ 1 - 1
lib/StringConstants.h

@@ -68,7 +68,7 @@ namespace EBuildingType
 	};
 }
 
-namespace ETownType
+namespace NFaction
 {
 	const std::string names [GameConstants::F_NUMBER] =
 	{

+ 1 - 1
lib/gameState/CGameState.cpp

@@ -697,7 +697,7 @@ void CGameState::initRandomFactionsForPlayers()
 	logGlobal->debug("\tPicking random factions for players");
 	for(auto & elem : scenarioOps->playerInfos)
 	{
-		if(elem.second.castle==-1)
+		if(elem.second.castle==FactionID::RANDOM)
 		{
 			auto randomID = getRandomGenerator().nextInt((int)map->players[elem.first.getNum()].allowedFactions.size() - 1);
 			auto iter = map->players[elem.first.getNum()].allowedFactions.begin();

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -516,7 +516,7 @@ void CGTownInstance::newTurn(CRandomGenerator & rand) const
 			std::vector<SlotID> nativeCrits; //slots
 			for(const auto & elem : Slots())
 			{
-				if (elem.second->type->getFaction() == subID) //native
+				if (elem.second->type->getFaction() == getFaction()) //native
 				{
 					nativeCrits.push_back(elem.first); //collect matching slots
 				}

+ 1 - 1
lib/mapObjects/MiscObjects.cpp

@@ -914,7 +914,7 @@ void CGWitchHut::serializeJsonOptions(JsonSerializeFormat & handler)
 
 	if(handler.saving)
 	{
-		for(si32 i = 0; i < skillCount; ++i)
+		for(SecondarySkill i(0); i < SecondarySkill(skillCount); ++i)
 			if(vstd::contains(allowedAbilities, i))
 				temp[i] = true;
 	}

+ 1 - 1
lib/mapping/MapFormatJson.cpp

@@ -396,7 +396,7 @@ void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std
 	if(handler.saving)
 	{
 		for(auto faction : VLC->townh->objects)
-			if(faction->town && vstd::contains(value, faction->getIndex()))
+			if(faction->town && vstd::contains(value, faction->getId()))
 				temp[static_cast<std::size_t>(faction->getIndex())] = true;
 	}
 

+ 1 - 1
lib/mapping/MapReaderH3M.cpp

@@ -161,7 +161,7 @@ RiverId MapReaderH3M::readRiver()
 SecondarySkill MapReaderH3M::readSkill()
 {
 	SecondarySkill result(readUInt8());
-	assert(result < features.skillsCount);
+	assert(result.getNum() < features.skillsCount);
 	return remapIdentifier(result);;
 }
 

+ 1 - 1
lib/rmg/CMapGenOptions.cpp

@@ -506,7 +506,7 @@ const CRmgTemplate * CMapGenOptions::getPossibleTemplate(CRandomGenerator & rand
 	return *RandomGeneratorUtil::nextItem(templates, rand);
 }
 
-CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(RANDOM_TOWN), playerType(EPlayerType::AI), team(TeamID::NO_TEAM)
+CMapGenOptions::CPlayerSettings::CPlayerSettings() : color(0), startingTown(FactionID::RANDOM), playerType(EPlayerType::AI), team(TeamID::NO_TEAM)
 {
 
 }

+ 0 - 3
lib/rmg/CMapGenOptions.h

@@ -53,9 +53,6 @@ public:
 		TeamID getTeam() const;
 		void setTeam(const TeamID & value);
 
-		/// Constant for a random town selection.
-		static const si32 RANDOM_TOWN = -1;
-
 	private:
 		PlayerColor color;
 		si32 startingTown;

+ 1 - 1
lib/rmg/CMapGenerator.cpp

@@ -174,7 +174,7 @@ std::string CMapGenerator::getMapDescription() const
 		{
 			ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor().getNum()] << " is human";
 		}
-		if(pSettings.getStartingTown() != CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
+		if(pSettings.getStartingTown() != FactionID::RANDOM)
 		{
 			ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor().getNum()]
 			   << " town choice is " << (*VLC->townh)[pSettings.getStartingTown()]->getNameTranslated();

+ 6 - 6
lib/rmg/CRmgTemplate.cpp

@@ -374,15 +374,15 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 
 	if(terrainTypeLikeZone == NO_ZONE)
 	{
-		handler.serializeIdArray<TerrainId, TerrainID>("terrainTypes", terrainTypes, std::set<TerrainId>());
-		handler.serializeIdArray<TerrainId, TerrainID>("bannedTerrains", bannedTerrains, std::set<TerrainId>());
+		handler.serializeIdArray("terrainTypes", terrainTypes);
+		handler.serializeIdArray("bannedTerrains", bannedTerrains);
 	}
 
 	handler.serializeBool("townsAreSameType", townsAreSameType, false);
-	handler.serializeIdArray<FactionID>("allowedMonsters", monsterTypes, std::set<FactionID>());
-	handler.serializeIdArray<FactionID>("bannedMonsters", bannedMonsters, std::set<FactionID>());
-	handler.serializeIdArray<FactionID>("allowedTowns", townTypes, std::set<FactionID>());
-	handler.serializeIdArray<FactionID>("bannedTowns", bannedTownTypes, std::set<FactionID>());
+	handler.serializeIdArray("allowedMonsters", monsterTypes);
+	handler.serializeIdArray("bannedMonsters", bannedMonsters);
+	handler.serializeIdArray("allowedTowns", townTypes);
+	handler.serializeIdArray("bannedTowns", bannedTownTypes);
 
 	{
 		//TODO: add support for std::map to serializeEnum

+ 2 - 2
lib/rmg/CZonePlacer.cpp

@@ -442,13 +442,13 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
 			{
 				auto player = PlayerColor(*owner - 1);
 				auto playerSettings = map.getMapGenOptions().getPlayersSettings();
-				si32 faction = CMapGenOptions::CPlayerSettings::RANDOM_TOWN;
+				si32 faction = FactionID::RANDOM;
 				if (vstd::contains(playerSettings, player))
 					faction = playerSettings[player].getStartingTown();
 				else
 					logGlobal->error("Can't find info for player %d (starting zone)", player.getNum());
 
-				if (faction == CMapGenOptions::CPlayerSettings::RANDOM_TOWN) //TODO: check this after a town has already been randomized
+				if (faction == FactionID::RANDOM) //TODO: check this after a town has already been randomized
 					zonesToPlace.push_back(zone);
 				else
 				{

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

@@ -60,7 +60,7 @@ void TownPlacer::placeTowns(ObjectManager & manager)
 			player = PlayerColor(player_id);
 			zone.setTownType(map.getMapGenOptions().getPlayersSettings().find(player)->second.getStartingTown());
 			
-			if(zone.getTownType() == CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
+			if(zone.getTownType() == FactionID::RANDOM)
 				zone.setTownType(getRandomTownType(true));
 		}
 		else //no player - randomize town

+ 1 - 1
lib/spells/AdventureSpellMechanics.cpp

@@ -215,7 +215,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
 	{
 		NewObject no;
 		no.ID = Obj::BOAT;
-		no.subID = BoatId(EBoatId::NECROPOLIS);
+		no.subID = BoatId::NECROPOLIS;
 		no.targetPos = summonPos;
 		env->apply(&no);
 	}

+ 1 - 1
lib/spells/CSpellHandler.cpp

@@ -608,7 +608,7 @@ std::vector<JsonNode> CSpellHandler::loadLegacyData()
 
 			auto & chances = lineNode["gainChance"].Struct();
 
-			for(const auto & name : ETownType::names)
+			for(const auto & name : NFaction::names)
 				chances[name].Integer() = static_cast<si64>(parser.readNumber());
 
 			auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);

+ 5 - 5
server/CVCMIServer.cpp

@@ -840,10 +840,10 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
 	auto & allowed = getPlayerInfo(player.getNum()).allowedFactions;
 	const bool allowRandomTown = getPlayerInfo(player.getNum()).isFactionRandom;
 
-	if(cur == PlayerSettings::NONE) //no change
+	if(cur.getNum() == PlayerSettings::NONE) //no change
 		return;
 
-	if(cur == PlayerSettings::RANDOM) //first/last available
+	if(cur.getNum() == PlayerSettings::RANDOM) //first/last available
 	{
 		if(dir > 0)
 			cur = *allowed.begin(); //id of first town
@@ -880,7 +880,7 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
 	{
 		s.hero = PlayerSettings::RANDOM;
 	}
-	if(cur < 0 && s.bonus == PlayerSettings::RESOURCE)
+	if(cur.getNum() < 0 && s.bonus == PlayerSettings::RESOURCE)
 		s.bonus = PlayerSettings::RANDOM;
 }
 
@@ -935,7 +935,7 @@ void CVCMIServer::setCampaignBonus(int bonusId)
 void CVCMIServer::optionNextHero(PlayerColor player, int dir)
 {
 	PlayerSettings & s = si->playerInfos[player];
-	if(s.castle < 0 || s.hero == PlayerSettings::NONE)
+	if(s.castle.getNum() < 0 || s.hero == PlayerSettings::NONE)
 		return;
 
 	if(s.hero == PlayerSettings::RANDOM) // first/last available
@@ -1004,7 +1004,7 @@ void CVCMIServer::optionNextBonus(PlayerColor player, int dir)
 	if(ret < PlayerSettings::RANDOM)
 		ret = PlayerSettings::RESOURCE;
 
-	if(s.castle == PlayerSettings::RANDOM && ret == PlayerSettings::RESOURCE) //random castle - can't be resource
+	if(s.castle.getNum() == PlayerSettings::RANDOM && ret == PlayerSettings::RESOURCE) //random castle - can't be resource
 	{
 		if(dir < 0)
 			ret = PlayerSettings::GOLD;

+ 1 - 1
test/entity/CCreatureTest.cpp

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

+ 1 - 1
test/mock/mock_Creature.h

@@ -57,7 +57,7 @@ public:
 	MOCK_CONST_METHOD1(getCost, int32_t(int32_t));
 	MOCK_CONST_METHOD0(isDoubleWide, bool());
 
-	MOCK_CONST_METHOD1(getRecruitCost, int32_t(Identifier<EGameResID>));
+	MOCK_CONST_METHOD1(getRecruitCost, int32_t(GameResID));
 	MOCK_CONST_METHOD0(getFullRecruitCost, ResourceSet());
 	MOCK_CONST_METHOD0(hasUpgrades, bool());
 };