浏览代码

Terrain/Road/River handler are now in compileable state

Ivan Savenko 2 年之前
父节点
当前提交
e1799379dd

+ 10 - 10
client/CMusicHandler.cpp

@@ -94,16 +94,16 @@ CSoundHandler::CSoundHandler():
 	//TODO: support custom sounds for new terrains and load from json
 	horseSounds =
 	{
-		{TerrainId::DIRT, soundBase::horseDirt},
-		{TerrainId::SAND, soundBase::horseSand},
-		{TerrainId::GRASS, soundBase::horseGrass},
-		{TerrainId::SNOW, soundBase::horseSnow},
-		{TerrainId::SWAMP, soundBase::horseSwamp},
-		{TerrainId::ROUGH, soundBase::horseRough},
-		{TerrainId::SUBTERRANEAN, soundBase::horseSubterranean},
-		{TerrainId::LAVA, soundBase::horseLava},
-		{TerrainId::WATER, soundBase::horseWater},
-		{TerrainId::ROCK, soundBase::horseRock}
+		{ETerrainId::DIRT, soundBase::horseDirt},
+		{ETerrainId::SAND, soundBase::horseSand},
+		{ETerrainId::GRASS, soundBase::horseGrass},
+		{ETerrainId::SNOW, soundBase::horseSnow},
+		{ETerrainId::SWAMP, soundBase::horseSwamp},
+		{ETerrainId::ROUGH, soundBase::horseRough},
+		{ETerrainId::SUBTERRANEAN, soundBase::horseSubterranean},
+		{ETerrainId::LAVA, soundBase::horseLava},
+		{ETerrainId::WATER, soundBase::horseWater},
+		{ETerrainId::ROCK, soundBase::horseRock}
 	};
 }
 

+ 1 - 1
client/CPlayerInterface.cpp

@@ -2372,7 +2372,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 		for (auto & elem : path.nodes)
 			elem.coord = h->convertFromVisitablePos(elem.coord);
 
-		TerrainId currentTerrain = TerrainId::BORDER; // not init yet
+		TerrainId currentTerrain = ETerrainId::BORDER; // not init yet
 		TerrainId newTerrain;
 		int sh = -1;
 

+ 6 - 6
client/lobby/RandomMapTab.cpp

@@ -108,12 +108,12 @@ RandomMapTab::RandomMapTab():
 		GH.pushIntT<TeamAlignmentsWidget>(*this);
 	});
 	
-	for(auto road : VLC->terrainTypeHandler->roads())
+	for(auto road : VLC->roadTypeHandler->objects)
 	{
-		std::string cbRoadType = "selectRoad_" + road.name;
+		std::string cbRoadType = "selectRoad_" + road->getName();
 		addCallback(cbRoadType, [&, road](bool on)
 		{
-			mapGenOptions->setRoadEnabled(road.name, on);
+			mapGenOptions->setRoadEnabled(road->getName(), on);
 			updateMapInfoByHost();
 		});
 	}
@@ -283,11 +283,11 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
 		else
 			w->addTextOverlay(readText(variables["defaultTemplate"]), EFonts::FONT_SMALL);
 	}
-	for(auto r : VLC->terrainTypeHandler->roads())
+	for(auto r : VLC->roadTypeHandler->objects)
 	{
-		if(auto w = widget<CToggleButton>(r.name))
+		if(auto w = widget<CToggleButton>(r->getName()))
 		{
-			w->setSelected(opts->isRoadEnabled(r.name));
+			w->setSelected(opts->isRoadEnabled(r->getName()));
 		}
 	}
 }

+ 2 - 1
client/mapHandler.cpp

@@ -1389,7 +1389,8 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
 		}
 	}
 
-	VLC->terrainTypeHandler->getById(t.terType->id)->terrainText;
+	if(!isTile2Terrain || out.empty())
+		out = VLC->terrainTypeHandler->getById(t.terType->id)->terrainText;
 
 	if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG)
 	{

+ 1 - 1
config/randomMap.json

@@ -42,4 +42,4 @@
     "value" : [2000, 5333, 8666, 12000],
     "rewardValue" : [5000, 10000, 15000, 20000]
   }
-}
+}

+ 2 - 2
lib/CCreatureHandler.cpp

@@ -288,7 +288,7 @@ std::string CCreature::nodeName() const
 bool CCreature::isItNativeTerrain(TerrainId terrain) const
 {
 	auto native = getNativeTerrain();
-	return native == terrain || native == TerrainId::ANY_TERRAIN;
+	return native == terrain || native == ETerrainId::ANY_TERRAIN;
 }
 
 TerrainId CCreature::getNativeTerrain() const
@@ -299,7 +299,7 @@ TerrainId CCreature::getNativeTerrain() const
 	//this code is used in the CreatureTerrainLimiter::limit to setup battle bonuses
 	//and in the CGHeroInstance::getNativeTerrain() to setup mevement bonuses or/and penalties.
 	return hasBonus(selectorNoTerrainPenalty, selectorNoTerrainPenalty)
-		? TerrainId(TerrainId::ANY_TERRAIN)
+		? TerrainId(ETerrainId::ANY_TERRAIN)
 		: (*VLC->townh)[faction]->nativeTerrain;
 }
 

+ 1 - 1
lib/CGameState.cpp

@@ -2135,7 +2135,7 @@ void CGameState::updateRumor()
 			rumorId = *RandomGeneratorUtil::nextItem(sRumorTypes, rand);
 			if(rumorId == RumorState::RUMOR_GRAIL)
 			{
-				rumorExtra = getTile(map->grailPos)->terType->id;
+				rumorExtra = getTile(map->grailPos)->terType->id.getNum();
 				break;
 			}
 

+ 1 - 1
lib/CPathfinder.cpp

