浏览代码

Converted terrainTypeHandler into proper handler class

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

+ 15 - 16
client/CMusicHandler.cpp

@@ -94,30 +94,29 @@ CSoundHandler::CSoundHandler():
 	//TODO: support custom sounds for new terrains and load from json
 	horseSounds =
 	{
-		{Terrain::DIRT, soundBase::horseDirt},
-		{Terrain::SAND, soundBase::horseSand},
-		{Terrain::GRASS, soundBase::horseGrass},
-		{Terrain::SNOW, soundBase::horseSnow},
-		{Terrain::SWAMP, soundBase::horseSwamp},
-		{Terrain::ROUGH, soundBase::horseRough},
-		{Terrain::SUBTERRANEAN, soundBase::horseSubterranean},
-		{Terrain::LAVA, soundBase::horseLava},
-		{Terrain::WATER, soundBase::horseWater},
-		{Terrain::ROCK, soundBase::horseRock}
+		{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}
 	};
 }
 
 void CSoundHandler::loadHorseSounds()
 {
-	const auto & terrains = CGI->terrainTypeHandler->terrains();
-	for(const auto & terrain : terrains)
+	for(const auto & terrain : CGI->terrainTypeHandler->objects)
 	{
 		//since all sounds are hardcoded, let's keep it
-		if(vstd::contains(horseSounds, terrain.id))
+		if(vstd::contains(horseSounds, terrain->id))
 			continue;
 
 		//Use already existing horse sound
-		horseSounds[terrain.id] = horseSounds.at(terrains[terrain.id].horseSoundId);
+		horseSounds[terrain->id] = horseSounds.at(static_cast<TerrainId>(CGI->terrainTypeHandler->getById(terrain->id)->horseSoundId));
 	}
 }
 
@@ -368,9 +367,9 @@ CMusicHandler::CMusicHandler():
 
 void CMusicHandler::loadTerrainMusicThemes()
 {
-	for (const auto & terrain : CGI->terrainTypeHandler->terrains())
+	for (const auto & terrain : CGI->terrainTypeHandler->objects)
 	{
-		addEntryToSet("terrain_" + terrain.name, "Music/" + terrain.musicFilename);
+		addEntryToSet("terrain_" + terrain->name, "Music/" + terrain->musicFilename);
 	}
 }
 

+ 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 = Terrain::BORDER; // not init yet
+		TerrainId currentTerrain = TerrainId::BORDER; // not init yet
 		TerrainId newTerrain;
 		int sh = -1;
 

+ 8 - 8
client/mapHandler.cpp

@@ -175,17 +175,17 @@ void CMapHandler::initTerrainGraphics()
 	std::map<std::string, std::string> terrainFiles;
 	std::map<std::string, std::string> riverFiles;
 	std::map<std::string, std::string> roadFiles;
-	for(const auto & terrain : VLC->terrainTypeHandler->terrains())
+	for(const auto & terrain : VLC->terrainTypeHandler->objects)
 	{
-		terrainFiles[terrain.name] = terrain.tilesFilename;
+		terrainFiles[terrain->name] = terrain->tilesFilename;
 	}
-	for(const auto & river : VLC->terrainTypeHandler->rivers())
+	for(const auto & river : VLC->riverTypeHandler->objects)
 	{
-		riverFiles[river.fileName] = river.fileName;
+		riverFiles[river->fileName] = river->fileName;
 	}
-	for(const auto & road : VLC->terrainTypeHandler->roads())
+	for(const auto & road : VLC->roadTypeHandler->objects)
 	{
-		roadFiles[road.fileName] = road.fileName;
+		roadFiles[road->fileName] = road->fileName;
 	}
 	
 	loadFlipped(terrainAnimations, terrainImages, terrainFiles);
@@ -1388,8 +1388,8 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
 			break;
 		}
 	}
-	if(!isTile2Terrain || out.empty())
-		out = CGI->generaltexth->terrainNames[t.terType->id];
+
+	VLC->terrainTypeHandler->getById(t.terType->id)->terrainText;
 
 	if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG)
 	{

+ 8 - 8
client/widgets/AdventureMapClasses.cpp

@@ -499,25 +499,25 @@ std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > CMinimap::loadColors()
 {
 	std::map<TerrainId, std::pair<SDL_Color, SDL_Color> > ret;
 
-	for(const auto & terrain : CGI->terrainTypeHandler->terrains())
+	for(const auto & terrain : CGI->terrainTypeHandler->objects)
 	{
 		SDL_Color normal =
 		{
-			ui8(terrain.minimapUnblocked[0]),
-			ui8(terrain.minimapUnblocked[1]),
-			ui8(terrain.minimapUnblocked[2]),
+			ui8(terrain->minimapUnblocked[0]),
+			ui8(terrain->minimapUnblocked[1]),
+			ui8(terrain->minimapUnblocked[2]),
 			ui8(255)
 		};
 
 		SDL_Color blocked =
 		{
-			ui8(terrain.minimapBlocked[0]),
-			ui8(terrain.minimapBlocked[1]),
-			ui8(terrain.minimapBlocked[2]),
+			ui8(terrain->minimapBlocked[0]),
+			ui8(terrain->minimapBlocked[1]),
+			ui8(terrain->minimapBlocked[2]),
 			ui8(255)
 		};
 
-		ret[terrain.id] = std::make_pair(normal, blocked);
+		ret[terrain->id] = std::make_pair(normal, blocked);
 	}
 	return ret;
 }

+ 10 - 10
config/terrains.json

@@ -1,7 +1,7 @@
 {
 	"dirt" :
 	{
-		"originalTerrainId": 0,
+		"index": 0,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 82, 56, 8 ],
 		"minimapBlocked"   : [ 57, 40, 8 ],
@@ -15,7 +15,7 @@
 	},
 	"sand" :
 	{
-		"originalTerrainId": 1,
+		"index": 1,
 		"moveCost" : 150,
 		"minimapUnblocked" : [ 222, 207, 140 ],
 		"minimapBlocked"   : [ 165, 158, 107 ],
@@ -30,7 +30,7 @@
 	},
 	"grass" :
 	{
-		"originalTerrainId": 2,
+		"index": 2,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 0, 65, 0 ],
 		"minimapBlocked"   : [ 0, 48, 0 ],
@@ -43,7 +43,7 @@
 	},
 	"snow" :
 	{
-		"originalTerrainId": 3,
+		"index": 3,
 		"moveCost" : 150,
 		"minimapUnblocked" : [ 181, 199, 198 ],
 		"minimapBlocked"   : [ 140, 158, 156 ],
@@ -56,7 +56,7 @@
 	},
 	"swamp" :
 	{
-		"originalTerrainId": 4,
+		"index": 4,
 		"moveCost" : 175,
 		"minimapUnblocked" : [ 74, 134, 107 ],
 		"minimapBlocked"   : [ 33,  89,  66 ],
@@ -69,7 +69,7 @@
 	},
 	"rough" :
 	{
-		"originalTerrainId": 5,
+		"index": 5,
 		"moveCost" : 125,
 		"minimapUnblocked" : [ 132, 113, 49 ],
 		"minimapBlocked"   : [  99,  81, 33 ],
@@ -82,7 +82,7 @@
 	},
 	"subterra" :
 	{
-		"originalTerrainId": 6,
+		"index": 6,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 132, 48, 0 ],
 		"minimapBlocked"   : [  90,  8, 0 ],
@@ -97,7 +97,7 @@
 	},
 	"lava" :
 	{
-		"originalTerrainId": 7,
+		"index": 7,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 74, 73, 74 ],
 		"minimapBlocked"   : [ 41, 40, 41 ],
@@ -112,7 +112,7 @@
 	},
 	"water" :
 	{
-		"originalTerrainId": 8,
+		"index": 8,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 8, 81, 148 ],
 		"minimapBlocked"   : [ 8, 81, 148 ],
@@ -130,7 +130,7 @@
 	},
 	"rock" :
 	{
-		"originalTerrainId": 9,
+		"index": 9,
 		"moveCost" : -1,
 		"minimapUnblocked" : [ 0, 0, 0 ],
 		"minimapBlocked"   : [ 0, 0, 0 ],

+ 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 == Terrain::ANY_TERRAIN;
+	return native == terrain || native == TerrainId::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(Terrain::ANY_TERRAIN)
+		? TerrainId(TerrainId::ANY_TERRAIN)
 		: (*VLC->townh)[faction]->nativeTerrain;
 }
 