@@ -1003,7 +1003,7 @@ TurnInfo::BonusCache::BonusCache(TConstBonusListPtr bl)
 	for(const auto & terrain : VLC->terrainTypeHandler->objects)
 	{
 		noTerrainPenalty.push_back(static_cast<bool>(
-				bl->getFirst(Selector::type()(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype()(terrain->id)))));
+				bl->getFirst(Selector::type()(Bonus::NO_TERRAIN_PENALTY).And(Selector::subtype()(terrain->id.getNum())))));
 	}
 
 	freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(Bonus::FREE_SHIP_BOARDING)));

+ 1 - 1
lib/CStack.cpp

@@ -333,7 +333,7 @@ bool CStack::canBeHealed() const
 bool CStack::isOnNativeTerrain() const
 {
 	//this code is called from CreatureTerrainLimiter::limit on battle start
-	auto res = nativeTerrain == TerrainId::ANY_TERRAIN || nativeTerrain == battle->getTerrainType();
+	auto res = nativeTerrain == ETerrainId::ANY_TERRAIN || nativeTerrain == battle->getTerrainType();
 	return res;
 }
 

+ 14 - 6
lib/CTownHandler.cpp

@@ -28,9 +28,9 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 const int NAMES_PER_TOWN=16; // number of town names per faction in H3 files. Json can define any number
 
-const TerrainId CTownHandler::defaultGoodTerrain(TerrainId::GRASS);
-const TerrainId CTownHandler::defaultEvilTerrain(TerrainId::LAVA);
-const TerrainId CTownHandler::defaultNeutralTerrain(TerrainId::ROUGH);
+const TerrainId CTownHandler::defaultGoodTerrain(ETerrainId::GRASS);
+const TerrainId CTownHandler::defaultEvilTerrain(ETerrainId::LAVA);
+const TerrainId CTownHandler::defaultNeutralTerrain(ETerrainId::ROUGH);
 
 const std::map<std::string, CBuilding::EBuildMode> CBuilding::MODES =
 {
@@ -989,9 +989,17 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
 
 	//Contructor is not called here, but operator=
 	auto nativeTerrain = source["nativeTerrain"];
-	faction->nativeTerrain = nativeTerrain.isNull()
-		? getDefaultTerrainForAlignment(faction->alignment)
-		: VLC->terrainTypeHandler->getInfoByName(nativeTerrain.String())->id;
+
+	if ( nativeTerrain.isNull())
+	{
+		faction->nativeTerrain = getDefaultTerrainForAlignment(faction->alignment);
+	}
+	else
+	{
+		VLC->modh->identifiers.requestIdentifier("terrain", nativeTerrain, [=](int32_t index){
+			faction->nativeTerrain = TerrainId(index);
+		});
+	}
 
 	if (!source["town"].isNull())
 	{

+ 116 - 79
lib/GameConstants.h

@@ -149,12 +149,6 @@ STRONG_INLINE bool operator>=(const A & a, const B & b)			\
 	ID_LIKE_OPERATORS_INTERNAL(ENUM_NAME, CLASS_NAME, a, b.num)
 
 
-#define OP_DECL_INT(CLASS_NAME, OP)					\
-bool operator OP (const CLASS_NAME & b) const		\
-{													\
-	return num OP b.num;							\
-}
-
 #define INSTID_LIKE_CLASS_COMMON(CLASS_NAME, NUMERIC_NAME)	\
 public:														\
 CLASS_NAME() : BaseForID<CLASS_NAME, NUMERIC_NAME>(-1) {}	\
@@ -205,14 +199,79 @@ public:
 	}
 
 	typedef BaseForID<Derived, NumericType> __SelfType;
-	OP_DECL_INT(__SelfType, ==)
-	OP_DECL_INT(__SelfType, !=)
-	OP_DECL_INT(__SelfType, <)
-	OP_DECL_INT(__SelfType, >)
-	OP_DECL_INT(__SelfType, <=)
-	OP_DECL_INT(__SelfType, >=)
+	bool operator == (const BaseForID & b) const { return num == b.num; }
+	bool operator <= (const BaseForID & b) const { return num >= b.num; }
+	bool operator >= (const BaseForID & b) const { return num <= b.num; }
+	bool operator != (const BaseForID & b) const { return num != b.num; }
+	bool operator <  (const BaseForID & b) const { return num <  b.num; }
+	bool operator >  (const BaseForID & b) const { return num > b.num; }
+
+	BaseForID & operator++() { ++num; return *this; }
 };
 
+template < typename T>
+class Identifier : public IdTag
+{
+public:
+	using EnumType    = T;
+	using NumericType = typename std::underlying_type<EnumType>::type;
+
+private:
+	NumericType num;
+
+public:
+	NumericType getNum() const
+	{
+		return num;
+	}
+
+	EnumType toEnum() const
+	{
+		return static_cast<EnumType>(num);
+	}
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & num;
+	}
+
+	explicit Identifier(NumericType _num = -1)
+	{
+		num = _num;
+	}
+
+	/* implicit */ Identifier(EnumType _num)
+	{
+		num = static_cast<NumericType>(_num);
+	}
+
+	void advance(int change)
+	{
+		num += change;
+	}
+
+	bool operator == (const Identifier & b) const { return num == b.num; }
+	bool operator <= (const Identifier & b) const { return num >= b.num; }
+	bool operator >= (const Identifier & b) const { return num <= b.num; }
+	bool operator != (const Identifier & b) const { return num != b.num; }
+	bool operator <  (const Identifier & b) const { return num <  b.num; }
+	bool operator >  (const Identifier & b) const { return num > b.num; }
+
+	Identifier & operator++()
+	{
+		++num;
+		return *this;
+	}
+
+	Identifier operator++(int)
+	{
+		Identifier ret(*this);
+		++num;
+		return ret;
+	}
+};
+
+
 template<typename Der, typename Num>
 std::ostream & operator << (std::ostream & os, BaseForID<Der, Num> id);
 
@@ -224,6 +283,14 @@ std::ostream & operator << (std::ostream & os, BaseForID<Der, Num> id)
 	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>
 {
 	INSTID_LIKE_CLASS_COMMON(ArtifactInstanceID, si32)
@@ -830,32 +897,26 @@ public:
 
 ID_LIKE_OPERATORS(Obj, Obj::EObj)
 
-namespace Road
+enum class Road : int8_t
 {
-	enum ERoad : ui8
-	{
-		NO_ROAD = 0,
-		FIRST_REGULAR_ROAD = 1,
-		DIRT_ROAD = 1,
-		GRAVEL_ROAD = 2,
-		COBBLESTONE_ROAD = 3,
-		ORIGINAL_ROAD_COUNT //+1
-	};
-}
+	NO_ROAD = 0,
+	FIRST_REGULAR_ROAD = 1,
+	DIRT_ROAD = 1,
+	GRAVEL_ROAD = 2,
+	COBBLESTONE_ROAD = 3,
+	ORIGINAL_ROAD_COUNT //+1
+};
 
-namespace River
+enum class River : int8_t
 {
-	enum ERiver : ui8
-	{
-		NO_RIVER = 0,
-		FIRST_REGULAR_RIVER = 1,
-		WATER_RIVER = 1,
-		ICY_RIVER = 2,
-		MUD_RIVER = 3,
-		LAVA_RIVER = 4,
-		ORIGINAL_RIVER_COUNT //+1
-	};
-}
+	NO_RIVER = 0,
+	FIRST_REGULAR_RIVER = 1,
+	WATER_RIVER = 1,
+	ICY_RIVER = 2,
+	MUD_RIVER = 3,
+	LAVA_RIVER = 4,
+	ORIGINAL_RIVER_COUNT //+1
+};
 
 namespace SecSkillLevel
 {
@@ -1159,48 +1220,28 @@ class BattleField : public BaseForID<BattleField, si32>
 	DLL_LINKAGE static BattleField fromString(std::string identifier);
 };
 
-class TerrainId
-{
-public:
-	enum ETerrainID {
-			NATIVE_TERRAIN = -4,
-			ANY_TERRAIN = -3,
-			WRONG = -2,
-			BORDER = -1,
-			FIRST_REGULAR_TERRAIN = 0,
-			DIRT,
-			SAND,
-			GRASS,
-			SNOW,
-			SWAMP,
-			ROUGH,
-			SUBTERRANEAN,
-			LAVA,
-			WATER,
-			ROCK,
-			ORIGINAL_TERRAIN_COUNT
-	};
-
-	TerrainId(ETerrainID _num = WRONG) : num(_num)
-	{}
-
-	ETerrainID num;
-
-	ID_LIKE_CLASS_COMMON(TerrainId, ETerrainID)
-
-	DLL_LINKAGE operator std::string() const;
-	DLL_LINKAGE const TerrainId * getInfo() const;
-
-	DLL_LINKAGE static ETerrainID fromString(std::string identifier);
-
-	TerrainId & operator++()
-	{
-		num = static_cast<ETerrainID>(static_cast<int>(num) + 1);
-		return *this;
-	}
+enum class ETerrainId {
+	NATIVE_TERRAIN = -4,
+	ANY_TERRAIN = -3,
+	WRONG = -2,
+	BORDER = -1,
+	FIRST_REGULAR_TERRAIN = 0,
+	DIRT,
+	SAND,
+	GRASS,
+	SNOW,
+	SWAMP,
+	ROUGH,
+	SUBTERRANEAN,
+	LAVA,
+	WATER,
+	ROCK,
+	ORIGINAL_TERRAIN_COUNT
 };
 
-ID_LIKE_OPERATORS(TerrainId, TerrainId::ETerrainID)
+using TerrainId = Identifier<ETerrainId>;
+using RoadId = Identifier<Road>;
+using RiverId = Identifier<River>;
 
 class ObstacleInfo;
 class Obstacle : public BaseForID<Obstacle, si32>
@@ -1259,9 +1300,6 @@ typedef si64 TExpType;
 typedef std::pair<si64, si64> TDmgRange;
 typedef si32 TBonusSubtype;
 typedef si32 TQuantity;
-//typedef si8 TerrainId;
-typedef si8 RoadId;
-typedef si8 RiverId;
 
 typedef int TRmgTemplateZoneId;
 
@@ -1269,6 +1307,5 @@ typedef int TRmgTemplateZoneId;
 #undef ID_LIKE_OPERATORS
 #undef ID_LIKE_OPERATORS_INTERNAL
 #undef INSTID_LIKE_CLASS_COMMON
-#undef OP_DECL_INT
 
 VCMI_LIB_NAMESPACE_END

+ 3 - 3
lib/HeroBonus.cpp

@@ -2106,7 +2106,7 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
 }
 
 CreatureTerrainLimiter::CreatureTerrainLimiter()
-	: terrainType(TerrainId::NATIVE_TERRAIN)
+	: terrainType(ETerrainId::NATIVE_TERRAIN)
 {
 }
 