+ 0 - 2
lib/CGeneralTextHandler.cpp

@@ -343,7 +343,6 @@ CGeneralTextHandler::CGeneralTextHandler():
 	advobtxt         (*this, "core.advevent" ),
 	xtrainfo         (*this, "core.xtrainfo" ),
 	restypes         (*this, "core.restypes" ),
-	terrainNames     (*this, "core.terrname" ),
 	randsign         (*this, "core.randsign" ),
 	overview         (*this, "core.overview" ),
 	arraytxt         (*this, "core.arraytxt" ),
@@ -372,7 +371,6 @@ CGeneralTextHandler::CGeneralTextHandler():
 	readToVector("core.advevent", "DATA/ADVEVENT.TXT" );
 	readToVector("core.xtrainfo", "DATA/XTRAINFO.TXT" );
 	readToVector("core.restypes", "DATA/RESTYPES.TXT" );
-	readToVector("core.terrname", "DATA/TERRNAME.TXT" );
 	readToVector("core.randsign", "DATA/RANDSIGN.TXT" );
 	readToVector("core.overview", "DATA/OVERVIEW.TXT" );
 	readToVector("core.arraytxt", "DATA/ARRAYTXT.TXT" );

+ 0 - 1
lib/CGeneralTextHandler.h

@@ -207,7 +207,6 @@ public:
 	LegacyTextContainer advobtxt;
 	LegacyTextContainer xtrainfo;
 	LegacyTextContainer restypes; //names of resources
-	LegacyTextContainer terrainNames;
 	LegacyTextContainer randsign;
 	LegacyTextContainer seerEmpty;
 	LegacyTextContainer seerNames;

+ 0 - 13
lib/CHeroHandler.cpp

@@ -345,11 +345,6 @@ CHeroHandler::~CHeroHandler() = default;
 
 CHeroHandler::CHeroHandler()
 {
-	loadTerrains();
-	for(const auto & terrain : VLC->terrainTypeHandler->terrains())
-	{
-		VLC->modh->identifiers.registerObject(CModHandler::scopeBuiltin(), "terrain", terrain.name, terrain.id);
-	}
 	loadBallistics();
 	loadExperience();
 }
@@ -972,14 +967,6 @@ ui64 CHeroHandler::reqExp (ui32 level) const
 	}
 }
 
-void CHeroHandler::loadTerrains()
-{
-	for(const auto & terrain : VLC->terrainTypeHandler->terrains())
-	{
-		terrCosts[terrain.id] = terrain.moveCost;
-	}
-}
-
 std::vector<bool> CHeroHandler::getDefaultAllowed() const
 {
 	// Look Data/HOTRAITS.txt for reference

+ 0 - 1
lib/CHeroHandler.h

@@ -260,7 +260,6 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
 
 	void loadExperience();
 	void loadBallistics();
-	void loadTerrains();
 
 public:
 	CHeroClassHandler classes;

+ 3 - 0
lib/CModHandler.cpp

@@ -465,6 +465,9 @@ void CContentHandler::init()
 	handlers.insert(std::make_pair("scripts", ContentTypeHandler(VLC->scriptHandler, "script")));
 #endif
 	handlers.insert(std::make_pair("battlefields", ContentTypeHandler(VLC->battlefieldsHandler, "battlefield")));
+	handlers.insert(std::make_pair("terrains", ContentTypeHandler(VLC->terrainTypeHandler, "terrain")));
+	handlers.insert(std::make_pair("rivers", ContentTypeHandler(VLC->riverTypeHandler, "river")));
+	handlers.insert(std::make_pair("roads", ContentTypeHandler(VLC->roadTypeHandler, "road")));
 	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(VLC->obstacleHandler, "obstacle")));
 	//TODO: any other types of moddables?
 }

+ 2 - 2
lib/CPathfinder.cpp

@@ -1000,10 +1000,10 @@ bool CPathfinderHelper::passOneTurnLimitCheck(const PathNodeInfo & source) const
 
 TurnInfo::BonusCache::BonusCache(TConstBonusListPtr bl)
 {
-	for(const auto & terrain : VLC->terrainTypeHandler->terrains())
+	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)))));
 	}
 
 	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 == Terrain::ANY_TERRAIN || nativeTerrain == battle->getTerrainType();
+	auto res = nativeTerrain == TerrainId::ANY_TERRAIN || nativeTerrain == battle->getTerrainType();
 	return res;
 }
 

+ 3 - 3
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(Terrain::GRASS);
-const TerrainId CTownHandler::defaultEvilTerrain(Terrain::LAVA);
-const TerrainId CTownHandler::defaultNeutralTerrain(Terrain::ROUGH);
+const TerrainId CTownHandler::defaultGoodTerrain(TerrainId::GRASS);
+const TerrainId CTownHandler::defaultEvilTerrain(TerrainId::LAVA);
+const TerrainId CTownHandler::defaultNeutralTerrain(TerrainId::ROUGH);
 
 const std::map<std::string, CBuilding::EBuildMode> CBuilding::MODES =
 {

+ 45 - 25
lib/GameConstants.h

@@ -830,29 +830,6 @@ public:
 
 ID_LIKE_OPERATORS(Obj, Obj::EObj)
 
-namespace Terrain
-{
-	enum ETerrain : si8
-	{
-		NATIVE_TERRAIN = -4,
-		ANY_TERRAIN = -3,
-		WRONG = -2,
-		BORDER = -1,
-		FIRST_REGULAR_TERRAIN = 0,
-		DIRT = 0,
-		SAND,
-		GRASS,
-		SNOW,
-		SWAMP,
-		ROUGH,
-		SUBTERRANEAN,
-		LAVA,
-		WATER,
-		ROCK,
-		ORIGINAL_TERRAIN_COUNT
-	};
-}
-
 namespace Road
 {
 	enum ERoad : ui8
@@ -1181,7 +1158,50 @@ 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;
+	}
+};
+
+ID_LIKE_OPERATORS(TerrainId, TerrainId::ETerrainID)
+
 class ObstacleInfo;
 class Obstacle : public BaseForID<Obstacle, si32>
 {
@@ -1239,7 +1259,7 @@ typedef si64 TExpType;
 typedef std::pair<si64, si64> TDmgRange;
 typedef si32 TBonusSubtype;
 typedef si32 TQuantity;
-typedef si8 TerrainId;
+//typedef si8 TerrainId;
 typedef si8 RoadId;
 typedef si8 RiverId;
 

+ 5 - 5
lib/HeroBonus.cpp

@@ -2106,7 +2106,7 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
 }
 
 CreatureTerrainLimiter::CreatureTerrainLimiter()
-	: terrainType(Terrain::NATIVE_TERRAIN)
+	: terrainType(TerrainId::NATIVE_TERRAIN)
 {
 }
 
@@ -2120,7 +2120,7 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const
 	const CStack *stack = retrieveStackBattle(&context.node);
 	if(stack)
 	{
-		if (terrainType == Terrain::NATIVE_TERRAIN)//terrainType not specified = native
+		if (terrainType == TerrainId::NATIVE_TERRAIN)//terrainType not specified = native
 		{
 			return !stack->isOnNativeTerrain();
 		}
@@ -2136,8 +2136,8 @@ int CreatureTerrainLimiter::limit(const BonusLimitationContext &context) const
 std::string CreatureTerrainLimiter::toString() const
 {
 	boost::format fmt("CreatureTerrainLimiter(terrainType=%s)");
-	auto terrainName = VLC->terrainTypeHandler->terrains()[terrainType].name;
-	fmt % (terrainType == Terrain::NATIVE_TERRAIN ? "native" : terrainName);
+	auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->name;
+	fmt % (terrainType == TerrainId::NATIVE_TERRAIN ? "native" : terrainName);
 	return fmt.str();
 }
 
@@ -2146,7 +2146,7 @@ JsonNode CreatureTerrainLimiter::toJsonNode() const
 	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
 
 	root["type"].String() = "CREATURE_TERRAIN_LIMITER";
-	auto terrainName = VLC->terrainTypeHandler->terrains()[terrainType].name;
+	auto terrainName = VLC->terrainTypeHandler->getById(terrainType)->name;
 	root["parameters"].Vector().push_back(JsonUtils::stringNode(terrainName));
 
 	return root;

+ 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 = Terrain::BORDER;
+	TerrainId terrainType = TerrainId::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(Terrain::WATER).front();
+		testObject.appearance = VLC->objtypeh->getHandlerFor(ID, subID)->getTemplates(TerrainId::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 = Terrain::WATER; //TODO: either boat should only spawn on water, or all water objects should be handled this way
+		terrainType = TerrainId::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();

+ 98 - 321
lib/Terrain.cpp

@@ -19,6 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 //("allowedTerrain"\s*:\s*\[.*)9(.*\],\n)
 //\1"rock"\2
 
+/*
 TerrainTypeHandler::TerrainTypeHandler()
 {
 	auto allConfigs = VLC->modh->getActiveMods();
@@ -29,377 +30,168 @@ TerrainTypeHandler::TerrainTypeHandler()
 	initRoads(allConfigs);
 	recreateRoadMaps();
 	initTerrains(allConfigs); //maps will be populated inside
-}
+}*/
 
-void TerrainTypeHandler::initTerrains(const std::vector<std::string> & allConfigs)
+TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index)
 {
-	std::vector<std::function<void()>> resolveLater;
+	TerrainType * info = new TerrainType;
 
-	objects.resize(Terrain::ORIGINAL_TERRAIN_COUNT); //make space for original terrains
+	info->id = TerrainId(index);
 
-	for(auto & mod : allConfigs)
-	{
-		if(!CResourceHandler::get(mod)->existsResource(ResourceID("config/terrains.json")))
-			continue;
-		
-		JsonNode terrs(mod, ResourceID("config/terrains.json"));
-		for(auto & terr : terrs.Struct())
-		{
-			TerrainType info(terr.first); //set name
-
-			info.moveCost = static_cast<int>(terr.second["moveCost"].Integer());
-			const JsonVector &unblockedVec = terr.second["minimapUnblocked"].Vector();
-			info.minimapUnblocked =
-			{
-				ui8(unblockedVec[0].Float()),
-				ui8(unblockedVec[1].Float()),
-				ui8(unblockedVec[2].Float())
-			};
-			
-			const JsonVector &blockedVec = terr.second["minimapBlocked"].Vector();
-			info.minimapBlocked =
-			{
-				ui8(blockedVec[0].Float()),
-				ui8(blockedVec[1].Float()),
-				ui8(blockedVec[2].Float())
-			};
-			info.musicFilename = terr.second["music"].String();
-			info.tilesFilename = terr.second["tiles"].String();
-			
-			if(terr.second["type"].isNull())
-			{
-				info.passabilityType = TerrainType::PassabilityType::LAND | TerrainType::PassabilityType::SURFACE;
-			}
-			else if (terr.second["type"].getType() == JsonNode::JsonType::DATA_VECTOR)
-			{
-				for(const auto& node : terr.second["type"].Vector())
-				{
-					//Set bits
-					auto s = node.String();
-					if (s == "LAND") info.passabilityType |= TerrainType::PassabilityType::LAND;
-					if (s == "WATER") info.passabilityType |= TerrainType::PassabilityType::WATER;
-					if (s == "ROCK") info.passabilityType |= TerrainType::PassabilityType::ROCK;
-					if (s == "SURFACE") info.passabilityType |= TerrainType::PassabilityType::SURFACE;
-					if (s == "SUB") info.passabilityType |= TerrainType::PassabilityType::SUBTERRANEAN;
-				}
-			}
-			else  //should be string - one option only
-			{
-				auto s = terr.second["type"].String();
-				if (s == "LAND") info.passabilityType = TerrainType::PassabilityType::LAND;
-				if (s == "WATER") info.passabilityType = TerrainType::PassabilityType::WATER;
-				if (s == "ROCK") info.passabilityType = TerrainType::PassabilityType::ROCK;
-				if (s == "SURFACE") info.passabilityType = TerrainType::PassabilityType::SURFACE;
-				if (s == "SUB") info.passabilityType = TerrainType::PassabilityType::SUBTERRANEAN;
-			}
-			
-			if(terr.second["river"].isNull())
-			{
-				info.river = River::NO_RIVER;
-			}
-			else
-			{
-				info.river = getRiverByCode(terr.second["river"].String())->id;
-			}
-			
-			if(terr.second["horseSoundId"].isNull())
-			{
-				info.horseSoundId = Terrain::ROCK; //rock sound as default
-			}
-			else
-			{
-				info.horseSoundId = static_cast<int>(terr.second["horseSoundId"].Float());
-			}
-			
-			if(!terr.second["text"].isNull())
-			{
-				info.terrainText = terr.second["text"].String();
-			}
-			
-			if(terr.second["code"].isNull())
-			{
-				info.typeCode = terr.first.substr(0, 2);
-			}
-			else
-			{
-				info.typeCode = terr.second["code"].String();
-				assert(info.typeCode.length() == 2);
-			}
-			
-			if(!terr.second["battleFields"].isNull())
-			{
-				for(auto & t : terr.second["battleFields"].Vector())
-				{
-					info.battleFields.emplace_back(t.String());
-				}
-			}
-			
-			info.transitionRequired = false;
-			if(!terr.second["transitionRequired"].isNull())
-			{
-				info.transitionRequired = terr.second["transitionRequired"].Bool();
-			}
-			
-			info.terrainViewPatterns = "normal";
-			if(!terr.second["terrainViewPatterns"].isNull())
-			{
-				info.terrainViewPatterns = terr.second["terrainViewPatterns"].String();
-			}
-
-			if(!terr.second["originalTerrainId"].isNull())
-			{
-				//place in reserved slot
-				info.id = (TerrainId)(terr.second["originalTerrainId"].Float());
-				objects[info.id] = info;
-			}
-			else
-			{
-				//append at the end
-				info.id = static_cast<TerrainId>(objects.size());
-				objects.push_back(info);
-			}
-			TerrainId id = info.id;
-
-			//Update terrain with this id in the future, after all terrain types are populated
-
-			if(!terr.second["prohibitTransitions"].isNull())
-			{
-				for(auto & t : terr.second["prohibitTransitions"].Vector())
-				{
-					std::string prohibitedTerrainName = t.String();
-					resolveLater.push_back([this, prohibitedTerrainName, id]()
-					{
-						//FIXME: is that reference to the element in vector?
-						objects[id].prohibitTransitions.emplace_back(getInfoByName(prohibitedTerrainName)->id);
-					});
-				}
-			}
-
-			if(terr.second["rockTerrain"].isNull())
-			{
-				objects[id].rockTerrain = Terrain::ROCK;
-			}
-			else
-			{
-				auto rockTerrainName = terr.second["rockTerrain"].String();
-				resolveLater.push_back([this, rockTerrainName, id]()
-				{
-					//FIXME: is that reference to the element in vector?
-						objects[id].rockTerrain = getInfoByName(rockTerrainName)->id;
-				});
-			}
-		}
-	}
+	info->moveCost = static_cast<int>(json["moveCost"].Integer());
+	info->musicFilename = json["music"].String();
+	info->tilesFilename = json["tiles"].String();
+	info->horseSoundId = static_cast<int>(json["horseSoundId"].Float());
+	info->transitionRequired = json["transitionRequired"].Bool();
+	info->terrainViewPatterns = json["terrainViewPatterns"].String();
+	info->terrainText = json["text"].String();
 
-	for(size_t i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ORIGINAL_TERRAIN_COUNT; i++)
+	const JsonVector & unblockedVec = json["minimapUnblocked"].Vector();
+	info->minimapUnblocked =
 	{
-		//Make sure that original terrains are loaded
-		assert(objects[i].id != Terrain::WRONG);
-	}
+		ui8(unblockedVec[0].Float()),
+		ui8(unblockedVec[1].Float()),
+		ui8(unblockedVec[2].Float())
+	};
 
-	recreateTerrainMaps();
+	const JsonVector &blockedVec = json["minimapBlocked"].Vector();
+	info->minimapBlocked =
+	{
+		ui8(blockedVec[0].Float()),
+		ui8(blockedVec[1].Float()),
+		ui8(blockedVec[2].Float())
+	};
 
-	for(auto& functor : resolveLater)
+	for(const auto& node : json["type"].Vector())
 	{
-		functor();
+		//Set bits
+		auto s = node.String();
+		if (s == "LAND") info->passabilityType |= TerrainType::PassabilityType::LAND;
+		if (s == "WATER") info->passabilityType |= TerrainType::PassabilityType::WATER;
+		if (s == "ROCK") info->passabilityType |= TerrainType::PassabilityType::ROCK;
+		if (s == "SURFACE") info->passabilityType |= TerrainType::PassabilityType::SURFACE;
+		if (s == "SUB") info->passabilityType |= TerrainType::PassabilityType::SUBTERRANEAN;
 	}
-}
 