@@ -2120,7 +2120,7 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const
 	const CStack *stack = retrieveStackBattle(&context.node);
 	if(stack)
 	{
-		if (terrainType == TerrainId::NATIVE_TERRAIN)//terrainType not specified = native
+		if (terrainType == ETerrainId::NATIVE_TERRAIN)//terrainType not specified = native
 		{
 			return !stack->isOnNativeTerrain();
 		}
@@ -2137,7 +2137,7 @@ std::string CreatureTerrainLimiter::toString() const
 {
 	boost::format fmt("CreatureTerrainLimiter(terrainType=%s)");
 	auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->name;
-	fmt % (terrainType == TerrainId::NATIVE_TERRAIN ? "native" : terrainName);
+	fmt % (terrainType == ETerrainId::NATIVE_TERRAIN ? "native" : terrainName);
 	return fmt.str();
 }
 

+ 3 - 3
lib/NetPacksLib.cpp

@@ -720,13 +720,13 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
 
 DLL_LINKAGE void NewObject::applyGs(CGameState *gs)
 {
-	TerrainId terrainType = TerrainId::BORDER;
+	TerrainId terrainType = ETerrainId::BORDER;
 
 	if(ID == Obj::BOAT && !gs->isInTheMap(pos)) //special handling for bug #3060 - pos outside map but visitablePos is not
 	{
 		CGObjectInstance testObject = CGObjectInstance();
 		testObject.pos = pos;
-		testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(TerrainId::WATER).front();
+		testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(ETerrainId::WATER).front();
 
 		const int3 previousXAxisTile = int3(pos.x - 1, pos.y, pos.z);
 		assert(gs->isInTheMap(previousXAxisTile) && (testObject.visitablePos() == previousXAxisTile));
@@ -743,7 +743,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs)
 	{
 	case Obj::BOAT:
 		o = new CGBoat();
-		terrainType = TerrainId::WATER; //TODO: either boat should only spawn on water, or all water objects should be handled this way
+		terrainType = ETerrainId::WATER; //TODO: either boat should only spawn on water, or all water objects should be handled this way
 		break;
 	case Obj::MONSTER: //probably more options will be needed
 		o = new CGCreature();

+ 7 - 1
lib/ObstacleHandler.cpp

@@ -10,6 +10,7 @@
 #include "StdInc.h"
 #include "ObstacleHandler.h"
 #include "BattleFieldHandler.h"
+#include "CModHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -86,8 +87,13 @@ ObstacleInfo * ObstacleHandler::loadFromJson(const std::string & scope, const Js
 	info->width = json["width"].Integer();
 	info->height = json["height"].Integer();
 	for(auto & t : json["allowedTerrains"].Vector())
-		info->allowedTerrains.emplace_back(VLC->terrainTypeHandler->getInfoByName(t.String())->id);
+	{
+		VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier){
+			info->allowedTerrains.emplace_back(identifier);
+		});
+	}
 	for(auto & t : json["specialBattlefields"].Vector())
+
 		info->allowedSpecialBfields.emplace_back(t.String());
 	info->blockedTiles = json["blockedTiles"].convertTo<std::vector<si16>>();
 	info->isAbsoluteObstacle = json["absolute"].Bool();

+ 9 - 1
lib/Terrain.cpp

@@ -95,7 +95,7 @@ TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const
 		});
 	}
 
-	info->rockTerrain = TerrainId::ROCK;
+	info->rockTerrain = ETerrainId::ROCK;
 
 	if(!json["rockTerrain"].isNull())
 	{
@@ -247,4 +247,12 @@ bool TerrainType::isTransitionRequired() const
 	return transitionRequired;
 }
 
+TerrainType::TerrainType()
+{}
+
+RiverType::RiverType()
+{}
+
+RoadType::RoadType()
+{}
 VCMI_LIB_NAMESPACE_END

+ 30 - 33
lib/Terrain.h

@@ -22,12 +22,12 @@ VCMI_LIB_NAMESPACE_BEGIN
 class DLL_LINKAGE TerrainType : public EntityT<TerrainId>
 {
 public:
-	int32_t getIndex() const override;
-	int32_t getIconIndex() const override;
-	const std::string & getName() const override;
-	const std::string & getJsonKey() const override;
-	void registerIcons(const IconRegistar & cb) const override;
-	TerrainId getId() const override;
+	int32_t getIndex() const override { return id.getNum(); }
+	int32_t getIconIndex() const override { return 0; }
+	const std::string & getName() const override { return name;}
+	const std::string & getJsonKey() const override { return name;}
+	void registerIcons(const IconRegistar & cb) const override {}
+	TerrainId getId() const override { return id;}
 
 	enum PassabilityType : ui8
 	{
@@ -57,7 +57,7 @@ public:
 	ui8 passabilityType;
 	bool transitionRequired;
 	
-	TerrainType(const std::string & name = "");
+	TerrainType();
 	
 	bool operator==(const TerrainType & other);
 	bool operator!=(const TerrainType & other);
@@ -97,22 +97,22 @@ public:
 	}
 };
 
-class DLL_LINKAGE RiverType : public EntityT<TerrainId>
+class DLL_LINKAGE RiverType : public EntityT<RiverId>
 {
 public:
-	int32_t getIndex() const override;
-	int32_t getIconIndex() const override;
-	const std::string & getName() const override;
-	const std::string & getJsonKey() const override;
-	void registerIcons(const IconRegistar & cb) const override;
-	TerrainId getId() const override;
+	int32_t getIndex() const override { return id.getNum(); }
+	int32_t getIconIndex() const override { return 0; }
+	const std::string & getName() const override { return code;}
+	const std::string & getJsonKey() const override { return code;}
+	void registerIcons(const IconRegistar & cb) const override {}
+	RiverId getId() const override { return id;}
 
 	std::string fileName;
 	std::string code;
 	std::string deltaName;
 	RiverId id;
 
-	RiverType(const std::string & fileName = "", const std::string & code = "", RiverId id = River::NO_RIVER);
+	RiverType();
 
 	template <typename Handler> void serialize(Handler& h, const int version)
 	{
@@ -123,22 +123,22 @@ public:
 	}
 };
 