-void TerrainTypeHandler::initRivers(const std::vector<std::string> & allConfigs)
-{
-	riverTypes.resize(River::ORIGINAL_RIVER_COUNT); //make space for original rivers
-	//First object will be default NO_RIVER
+//	if(json["river"].isNull())
+//		info->river = River::NO_RIVER;
+//	else
+//		info->river = getRiverByCode(json["river"].String())->id;
 
-	for(auto & mod : allConfigs)
-	{
-		if (!CResourceHandler::get(mod)->existsResource(ResourceID("config/rivers.json")))
-			continue;
+	info->typeCode = json["code"].String();
+	assert(info->typeCode.length() == 2);
 
-		JsonNode rivs(mod, ResourceID("config/rivers.json"));
-		for(auto & river : rivs.Struct())
-		{
-			RiverType info;
-
-			info.name = river.first;
-			info.fileName = river.second["animation"].String();
-			info.code = river.second["code"].String();
-			info.deltaName = river.second["delta"].String();
-
-			if (!river.second["originalRiverId"].isNull())
-			{
-				info.id = static_cast<RiverId>(river.second["originalRiverId"].Float());
-				riverTypes[info.id] = info;
-			}
-			else
-			{
-				info.id = static_cast<RiverId>(riverTypes.size());
-				riverTypes.push_back(info);
-			}
-		}
-	}
+	for(auto & t : json["battleFields"].Vector())
+		info->battleFields.emplace_back(t.String());
 
-	recreateRiverMaps();
-}
 
-void TerrainTypeHandler::initRoads(const std::vector<std::string> & allConfigs)
-{
-	roadTypes.resize(Road::ORIGINAL_ROAD_COUNT); //make space for original rivers
-	//first object will be default NO_ROAD
+	//Update terrain with this id in the future, after all terrain types are populated
 
-	for(auto & mod : allConfigs)
+	for(auto & t : json["prohibitTransitions"].Vector())
 	{
-		if (!CResourceHandler::get(mod)->existsResource(ResourceID("config/roads.json")))
-			continue;
-
-		JsonNode rds(mod, ResourceID("config/roads.json"));
-		for(auto & road : rds.Struct())
+		VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier)
 		{
-			RoadType info;
-
-			info.name = road.first;
-			info.fileName = road.second["animation"].String();
-			info.code = road.second["code"].String();
-			info.movementCost = static_cast<ui8>(road.second["moveCost"].Float());
-
-			if (!road.second["originalRoadId"].isNull())
-			{
-				info.id = static_cast<RoadId>(road.second["originalRoadId"].Float());
-				roadTypes[info.id] = info;
-			}
-			else
-			{
-				info.id = static_cast<RoadId>(roadTypes.size());
-				roadTypes.push_back(info);
-			}
-		}
+			info->prohibitTransitions.push_back(TerrainId(identifier));
+		});
 	}
 
-	recreateRoadMaps();
-}
-
-void TerrainTypeHandler::recreateTerrainMaps()
-{
-	//This assumes the vector will never be updated or reallocated in the future
+	info->rockTerrain = TerrainId::ROCK;
 
-	for(size_t i = 0; i < objects.size(); i++)
+	if(!json["rockTerrain"].isNull())
 	{
-		const auto * terrainInfo = &objects[i];
-
-		terrainInfoByName[terrainInfo->name] = terrainInfo;
-		terrainInfoByCode[terrainInfo->typeCode] = terrainInfo;
-		terrainInfoById[terrainInfo->id] = terrainInfo;
+		auto rockTerrainName = json["rockTerrain"].String();
+		VLC->modh->identifiers.requestIdentifier("terrain", rockTerrainName, [info](int32_t identifier)
+		{
+			info->rockTerrain = TerrainId(identifier);
+		});
 	}
-}
-
-void TerrainTypeHandler::recreateRiverMaps()
-{
-	for(size_t i = River::FIRST_REGULAR_RIVER ; i < riverTypes.size(); i++)
-	{
-		const auto * riverInfo = &riverTypes[i];
 
-		riverInfoByName[riverInfo->name] = riverInfo;
-		riverInfoByCode[riverInfo->code] = riverInfo;
-		riverInfoById[riverInfo->id] = riverInfo;
-	}
+	return info;
 }
 
-void TerrainTypeHandler::recreateRoadMaps()
+const std::vector<std::string> & TerrainTypeHandler::getTypeNames() const
 {
-	for(size_t i = Road::FIRST_REGULAR_ROAD ; i < roadTypes.size(); i++)
-	{
-		const auto * roadInfo = &roadTypes[i];
-
-		roadInfoByName[roadInfo->name] = roadInfo;
-		roadInfoByCode[roadInfo->code] = roadInfo;
-		roadInfoById[roadInfo->id] = roadInfo;
-	}
+	static const std::vector<std::string> typeNames = { "terrain" };
+	return typeNames;
 }
 
-const std::vector<TerrainType> & TerrainTypeHandler::terrains() const
+std::vector<JsonNode> TerrainTypeHandler::loadLegacyData(size_t dataSize)
 {
-	//FIXME: somehow make it non-copyable? Pointers must point to original data and not its copy
-	return objects;
+	return {};
 }
 
-const std::vector<RiverType>& TerrainTypeHandler::rivers() const
+std::vector<bool> TerrainTypeHandler::getDefaultAllowed() const
 {
-	return riverTypes;
+	return {};
 }
 
-const std::vector<RoadType>& TerrainTypeHandler::roads() const
+RiverType * RiverTypeHandler::loadFromJson(
+	const std::string & scope,
+	const JsonNode & json,
+	const std::string & identifier,
+	size_t index)
 {
-	return roadTypes;
-}
+	RiverType * info = new RiverType;
 
-const TerrainType* TerrainTypeHandler::getInfoByName(const std::string& terrainName) const
-{
-	return terrainInfoByName.at(terrainName);
-}
+	info->fileName   = json["animation"].String();
+	info->code       = json["code"].String();
+	info->deltaName  = json["delta"].String();
 
-const TerrainType* TerrainTypeHandler::getInfoByCode(const std::string& terrainCode) const
-{
-	return terrainInfoByCode.at(terrainCode);
+	return info;
 }
 
-const TerrainType* TerrainTypeHandler::getInfoById(TerrainId id) const
+const std::vector<std::string> & RiverTypeHandler::getTypeNames() const
 {
-	return terrainInfoById.at(id);
+	static const std::vector<std::string> typeNames = { "river" };
+	return typeNames;
 }
 
-const RiverType* TerrainTypeHandler::getRiverByName(const std::string& riverName) const
+std::vector<JsonNode> RiverTypeHandler::loadLegacyData(size_t dataSize)
 {
-	return riverInfoByName.at(riverName);
+	return {};
 }
 
-const RiverType* TerrainTypeHandler::getRiverByCode(const std::string& riverCode) const
+std::vector<bool> RiverTypeHandler::getDefaultAllowed() const
 {
-	return riverInfoByCode.at(riverCode);
+	return {};
 }
 
-const RiverType* TerrainTypeHandler::getRiverById(RiverId id) const
+RoadType * RoadTypeHandler::loadFromJson(
+	const std::string & scope,
+	const JsonNode & json,
+	const std::string & identifier,
+	size_t index)
 {
-	return riverInfoById.at(id);
-}
+	RoadType * info = new RoadType;
 
-const RoadType* TerrainTypeHandler::getRoadByName(const std::string& roadName) const
-{
-	return roadInfoByName.at(roadName);
+	info->fileName     = json["animation"].String();
+	info->code         = json["code"].String();
+	info->movementCost = json["moveCost"].Integer();
+
+	return info;
 }
 
-const RoadType* TerrainTypeHandler::getRoadByCode(const std::string& roadCode) const
+const std::vector<std::string> & RoadTypeHandler::getTypeNames() const
 {
-	return roadInfoByCode.at(roadCode);
+	static const std::vector<std::string> typeNames = { "river" };
+	return typeNames;
 }
 
-const RoadType* TerrainTypeHandler::getRoadById(RoadId id) const
+std::vector<JsonNode> RoadTypeHandler::loadLegacyData(size_t dataSize)
 {
-	return roadInfoById.at(id);
+	return {};
 }
 
-std::ostream & operator<<(std::ostream & os, const TerrainType & terrainType)
+std::vector<bool> RoadTypeHandler::getDefaultAllowed() const
 {
-	return os << static_cast<const std::string &>(terrainType);
+	return {};
 }
-	
+
 TerrainType::operator std::string() const
 {
 	return name;
 }
-	
-TerrainType::TerrainType(const std::string& _name):
-	minimapBlocked({0,0,0}), //black
-	minimapUnblocked({ 128,128,128 }), //grey
-	name(_name),
-	river(River::NO_RIVER),
-	id(Terrain::WRONG),
-	rockTerrain(Terrain::ROCK),
-	moveCost(GameConstants::BASE_MOVEMENT_COST),
-	horseSoundId(0),
-	passabilityType(0),
-	transitionRequired(false)
-{
-}
-	
+
 bool TerrainType::operator==(const TerrainType& other)
 {
 	return id == other.id;
@@ -455,19 +247,4 @@ bool TerrainType::isTransitionRequired() const
 	return transitionRequired;
 }
 
-RiverType::RiverType(const std::string & fileName, const std::string & code, RiverId id):
-	fileName(fileName),
-	code(code),
-	id(id)
-{
-}
-
-RoadType::RoadType(const std::string& fileName, const std::string& code, RoadId id):
-	fileName(fileName),
-	code(code),
-	id(id),
-	movementCost(GameConstants::BASE_MOVEMENT_COST)
-{
-}
-
 VCMI_LIB_NAMESPACE_END

+ 88 - 59
lib/Terrain.h

@@ -10,16 +10,25 @@
 
 #pragma once
 
+#include <vcmi/EntityService.h>
+#include <vcmi/Entity.h>
 #include "ConstTransitivePtr.h"
 #include "GameConstants.h"
 #include "JsonNode.h"
+#include "IHandlerBase.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class DLL_LINKAGE TerrainType
+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;
+
 	enum PassabilityType : ui8
 	{
 		LAND = 1,
@@ -88,10 +97,16 @@ public:
 	}
 };
 
-class DLL_LINKAGE RiverType
+class DLL_LINKAGE RiverType : public EntityT<TerrainId>
 {
 public:
-	std::string name;
+	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;
+
 	std::string fileName;
 	std::string code;
 	std::string deltaName;
@@ -101,10 +116,6 @@ public:
 
 	template <typename Handler> void serialize(Handler& h, const int version)
 	{
-		if(version >= 806)
-		{
-			h & name;
-		}
 		h & fileName;
 		h & code;
 		h & deltaName;
@@ -112,10 +123,16 @@ public:
 	}
 };
 
-class DLL_LINKAGE RoadType
+class DLL_LINKAGE RoadType : public EntityT<TerrainId>
 {
 public:
-	std::string name;
+	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;
+
 	std::string fileName;
 	std::string code;
 	RoadId id;
@@ -125,10 +142,6 @@ public:
 
 	template <typename Handler> void serialize(Handler& h, const int version)
 	{
-		if(version >= 806)
-		{
-			h & name;
-		}
 		h & fileName;
 		h & code;
 		h & id;
@@ -136,69 +149,85 @@ public:
 	}
 };
 
-DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const TerrainType & terrainType);
+class DLL_LINKAGE TerrainTypeService : public EntityServiceT<TerrainId, TerrainType>
+{
+public:
+};
 
-class DLL_LINKAGE TerrainTypeHandler //TODO: public IHandlerBase ?
+class DLL_LINKAGE RiverTypeService : public EntityServiceT<TerrainId, RiverType>
 {
 public:
+};
 
-	TerrainTypeHandler();
-	~TerrainTypeHandler() {};
+class DLL_LINKAGE RoadTypeService : public EntityServiceT<TerrainId, RoadType>
+{
+public:
+};
 
-	const std::vector<TerrainType> & terrains() const;
-	const TerrainType * getInfoByName(const std::string & terrainName) const;
-	const TerrainType * getInfoByCode(const std::string & terrainCode) const;
-	const TerrainType * getInfoById(TerrainId id) const;
+class DLL_LINKAGE TerrainTypeHandler : public CHandlerBase<TerrainId, TerrainType, TerrainType, TerrainTypeService>
+{
+public:
+	virtual TerrainType * loadFromJson(
+		const std::string & scope,
+		const JsonNode & json,
+		const std::string & identifier,
+		size_t index) override;
 
-	const std::vector<RiverType> & rivers() const;
-	const RiverType * getRiverByName(const std::string & riverName) const;
-	const RiverType * getRiverByCode(const std::string & riverCode) const;
-	const RiverType * getRiverById(RiverId id) const;
+	virtual const std::vector<std::string> & getTypeNames() const override;
+	virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+	virtual std::vector<bool> getDefaultAllowed() const override;
 
-	const std::vector<RoadType> & roads() const;
-	const RoadType * getRoadByName(const std::string & roadName) const;
-	const RoadType * getRoadByCode(const std::string & roadCode) const;
-	const RoadType * getRoadById(RoadId id) const;
+	TerrainType * getInfoByCode(const std::string & identifier);
+	TerrainType * getInfoByName(const std::string & identifier);
 
-	template <typename Handler> void serialize(Handler &h, const int version)
+	template <typename Handler> void serialize(Handler & h, const int version)
 	{
 		h & objects;
-		h & riverTypes;
-		h & roadTypes;
-
-		if (!h.saving)
-		{
-			recreateTerrainMaps();
-			recreateRiverMaps();
-			recreateRoadMaps();
-		}
 	}
+};
 
-private:
+class DLL_LINKAGE RiverTypeHandler : public CHandlerBase<TerrainId, RiverType, RiverType, RiverTypeService>
+{
+public:
+	virtual RiverType * loadFromJson(
+		const std::string & scope,
+		const JsonNode & json,
+		const std::string & identifier,
+		size_t index) override;
 
-	std::vector<TerrainType> objects;
-	std::vector<RiverType> riverTypes;
-	std::vector<RoadType> roadTypes;
+	virtual const std::vector<std::string> & getTypeNames() const override;
+	virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+	virtual std::vector<bool> getDefaultAllowed() const override;
 
-	std::unordered_map<std::string, const TerrainType*> terrainInfoByName;
-	std::unordered_map<std::string, const TerrainType*> terrainInfoByCode;
-	std::unordered_map<TerrainId, const TerrainType*> terrainInfoById;
+	RiverType * getInfoByCode(const std::string & identifier);
+	RiverType * getInfoByName(const std::string & identifier);
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & objects;
+	}
+};
 
-	std::unordered_map<std::string, const RiverType*> riverInfoByName;
-	std::unordered_map<std::string, const RiverType*> riverInfoByCode;
-	std::unordered_map<RiverId, const RiverType*> riverInfoById;
+class DLL_LINKAGE RoadTypeHandler : public CHandlerBase<TerrainId, RoadType, RoadType, RoadTypeService>
+{
+public:
+	virtual RoadType * loadFromJson(
+		const std::string & scope,
+		const JsonNode & json,
+		const std::string & identifier,
+		size_t index) override;
 
-	std::unordered_map<std::string, const RoadType*> roadInfoByName;
-	std::unordered_map<std::string, const RoadType*> roadInfoByCode;
-	std::unordered_map<RoadId, const RoadType*> roadInfoById;
+	virtual const std::vector<std::string> & getTypeNames() const override;
+	virtual std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
+	virtual std::vector<bool> getDefaultAllowed() const override;
 
-	void initTerrains(const std::vector<std::string> & allConfigs);
-	void initRivers(const std::vector<std::string> & allConfigs);
-	void initRoads(const std::vector<std::string> & allConfigs);
-	void recreateTerrainMaps();
-	void recreateRiverMaps();
-	void recreateRoadMaps();
+	RoadType * getInfoByCode(const std::string & identifier);
+	RoadType * getInfoByName(const std::string & identifier);
 
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & objects;
+	}
 };
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 0
lib/VCMI_Lib.cpp

@@ -197,6 +197,8 @@ void LibClasses::init(bool onlyEssential)
 
 	createHandler(bth, "Bonus type", pomtime);
 
+	createHandler(roadTypeHandler, "Road", pomtime);
+	createHandler(riverTypeHandler, "River", pomtime);
 	createHandler(terrainTypeHandler, "Terrain", pomtime);
 
 	createHandler(generaltexth, "General text", pomtime);

+ 8 - 0
lib/VCMI_Lib.h