-class DLL_LINKAGE RoadType : public EntityT<TerrainId>
+class DLL_LINKAGE RoadType : public EntityT<RoadId>
 {
 public:
-	int32_t getIndex() const override;
-	int32_t getIconIndex() const override;
-	const std::string & getName() const override;
-	const std::string & getJsonKey() const override;
-	void registerIcons(const IconRegistar & cb) const override;
-	TerrainId getId() const override;
+	int32_t getIndex() const override { return id.getNum(); }
+	int32_t getIconIndex() const override { return 0; }
+	const std::string & getName() const override { return code;}
+	const std::string & getJsonKey() const override { return code;}
+	void registerIcons(const IconRegistar & cb) const override {}
+	RoadId getId() const override { return id;}
 
 	std::string fileName;
 	std::string code;
 	RoadId id;
 	ui8 movementCost;
 
-	RoadType(const std::string & fileName = "", const std::string& code = "", RoadId id = Road::NO_ROAD);
+	RoadType();
 
 	template <typename Handler> void serialize(Handler& h, const int version)
 	{
@@ -154,12 +154,12 @@ class DLL_LINKAGE TerrainTypeService : public EntityServiceT<TerrainId, TerrainT
 public:
 };
 
-class DLL_LINKAGE RiverTypeService : public EntityServiceT<TerrainId, RiverType>
+class DLL_LINKAGE RiverTypeService : public EntityServiceT<RiverId, RiverType>
 {
 public:
 };
 
-class DLL_LINKAGE RoadTypeService : public EntityServiceT<TerrainId, RoadType>
+class DLL_LINKAGE RoadTypeService : public EntityServiceT<RoadId, RoadType>
 {
 public:
 };
@@ -177,8 +177,7 @@ public:
 	virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
 	virtual std::vector<bool> getDefaultAllowed() const override;
 
-	TerrainType * getInfoByCode(const std::string & identifier);
-	TerrainType * getInfoByName(const std::string & identifier);
+//	TerrainType * getInfoByCode(const std::string & identifier);
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -186,7 +185,7 @@ public:
 	}
 };
 
-class DLL_LINKAGE RiverTypeHandler : public CHandlerBase<TerrainId, RiverType, RiverType, RiverTypeService>
+class DLL_LINKAGE RiverTypeHandler : public CHandlerBase<RiverId, RiverType, RiverType, RiverTypeService>
 {
 public:
 	virtual RiverType * loadFromJson(
@@ -199,8 +198,7 @@ public:
 	virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
 	virtual std::vector<bool> getDefaultAllowed() const override;
 
-	RiverType * getInfoByCode(const std::string & identifier);
-	RiverType * getInfoByName(const std::string & identifier);
+//	RiverType * getInfoByCode(const std::string & identifier);
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
@@ -208,7 +206,7 @@ public:
 	}
 };
 
-class DLL_LINKAGE RoadTypeHandler : public CHandlerBase<TerrainId, RoadType, RoadType, RoadTypeService>
+class DLL_LINKAGE RoadTypeHandler : public CHandlerBase<RoadId, RoadType, RoadType, RoadTypeService>
 {
 public:
 	virtual RoadType * loadFromJson(
@@ -221,8 +219,7 @@ public:
 	virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
 	virtual std::vector<bool> getDefaultAllowed() const override;
 
-	RoadType * getInfoByCode(const std::string & identifier);
-	RoadType * getInfoByName(const std::string & identifier);
+//	RoadType * getInfoByCode(const std::string & identifier);
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{

+ 7 - 7
lib/mapObjects/CGHeroInstance.cpp

@@ -81,13 +81,13 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f
 	int64_t ret = GameConstants::BASE_MOVEMENT_COST;
 
 	//if there is road both on dest and src tiles - use road movement cost
-	if(dest.roadType->id && from.roadType->id)
+	if(dest.roadType->id != Road::NO_ROAD && from.roadType->id != Road::NO_ROAD)
 	{
 		ret = std::max(dest.roadType->movementCost, from.roadType->movementCost);
 	}
 	else if(ti->nativeTerrain != from.terType->id &&//the terrain is not native
-			ti->nativeTerrain != TerrainId::ANY_TERRAIN && //no special creature bonus
-			!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->id)) //no special movement bonus
+			ti->nativeTerrain != ETerrainId::ANY_TERRAIN && //no special creature bonus
+			!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->id.getNum())) //no special movement bonus
 	{
 
 		ret = VLC->heroh->terrCosts[from.terType->id];
@@ -106,18 +106,18 @@ TerrainId CGHeroInstance::getNativeTerrain() const
 	// will always have best penalty without any influence from player-defined stacks order
 
 	// TODO: What should we do if all hero stacks are neutral creatures?
-	TerrainId nativeTerrain = TerrainId::BORDER;
+	TerrainId nativeTerrain = ETerrainId::BORDER;
 
 	for(auto stack : stacks)
 	{
 		TerrainId stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar.
 
-		if(stackNativeTerrain == TerrainId::BORDER) //where does this value come from?
+		if(stackNativeTerrain == ETerrainId::BORDER) //where does this value come from?
 			continue;
-		if(nativeTerrain == TerrainId::BORDER)
+		if(nativeTerrain == ETerrainId::BORDER)
 			nativeTerrain = stackNativeTerrain;
 		else if(nativeTerrain != stackNativeTerrain)
-			return TerrainId::BORDER;
+			return ETerrainId::BORDER;
 	}
 	return nativeTerrain;
 }

+ 10 - 14
lib/mapObjects/ObjectTemplate.cpp

@@ -157,15 +157,15 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
 	// so these two fields can be interpreted as "strong affinity" and "weak affinity" towards terrains
 	std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain
 
-	assert(terrStr.size() == TerrainId::ROCK); // all terrains but rock - counting from 0
-	for(TerrainId i = TerrainId(0); i < TerrainId::ROCK; ++i)
+	assert(terrStr.size() == TerrainId(ETerrainId::ROCK).getNum()); // all terrains but rock - counting from 0
+	for(TerrainId i = TerrainId(0); i < ETerrainId::ROCK; ++i)
 	{
-		if (terrStr[8-i] == '1')
+		if (terrStr[8-i.getNum()] == '1')
 			allowedTerrains.insert(i);
 	}
 	
 	//assuming that object can be placed on other land terrains