@@ -30,6 +30,8 @@ class BattleFieldHandler;
 class IBonusTypeHandler;
 class CBonusTypeHandler;
 class TerrainTypeHandler;
+class RoadTypeHandler;
+class RiverTypeHandler;
 class ObstacleHandler;
 class CTerrainViewPatternConfig;
 class CRmgTemplateStorage;
@@ -86,7 +88,11 @@ public:
 	CTownHandler * townh;
 	CGeneralTextHandler * generaltexth;
 	CModHandler * modh;
+
 	TerrainTypeHandler * terrainTypeHandler;
+	RoadTypeHandler * roadTypeHandler;
+	RiverTypeHandler * riverTypeHandler;
+
 	CTerrainViewPatternConfig * terviewh;
 	CRmgTemplateStorage * tplh;
 	BattleFieldHandler * battlefieldsHandler;
@@ -127,6 +133,8 @@ public:
 		h & skillh;
 		h & battlefieldsHandler;
 		h & obstacleHandler;
+		h & roadTypeHandler;
+		h & riverTypeHandler;
 		h & terrainTypeHandler;
 
 		if(!h.saving)

+ 5 - 5
lib/mapObjects/CGHeroInstance.cpp

@@ -86,7 +86,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f
 		ret = std::max(dest.roadType->movementCost, from.roadType->movementCost);
 	}
 	else if(ti->nativeTerrain != from.terType->id &&//the terrain is not native
-			ti->nativeTerrain != Terrain::ANY_TERRAIN && //no special creature bonus
+			ti->nativeTerrain != TerrainId::ANY_TERRAIN && //no special creature bonus
 			!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->id)) //no special movement bonus
 	{
 
@@ -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 = Terrain::BORDER;
+	TerrainId nativeTerrain = TerrainId::BORDER;
 
 	for(auto stack : stacks)
 	{
 		TerrainId stackNativeTerrain = stack.second->type->getNativeTerrain(); //consider terrain bonuses e.g. Lodestar.
 
-		if(stackNativeTerrain == Terrain::BORDER) //where does this value come from?
+		if(stackNativeTerrain == TerrainId::BORDER) //where does this value come from?
 			continue;
-		if(nativeTerrain == Terrain::BORDER)
+		if(nativeTerrain == TerrainId::BORDER)
 			nativeTerrain = stackNativeTerrain;
 		else if(nativeTerrain != stackNativeTerrain)
-			return Terrain::BORDER;
+			return TerrainId::BORDER;
 	}
 	return nativeTerrain;
 }

+ 16 - 16
lib/mapObjects/ObjectTemplate.cpp

@@ -157,20 +157,20 @@ 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() == Terrain::ROCK); // all terrains but rock - counting from 0
-	for(TerrainId i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ROCK; i++)
+	assert(terrStr.size() == TerrainId::ROCK); // all terrains but rock - counting from 0
+	for(TerrainId i = TerrainId(0); i < TerrainId::ROCK; ++i)
 	{
 		if (terrStr[8-i] == '1')
 			allowedTerrains.insert(i);
 	}
 	
 	//assuming that object can be placed on other land terrains
-	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(Terrain::WATER))
+	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(TerrainId::WATER))
 	{
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
+		for(const auto & terrain : VLC->terrainTypeHandler->objects)
 		{
-			if(terrain.isLand() && terrain.isPassable())
-				allowedTerrains.insert(terrain.id);
+			if(terrain->isLand() && terrain->isPassable())
+				allowedTerrains.insert(terrain->id);
 		}
 	}
 
@@ -231,19 +231,19 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
 
 	reader.readUInt16();
 	ui16 terrMask = reader.readUInt16();
-	for(size_t i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ROCK; i++)
+	for(TerrainId i = TerrainId::FIRST_REGULAR_TERRAIN; i < TerrainId::ORIGINAL_TERRAIN_COUNT; ++i)
 	{
 		if (((terrMask >> i) & 1 ) != 0)
 			allowedTerrains.insert(i);
 	}
 	
 	//assuming that object can be placed on other land terrains
-	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(Terrain::WATER))
+	if(allowedTerrains.size() >= 8 && !allowedTerrains.count(TerrainId::WATER))
 	{
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
+		for(const auto & terrain : VLC->terrainTypeHandler->objects)
 		{
-			if(terrain.isLand() && terrain.isPassable())
-				allowedTerrains.insert(terrain.id);
+			if(terrain->isLand() && terrain->isPassable())
+				allowedTerrains.insert(terrain->id);
 		}
 	}
 
@@ -299,11 +299,11 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
 	}
 	else
 	{
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
+		for(const auto & terrain : VLC->terrainTypeHandler->objects)
 		{
-			if(!terrain.isPassable() || terrain.isWater())
+			if(!terrain->isPassable() || terrain->isWater())
 				continue;
-			allowedTerrains.insert(terrain.id);
+			allowedTerrains.insert(terrain->id);
 		}
 	}
 
@@ -378,14 +378,14 @@ void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const
 	if(withTerrain)
 	{
 		//assumed that ROCK and WATER terrains are not included
-		if(allowedTerrains.size() < (VLC->terrainTypeHandler->terrains().size() - 2))
+		if(allowedTerrains.size() < (VLC->terrainTypeHandler->objects.size() - 2))
 		{
 			JsonVector & data = node["allowedTerrains"].Vector();
 
 			for(auto type : allowedTerrains)
 			{
 				JsonNode value(JsonNode::JsonType::DATA_STRING);
-				value.String() = type;
+				value.String() = VLC->terrainTypeHandler->getById(type)->name;
 				data.push_back(value);
 			}
 		}

+ 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->terrainTypeHandler->roads()[roadType]);
+	tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getByIndex(roadType));
 }
 
 void CDrawRiversOperation::executeTile(TerrainTile & tile)
 {
-	tile.riverType = const_cast<RiverType*>(&VLC->terrainTypeHandler->rivers()[riverType]);
+	tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getByIndex(riverType));
 }
 
 bool CDrawRoadsOperation::canApplyPattern(const LinePattern & pattern) const

+ 2 - 2
lib/mapping/CMap.cpp

@@ -128,9 +128,9 @@ CCastleEvent::CCastleEvent() : town(nullptr)
 TerrainTile::TerrainTile():
 	terType(nullptr),
 	terView(0),
-	riverType(const_cast<RiverType*>(&VLC->terrainTypeHandler->rivers()[River::NO_RIVER])),
+	riverType(nullptr),
 	riverDir(0),
-	roadType(const_cast<RoadType*>(&VLC->terrainTypeHandler->roads()[Road::NO_ROAD])),
+	roadType(nullptr),
 	roadDir(0),
 	extTileFlags(0),
 	visitable(false),

+ 5 - 5
lib/mapping/CMapOperation.cpp

@@ -97,7 +97,7 @@ void CDrawTerrainOperation::execute()
 	for(const auto & pos : terrainSel.getSelectedItems())
 	{
 		auto & tile = map->getTile(pos);
-		tile.terType = const_cast<TerrainType*>(&VLC->terrainTypeHandler->terrains()[terType]);
+		tile.terType = const_cast<TerrainType*>(VLC->terrainTypeHandler->getById(terType));
 		invalidateTerrainViews(pos);
 	}
 
@@ -422,14 +422,14 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 			bool nativeTestOk, nativeTestStrongOk;
 			nativeTestOk = nativeTestStrongOk = (rule.isNativeStrong() || rule.isNativeRule()) && !isAlien;
 
-			if(centerTerType->id == Terrain::DIRT)
+			if(centerTerType->id == TerrainId::DIRT)
 			{
 				nativeTestOk = rule.isNativeRule() && !terType->isTransitionRequired();
 				bool sandTestOk = (rule.isSandRule() || rule.isTransition())
 					&& terType->isTransitionRequired();
 				applyValidationRslt(rule.isAnyRule() || sandTestOk || nativeTestOk || nativeTestStrongOk);
 			}
-			else if(centerTerType->id == Terrain::SAND)
+			else if(centerTerType->id == TerrainId::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, Terrain::WATER, gen));
+	addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, TerrainId::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, Terrain::ROCK, gen));
+		addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, TerrainId::ROCK, gen));
 	}
 }
 

+ 1 - 1
lib/mapping/MapEditUtils.cpp

@@ -272,7 +272,7 @@ CTerrainViewPatternConfig::~CTerrainViewPatternConfig()
 
 const std::vector<CTerrainViewPatternConfig::TVPVector> & CTerrainViewPatternConfig::getTerrainViewPatterns(TerrainId terrain) const
 {
-	auto iter = terrainViewPatterns.find(VLC->terrainTypeHandler->terrains()[terrain].terrainViewPatterns);
+	auto iter = terrainViewPatterns.find(VLC->terrainTypeHandler->getById(terrain)->terrainViewPatterns);
 	if (iter == terrainViewPatterns.end())
 		return terrainViewPatterns.at("normal");
 	return iter->second;

+ 4 - 7
lib/mapping/MapFormatH3M.cpp

@@ -923,9 +923,6 @@ bool CMapLoaderH3M::loadArtifactToSlot(CGHeroInstance * hero, int slot)
 void CMapLoaderH3M::readTerrain()
 {
 	map->initTerrain();
-	const auto & terrains = VLC->terrainTypeHandler->terrains();
-	const auto & rivers = VLC->terrainTypeHandler->rivers();
-	const auto & roads = VLC->terrainTypeHandler->roads();
 
 	// Read terrain
 	int3 pos;
@@ -937,14 +934,14 @@ void CMapLoaderH3M::readTerrain()
 			for(pos.x = 0; pos.x < map->width; pos.x++)
 			{
 				auto & tile = map->getTile(pos);
-				tile.terType = const_cast<TerrainType*>(&terrains[reader.readUInt8()]);
+				tile.terType = const_cast<TerrainType*>(VLC->terrainTypeHandler->getByIndex(reader.readUInt8()));
 				tile.terView = reader.readUInt8();
-				tile.riverType = const_cast<RiverType*>(&rivers[reader.readUInt8()]);
+				tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getByIndex(reader.readUInt8()));
 				tile.riverDir = reader.readUInt8();
-				tile.roadType = const_cast<RoadType*>(&roads[reader.readUInt8()]);
+				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 == Terrain::BORDER ) ? true : false); //underground tiles are always blocked
+				tile.blocked = ((!tile.terType->isPassable() || tile.terType->id == TerrainId::BORDER ) ? true : false); //underground tiles are always blocked
 				tile.visitable = 0;
 			}
 		}

+ 3 - 3
lib/mapping/MapFormatJson.cpp

@@ -979,13 +979,13 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile
 			startPos += 2;
 			try
 			{
-				tile.roadType = const_cast<RoadType*>(VLC->terrainTypeHandler->getRoadByCode(typeCode));
+				tile.roadType = const_cast<RoadType*>(VLC->roadTypeHandler->getInfoByCode(typeCode));
 			}
 			catch (const std::exception&) //it's not a road, it's a river
 			{
 				try
 				{
-					tile.riverType = const_cast<RiverType*>(VLC->terrainTypeHandler->getRiverByCode(typeCode));
+					tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getInfoByCode(typeCode));
 					hasRoad = false;
 				}
 				catch (const std::exception&)
@@ -1021,7 +1021,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->terrainTypeHandler->getRiverByCode(typeCode));
+			tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getInfoByCode(typeCode));
 		}
 		{//river dir
 			int pos = startPos;

+ 6 - 6
lib/rmg/CRmgTemplate.cpp

@@ -74,8 +74,8 @@ public:
 
 	static std::string encode(const si32 index)
 	{
-		const auto& terrains = VLC->terrainTypeHandler->terrains();
-		return (index >=0 && index < terrains.size()) ? terrains[index].name : "<INVALID TERRAIN>";
+		const auto& terrains = VLC->terrainTypeHandler->objects;
+		return (index >=0 && index < terrains.size()) ? terrains[index]->name : "<INVALID TERRAIN>";
 	}
 };
 
@@ -152,9 +152,9 @@ ZoneOptions::ZoneOptions()
 	terrainTypeLikeZone(NO_ZONE),
 	treasureLikeZone(NO_ZONE)
 {
-	for(const auto & terr : VLC->terrainTypeHandler->terrains())
-		if(terr.isLand() && terr.isPassable())
-			terrainTypes.insert(terr.id);
+	for(const auto & terr : VLC->terrainTypeHandler->objects)
+		if(terr->isLand() && terr->isPassable())
+			terrainTypes.insert(terr->id);
 }
 
 ZoneOptions & ZoneOptions::operator=(const ZoneOptions & other)
@@ -365,7 +365,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 			for(auto & ttype : terrainTypes)
 			{
 				JsonNode n;
-				n.String() = ttype;
+				n.String() = VLC->terrainTypeHandler->getById(ttype)->name;
 				node.Vector().push_back(n);
 			}
 		}

+ 4 - 4
lib/rmg/CZonePlacer.cpp

@@ -194,15 +194,15 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
 				else
 				{
 					auto & tt = (*VLC->townh)[faction]->nativeTerrain;
-					if(tt == Terrain::DIRT)
+					if(tt == TerrainId::DIRT)
 					{
 						//any / random
 						zonesToPlace.push_back(zone);
 					}
 					else
 					{
-						const auto & terrainType = VLC->terrainTypeHandler->terrains()[tt];
-						if(terrainType.isUnderground() && !terrainType.isSurface())
+						const auto & terrainType = VLC->terrainTypeHandler->getById(tt);
+						if(terrainType->isUnderground() && !terrainType->isSurface())
 						{
 							//underground only
 							zonesOnLevel[1]++;
@@ -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, Terrain::SUBTERRANEAN);
+			paintZoneTerrain(*zone.second, *rand, map, TerrainId::SUBTERRANEAN);
 		}
 	}
 	logGlobal->info("Finished zone colouring");

+ 3 - 3
lib/rmg/ConnectionsPlacer.cpp

@@ -84,9 +84,9 @@ 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->terrains();
-	bool directProhibited = vstd::contains(terrains[zone.getTerrainType()].prohibitTransitions, otherZone->getTerrainType())
-						 || vstd::contains(terrains[otherZone->getTerrainType()].prohibitTransitions, zone.getTerrainType());
+	const auto& terrains = VLC->terrainTypeHandler->objects;
+	bool directProhibited = vstd::contains(terrains[zone.getTerrainType()]->prohibitTransitions, otherZone->getTerrainType())
+						 || vstd::contains(terrains[otherZone->getTerrainType()]->prohibitTransitions, zone.getTerrainType());
 	auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
 	if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
 	{

+ 8 - 8
lib/rmg/Functions.cpp

@@ -119,9 +119,9 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
 	{
 		//collect all water terrain types
 		std::vector<TerrainId> waterTerrains;
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
-			if(terrain.isWater())
-				waterTerrains.push_back(terrain.id);
+		for(const auto & terrain : VLC->terrainTypeHandler->objects)
+			if(terrain->isWater())
+				waterTerrains.push_back(terrain->id);
 		
 		zone.setTerrainType(*RandomGeneratorUtil::nextItem(waterTerrains, gen.rand));
 	}
@@ -137,20 +137,20 @@ void initTerrainType(Zone & zone, CMapGenerator & gen)
 		}
 		
 		//Now, replace disallowed terrains on surface and in the underground
-		const auto & terrainType = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()];
+		const auto & terrainType = VLC->terrainTypeHandler->getById(zone.getTerrainType());
 
 		if(zone.isUnderground())
 		{
-			if(!terrainType.isUnderground())
+			if(!terrainType->isUnderground())
 			{
-				zone.setTerrainType(Terrain::SUBTERRANEAN);
+				zone.setTerrainType(TerrainId::SUBTERRANEAN);
 			}
 		}
 		else
 		{
-			if (!terrainType.isSurface())
+			if (!terrainType->isSurface())
 			{
-				zone.setTerrainType(Terrain::DIRT);
+				zone.setTerrainType(TerrainId::DIRT);
 			}
 		}
 	}

+ 7 - 7
lib/rmg/RiverPlacer.cpp

@@ -86,7 +86,7 @@ void RiverPlacer::init()
 void RiverPlacer::drawRivers()
 {
 	map.getEditManager()->getTerrainSelection().setSelection(rivers.getTilesVector());
-	map.getEditManager()->drawRiver(VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].river, &generator.rand);
+	map.getEditManager()->drawRiver(VLC->terrainTypeHandler->getById(zone.getTerrainType())->river, &generator.rand);
 }
 
 char RiverPlacer::dump(const int3 & t)