-	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(TerrainId::WATER))
+	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER))
 	{
 		for(const auto & terrain : VLC->terrainTypeHandler->objects)
 		{
@@ -231,14 +231,14 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
 
 	reader.readUInt16();
 	ui16 terrMask = reader.readUInt16();
-	for(TerrainId i = TerrainId::FIRST_REGULAR_TERRAIN; i < TerrainId::ORIGINAL_TERRAIN_COUNT; ++i)
+	for(TerrainId i = ETerrainId::FIRST_REGULAR_TERRAIN; i < ETerrainId::ORIGINAL_TERRAIN_COUNT; ++i)
 	{
-		if (((terrMask >> i) & 1 ) != 0)
+		if (((terrMask >> i.getNum()) & 1 ) != 0)
 			allowedTerrains.insert(i);
 	}
 	
 	//assuming that object can be placed on other land terrains
-	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(TerrainId::WATER))
+	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER))
 	{
 		for(const auto & terrain : VLC->terrainTypeHandler->objects)
 		{
@@ -288,13 +288,9 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
 	{
 		for(auto& entry : node["allowedTerrains"].Vector())
 		{
-			try {
-				allowedTerrains.insert(VLC->terrainTypeHandler->getInfoByName(entry.String())->id);
-			}
-			catch (const std::out_of_range & )
-			{
-				logGlobal->warn("Failed to find terrain '%s' for object '%s'", entry.String(), animationFile);
-			}
+			VLC->modh->identifiers.requestIdentifier("terrain", entry, [this](int32_t identifier){
+				allowedTerrains.insert(TerrainId(identifier));
+			});
 		}
 	}
 	else

+ 2 - 2
lib/mapping/CDrawRoadsOperation.cpp

@@ -338,12 +338,12 @@ std::string CDrawRiversOperation::getLabel() const
 
 void CDrawRoadsOperation::executeTile(TerrainTile & tile)
 {
-	tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getByIndex(roadType));
+	tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getByIndex(roadType.getNum()));
 }
 
 void CDrawRiversOperation::executeTile(TerrainTile & tile)
 {
-	tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getByIndex(riverType));
+	tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getByIndex(riverType.getNum()));
 }
 
 bool CDrawRoadsOperation::canApplyPattern(const LinePattern & pattern) const

+ 1 - 1
lib/mapping/CDrawRoadsOperation.h

@@ -80,7 +80,7 @@ private:
 class CDrawRiversOperation : public CDrawLinesOperation
 {
 public:
-	CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RoadId roadType, CRandomGenerator * gen);
+	CDrawRiversOperation(CMap * map, const CTerrainSelection & terrainSel, RiverId roadType, CRandomGenerator * gen);
 	std::string getLabel() const override;
 	
 protected:

+ 4 - 4
lib/mapping/CMapOperation.cpp

@@ -422,14 +422,14 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 			bool nativeTestOk, nativeTestStrongOk;
 			nativeTestOk = nativeTestStrongOk = (rule.isNativeStrong() || rule.isNativeRule()) && !isAlien;
 
-			if(centerTerType->id == TerrainId::DIRT)
+			if(centerTerType->id == ETerrainId::DIRT)
 			{
 				nativeTestOk = rule.isNativeRule() && !terType->isTransitionRequired();
 				bool sandTestOk = (rule.isSandRule() || rule.isTransition())
 					&& terType->isTransitionRequired();
 				applyValidationRslt(rule.isAnyRule() || sandTestOk || nativeTestOk || nativeTestStrongOk);
 			}
-			else if(centerTerType->id == TerrainId::SAND)
+			else if(centerTerType->id == ETerrainId::SAND)
 			{
 				applyValidationRslt(true);
 			}
@@ -551,12 +551,12 @@ CClearTerrainOperation::CClearTerrainOperation(CMap* map, CRandomGenerator* gen)
 {
 	CTerrainSelection terrainSel(map);
 	terrainSel.selectRange(MapRect(int3(0, 0, 0), map->width, map->height));
-	addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, TerrainId::WATER, gen));
+	addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, ETerrainId::WATER, gen));
 	if(map->twoLevel)
 	{
 		terrainSel.clearSelection();
 		terrainSel.selectRange(MapRect(int3(0, 0, 1), map->width, map->height));
-		addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, TerrainId::ROCK, gen));
+		addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, ETerrainId::ROCK, gen));
 	}
 }
 

+ 1 - 1
lib/mapping/MapFormatH3M.cpp

@@ -941,7 +941,7 @@ void CMapLoaderH3M::readTerrain()
 				tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getByIndex(reader.readUInt8()));
 				tile.roadDir = reader.readUInt8();
 				tile.extTileFlags = reader.readUInt8();
-				tile.blocked = ((!tile.terType->isPassable() || tile.terType->id == TerrainId::BORDER ) ? true : false); //underground tiles are always blocked
+				tile.blocked = ((!tile.terType->isPassable() || tile.terType->id == ETerrainId::BORDER ) ? true : false); //underground tiles are always blocked
 				tile.visitable = 0;
 			}
 		}

+ 35 - 4
lib/mapping/MapFormatJson.cpp

@@ -346,6 +346,37 @@ CMapFormatJson::CMapFormatJson():
 
 }
 
+TerrainType * CMapFormatJson::getTerrainByCode( std::string code)
+{
+	for ( auto const & object : VLC->terrainTypeHandler->objects)
+	{
+		if (object->typeCode == code)
+			return const_cast<TerrainType *>(object.get());
+	}
+	return nullptr;
+}
+
+RiverType * CMapFormatJson::getRiverByCode( std::string code)
+{
+	for ( auto const & object : VLC->riverTypeHandler->objects)
+	{
+		if (object->code == code)
+			return const_cast<RiverType *>(object.get());
+	}
+	return nullptr;
+}
+
+RoadType * CMapFormatJson::getRoadByCode( std::string code)
+{
+	for ( auto const & object : VLC->roadTypeHandler->objects)
+	{
+		if (object->code == code)
+			return const_cast<RoadType *>(object.get());
+	}
+	return nullptr;
+}
+
+
 void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std::set<TFaction> & value)
 {
 	//TODO: unify allowed factions with others - make them std::vector<bool>
@@ -949,7 +980,7 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile
 		using namespace TerrainDetail;
 		{//terrain type
 			const std::string typeCode = src.substr(0, 2);
-			tile.terType = const_cast<TerrainType*>(VLC->terrainTypeHandler->getInfoByCode(typeCode));
+			tile.terType = getTerrainByCode(typeCode);
 		}
 		int startPos = 2; //0+typeCode fixed length
 		{//terrain view
@@ -979,13 +1010,13 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile
 			startPos += 2;
 			try
 			{
-				tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getInfoByCode(typeCode));
+				tile.roadType = getRoadByCode(typeCode);
 			}
 			catch (const std::exception&) //it's not a road, it's a river
 			{
 				try
 				{
-					tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getInfoByCode(typeCode));
+					tile.riverType = getRiverByCode(typeCode);
 					hasRoad = false;
 				}
 				catch (const std::exception&)
@@ -1021,7 +1052,7 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile
 		{//river type
 			const std::string typeCode = src.substr(startPos, 2);
 			startPos += 2;
-			tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getInfoByCode(typeCode));
+			tile.riverType = getRiverByCode(typeCode);
 		}
 		{//river dir
 			int pos = startPos;

+ 7 - 0
lib/mapping/MapFormatJson.h

@@ -26,6 +26,9 @@ struct TerrainTile;
 struct PlayerInfo;
 class CGObjectInstance;
 class AObjectTypeHandler;
+class TerrainType;
+class RoadType;
+class RiverType;
 
 class JsonSerializeFormat;
 class JsonDeserializer;
@@ -57,6 +60,10 @@ protected:
 
 	CMapFormatJson();
 
+	static TerrainType * getTerrainByCode( std::string code);
+	static RiverType * getRiverByCode( std::string code);
+	static RoadType * getRoadByCode( std::string code);
+
 	void serializeAllowedFactions(JsonSerializeFormat & handler, std::set<TFaction> & value);
 
 	///common part of header saving/loading

+ 5 - 4
lib/rmg/CRmgTemplate.cpp

@@ -16,6 +16,7 @@
 #include "../mapping/CMap.h"
 #include "../VCMI_Lib.h"
 #include "../CTownHandler.h"
+#include "../CModHandler.h"
 #include "../Terrain.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../StringConstants.h"
@@ -69,13 +70,12 @@ class TerrainEncoder
 public:
 	static si32 decode(const std::string & identifier)
 	{
-		return VLC->terrainTypeHandler->getInfoByCode(identifier)->id;
+		return *VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "terrain", identifier);
 	}
 
 	static std::string encode(const si32 index)
 	{
-		const auto& terrains = VLC->terrainTypeHandler->objects;
-		return (index >=0 && index < terrains.size()) ? terrains[index]->name : "<INVALID TERRAIN>";
+		return VLC->terrainTypeHandler->getByIndex(index)->name;
 	}
 };
 
@@ -377,7 +377,8 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 				terrainTypes.clear();
 				for(auto ttype : node.Vector())
 				{
-					terrainTypes.emplace(VLC->terrainTypeHandler->getInfoByName(ttype.String())->id);
+					auto identifier = VLC->modh->identifiers.getIdentifier("terrain", ttype);
+					terrainTypes.emplace(*identifier);
 				}
 			}
 		}

+ 2 - 2
lib/rmg/CZonePlacer.cpp

@@ -194,7 +194,7 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
 				else
 				{
 					auto & tt = (*VLC->townh)[faction]->nativeTerrain;
-					if(tt == TerrainId::DIRT)
+					if(tt == ETerrainId::DIRT)
 					{
 						//any / random
 						zonesToPlace.push_back(zone);
@@ -580,7 +580,7 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
 
 			//make sure that terrain inside zone is not a rock
 			//FIXME: reorder actions?
-			paintZoneTerrain(*zone.second, *rand, map, TerrainId::SUBTERRANEAN);
+			paintZoneTerrain(*zone.second, *rand, map, ETerrainId::SUBTERRANEAN);
 		}
 	}
 	logGlobal->info("Finished zone colouring");

+ 2 - 3
lib/rmg/ConnectionsPlacer.cpp