@@ -195,7 +195,7 @@ void RiverPlacer::preprocess()
 	//calculate delta positions
 	if(connectedToWaterZoneId > -1)
 	{
-		auto river = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].river;
+		auto river = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river;
 		auto & a = neighbourZonesTiles[connectedToWaterZoneId];
 		auto availableArea = zone.areaPossible() + zone.freePaths();
 		for(auto & tileToProcess : availableArea.getTilesVector())
@@ -321,9 +321,9 @@ void RiverPlacer::preprocess()
 
 void RiverPlacer::connectRiver(const int3 & tile)
 {
-	auto riverType = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].river;
-	const auto & river = VLC->terrainTypeHandler->rivers()[riverType];
-	if(river.id == River::NO_RIVER)
+	auto riverType = VLC->terrainTypeHandler->getById(zone.getTerrainType())->river;
+	const auto * river = VLC->riverTypeHandler->getByIndex(riverType);
+	if(river->id == River::NO_RIVER)
 		return;
 	
 	rmg::Area roads;
@@ -381,9 +381,9 @@ void RiverPlacer::connectRiver(const int3 & tile)
 		{
 			if(tmplates.size() % 4 != 0)
 				throw rmgException(boost::to_string(boost::format("River templates for (%d,%d) at terrain %s, river %s are incorrect") %
-					RIVER_DELTA_ID % RIVER_DELTA_SUBTYPE % zone.getTerrainType() % river.code));
+					RIVER_DELTA_ID % RIVER_DELTA_SUBTYPE % zone.getTerrainType() % river->code));
 			
-			std::string targetTemplateName = river.deltaName + std::to_string(deltaOrientations[pos]) + ".def";
+			std::string targetTemplateName = river->deltaName + std::to_string(deltaOrientations[pos]) + ".def";
 			for(auto & templ : tmplates)
 			{
 				if(templ->animationFile == targetTemplateName)

+ 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(Terrain::GRASS, &generator.rand);
+	getEditManager()->drawTerrain(TerrainId::GRASS, &generator.rand);
 	
 	auto tmpl = mapGenOptions.getMapTemplate();
 	zones.clear();

+ 1 - 1
lib/rmg/RmgObject.cpp

@@ -121,7 +121,7 @@ void Object::Instance::setTemplate(TerrainId terrain)
 	auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
 	if (templates.empty())
 	{
-		auto terrainName = VLC->terrainTypeHandler->terrains()[terrain].name;
+		auto terrainName = VLC->terrainTypeHandler->getById(terrain)->name;
 		throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName));
 	}
 	dObject.appearance = templates.front();

+ 3 - 1
lib/rmg/RoadPlacer.cpp

@@ -78,8 +78,10 @@ void RoadPlacer::drawRoads(bool secondary)
 	zone.areaPossible().subtract(roads);
 	zone.freePaths().unite(roads);
 	map.getEditManager()->getTerrainSelection().setSelection(roads.getTilesVector());
+
 	std::string roadName = (secondary ? generator.getConfig().secondaryRoadType : generator.getConfig().defaultRoadType);
-	RoadId roadType = VLC->terrainTypeHandler->getRoadByName(roadName)->id;
+	RoadId roadType = VLC->roadTypeHandler->getInfoByName(roadName)->id;
+
 	map.getEditManager()->drawRoad(roadType, &generator.rand);
 }
 

+ 2 - 2
lib/rmg/RockPlacer.cpp

@@ -24,8 +24,8 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 void RockPlacer::process()
 {
-	rockTerrain = VLC->terrainTypeHandler->terrains()[zone.getTerrainType()].rockTerrain;
-	assert(!VLC->terrainTypeHandler->terrains()[rockTerrain].isPassable());
+	rockTerrain = VLC->terrainTypeHandler->getById(zone.getTerrainType())->rockTerrain;
+	assert(!VLC->terrainTypeHandler->getById(rockTerrain)->isPassable());
 	
 	accessibleArea = zone.freePaths() + zone.areaUsed();
 	if(auto * m = zone.getModificator<ObjectManager>())

+ 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(Terrain::GRASS),
+					terrainType(TerrainId::GRASS),
 					map(map),
 					generator(generator)
 {

+ 11 - 11
mapeditor/mainwindow.cpp

@@ -542,32 +542,32 @@ void MainWindow::loadObjectsTree()
 	{
 	ui->terrainFilterCombo->addItem("");
 	//adding terrains
-	for(auto & terrain : VLC->terrainTypeHandler->terrains())
+	for(auto & terrain : VLC->terrainTypeHandler->objects)
 	{
-		QPushButton *b = new QPushButton(QString::fromStdString(terrain.name));
+		QPushButton *b = new QPushButton(QString::fromStdString(terrain->name));
 		ui->terrainLayout->addWidget(b);
-		connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain.id); });
+		connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain->id); });
 
 		//filter
-		ui->terrainFilterCombo->addItem(QString::fromStdString(terrain));
+		ui->terrainFilterCombo->addItem(QString::fromStdString(terrain->name));
 	}
 	//add spacer to keep terrain button on the top
 	ui->terrainLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
 	//adding roads
-	for(auto & road : VLC->terrainTypeHandler->roads())
+	for(auto & road : VLC->roadTypeHandler->objects)
 	{
-		QPushButton *b = new QPushButton(QString::fromStdString(road.fileName));
+		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, true); });
 	}
 	//add spacer to keep terrain button on the top
 	ui->roadLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
 	//adding rivers
-	for(auto & river : VLC->terrainTypeHandler->rivers())
+	for(auto & river : VLC->riverTypeHandler->objects)
 	{
-		QPushButton *b = new QPushButton(QString::fromStdString(river.fileName));
+		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, false); });
 	}
 	//add spacer to keep terrain button on the top
 	ui->riverLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
@@ -914,7 +914,7 @@ void MainWindow::on_terrainFilterCombo_currentTextChanged(const QString &arg1)
 	if(!objectBrowser)
 		return;
 
-	objectBrowser->terrain = arg1.isEmpty() ? TerrainId(Terrain::ANY_TERRAIN) : VLC->terrainTypeHandler->getInfoByName(arg1.toStdString())->id;
+	objectBrowser->terrain = arg1.isEmpty() ? TerrainId(TerrainId::ANY_TERRAIN) : VLC->terrainTypeHandler->getInfoByName(arg1.toStdString())->id;
 	objectBrowser->invalidate();
 	objectBrowser->sort(0);
 }

+ 6 - 6
mapeditor/maphandler.cpp

@@ -78,17 +78,17 @@ void MapHandler::initTerrainGraphics()
 	std::map<std::string, std::string> terrainFiles;
 	std::map<std::string, std::string> roadFiles;
 	std::map<std::string, std::string> riverFiles;
-	for(const auto & terrain : VLC->terrainTypeHandler->terrains())
+	for(const auto & terrain : VLC->terrainTypeHandler->objects)
 	{
-		terrainFiles[terrain.name] = terrain.tilesFilename;
+		terrainFiles[terrain->name] = terrain->tilesFilename;
 	}
-	for(const auto & river : VLC->terrainTypeHandler->rivers())
+	for(const auto & river : VLC->riverTypeHandler->objects)
 	{
-		riverFiles[river.fileName] = river.fileName;
+		riverFiles[river->fileName] = river->fileName;
 	}
-	for(const auto & road : VLC->terrainTypeHandler->roads())
+	for(const auto & road : VLC->roadTypeHandler->objects)
 	{
-		roadFiles[road.fileName] = road.fileName;
+		roadFiles[road->fileName] = road->fileName;
 	}
 	
 	loadFlipped(terrainAnimations, terrainImages, terrainFiles);

+ 2 - 2
mapeditor/objectbrowser.cpp

@@ -13,7 +13,7 @@
 #include "../lib/mapObjects/CObjectClassesHandler.h"
 
 ObjectBrowserProxyModel::ObjectBrowserProxyModel(QObject *parent)
-	: QSortFilterProxyModel{parent}, terrain(Terrain::ANY_TERRAIN)
+	: QSortFilterProxyModel{parent}, terrain(TerrainId::ANY_TERRAIN)
 {
 }
 
@@ -33,7 +33,7 @@ bool ObjectBrowserProxyModel::filterAcceptsRow(int source_row, const QModelIndex
 	if(!filterAcceptsRowText(source_row, source_parent))
 		return false;
 
-	if(terrain == Terrain::ANY_TERRAIN)
+	if(terrain == TerrainId::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 = Terrain::SAND;
+		terrain = TerrainId::SAND;
 
 	BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator());
 	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)