@@ -84,9 +84,8 @@ void ConnectionsPlacer::selfSideDirectConnection(const rmg::ZoneConnection & con
 	
 	//1. Try to make direct connection
 	//Do if it's not prohibited by terrain settings
-	const auto& terrains = VLC->terrainTypeHandler->objects;
-	bool directProhibited = vstd::contains(terrains[zone.getTerrainType()]->prohibitTransitions, otherZone->getTerrainType())
-						 || vstd::contains(terrains[otherZone->getTerrainType()]->prohibitTransitions, zone.getTerrainType());
+	bool directProhibited = vstd::contains(VLC->terrainTypeHandler->getById(zone.getTerrainType())->prohibitTransitions, otherZone->getTerrainType())
+						 || vstd::contains(VLC->terrainTypeHandler->getById(otherZone->getTerrainType())->prohibitTransitions, zone.getTerrainType());
 	auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
 	if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
 	{

+ 2 - 2
lib/rmg/Functions.cpp

@@ -143,14 +143,14 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
 		{
 			if(!terrainType->isUnderground())
 			{
-				zone.setTerrainType(TerrainId::SUBTERRANEAN);
+				zone.setTerrainType(ETerrainId::SUBTERRANEAN);
 			}
 		}
 		else
 		{
 			if (!terrainType->isSurface())
 			{
-				zone.setTerrainType(TerrainId::DIRT);
+				zone.setTerrainType(ETerrainId::DIRT);
 			}
 		}
 	}

+ 1 - 1
lib/rmg/RiverPlacer.cpp

@@ -322,7 +322,7 @@ void RiverPlacer::preprocess()
 void RiverPlacer::connectRiver(const int3 & tile)
 {
 	auto riverType = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river;
-	const auto * river = VLC->riverTypeHandler->getByIndex(riverType);
+	const auto * river = VLC->riverTypeHandler->getById(riverType);
 	if(river->id == River::NO_RIVER)
 		return;
 	

+ 1 - 1
lib/rmg/RmgMap.cpp

@@ -84,7 +84,7 @@ void RmgMap::initTiles(CMapGenerator & generator)
 	
 	getEditManager()->clearTerrain(&generator.rand);
 	getEditManager()->getTerrainSelection().selectRange(MapRect(int3(0, 0, 0), mapGenOptions.getWidth(), mapGenOptions.getHeight()));
-	getEditManager()->drawTerrain(TerrainId::GRASS, &generator.rand);
+	getEditManager()->drawTerrain(ETerrainId::GRASS, &generator.rand);
 	
 	auto tmpl = mapGenOptions.getMapTemplate();
 	zones.clear();

+ 2 - 2
lib/rmg/RoadPlacer.cpp

@@ -16,6 +16,7 @@
 #include "RmgMap.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
+#include "../CModHandler.h"
 #include "RmgPath.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -80,8 +81,7 @@ void RoadPlacer::drawRoads(bool secondary)
 	map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector());
 
 	std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
-	RoadId roadType = VLC->roadTypeHandler->getInfoByName(roadName)->id;
-
+	RoadId roadType(*VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "road", roadName));
 	map.getEditManager()->drawRoad(roadType, &generator.rand);
 }
 

+ 1 - 1
lib/rmg/Zone.cpp

@@ -28,7 +28,7 @@ std::function<bool(const int3 &)> AREA_NO_FILTER = [](const int3 & t)
 Zone::Zone(RmgMap & map, CMapGenerator & generator)
 					: ZoneOptions(),
 					townType(ETownType::NEUTRAL),
-					terrainType(TerrainId::GRASS),
+					terrainType(ETerrainId::GRASS),
 					map(map),
 					generator(generator)
 {

+ 10 - 3
mapeditor/mainwindow.cpp

@@ -21,6 +21,7 @@
 #include "../lib/VCMI_Lib.h"
 #include "../lib/logging/CBasicLogConfigurator.h"
 #include "../lib/CConfigHandler.h"
+#include "../lib/CModHandler.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/GameConstants.h"
 #include "../lib/mapping/CMapService.h"
@@ -558,7 +559,7 @@ void MainWindow::loadObjectsTree()
 	{
 		QPushButton *b = new QPushButton(QString::fromStdString(road->fileName));
 		ui->roadLayout->addWidget(b);
-		connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road->id, true); });
+		connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road->id.getNum(), true); });
 	}
 	//add spacer to keep terrain button on the top
 	ui->roadLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
@@ -567,7 +568,7 @@ void MainWindow::loadObjectsTree()
 	{
 		QPushButton *b = new QPushButton(QString::fromStdString(river->fileName));
 		ui->riverLayout->addWidget(b);
-		connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river->id, false); });
+		connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river->id.getNum(), false); });
 	}
 	//add spacer to keep terrain button on the top
 	ui->riverLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
@@ -914,7 +915,13 @@ void MainWindow::on_terrainFilterCombo_currentTextChanged(const QString &arg1)
 	if(!objectBrowser)
 		return;
 
-	objectBrowser->terrain = arg1.isEmpty() ? TerrainId(TerrainId::ANY_TERRAIN) : VLC->terrainTypeHandler->getInfoByName(arg1.toStdString())->id;
+	objectBrowser->terrain = TerrainId(ETerrainId::ANY_TERRAIN);
+	if (!arg1.isEmpty())
+	{
+		for (auto const & terrain : VLC->terrainTypeHandler->objects)
+			if (terrain->name == arg1.toStdString())
+				objectBrowser->terrain = terrain->id;
+	}
 	objectBrowser->invalidate();
 	objectBrowser->sort(0);
 }

+ 2 - 2
mapeditor/objectbrowser.cpp

@@ -13,7 +13,7 @@
 #include "../lib/mapObjects/CObjectClassesHandler.h"
 
 ObjectBrowserProxyModel::ObjectBrowserProxyModel(QObject *parent)
-	: QSortFilterProxyModel{parent}, terrain(TerrainId::ANY_TERRAIN)
+	: QSortFilterProxyModel{parent}, terrain(ETerrainId::ANY_TERRAIN)
 {
 }
 
@@ -33,7 +33,7 @@ bool ObjectBrowserProxyModel::filterAcceptsRow(int source_row, const QModelIndex
 	if(!filterAcceptsRowText(source_row, source_parent))
 		return false;
 
-	if(terrain == TerrainId::ANY_TERRAIN)
+	if(terrain == ETerrainId::ANY_TERRAIN)
 		return result;
 
 	auto data = item->data().toJsonObject();

+ 1 - 1
server/CGameHandler.cpp

@@ -2244,7 +2244,7 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const
 	const auto & t = *getTile(tile);
 	TerrainId terrain = t.terType->id;
 	if (gs->map->isCoastalTile(tile)) //coastal tile is always ground
-		terrain = TerrainId::SAND;
+		terrain = ETerrainId::SAND;
 
 	BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator());
 	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)