فهرست منبع

Merge pull request #1409 from IvanSavenko/translate_terrain

Refactoring of terrain handlers
Ivan Savenko 2 سال پیش
والد
کامیت
c400416058
98فایلهای تغییر یافته به همراه1352 افزوده شده و 1166 حذف شده
  1. 3 33
      client/CMusicHandler.cpp
  2. 0 3
      client/CMusicHandler.h
  3. 25 11
      client/CPlayerInterface.cpp
  4. 3 2
      client/battle/BattleInterface.cpp
  5. 7 6
      client/lobby/RandomMapTab.cpp
  6. 18 16
      client/mapHandler.cpp
  7. 10 10
      client/widgets/AdventureMapClasses.cpp
  8. 0 1
      client/widgets/AdventureMapClasses.h
  9. 2 1
      client/windows/CAdvmapInterface.cpp
  10. 6 2
      cmake_modules/VCMI_lib.cmake
  11. 3 0
      config/defaultMods.json
  12. 1 0
      config/factions/neutral.json
  13. 8 0
      config/gameConfig.json
  14. 1 1
      config/randomMap.json
  15. 17 13
      config/rivers.json
  16. 13 10
      config/roads.json
  17. 1 1
      config/schemas/faction.json
  18. 37 0
      config/schemas/river.json
  19. 37 0
      config/schemas/road.json
  20. 43 12
      config/schemas/terrain.json
  21. 57 41
      config/terrains.json
  22. 0 1
      lib/BattleFieldHandler.h
  23. 2 3
      lib/CCreatureHandler.cpp
  24. 0 1
      lib/CCreatureHandler.h
  25. 1 0
      lib/CGameInfoCallback.cpp
  26. 3 3
      lib/CGameState.cpp
  27. 0 3
      lib/CGeneralTextHandler.cpp
  28. 0 1
      lib/CGeneralTextHandler.h
  29. 0 14
      lib/CHeroHandler.cpp
  30. 0 2
      lib/CHeroHandler.h
  31. 6 0
      lib/CModHandler.cpp
  32. 2 2
      lib/CPathfinder.cpp
  33. 0 1
      lib/CPathfinder.h
  34. 1 1
      lib/CStack.cpp
  35. 0 1
      lib/CStack.h
  36. 11 25
      lib/CTownHandler.cpp
  37. 0 6
      lib/CTownHandler.h
  38. 119 64
      lib/GameConstants.h
  39. 6 5
      lib/HeroBonus.cpp
  40. 0 1
      lib/HeroBonus.h
  41. 3 0
      lib/IGameCallback.cpp
  42. 5 4
      lib/NetPacksLib.cpp
  43. 7 1
      lib/ObstacleHandler.cpp
  44. 0 1
      lib/ObstacleHandler.h
  45. 2 0
      lib/PathfinderUtil.h
  46. 78 0
      lib/RiverHandler.cpp
  47. 79 0
      lib/RiverHandler.h
  48. 78 0
      lib/RoadHandler.cpp
  49. 79 0
      lib/RoadHandler.h
  50. 0 473
      lib/Terrain.cpp
  51. 0 204
      lib/Terrain.h
  52. 191 0
      lib/TerrainHandler.cpp
  53. 122 0
      lib/TerrainHandler.h
  54. 5 0
      lib/VCMI_Lib.cpp
  55. 8 0
      lib/VCMI_Lib.h
  56. 0 1
      lib/battle/BattleInfo.cpp
  57. 0 1
      lib/battle/BattleProxy.cpp
  58. 14 11
      lib/mapObjects/CGHeroInstance.cpp
  59. 2 1
      lib/mapObjects/CGTownInstance.cpp
  60. 0 1
      lib/mapObjects/CObjectClassesHandler.h
  61. 4 3
      lib/mapObjects/CObjectHandler.cpp
  62. 2 1
      lib/mapObjects/CommonConstructors.cpp
  63. 22 42
      lib/mapObjects/ObjectTemplate.cpp
  64. 3 0
      lib/mapObjects/ObjectTemplate.h
  65. 9 6
      lib/mapping/CDrawRoadsOperation.cpp
  66. 1 1
      lib/mapping/CDrawRoadsOperation.h
  67. 5 2
      lib/mapping/CMap.cpp
  68. 7 3
      lib/mapping/CMapDefines.h
  69. 0 1
      lib/mapping/CMapEditManager.h
  70. 11 10
      lib/mapping/CMapOperation.cpp
  71. 3 2
      lib/mapping/MapEditUtils.cpp
  72. 1 1
      lib/mapping/MapEditUtils.h
  73. 9 7
      lib/mapping/MapFormatH3M.cpp
  74. 41 7
      lib/mapping/MapFormatJson.cpp
  75. 7 0
      lib/mapping/MapFormatJson.h
  76. 3 0
      lib/registerTypes/TypesLobbyPacks.cpp
  77. 13 10
      lib/rmg/CRmgTemplate.cpp
  78. 0 1
      lib/rmg/CRmgTemplate.h
  79. 5 4
      lib/rmg/CZonePlacer.cpp
  80. 6 3
      lib/rmg/ConnectionsPlacer.cpp
  81. 9 8
      lib/rmg/Functions.cpp
  82. 9 7
      lib/rmg/RiverPlacer.cpp
  83. 1 1
      lib/rmg/RmgMap.cpp
  84. 4 3
      lib/rmg/RmgObject.cpp
  85. 3 1
      lib/rmg/RoadPlacer.cpp
  86. 3 2
      lib/rmg/RockPlacer.cpp
  87. 0 1
      lib/rmg/TileInfo.h
  88. 3 2
      lib/rmg/WaterProxy.cpp
  89. 1 1
      lib/rmg/Zone.cpp
  90. 2 2
      lib/serializer/CSerializer.h
  91. 25 16
      mapeditor/mainwindow.cpp
  92. 0 1
      mapeditor/mainwindow.h
  93. 2 2
      mapeditor/mapcontroller.cpp
  94. 0 1
      mapeditor/mapcontroller.h
  95. 16 14
      mapeditor/maphandler.cpp
  96. 2 2
      mapeditor/objectbrowser.cpp
  97. 1 1
      mapeditor/objectbrowser.h
  98. 3 2
      server/CGameHandler.cpp

+ 3 - 33
client/CMusicHandler.cpp

@@ -20,7 +20,7 @@
 #include "../lib/StringConstants.h"
 #include "../lib/CRandomGenerator.h"
 #include "../lib/VCMIDirs.h"
-#include "../lib/Terrain.h"
+#include "../lib/TerrainHandler.h"
 
 #define VCMI_SOUND_NAME(x)
 #define VCMI_SOUND_FILE(y) #y,
@@ -89,36 +89,6 @@ CSoundHandler::CSoundHandler():
 		soundBase::battle02, soundBase::battle03, soundBase::battle04,
 		soundBase::battle05, soundBase::battle06, soundBase::battle07
 	};
-
-	//predefine terrain set
-	//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}
-	};
-}
-
-void CSoundHandler::loadHorseSounds()
-{
-	const auto & terrains = CGI->terrainTypeHandler->terrains();
-	for(const auto & terrain : terrains)
-	{
-		//since all sounds are hardcoded, let's keep it
-		if(vstd::contains(horseSounds, terrain.id))
-			continue;
-
-		//Use already existing horse sound
-		horseSounds[terrain.id] = horseSounds.at(terrains[terrain.id].horseSoundId);
-	}
 }
 
 void CSoundHandler::init()
@@ -368,9 +338,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->getJsonKey(), "Music/" + terrain->musicFilename);
 	}
 }
 

+ 0 - 3
client/CMusicHandler.h

@@ -11,7 +11,6 @@
 
 #include "../lib/CConfigHandler.h"
 #include "../lib/CSoundBase.h"
-#include "../lib/Terrain.h"
 
 struct _Mix_Music;
 struct SDL_RWops;
@@ -61,7 +60,6 @@ public:
 	CSoundHandler();
 
 	void init() override;
-	void loadHorseSounds();
 	void release() override;
 
 	void setVolume(ui32 percent) override;
@@ -84,7 +82,6 @@ public:
 	// Sets
 	std::vector<soundBase::soundID> pickupSounds;
 	std::vector<soundBase::soundID> battleIntroSounds;
-	std::map<TerrainId, soundBase::soundID> horseSounds;
 };
 
 // Helper //now it looks somewhat useless

+ 25 - 11
client/CPlayerInterface.cpp

@@ -61,6 +61,8 @@
 #include "windows/InfoWindows.h"
 #include "../lib/UnlockGuard.h"
 #include "../lib/CPathfinder.h"
+#include "../lib/RoadHandler.h"
+#include "../lib/TerrainHandler.h"
 #include <SDL.h>
 #include "CServerHandler.h"
 // FIXME: only needed for CGameState::mutex
@@ -156,7 +158,6 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
 	cb = CB;
 	env = ENV;
 
-	CCS->soundh->loadHorseSounds();
 	CCS->musich->loadTerrainMusicThemes();
 
 	initializeHeroTownList();
@@ -260,7 +261,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
 	{
 		updateAmbientSounds();
 		//We may need to change music - select new track, music handler will change it if needed
-		CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->name, true, false);
+		CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->getJsonKey(), true, false);
 
 		if(details.result == TryMoveHero::TELEPORTATION)
 		{
@@ -436,7 +437,7 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 		adventureInt->select(newSelection, true);
 	else if (adventureInt->selection == hero)
 		adventureInt->selection = nullptr;
-	
+
 	if (vstd::contains(paths, hero))
 		paths.erase(hero);
 }
@@ -2372,8 +2373,9 @@ 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 = ETerrainId::NONE;
 		TerrainId newTerrain;
+		bool wasOnRoad = true;
 		int sh = -1;
 
 		auto canStop = [&](CGPathNode * node) -> bool
@@ -2389,13 +2391,18 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 
 		for (i=(int)path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--)
 		{
-			int3 currentCoord = path.nodes[i].coord;
+			int3 prevCoord = path.nodes[i].coord;
 			int3 nextCoord = path.nodes[i-1].coord;
 
-			auto currentObject = getObj(currentCoord, currentCoord == h->pos);
+			auto prevRoad = cb->getTile(h->convertToVisitablePos(prevCoord))->roadType;
+			auto nextRoad = cb->getTile(h->convertToVisitablePos(nextCoord))->roadType;
+
+			bool movingOnRoad = prevRoad->getId() != Road::NO_ROAD && nextRoad->getId() != Road::NO_ROAD;
+
+			auto prevObject = getObj(prevCoord, prevCoord == h->pos);
 			auto nextObjectTop = getObj(nextCoord, false);
 			auto nextObject = getObj(nextCoord, true);
-			auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
+			auto destTeleportObj = getDestTeleportObj(prevObject, nextObjectTop, nextObject);
 			if (isTeleportAction(path.nodes[i-1].action) && destTeleportObj != nullptr)
 			{
 				CCS->soundh->stopSound(sh);
@@ -2410,7 +2417,10 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 				}
 				if(i != path.nodes.size() - 1)
 				{
-					sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
+					if (movingOnRoad)
+						sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSound, -1);
+					else
+						sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSoundPenalty, -1);
 				}
 				continue;
 			}
@@ -2428,12 +2438,16 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
 				sh = CCS->soundh->playSound(soundBase::horseFlying, -1);
 #endif
 			{
-				newTerrain = cb->getTile(h->convertToVisitablePos(currentCoord))->terType->id;
-				if(newTerrain != currentTerrain)
+				newTerrain = cb->getTile(h->convertToVisitablePos(prevCoord))->terType->getId();
+				if(newTerrain != currentTerrain || wasOnRoad != movingOnRoad)
 				{
 					CCS->soundh->stopSound(sh);
-					sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
+					if (movingOnRoad)
+						sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSound, -1);
+					else
+						sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSoundPenalty, -1);
 					currentTerrain = newTerrain;
+					wasOnRoad = movingOnRoad;
 				}
 			}
 

+ 3 - 2
client/battle/BattleInterface.cpp

@@ -41,6 +41,7 @@
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/NetPacks.h"
 #include "../../lib/UnlockGuard.h"
+#include "../../lib/TerrainHandler.h"
 
 CondSh<BattleAction *> BattleInterface::givenCommand(nullptr);
 
@@ -136,8 +137,8 @@ BattleInterface::~BattleInterface()
 	if (adventureInt && adventureInt->selection)
 	{
 		//FIXME: this should be moved to adventureInt which should restore correct track based on selection/active player
-		const auto & terrain = *(LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType);
-		CCS->musich->playMusicFromSet("terrain", terrain.name, true, false);
+		const auto * terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType;
+		CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
 	}
 
 	// may happen if user decided to close game while in battle

+ 7 - 6
client/lobby/RandomMapTab.cpp

@@ -29,6 +29,7 @@
 #include "../../lib/rmg/CMapGenOptions.h"
 #include "../../lib/CModHandler.h"
 #include "../../lib/rmg/CRmgTemplateStorage.h"
+#include "../../lib/RoadHandler.h"
 
 RandomMapTab::RandomMapTab():
 	InterfaceObjectConfigurable()
@@ -108,12 +109,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->getJsonKey();
 		addCallback(cbRoadType, [&, road](bool on)
 		{
-			mapGenOptions->setRoadEnabled(road.name, on);
+			mapGenOptions->setRoadEnabled(road->getJsonKey(), on);
 			updateMapInfoByHost();
 		});
 	}
@@ -283,11 +284,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->getJsonKey()))
 		{
-			w->setSelected(opts->isRoadEnabled(r.name));
+			w->setSelected(opts->isRoadEnabled(r->getJsonKey()));
 		}
 	}
 }

+ 18 - 16
client/mapHandler.cpp

@@ -30,7 +30,9 @@
 #include "CMT.h"
 #include "CMusicHandler.h"
 #include "../lib/CRandomGenerator.h"
-#include "../lib/Terrain.h"
+#include "../lib/RoadHandler.h"
+#include "../lib/RiverHandler.h"
+#include "../lib/TerrainHandler.h"
 #include "../lib/filesystem/ResourceID.h"
 #include "../lib/JsonDetail.h"
 
@@ -175,17 +177,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->getJsonKey()] = terrain->tilesFilename;
 	}
-	for(const auto & river : VLC->terrainTypeHandler->rivers())
+	for(const auto & river : VLC->riverTypeHandler->objects)
 	{
-		riverFiles[river.fileName] = river.fileName;
+		riverFiles[river->getJsonKey()] = river->tilesFilename;
 	}
-	for(const auto & road : VLC->terrainTypeHandler->roads())
+	for(const auto & road : VLC->roadTypeHandler->objects)
 	{
-		roadFiles[road.fileName] = road.fileName;
+		roadFiles[road->getJsonKey()] = road->tilesFilename;
 	}
 	
 	loadFlipped(terrainAnimations, terrainImages, terrainFiles);
@@ -606,7 +608,7 @@ void CMapHandler::CMapBlitter::drawTileTerrain(SDL_Surface * targetSurf, const T
 	ui8 rotation = tinfo.extTileFlags % 4;
 	
 	//TODO: use ui8 instead of string key
-	auto terrainName = tinfo.terType->name;
+	auto terrainName = tinfo.terType->getJsonKey();
 
 	if(parent->terrainImages[terrainName].size()<=tinfo.terView)
 		return;
@@ -786,21 +788,21 @@ void CMapHandler::CMapBlitter::drawObjects(SDL_Surface * targetSurf, const Terra
 
 void CMapHandler::CMapBlitter::drawRoad(SDL_Surface * targetSurf, const TerrainTile & tinfo, const TerrainTile * tinfoUpper) const
 {
-	if (tinfoUpper && tinfoUpper->roadType->id != Road::NO_ROAD)
+	if (tinfoUpper && tinfoUpper->roadType->getId() != Road::NO_ROAD)
 	{
 		ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4;
 		Rect source(0, tileSize / 2, tileSize, tileSize / 2);
 		Rect dest(realPos.x, realPos.y, tileSize, tileSize / 2);
-		drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType->fileName][tinfoUpper->roadDir][rotation],
+		drawElement(EMapCacheType::ROADS, parent->roadImages[tinfoUpper->roadType->getJsonKey()][tinfoUpper->roadDir][rotation],
 				&source, targetSurf, &dest);
 	}
 
-	if(tinfo.roadType->id != Road::NO_ROAD) //print road from this tile
+	if(tinfo.roadType->getId() != Road::NO_ROAD) //print road from this tile
 	{
 		ui8 rotation = (tinfo.extTileFlags >> 4) % 4;
 		Rect source(0, 0, tileSize, halfTileSizeCeil);
 		Rect dest(realPos.x, realPos.y + tileSize / 2, tileSize, tileSize / 2);
-		drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType->fileName][tinfo.roadDir][rotation],
+		drawElement(EMapCacheType::ROADS, parent->roadImages[tinfo.roadType->getJsonKey()][tinfo.roadDir][rotation],
 				&source, targetSurf, &dest);
 	}
 }
@@ -809,7 +811,7 @@ void CMapHandler::CMapBlitter::drawRiver(SDL_Surface * targetSurf, const Terrain
 {
 	Rect destRect(realTileRect);
 	ui8 rotation = (tinfo.extTileFlags >> 2) % 4;
-	drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->fileName][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect);
+	drawElement(EMapCacheType::RIVERS, parent->riverImages[tinfo.riverType->getJsonKey()][tinfo.riverDir][rotation], nullptr, targetSurf, &destRect);
 }
 
 void CMapHandler::CMapBlitter::drawFow(SDL_Surface * targetSurf) const
@@ -860,7 +862,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
 			if(isVisible || info->showAllTerrain)
 			{
 				drawTileTerrain(targetSurf, tinfo, tile);
-				if(tinfo.riverType->id != River::NO_RIVER)
+				if(tinfo.riverType->getId() != River::NO_RIVER)
 					drawRiver(targetSurf, tinfo);
 				drawRoad(targetSurf, tinfo, tinfoUpper);
 			}
@@ -1388,8 +1390,9 @@ void CMapHandler::getTerrainDescr(const int3 & pos, std::string & out, bool isRM
 			break;
 		}
 	}
+
 	if(!isTile2Terrain || out.empty())
-		out = CGI->generaltexth->terrainNames[t.terType->id];
+		out = t.terType->getNameTranslated();
 
 	if(t.getDiggingStatus(false) == EDiggingStatus::CAN_DIG)
 	{
@@ -1485,4 +1488,3 @@ TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, SDL_Rect rec
 TerrainTileObject::~TerrainTileObject()
 {
 }
-

+ 10 - 10
client/widgets/AdventureMapClasses.cpp

@@ -41,7 +41,7 @@
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CModHandler.h"
 #include "../../lib/CTownHandler.h"
-#include "../../lib/Terrain.h"
+#include "../../lib/TerrainHandler.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/JsonNode.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
@@ -391,7 +391,7 @@ const SDL_Color & CMinimapInstance::getTileColor(const int3 & pos)
 	}
 
 	// else - use terrain color (blocked version or normal)
-	const auto & colorPair = parent->colors.find(tile->terType->id)->second;
+	const auto & colorPair = parent->colors.find(tile->terType->getId())->second;
 	if (tile->blocked && (!tile->visitable))
 		return colorPair.second;
 	else
@@ -500,25 +500,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->getId()] = std::make_pair(normal, blocked);
 	}
 	return ret;
 }

+ 0 - 1
client/widgets/AdventureMapClasses.h

@@ -11,7 +11,6 @@
 
 #include "ObjectLists.h"
 #include "../../lib/FunctionList.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 2 - 1
client/windows/CAdvmapInterface.cpp

@@ -53,6 +53,7 @@
 #include "../../lib/VCMI_Lib.h"
 #include "../../lib/StartInfo.h"
 #include "../../lib/mapping/CMapInfo.h"
+#include "../../lib/TerrainHandler.h"
 
 #define ADVOPT (conf.go()->ac)
 using namespace CSDL_Ext;
@@ -1414,7 +1415,7 @@ void CAdvMapInt::select(const CArmedInstance *sel, bool centerView)
 		auto pos = sel->visitablePos();
 		auto tile = LOCPLINT->cb->getTile(pos);
 		if(tile)
-			CCS->musich->playMusicFromSet("terrain", tile->terType->name, true, false);
+			CCS->musich->playMusicFromSet("terrain", tile->terType->getJsonKey(), true, false);
 	}
 	if(centerView)
 		centerOn(sel);

+ 6 - 2
cmake_modules/VCMI_lib.cmake

@@ -191,8 +191,10 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/ObstacleHandler.cpp
 		${MAIN_LIB_DIR}/StartInfo.cpp
 		${MAIN_LIB_DIR}/ResourceSet.cpp
+		${MAIN_LIB_DIR}/RiverHandler.cpp
+		${MAIN_LIB_DIR}/RoadHandler.cpp
 		${MAIN_LIB_DIR}/ScriptHandler.cpp
-		${MAIN_LIB_DIR}/Terrain.cpp
+		${MAIN_LIB_DIR}/TerrainHandler.cpp
 		${MAIN_LIB_DIR}/VCMIDirs.cpp
 		${MAIN_LIB_DIR}/VCMI_Lib.cpp
 
@@ -439,11 +441,13 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/ObstacleHandler.h
 		${MAIN_LIB_DIR}/PathfinderUtil.h
 		${MAIN_LIB_DIR}/ResourceSet.h
+		${MAIN_LIB_DIR}/RiverHandler.h
+		${MAIN_LIB_DIR}/RoadHandler.h
 		${MAIN_LIB_DIR}/ScriptHandler.h
 		${MAIN_LIB_DIR}/ScopeGuard.h
 		${MAIN_LIB_DIR}/StartInfo.h
 		${MAIN_LIB_DIR}/StringConstants.h
-		${MAIN_LIB_DIR}/Terrain.h
+		${MAIN_LIB_DIR}/TerrainHandler.h
 		${MAIN_LIB_DIR}/UnlockGuard.h
 		${MAIN_LIB_DIR}/VCMIDirs.h
 		${MAIN_LIB_DIR}/vcmi_endian.h

+ 3 - 0
config/defaultMods.json

@@ -10,6 +10,9 @@
 		"hero"       : 156,
 		"spell"      : 81,
 		"object"     : 256,
+		"terrain"    : 10,
+		"river"      : 5,
+		"road"       : 4,
 		"mapVersion" : 28 // max supported version, SoD
 	},
 

+ 1 - 0
config/factions/neutral.json

@@ -3,6 +3,7 @@
 	{
 		"name" : "Neutral",
 		"index" : 9,
+		"nativeTerrain" : "none",
 		"alignment" : "neutral",
 		"creatureBackground" :
 		{

+ 8 - 0
config/gameConfig.json

@@ -85,6 +85,14 @@
 	[
 		"config/terrains.json"
 	],
+	"roads":
+	[
+		"config/roads.json"
+	],
+	"rivers":
+	[
+		"config/rivers.json"
+	],
 	"battlefields":
 	[
 		"config/battlefields.json"

+ 1 - 1
config/randomMap.json

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

+ 17 - 13
config/rivers.json

@@ -1,30 +1,34 @@
 {
     "waterRiver":
     {
-        "originalRiverId": 1,
-        "code": "rw", //must be 2 characters
-        "animation": "clrrvr",
+        "index": 1,
+        "text" : "Water river",
+        "shortIdentifier": "rw", //must be 2 characters
+        "tilesFilename": "clrrvr",
         "delta": "clrdelt"
     },
     "iceRiver":
     {
-        "originalRiverId": 2,
-        "code": "ri",
-        "animation": "icyrvr",
+        "index": 2,
+        "text" : "Ice river",
+        "shortIdentifier": "ri",
+        "tilesFilename": "icyrvr",
         "delta": "icedelt"
     },
     "mudRiver":
     {
-        "originalRiverId": 3,
-        "code": "rm",
-        "animation": "mudrvr",
+        "index": 3,
+        "text" : "Mud river",
+        "shortIdentifier": "rm",
+        "tilesFilename": "mudrvr",
         "delta": "muddelt"
     },
     "lavaRiver":
     {
-        "originalRiverId": 4,
-        "code": "rl",
-        "animation": "lavrvr",
+        "index": 4,
+        "text" : "Lava river",
+        "shortIdentifier": "rl",
+        "tilesFilename": "lavrvr",
         "delta": "lavdelt"
     }
-}
+}

+ 13 - 10
config/roads.json

@@ -1,23 +1,26 @@
 {
     "dirtRoad":
     {
-        "originalRoadId": 1,
-        "code": "pd", //must be 2 characters
-        "animation": "dirtrd",
+        "index": 1,
+        "text" : "Dirt road",
+        "shortIdentifier": "pd", //must be 2 characters
+        "tilesFilename": "dirtrd",
         "moveCost": 75
     },
     "gravelRoad":
     {
-        "originalRoadId": 2,
-        "code": "pg",
-        "animation": "gravrd",
+        "index": 2,
+        "text" : "Gravel road",
+        "shortIdentifier": "pg",
+        "tilesFilename": "gravrd",
         "moveCost": 65
     },
     "cobblestoneRoad":
     {
-        "originalRoadId": 3,
-        "code": "pc",
-        "animation": "cobbrd",
+        "index": 3,
+        "text" : "Cobblestone road",
+        "shortIdentifier": "pc",
+        "tilesFilename": "cobbrd",
         "moveCost": 50
     }
-}
+}

+ 1 - 1
config/schemas/faction.json

@@ -31,7 +31,7 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI faction format",
 	"description": "Json format for defining new faction (aka towns) in VCMI",
-	"required" : [ "name", "alignment", "creatureBackground" ],
+	"required" : [ "name", "alignment", "creatureBackground", "nativeTerrain" ],
 	"dependencies" : {
 		"town" : [ "puzzleMap" ]
 	},

+ 37 - 0
config/schemas/river.json

@@ -0,0 +1,37 @@
+{
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI river format",
+	"description" : "Format used to define new rivers in VCMI",
+	"required" : [ "text", "shortIdentifier", "tilesFilename", "delta" ],
+
+	"additionalProperties" : false,
+	"properties":{
+		"index" : 
+		{
+			"type": "number",
+			"description": "Internal, do not use"
+		},
+		"text":
+		{
+			"type": "string",
+			"description": "Human-readable name of the river"
+		},
+		"shortIdentifier":
+		{
+			"type": "string",
+			"description": "Two-letters unique indentifier for this road. Used in map format"
+		},
+		"tilesFilename":
+		{
+			"type": "string",
+			"description": "Name of file with river graphics",
+			"format": "defFile"
+		},
+		"delta":
+		{
+			"type": "string",
+			"description": "Name of file with river delta graphics"
+		}
+	}
+}

+ 37 - 0
config/schemas/road.json

@@ -0,0 +1,37 @@
+{
+	"type":"object",
+	"$schema": "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI road format",
+	"description" : "Format used to define new roads in VCMI",
+	"required" : [ "text", "shortIdentifier", "tilesFilename", "moveCost" ],
+
+	"additionalProperties" : false,
+	"properties":{
+		"index" : 
+		{
+			"type": "number",
+			"description": "Internal, do not use"
+		},
+		"text":
+		{
+			"type": "string",
+			"description": "Human-readable name of the road"
+		},
+		"shortIdentifier":
+		{
+			"type": "string",
+			"description": "Two-letters unique indentifier for this road. Used in map format"
+		},
+		"tilesFilename":
+		{
+			"type": "string",
+			"description": "Name of file with road graphics",
+			"format": "defFile"
+		},
+		"moveCost":
+		{
+			"type": "number",
+			"description": "How many movement points needed to move hero"
+		}
+	}
+}

+ 43 - 12
config/schemas/terrain.json

@@ -3,10 +3,20 @@
 	"$schema": "http://json-schema.org/draft-04/schema",
 	"title" : "VCMI terrain format",
 	"description" : "Format used to define new terrains in VCMI",
-	"required" : [ "tiles", "code", "moveCost" ],
+	"required" : [ "text", "moveCost", "minimapUnblocked", "minimapBlocked", "music", "tiles", "type", "horseSound", "horseSoundPenalty", "shortIdentifier", "battleFields" ],
 
 	"additionalProperties" : false,
 	"properties":{
+		"index" :
+		{
+			"type": "number",
+			"description": "Internal, do not use"
+		},
+		"text":
+		{
+			"type": "string",
+			"description": "Human-readable name of this terrain"
+		},
 		"moveCost":
 		{
 			"type": "number",
@@ -47,9 +57,13 @@
 		},
 		"type":
 		{
-			"type": "string",
+			"type": "array",
 			"description": "Type of this terrain. Can be land, water, subterranean or rock",
-			"enum": ["LAND", "WATER", "SUB", "ROCK"]
+			"items":
+			{
+				"enum": ["LAND", "WATER", "SUB", "ROCK", "SURFACE"],
+				"type": "string"
+			}
 		},
 		"rockTerrain":
 		{
@@ -59,23 +73,22 @@
 		"river":
 		{
 			"type": "string",
-			"description": "River type which should be used for that terrain",
-			"enum": ["", "rw", "ri", "rm", "rl"]
+			"description": "River type which should be used for that terrain"
 		},
-		"horseSoundId":
+		"horseSound":
 		{
-			"type": "number",
-			"description": "Id of horse sound to be played when hero is moving across terrain"
+			"type": "string",
+			"description": "Hero movement sound for this terrain, version for moving on tiles with road"
 		},
-		"text":
+		"horseSoundPenalty":
 		{
 			"type": "string",
-			"description": "Text to be shown when mouse if over terrain"
+			"description": "Hero movement sound for this terrain, version for moving on tiles without road"
 		},
-		"code":
+		"shortIdentifier":
 		{
 			"type": "string",
-			"description": "Two-letters unique indentifier for this terrain. Used for terrain serializaion"
+			"description": "Two-letters unique indentifier for this terrain. Used for map format"
 		},
 		"battleFields":
 		{
@@ -86,6 +99,24 @@
 				"type": "string"
 			}
 		},
+		"sounds":
+		{
+			"type": "object",
+			"description": "list of sounds for this terrain",
+			"additionalProperties" : false,
+			"properties":
+			{
+				"ambient" : 
+				{
+					"type": "array",
+					"description": "list of ambient sounds for this terrain",
+					"items":
+					{
+						"type": "string"
+					}
+				}
+			}
+		},
 		"prohibitTransitions":
 		{
 			"type": "array",

+ 57 - 41
config/terrains.json

@@ -1,146 +1,162 @@
 {
 	"dirt" :
 	{
-		"originalTerrainId": 0,
+		"index": 0,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 82, 56, 8 ],
 		"minimapBlocked"   : [ 57, 40, 8 ],
 		"music" : "Dirt.mp3",
 		"tiles" : "DIRTTL",
-		"code" : "dt",
-		"river" : "rm",
+		"type" : ["SURFACE"],
+		"shortIdentifier" : "dt",
+		"river" : "mudRiver",
 		"battleFields" : ["dirt_birches", "dirt_hills", "dirt_pines"],
 		"terrainViewPatterns" : "dirt",
-		"horseSoundId" : 0
+		"horseSound" : "horse00",
+		"horseSoundPenalty" : "horse20"
 	},
 	"sand" :
 	{
-		"originalTerrainId": 1,
+		"index": 1,
 		"moveCost" : 150,
 		"minimapUnblocked" : [ 222, 207, 140 ],
 		"minimapBlocked"   : [ 165, 158, 107 ],
 		"music" : "Sand.mp3",
 		"tiles" : "SANDTL",
-		"code" : "sa",
-		"river" : "rm",
+		"type" : ["SURFACE"],
+		"shortIdentifier" : "sa",
+		"river" : "mudRiver",
 		"battleFields" : ["sand_mesas"],
 		"transitionRequired" : true,
 		"terrainViewPatterns" : "sand",
-		"horseSoundId" : 1
+		"horseSound" : "horse01",
+		"horseSoundPenalty" : "horse21"
 	},
 	"grass" :
 	{
-		"originalTerrainId": 2,
+		"index": 2,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 0, 65, 0 ],
 		"minimapBlocked"   : [ 0, 48, 0 ],
 		"music" : "Grass.mp3",
 		"tiles" : "GRASTL",
-		"code" : "gr",
-		"river" : "rw",
+		"type" : ["SURFACE"],
+		"shortIdentifier" : "gr",
+		"river" : "waterRiver",
 		"battleFields" : ["grass_hills", "grass_pines"],
-		"horseSoundId" : 2
+		"horseSound" : "horse02",
+		"horseSoundPenalty" : "horse22"
 	},
 	"snow" :
 	{
-		"originalTerrainId": 3,
+		"index": 3,
 		"moveCost" : 150,
 		"minimapUnblocked" : [ 181, 199, 198 ],
 		"minimapBlocked"   : [ 140, 158, 156 ],
 		"music" : "Snow.mp3",
 		"tiles" : "SNOWTL",
-		"code" : "sn",
-		"river" : "ri",
+		"type" : ["SURFACE"],
+		"shortIdentifier" : "sn",
+		"river" : "iceRiver",
 		"battleFields" : ["snow_mountains", "snow_trees"],
-		"horseSoundId" : 3
+		"horseSound" : "horse03",
+		"horseSoundPenalty" : "horse23"
 	},
 	"swamp" :
 	{
-		"originalTerrainId": 4,
+		"index": 4,
 		"moveCost" : 175,
 		"minimapUnblocked" : [ 74, 134, 107 ],
 		"minimapBlocked"   : [ 33,  89,  66 ],
 		"music" : "Swamp.mp3",
 		"tiles" : "SWMPTL",
-		"code" : "sw",
-		"river" : "rw",
+		"type" : ["SURFACE"],
+		"shortIdentifier" : "sw",
+		"river" : "waterRiver",
 		"battleFields" : ["swamp_trees"],
-		"horseSoundId" : 4
+		"horseSound" : "horse04",
+		"horseSoundPenalty" : "horse24"
 	},
 	"rough" :
 	{
-		"originalTerrainId": 5,
+		"index": 5,
 		"moveCost" : 125,
 		"minimapUnblocked" : [ 132, 113, 49 ],
 		"minimapBlocked"   : [  99,  81, 33 ],
 		"music" : "Rough.mp3",
 		"tiles" : "ROUGTL",
-		"code" : "rg",
-		"river" : "rm",
+		"type" : ["SURFACE"],
+		"shortIdentifier" : "rg",
+		"river" : "mudRiver",
 		"battleFields" : ["rough"],
-		"horseSoundId" : 5
+		"horseSound" : "horse05",
+		"horseSoundPenalty" : "horse25"
 	},
 	"subterra" :
 	{
-		"originalTerrainId": 6,
+		"index": 6,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 132, 48, 0 ],
 		"minimapBlocked"   : [  90,  8, 0 ],
 		"music" : "Underground.mp3",
 		"tiles" : "SUBBTL",
-		"type" : "SUB",
-		"code" : "sb",
-		"river" : "rw",
+		"type" : [ "SUB" ],
+		"shortIdentifier" : "sb",
+		"river" : "waterRiver",
 		"battleFields" : ["subterranean"],
 		"rockTerrain" : "rock",
-		"horseSoundId" : 6
+		"horseSound" : "horse06",
+		"horseSoundPenalty" : "horse26"
 	},
 	"lava" :
 	{
-		"originalTerrainId": 7,
+		"index": 7,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 74, 73, 74 ],
 		"minimapBlocked"   : [ 41, 40, 41 ],
 		"music" : "Lava.mp3",
 		"tiles" : "LAVATL",
 		"type" : ["SUB", "SURFACE"],
-		"code" : "lv",
-		"river" : "rl",
+		"shortIdentifier" : "lv",
+		"river" : "lavaRiver",
 		"battleFields" : ["lava"],
 		"rockTerrain" : "rock",
-		"horseSoundId" : 7
+		"horseSound" : "horse07",
+		"horseSoundPenalty" : "horse27"
 	},
 	"water" :
 	{
-		"originalTerrainId": 8,
+		"index": 8,
 		"moveCost" : 100,
 		"minimapUnblocked" : [ 8, 81, 148 ],
 		"minimapBlocked"   : [ 8, 81, 148 ],
 		"music" : "Water.mp3",
 		"tiles" : "WATRTL",
-		"type" : "WATER",
-		"code" : "wt",
+		"type" : [ "WATER" ],
+		"shortIdentifier" : "wt",
 		"battleFields" : ["ship"],
 		"transitionRequired" : true,
 		"terrainViewPatterns" : "water",
-		"horseSoundId" : 8,
+		"horseSound" : "horse08",
+		"horseSoundPenalty" : "horse28",
 		"sounds": {
 			"ambient": ["LOOPOCEA"]
 		}
 	},
 	"rock" :
 	{
-		"originalTerrainId": 9,
+		"index": 9,
 		"moveCost" : -1,
 		"minimapUnblocked" : [ 0, 0, 0 ],
 		"minimapBlocked"   : [ 0, 0, 0 ],
 		"music" : "Underground.mp3", // Impossible in H3
 		"tiles" : "ROCKTL",
-		"type" : "ROCK",
-		"code" : "rc",
+		"type" : [ "ROCK" ],
+		"shortIdentifier" : "rc",
 		"battleFields" : ["rocklands"],
 		"transitionRequired" : true,
 		"terrainViewPatterns" : "rock",
-		"horseSoundId" : 9
+		"horseSound" : "horse09",
+		"horseSoundPenalty" : "horse29"
 	}
 }

+ 0 - 1
lib/BattleFieldHandler.h

@@ -14,7 +14,6 @@
 #include "HeroBonus.h"
 #include "GameConstants.h"
 #include "IHandlerBase.h"
-#include "Terrain.h"
 #include "battle/BattleHex.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 2 - 3
lib/CCreatureHandler.cpp

@@ -16,7 +16,6 @@
 #include "CGameState.h"
 #include "CTownHandler.h"
 #include "CModHandler.h"
-#include "Terrain.h"
 #include "StringConstants.h"
 #include "serializer/JsonDeserializer.h"
 #include "serializer/JsonUpdater.h"
@@ -288,7 +287,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 == ETerrainId::ANY_TERRAIN;
 }
 
 TerrainId CCreature::getNativeTerrain() const
@@ -299,7 +298,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(ETerrainId::ANY_TERRAIN)
 		: (*VLC->townh)[faction]->nativeTerrain;
 }
 

+ 0 - 1
lib/CCreatureHandler.h

@@ -19,7 +19,6 @@
 #include "JsonNode.h"
 #include "IHandlerBase.h"
 #include "CRandomGenerator.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 0
lib/CGameInfoCallback.cpp

@@ -17,6 +17,7 @@
 #include "battle/BattleInfo.h" // for BattleInfo
 #include "NetPacks.h" // for InfoWindow
 #include "CModHandler.h"
+#include "TerrainHandler.h"
 #include "spells/CSpellHandler.h"
 #include "mapping/CMap.h"
 #include "CPlayerState.h"

+ 3 - 3
lib/CGameState.cpp

@@ -20,6 +20,7 @@
 #include "CHeroHandler.h"
 #include "mapObjects/CObjectHandler.h"
 #include "CModHandler.h"
+#include "TerrainHandler.h"
 #include "CSkillHandler.h"
 #include "mapping/CMap.h"
 #include "mapping/CMapService.h"
@@ -1944,8 +1945,7 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, CRandomGenerator & r
 	if(map->isCoastalTile(tile)) //coastal tile is always ground
 		return BattleField::fromString("sand_shore");
 	
-	return BattleField::fromString(
-		*RandomGeneratorUtil::nextItem(t.terType->battleFields, rand));
+	return BattleField(*RandomGeneratorUtil::nextItem(t.terType->battleFields, rand));
 }
 
 
@@ -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->getIndex();
 				break;
 			}
 

+ 0 - 3
lib/CGeneralTextHandler.cpp

@@ -18,7 +18,6 @@
 #include "GameConstants.h"
 #include "mapObjects/CQuest.h"
 #include "VCMI_Lib.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -420,7 +419,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" ),
@@ -452,7 +450,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.crgen1",   "DATA/CRGEN1.TXT"   );
 	readToVector("core.crgen4",   "DATA/CRGEN4.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 - 14
lib/CHeroHandler.cpp

@@ -19,7 +19,6 @@
 #include "CCreatureHandler.h"
 #include "CModHandler.h"
 #include "CTownHandler.h"
-#include "Terrain.h"
 #include "mapObjects/CObjectHandler.h" //for hero specialty
 #include "CSkillHandler.h"
 #include <math.h>
@@ -345,11 +344,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 +966,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 - 2
lib/CHeroHandler.h

@@ -18,7 +18,6 @@
 #include "GameConstants.h"
 #include "HeroBonus.h"
 #include "IHandlerBase.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -260,7 +259,6 @@ class DLL_LINKAGE CHeroHandler : public CHandlerBase<HeroTypeID, HeroType, CHero
 
 	void loadExperience();
 	void loadBallistics();
-	void loadTerrains();
 
 public:
 	CHeroClassHandler classes;

+ 6 - 0
lib/CModHandler.cpp

@@ -25,6 +25,9 @@
 #include "spells/CSpellHandler.h"
 #include "CSkillHandler.h"
 #include "ScriptHandler.h"
+#include "RoadHandler.h"
+#include "RiverHandler.h"
+#include "TerrainHandler.h"
 #include "BattleFieldHandler.h"
 #include "ObstacleHandler.h"
 
@@ -465,6 +468,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->getIndex())))));
 	}
 
 	freeShipBoarding = static_cast<bool>(bl->getFirst(Selector::type()(Bonus::FREE_SHIP_BOARDING)));

+ 0 - 1
lib/CPathfinder.h

@@ -13,7 +13,6 @@
 #include "IGameCallback.h"
 #include "HeroBonus.h"
 #include "int3.h"
-#include "Terrain.h"
 
 #include <boost/heap/fibonacci_heap.hpp>
 

+ 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 == ETerrainId::ANY_TERRAIN || nativeTerrain == battle->getTerrainType();
 	return res;
 }
 

+ 0 - 1
lib/CStack.h

@@ -14,7 +14,6 @@
 #include "CCreatureHandler.h" //todo: remove
 #include "battle/BattleHex.h"
 #include "mapObjects/CGHeroInstance.h" // for commander serialization
-#include "Terrain.h"
 
 #include "battle/CUnitState.h"
 

+ 11 - 25
lib/CTownHandler.cpp

@@ -28,10 +28,6 @@ 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 std::map<std::string, CBuilding::EBuildMode> CBuilding::MODES =
 {
 	{ "normal", CBuilding::BUILD_NORMAL },
@@ -951,22 +947,6 @@ void CTownHandler::loadPuzzle(CFaction &faction, const JsonNode &source)
 	assert(faction.puzzleMap.size() == GameConstants::PUZZLE_MAP_PIECES);
 }
 
-TerrainId CTownHandler::getDefaultTerrainForAlignment(EAlignment::EAlignment alignment) const
-{
-	TerrainId terrain = defaultGoodTerrain;
-
-	switch(alignment)
-	{
-	case EAlignment::EAlignment::EVIL:
-		terrain = defaultEvilTerrain;
-		break;
-	case EAlignment::EAlignment::NEUTRAL:
-		terrain = defaultNeutralTerrain;
-		break;
-	}
-	return terrain;
-}
-
 CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode & source, const std::string & identifier, size_t index)
 {
 	auto faction = new CFaction();
@@ -987,11 +967,17 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
 	auto preferUndergound = source["preferUndergroundPlacement"];
 	faction->preferUndergroundPlacement = preferUndergound.isNull() ? false : preferUndergound.Bool();
 
-	//Contructor is not called here, but operator=
-	auto nativeTerrain = source["nativeTerrain"];
-	faction->nativeTerrain = nativeTerrain.isNull()
-		? getDefaultTerrainForAlignment(faction->alignment)
-		: VLC->terrainTypeHandler->getInfoByName(nativeTerrain.String())->id;
+	// NOTE: semi-workaround - normally, towns are supposed to have native terrains.
+	// Towns without one are exceptions. So, vcmi requires nativeTerrain to be defined
+	// But allows it to be defined with explicit value of "none" if town should not have native terrain
+	// This is better than allowing such terrain-less towns silently, leading to issues with RMG
+	faction->nativeTerrain = ETerrainId::NONE;
+	if ( !source["nativeTerrain"].isNull() && source["nativeTerrain"].String() != "none")
+	{
+		VLC->modh->identifiers.requestIdentifier("terrain", source["nativeTerrain"], [=](int32_t index){
+			faction->nativeTerrain = TerrainId(index);
+		});
+	}
 
 	if (!source["town"].isNull())
 	{

+ 0 - 6
lib/CTownHandler.h

@@ -20,7 +20,6 @@
 #include "LogicalExpression.h"
 #include "battle/BattleHex.h"
 #include "HeroBonus.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -364,10 +363,6 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
 	std::vector<BuildingRequirementsHelper> requirementsToLoad;
 	std::vector<BuildingRequirementsHelper> overriddenBidsToLoad; //list of buildings, which bonuses should be overridden.
 
-	const static TerrainId defaultGoodTerrain;
-	const static TerrainId defaultEvilTerrain;
-	const static TerrainId defaultNeutralTerrain;
-
 	static TPropagatorPtr & emptyPropagator();
 
 	void initializeRequirements();
@@ -397,7 +392,6 @@ class DLL_LINKAGE CTownHandler : public CHandlerBase<FactionID, Faction, CFactio
 
 	void loadPuzzle(CFaction & faction, const JsonNode & source);
 
-	TerrainId getDefaultTerrainForAlignment(EAlignment::EAlignment aligment) const;
 	void loadRandomFaction();
 
 

+ 119 - 64
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) {}	\
@@ -204,15 +198,79 @@ public:
 		num += change;
 	}
 
-	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 +282,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,55 +896,26 @@ 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 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
 {
@@ -1182,7 +1219,29 @@ class BattleField : public BaseForID<BattleField, si32>
 
 	DLL_LINKAGE static BattleField fromString(std::string identifier);
 };
-	
+
+enum class ETerrainId {
+	NATIVE_TERRAIN = -4,
+	ANY_TERRAIN = -3,
+	NONE = -1,
+	FIRST_REGULAR_TERRAIN = 0,
+	DIRT = 0,
+	SAND,
+	GRASS,
+	SNOW,
+	SWAMP,
+	ROUGH,
+	SUBTERRANEAN,
+	LAVA,
+	WATER,
+	ROCK,
+	ORIGINAL_REGULAR_TERRAIN_COUNT = ROCK
+};
+
+using TerrainId = Identifier<ETerrainId>;
+using RoadId = Identifier<Road>;
+using RiverId = Identifier<River>;
+
 class ObstacleInfo;
 class Obstacle : public BaseForID<Obstacle, si32>
 {
@@ -1240,9 +1299,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;
 
@@ -1250,6 +1306,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

+ 6 - 5
lib/HeroBonus.cpp

@@ -22,6 +22,7 @@
 #include "CStack.h"
 #include "CArtHandler.h"
 #include "CModHandler.h"
+#include "TerrainHandler.h"
 #include "StringConstants.h"
 #include "battle/BattleInfo.h"
 
@@ -2106,7 +2107,7 @@ bool CPropagatorNodeType::shouldBeAttached(CBonusSystemNode *dest)
 }
 
 CreatureTerrainLimiter::CreatureTerrainLimiter()
-	: terrainType(Terrain::NATIVE_TERRAIN)
+	: terrainType(ETerrainId::NATIVE_TERRAIN)
 {
 }
 
@@ -2120,7 +2121,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 == ETerrainId::NATIVE_TERRAIN)//terrainType not specified = native
 		{
 			return !stack->isOnNativeTerrain();
 		}
@@ -2136,8 +2137,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)->getJsonKey();
+	fmt % (terrainType == ETerrainId::NATIVE_TERRAIN ? "native" : terrainName);
 	return fmt.str();
 }
 
@@ -2146,7 +2147,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)->getJsonKey();
 	root["parameters"].Vector().push_back(JsonUtils::stringNode(terrainName));
 
 	return root;

+ 0 - 1
lib/HeroBonus.h

@@ -11,7 +11,6 @@
 
 #include "GameConstants.h"
 #include "JsonNode.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 3 - 0
lib/IGameCallback.cpp

@@ -32,6 +32,9 @@
 #include "CPlayerState.h"
 #include "CSkillHandler.h"
 #include "ScriptHandler.h"
+#include "RoadHandler.h"
+#include "RiverHandler.h"
+#include "TerrainHandler.h"
 
 #include "serializer/Connection.h"
 

+ 5 - 4
lib/NetPacksLib.cpp

@@ -27,6 +27,7 @@
 #include "mapping/CMapInfo.h"
 #include "StartInfo.h"
 #include "CPlayerState.h"
+#include "TerrainHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -720,13 +721,13 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
 
 DLL_LINKAGE void NewObject::applyGs(CGameState *gs)
 {
-	TerrainId terrainType = Terrain::BORDER;
+	TerrainId terrainType = ETerrainId::NONE;
 
 	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(ETerrainId::WATER).front();
 
 		const int3 previousXAxisTile = int3(pos.x - 1, pos.y, pos.z);
 		assert(gs->isInTheMap(previousXAxisTile) && (testObject.visitablePos() == previousXAxisTile));
@@ -735,7 +736,7 @@ DLL_LINKAGE void NewObject::applyGs(CGameState *gs)
 	else
 	{
 		const TerrainTile & t = gs->map->getTile(pos);
-		terrainType = t.terType->id;
+		terrainType = t.terType->getId();
 	}
 
 	CGObjectInstance *o = nullptr;
@@ -743,7 +744,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 = 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();

+ 0 - 1
lib/ObstacleHandler.h

@@ -13,7 +13,6 @@
 #include <vcmi/Entity.h>
 #include "GameConstants.h"
 #include "IHandlerBase.h"
-#include "Terrain.h"
 #include "battle/BattleHex.h"
 
 VCMI_LIB_NAMESPACE_BEGIN

+ 2 - 0
lib/PathfinderUtil.h

@@ -9,6 +9,8 @@
  */
 #pragma once
 
+#include "TerrainHandler.h"
+#include "mapObjects/CObjectHandler.h"
 #include "mapping/CMapDefines.h"
 #include "CGameState.h"
 

+ 78 - 0
lib/RiverHandler.cpp

@@ -0,0 +1,78 @@
+/*
+ * Terrain.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "RiverHandler.h"
+#include "CModHandler.h"
+#include "CGeneralTextHandler.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+RiverTypeHandler::RiverTypeHandler()
+{
+	objects.push_back(new RiverType);
+}
+
+RiverType * RiverTypeHandler::loadFromJson(
+	const std::string & scope,
+	const JsonNode & json,
+	const std::string & identifier,
+	size_t index)
+{
+	RiverType * info = new RiverType;
+
+	info->id              = RiverId(index);
+	if (identifier.find(':') == std::string::npos)
+		info->identifier = scope + ":" + identifier;
+	else
+		info->identifier = identifier;
+
+	info->tilesFilename   = json["tilesFilename"].String();
+	info->shortIdentifier = json["shortIdentifier"].String();
+	info->deltaName       = json["delta"].String();
+
+	VLC->generaltexth->registerString(info->getNameTextID(), json["text"].String());
+
+	return info;
+}
+
+const std::vector<std::string> & RiverTypeHandler::getTypeNames() const
+{
+	static const std::vector<std::string> typeNames = { "river" };
+	return typeNames;
+}
+
+std::vector<JsonNode> RiverTypeHandler::loadLegacyData(size_t dataSize)
+{
+	objects.resize(dataSize);
+	return {};
+}
+
+std::vector<bool> RiverTypeHandler::getDefaultAllowed() const
+{
+	return {};
+}
+
+std::string RiverType::getNameTextID() const
+{
+	return TextIdentifier( "river", identifier,  "name" ).get();
+}
+
+std::string RiverType::getNameTranslated() const
+{
+	return VLC->generaltexth->translate(getNameTextID());
+}
+
+RiverType::RiverType():
+	id(River::NO_RIVER),
+	identifier("core:empty")
+{}
+
+VCMI_LIB_NAMESPACE_END

+ 79 - 0
lib/RiverHandler.h

@@ -0,0 +1,79 @@
+/*
+ * TerrainHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include <vcmi/EntityService.h>
+#include <vcmi/Entity.h>
+#include "GameConstants.h"
+#include "IHandlerBase.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE RiverType : public EntityT<RiverId>
+{
+	friend class RiverTypeHandler;
+	std::string identifier;
+	RiverId id;
+
+	const std::string & getName() const override { return identifier;}
+public:
+	int32_t getIndex() const override { return id.getNum(); }
+	int32_t getIconIndex() const override { return 0; }
+	const std::string & getJsonKey() const override { return identifier;}
+	void registerIcons(const IconRegistar & cb) const override {}
+	RiverId getId() const override { return id;}
+	void updateFrom(const JsonNode & data) {};
+
+	std::string getNameTextID() const;
+	std::string getNameTranslated() const;
+
+	std::string tilesFilename;
+	std::string shortIdentifier;
+	std::string deltaName;
+
+	RiverType();
+
+	template <typename Handler> void serialize(Handler& h, const int version)
+	{
+		h & tilesFilename;
+		h & identifier;
+		h & deltaName;
+		h & id;
+	}
+};
+
+class DLL_LINKAGE RiverTypeService : public EntityServiceT<RiverId, RiverType>
+{
+public:
+};
+
+class DLL_LINKAGE RiverTypeHandler : public CHandlerBase<RiverId, RiverType, RiverType, RiverTypeService>
+{
+public:
+	virtual RiverType * loadFromJson(
+		const std::string & scope,
+		const JsonNode & json,
+		const std::string & identifier,
+		size_t index) override;
+
+	RiverTypeHandler();
+
+	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;
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & objects;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 78 - 0
lib/RoadHandler.cpp

@@ -0,0 +1,78 @@
+/*
+ * Terrain.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "RoadHandler.h"
+#include "CModHandler.h"
+#include "CGeneralTextHandler.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+RoadTypeHandler::RoadTypeHandler()
+{
+	objects.push_back(new RoadType);
+}
+
+RoadType * RoadTypeHandler::loadFromJson(
+	const std::string & scope,
+	const JsonNode & json,
+	const std::string & identifier,
+	size_t index)
+{
+	RoadType * info = new RoadType;
+
+	info->id              = RoadId(index);
+	if (identifier.find(':') == std::string::npos)
+		info->identifier = scope + ":" + identifier;
+	else
+		info->identifier = identifier;
+
+	info->tilesFilename   = json["tilesFilename"].String();
+	info->shortIdentifier = json["shortIdentifier"].String();
+	info->movementCost    = json["moveCost"].Integer();
+
+	VLC->generaltexth->registerString(info->getNameTextID(), json["text"].String());
+
+	return info;
+}
+
+const std::vector<std::string> & RoadTypeHandler::getTypeNames() const
+{
+	static const std::vector<std::string> typeNames = { "road" };
+	return typeNames;
+}
+
+std::vector<JsonNode> RoadTypeHandler::loadLegacyData(size_t dataSize)
+{
+	objects.resize(dataSize);
+	return {};
+}
+
+std::vector<bool> RoadTypeHandler::getDefaultAllowed() const
+{
+	return {};
+}
+
+std::string RoadType::getNameTextID() const
+{
+	return TextIdentifier( "road", identifier,  "name" ).get();
+}
+
+std::string RoadType::getNameTranslated() const
+{
+	return VLC->generaltexth->translate(getNameTextID());
+}
+
+RoadType::RoadType():
+	id(Road::NO_ROAD),
+	identifier("core:empty"),
+	movementCost(GameConstants::BASE_MOVEMENT_COST)
+{}
+VCMI_LIB_NAMESPACE_END

+ 79 - 0
lib/RoadHandler.h

@@ -0,0 +1,79 @@
+/*
+ * TerrainHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include <vcmi/EntityService.h>
+#include <vcmi/Entity.h>
+#include "GameConstants.h"
+#include "IHandlerBase.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE RoadType : public EntityT<RoadId>
+{
+	friend class RoadTypeHandler;
+	std::string identifier;
+	RoadId id;
+
+	const std::string & getName() const override { return identifier;}
+public:
+	int32_t getIndex() const override { return id.getNum(); }
+	int32_t getIconIndex() const override { return 0; }
+	const std::string & getJsonKey() const override { return identifier;}
+	void registerIcons(const IconRegistar & cb) const override {}
+	RoadId getId() const override { return id;}
+	void updateFrom(const JsonNode & data) {};
+
+	std::string getNameTextID() const;
+	std::string getNameTranslated() const;
+
+	std::string tilesFilename;
+	std::string shortIdentifier;
+	ui8 movementCost;
+
+	RoadType();
+
+	template <typename Handler> void serialize(Handler& h, const int version)
+	{
+		h & tilesFilename;
+		h & identifier;
+		h & id;
+		h & movementCost;
+	}
+};
+
+class DLL_LINKAGE RoadTypeService : public EntityServiceT<RoadId, RoadType>
+{
+public:
+};
+
+class DLL_LINKAGE RoadTypeHandler : public CHandlerBase<RoadId, RoadType, RoadType, RoadTypeService>
+{
+public:
+	virtual RoadType * loadFromJson(
+		const std::string & scope,
+		const JsonNode & json,
+		const std::string & identifier,
+		size_t index) override;
+
+	RoadTypeHandler();
+
+	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;
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & objects;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 0 - 473
lib/Terrain.cpp

@@ -1,473 +0,0 @@
-/*
- * Terrain.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-#include "StdInc.h"
-#include "Terrain.h"
-#include "VCMI_Lib.h"
-#include "CModHandler.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-//regular expression to change id for string at config
-//("allowedTerrain"\s*:\s*\[.*)9(.*\],\n)
-//\1"rock"\2
-
-TerrainTypeHandler::TerrainTypeHandler()
-{
-	auto allConfigs = VLC->modh->getActiveMods();
-	allConfigs.insert(allConfigs.begin(), CModHandler::scopeBuiltin());
-
-	initRivers(allConfigs);
-	recreateRiverMaps();
-	initRoads(allConfigs);
-	recreateRoadMaps();
-	initTerrains(allConfigs); //maps will be populated inside
-}
-
-void TerrainTypeHandler::initTerrains(const std::vector<std::string> & allConfigs)
-{
-	std::vector<std::function<void()>> resolveLater;
-
-	objects.resize(Terrain::ORIGINAL_TERRAIN_COUNT); //make space for original terrains
-
-	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;
-				});
-			}
-		}
-	}
-
-	for(size_t i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ORIGINAL_TERRAIN_COUNT; i++)
-	{
-		//Make sure that original terrains are loaded
-		assert(objects[i].id != Terrain::WRONG);
-	}
-
-	recreateTerrainMaps();
-
-	for(auto& functor : resolveLater)
-	{
-		functor();
-	}
-}
-
-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
-
-	for(auto & mod : allConfigs)
-	{
-		if (!CResourceHandler::get(mod)->existsResource(ResourceID("config/rivers.json")))
-			continue;
-
-		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);
-			}
-		}
-	}
-
-	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
-
-	for(auto & mod : allConfigs)
-	{
-		if (!CResourceHandler::get(mod)->existsResource(ResourceID("config/roads.json")))
-			continue;
-
-		JsonNode rds(mod, ResourceID("config/roads.json"));
-		for(auto & road : rds.Struct())
-		{
-			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);
-			}
-		}
-	}
-
-	recreateRoadMaps();
-}
-
-void TerrainTypeHandler::recreateTerrainMaps()
-{
-	//This assumes the vector will never be updated or reallocated in the future
-
-	for(size_t i = 0; i < objects.size(); i++)
-	{
-		const auto * terrainInfo = &objects[i];
-
-		terrainInfoByName[terrainInfo->name] = terrainInfo;
-		terrainInfoByCode[terrainInfo->typeCode] = terrainInfo;
-		terrainInfoById[terrainInfo->id] = terrainInfo;
-	}
-}
-
-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;
-	}
-}
-
-void TerrainTypeHandler::recreateRoadMaps()
-{
-	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;
-	}
-}
-
-const std::vector<TerrainType> & TerrainTypeHandler::terrains() const
-{
-	//FIXME: somehow make it non-copyable? Pointers must point to original data and not its copy
-	return objects;
-}
-
-const std::vector<RiverType>& TerrainTypeHandler::rivers() const
-{
-	return riverTypes;
-}
-
-const std::vector<RoadType>& TerrainTypeHandler::roads() const
-{
-	return roadTypes;
-}
-
-const TerrainType* TerrainTypeHandler::getInfoByName(const std::string& terrainName) const
-{
-	return terrainInfoByName.at(terrainName);
-}
-
-const TerrainType* TerrainTypeHandler::getInfoByCode(const std::string& terrainCode) const
-{
-	return terrainInfoByCode.at(terrainCode);
-}
-
-const TerrainType* TerrainTypeHandler::getInfoById(TerrainId id) const
-{
-	return terrainInfoById.at(id);
-}
-
-const RiverType* TerrainTypeHandler::getRiverByName(const std::string& riverName) const
-{
-	return riverInfoByName.at(riverName);
-}
-
-const RiverType* TerrainTypeHandler::getRiverByCode(const std::string& riverCode) const
-{
-	return riverInfoByCode.at(riverCode);
-}
-
-const RiverType* TerrainTypeHandler::getRiverById(RiverId id) const
-{
-	return riverInfoById.at(id);
-}
-
-const RoadType* TerrainTypeHandler::getRoadByName(const std::string& roadName) const
-{
-	return roadInfoByName.at(roadName);
-}
-
-const RoadType* TerrainTypeHandler::getRoadByCode(const std::string& roadCode) const
-{
-	return roadInfoByCode.at(roadCode);
-}
-
-const RoadType* TerrainTypeHandler::getRoadById(RoadId id) const
-{
-	return roadInfoById.at(id);
-}
-
-std::ostream & operator<<(std::ostream & os, const TerrainType & terrainType)
-{
-	return os << static_cast<const std::string &>(terrainType);
-}
-	
-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;
-}
-
-bool TerrainType::operator!=(const TerrainType& other)
-{
-	return id != other.id;
-}
-
-bool TerrainType::operator<(const TerrainType& other)
-{
-	return id < other.id;
-}
-	
-bool TerrainType::isLand() const
-{
-	return !isWater();
-}
-
-bool TerrainType::isWater() const
-{
-	return passabilityType & PassabilityType::WATER;
-}
-
-bool TerrainType::isPassable() const
-{
-	return !(passabilityType & PassabilityType::ROCK);
-}
-
-bool TerrainType::isSurface() const
-{
-	return passabilityType & PassabilityType::SURFACE;
-}
-
-bool TerrainType::isUnderground() const
-{
-	return passabilityType & PassabilityType::SUBTERRANEAN;
-}
-
-bool TerrainType::isSurfaceCartographerCompatible() const
-{
-	return isSurface();
-}
-
-bool TerrainType::isUndergroundCartographerCompatible() const
-{
-	return isLand() && isPassable() && !isSurface();
-}
-
-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

+ 0 - 204
lib/Terrain.h

@@ -1,204 +0,0 @@
-/*
- * Terrain.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-
-#pragma once
-
-#include "ConstTransitivePtr.h"
-#include "GameConstants.h"
-#include "JsonNode.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-class DLL_LINKAGE TerrainType
-{
-public:
-	
-	enum PassabilityType : ui8
-	{
-		LAND = 1,
-		WATER = 2,
-		SURFACE = 4,
-		SUBTERRANEAN = 8,
-		ROCK = 16
-	};
-	
-	std::vector<std::string> battleFields;
-	std::vector<TerrainId> prohibitTransitions;
-	std::array<int, 3> minimapBlocked;
-	std::array<int, 3> minimapUnblocked;
-	std::string name;
-	std::string musicFilename;
-	std::string tilesFilename;
-	std::string terrainText;
-	std::string typeCode;
-	std::string terrainViewPatterns;
-	RiverId river;
-
-	TerrainId id;
-	TerrainId rockTerrain;
-	int moveCost;
-	int horseSoundId;
-	ui8 passabilityType;
-	bool transitionRequired;
-	
-	TerrainType(const std::string & name = "");
-	
-	bool operator==(const TerrainType & other);
-	bool operator!=(const TerrainType & other);
-	bool operator<(const TerrainType & other);
-	
-	bool isLand() const;
-	bool isWater() const;
-	bool isPassable() const;
-	bool isSurface() const;
-	bool isUnderground() const;
-	bool isTransitionRequired() const;
-	bool isSurfaceCartographerCompatible() const;
-	bool isUndergroundCartographerCompatible() const;
-		
-	operator std::string() const;
-	
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & battleFields;
-		h & prohibitTransitions;
-		h & minimapBlocked;
-		h & minimapUnblocked;
-		h & name;
-		h & musicFilename;
-		h & tilesFilename;
-		h & terrainText;
-		h & typeCode;
-		h & terrainViewPatterns;
-		h & rockTerrain;
-		h & river;
-
-		h & id;
-		h & moveCost;
-		h & horseSoundId;
-		h & passabilityType;
-		h & transitionRequired;
-	}
-};
-
-class DLL_LINKAGE RiverType
-{
-public:
-	std::string name;
-	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);
-
-	template <typename Handler> void serialize(Handler& h, const int version)
-	{
-		if(version >= 806)
-		{
-			h & name;
-		}
-		h & fileName;
-		h & code;
-		h & deltaName;
-		h & id;
-	}
-};
-
-class DLL_LINKAGE RoadType
-{
-public:
-	std::string name;
-	std::string fileName;
-	std::string code;
-	RoadId id;
-	ui8 movementCost;
-
-	RoadType(const std::string & fileName = "", const std::string& code = "", RoadId id = Road::NO_ROAD);
-
-	template <typename Handler> void serialize(Handler& h, const int version)
-	{
-		if(version >= 806)
-		{
-			h & name;
-		}
-		h & fileName;
-		h & code;
-		h & id;
-		h & movementCost;
-	}
-};
-
-DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const TerrainType & terrainType);
-
-class DLL_LINKAGE TerrainTypeHandler //TODO: public IHandlerBase ?
-{
-public:
-
-	TerrainTypeHandler();
-	~TerrainTypeHandler() {};
-
-	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;
-
-	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;
-
-	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;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & objects;
-		h & riverTypes;
-		h & roadTypes;
-
-		if (!h.saving)
-		{
-			recreateTerrainMaps();
-			recreateRiverMaps();
-			recreateRoadMaps();
-		}
-	}
-
-private:
-
-	std::vector<TerrainType> objects;
-	std::vector<RiverType> riverTypes;
-	std::vector<RoadType> roadTypes;
-
-	std::unordered_map<std::string, const TerrainType*> terrainInfoByName;
-	std::unordered_map<std::string, const TerrainType*> terrainInfoByCode;
-	std::unordered_map<TerrainId, const TerrainType*> terrainInfoById;
-
-	std::unordered_map<std::string, const RiverType*> riverInfoByName;
-	std::unordered_map<std::string, const RiverType*> riverInfoByCode;
-	std::unordered_map<RiverId, const RiverType*> riverInfoById;
-
-	std::unordered_map<std::string, const RoadType*> roadInfoByName;
-	std::unordered_map<std::string, const RoadType*> roadInfoByCode;
-	std::unordered_map<RoadId, const RoadType*> roadInfoById;
-
-	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();
-
-};
-
-VCMI_LIB_NAMESPACE_END

+ 191 - 0
lib/TerrainHandler.cpp

@@ -0,0 +1,191 @@
+/*
+ * Terrain.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "TerrainHandler.h"
+#include "CModHandler.h"
+#include "CGeneralTextHandler.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+TerrainType * TerrainTypeHandler::loadFromJson( const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index)
+{
+	TerrainType * info = new TerrainType;
+
+	info->id = TerrainId(index);
+
+	if (identifier.find(':') == std::string::npos)
+		info->identifier = scope + ":" + identifier;
+	else
+		info->identifier = identifier;
+
+	info->moveCost = static_cast<int>(json["moveCost"].Integer());
+	info->musicFilename = json["music"].String();
+	info->tilesFilename = json["tiles"].String();
+	info->horseSound = json["horseSound"].String();
+	info->horseSoundPenalty = json["horseSoundPenalty"].String();
+	info->transitionRequired = json["transitionRequired"].Bool();
+	info->terrainViewPatterns = json["terrainViewPatterns"].String();
+
+	VLC->generaltexth->registerString(info->getNameTextID(), json["text"].String());
+
+	const JsonVector & unblockedVec = json["minimapUnblocked"].Vector();
+	info->minimapUnblocked =
+	{
+		ui8(unblockedVec[0].Float()),
+		ui8(unblockedVec[1].Float()),
+		ui8(unblockedVec[2].Float())
+	};
+
+	const JsonVector &blockedVec = json["minimapBlocked"].Vector();
+	info->minimapBlocked =
+	{
+		ui8(blockedVec[0].Float()),
+		ui8(blockedVec[1].Float()),
+		ui8(blockedVec[2].Float())
+	};
+
+	info->passabilityType = 0;
+
+	for(const auto& node : json["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;
+	}
+
+	info->river = River::NO_RIVER;
+	if(!json["river"].isNull())
+	{
+		VLC->modh->identifiers.requestIdentifier("river", json["river"], [info](int32_t identifier)
+		{
+			info->river = RiverId(identifier);
+		});
+	}
+
+	info->shortIdentifier = json["shortIdentifier"].String();
+	assert(info->shortIdentifier.length() == 2);
+
+	for(auto & t : json["battleFields"].Vector())
+	{
+		VLC->modh->identifiers.requestIdentifier("battlefield", t, [info](int32_t identifier)
+		{
+			info->battleFields.emplace_back(identifier);
+		});
+	}
+
+	for(auto & t : json["prohibitTransitions"].Vector())
+	{
+		VLC->modh->identifiers.requestIdentifier("terrain", t, [info](int32_t identifier)
+		{
+			info->prohibitTransitions.emplace_back(identifier);
+		});
+	}
+
+	info->rockTerrain = ETerrainId::ROCK;
+
+	if(!json["rockTerrain"].isNull())
+	{
+		VLC->modh->identifiers.requestIdentifier("terrain", json["rockTerrain"], [info](int32_t identifier)
+		{
+			info->rockTerrain = TerrainId(identifier);
+		});
+	}
+
+	return info;
+}
+
+const std::vector<std::string> & TerrainTypeHandler::getTypeNames() const
+{
+	static const std::vector<std::string> typeNames = { "terrain" };
+	return typeNames;
+}
+
+std::vector<JsonNode> TerrainTypeHandler::loadLegacyData(size_t dataSize)
+{
+	objects.resize(dataSize);
+
+	CLegacyConfigParser terrainParser("DATA/TERRNAME.TXT");
+
+	std::vector<JsonNode> result;
+	do
+	{
+		JsonNode terrain;
+		terrain["text"].String() = terrainParser.readString();
+		result.push_back(terrain);
+	}
+	while (terrainParser.endLine());
+
+	return result;
+}
+
+std::vector<bool> TerrainTypeHandler::getDefaultAllowed() const
+{
+	return {};
+}
+
+bool TerrainType::isLand() const
+{
+	return !isWater();
+}
+
+bool TerrainType::isWater() const
+{
+	return passabilityType & PassabilityType::WATER;
+}
+
+bool TerrainType::isPassable() const
+{
+	return !(passabilityType & PassabilityType::ROCK);
+}
+
+bool TerrainType::isSurface() const
+{
+	return passabilityType & PassabilityType::SURFACE;
+}
+
+bool TerrainType::isUnderground() const
+{
+	return passabilityType & PassabilityType::SUBTERRANEAN;
+}
+
+bool TerrainType::isSurfaceCartographerCompatible() const
+{
+	return isSurface();
+}
+
+bool TerrainType::isUndergroundCartographerCompatible() const
+{
+	return isLand() && isPassable() && !isSurface();
+}
+
+bool TerrainType::isTransitionRequired() const
+{
+	return transitionRequired;
+}
+
+std::string TerrainType::getNameTextID() const
+{
+	return TextIdentifier( "terrain", identifier,  "name" ).get();
+}
+
+std::string TerrainType::getNameTranslated() const
+{
+	return VLC->generaltexth->translate(getNameTextID());
+}
+
+TerrainType::TerrainType()
+{}
+
+VCMI_LIB_NAMESPACE_END

+ 122 - 0
lib/TerrainHandler.h

@@ -0,0 +1,122 @@
+/*
+ * TerrainHandler.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+#include <vcmi/EntityService.h>
+#include <vcmi/Entity.h>
+#include "GameConstants.h"
+#include "IHandlerBase.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE TerrainType : public EntityT<TerrainId>
+{
+	friend class TerrainTypeHandler;
+	std::string identifier;
+	TerrainId id;
+	ui8 passabilityType;
+
+	const std::string & getName() const override { return identifier;}
+public:
+	int32_t getIndex() const override { return id.getNum(); }
+	int32_t getIconIndex() const override { return 0; }
+	const std::string & getJsonKey() const override { return identifier;}
+	void registerIcons(const IconRegistar & cb) const override {}
+	TerrainId getId() const override { return id;}
+	void updateFrom(const JsonNode & data) {};
+
+	std::string getNameTextID() const;
+	std::string getNameTranslated() const;
+
+	enum PassabilityType : ui8
+	{
+		LAND = 1,
+		WATER = 2,
+		SURFACE = 4,
+		SUBTERRANEAN = 8,
+		ROCK = 16
+	};
+	
+	std::vector<BattleField> battleFields;
+	std::vector<TerrainId> prohibitTransitions;
+	std::array<int, 3> minimapBlocked;
+	std::array<int, 3> minimapUnblocked;
+	std::string shortIdentifier;
+	std::string musicFilename;
+	std::string tilesFilename;
+	std::string terrainViewPatterns;
+	std::string horseSound;
+	std::string horseSoundPenalty;
+
+	TerrainId rockTerrain;
+	RiverId river;
+	int moveCost;
+	bool transitionRequired;
+	
+	TerrainType();
+	
+	bool isLand() const;
+	bool isWater() const;
+	bool isPassable() const;
+	bool isSurface() const;
+	bool isUnderground() const;
+	bool isTransitionRequired() const;
+	bool isSurfaceCartographerCompatible() const;
+	bool isUndergroundCartographerCompatible() const;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & battleFields;
+		h & prohibitTransitions;
+		h & minimapBlocked;
+		h & minimapUnblocked;
+		h & identifier;
+		h & musicFilename;
+		h & tilesFilename;
+		h & shortIdentifier;
+		h & terrainViewPatterns;
+		h & rockTerrain;
+		h & river;
+
+		h & id;
+		h & moveCost;
+		h & horseSound;
+		h & horseSoundPenalty;
+		h & passabilityType;
+		h & transitionRequired;
+	}
+};
+
+class DLL_LINKAGE TerrainTypeService : public EntityServiceT<TerrainId, TerrainType>
+{
+public:
+};
+
+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;
+
+	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;
+
+	template <typename Handler> void serialize(Handler & h, const int version)
+	{
+		h & objects;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 5 - 0
lib/VCMI_Lib.cpp

@@ -18,6 +18,9 @@
 #include "CHeroHandler.h"
 #include "mapObjects/CObjectHandler.h"
 #include "CTownHandler.h"
+#include "RoadHandler.h"
+#include "RiverHandler.h"
+#include "TerrainHandler.h"
 #include "CBuildingHandler.h"
 #include "spells/CSpellHandler.h"
 #include "spells/effects/Registry.h"
@@ -197,6 +200,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)

+ 0 - 1
lib/battle/BattleInfo.cpp

@@ -15,7 +15,6 @@
 #include "../filesystem/Filesystem.h"
 #include "../mapObjects/CGTownInstance.h"
 #include "../CGeneralTextHandler.h"
-#include "../Terrain.h"
 #include "../BattleFieldHandler.h"
 #include "../ObstacleHandler.h"
 

+ 0 - 1
lib/battle/BattleProxy.cpp

@@ -10,7 +10,6 @@
 #include "StdInc.h"
 #include "BattleProxy.h"
 #include "Unit.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 14 - 11
lib/mapObjects/CGHeroInstance.cpp

@@ -17,6 +17,8 @@
 #include "../NetPacks.h"
 #include "../CGeneralTextHandler.h"
 #include "../CHeroHandler.h"
+#include "../TerrainHandler.h"
+#include "../RoadHandler.h"
 #include "../CModHandler.h"
 #include "../CSoundBase.h"
 #include "../spells/CSpellHandler.h"
@@ -81,16 +83,16 @@ 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->getId() != Road::NO_ROAD && from.roadType->getId() != 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 != Terrain::ANY_TERRAIN && //no special creature bonus
-			!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->id)) //no special movement bonus
+	else if(ti->nativeTerrain != from.terType->getId() &&//the terrain is not native
+			ti->nativeTerrain != ETerrainId::ANY_TERRAIN && //no special creature bonus
+			!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->getIndex())) //no special movement bonus
 	{
 
-		ret = VLC->heroh->terrCosts[from.terType->id];
+		ret = VLC->heroh->terrCosts[from.terType->getId()];
 		ret -= ti->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING);
 		if(ret < GameConstants::BASE_MOVEMENT_COST)
 			ret = GameConstants::BASE_MOVEMENT_COST;
@@ -104,20 +106,21 @@ TerrainId CGHeroInstance::getNativeTerrain() const
 	// This is clearly bug in H3 however intended behaviour is not clear.
 	// Current VCMI behaviour will ignore neutrals in calculations so army in VCMI
 	// will always have best penalty without any influence from player-defined stacks order
+	// and army that consist solely from neutral will always be considered to be on native terrain
 
-	// TODO: What should we do if all hero stacks are neutral creatures?
-	TerrainId nativeTerrain = Terrain::BORDER;
+	TerrainId nativeTerrain = ETerrainId::ANY_TERRAIN;
 
 	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 == ETerrainId::NONE)
 			continue;
-		if(nativeTerrain == Terrain::BORDER)
+
+		if(nativeTerrain == ETerrainId::ANY_TERRAIN)
 			nativeTerrain = stackNativeTerrain;
 		else if(nativeTerrain != stackNativeTerrain)
-			return Terrain::BORDER;
+			return ETerrainId::NONE;
 	}
 	return nativeTerrain;
 }
@@ -519,7 +522,7 @@ void CGHeroInstance::initObj(CRandomGenerator & rand)
 
 	if (ID != Obj::PRISON)
 	{
-		auto terrain = cb->gameState()->getTile(visitablePos())->terType->id;
+		auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId();
 		auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(terrain, this);
 		if (customApp)
 			appearance = customApp;

+ 2 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -20,6 +20,7 @@
 #include "../CGameState.h"
 #include "../mapping/CMap.h"
 #include "../CPlayerState.h"
+#include "../TerrainHandler.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../HeroBonus.h"
 
@@ -1132,7 +1133,7 @@ void CGTownInstance::setType(si32 ID, si32 subID)
 
 void CGTownInstance::updateAppearance()
 {
-	auto terrain = cb->gameState()->getTile(visitablePos())->terType->id;
+	auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId();
 	//FIXME: not the best way to do this
 	auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(terrain, this);
 	if (app)

+ 0 - 1
lib/mapObjects/CObjectClassesHandler.h

@@ -15,7 +15,6 @@
 #include "../ConstTransitivePtr.h"
 #include "../IHandlerBase.h"
 #include "../JsonNode.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 4 - 3
lib/mapObjects/CObjectHandler.cpp

@@ -20,6 +20,7 @@
 #include "../CGameState.h"
 #include "../StringConstants.h"
 #include "../mapping/CMap.h"
+#include "../TerrainHandler.h"
 
 #include "CObjectClassesHandler.h"
 #include "CGTownInstance.h"
@@ -207,13 +208,13 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
 		logGlobal->error("Unknown object type %d:%d at %s", ID, subID, visitablePos().toString());
 		return;
 	}
-	if(!handler->getTemplates(tile.terType->id).empty())
+	if(!handler->getTemplates(tile.terType->getId()).empty())
 	{
-		appearance = handler->getTemplates(tile.terType->id)[0];
+		appearance = handler->getTemplates(tile.terType->getId())[0];
 	}
 	else
 	{
-		logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->name);
+		logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->getNameTranslated());
 		appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
 	}
 

+ 2 - 1
lib/mapObjects/CommonConstructors.cpp

@@ -13,6 +13,7 @@
 #include "CGTownInstance.h"
 #include "CGHeroInstance.h"
 #include "CBank.h"
+#include "../TerrainHandler.h"
 #include "../mapping/CMap.h"
 #include "../CHeroHandler.h"
 #include "../CCreatureHandler.h"
@@ -84,7 +85,7 @@ CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptr<const Object
 
 void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
 {
-	auto templ = getOverride(object->cb->getTile(object->pos)->terType->id, object);
+	auto templ = getOverride(object->cb->getTile(object->pos)->terType->getId(), object);
 	if(templ)
 		object->appearance = templ;
 }

+ 22 - 42
lib/mapObjects/ObjectTemplate.cpp

@@ -19,7 +19,7 @@
 #include "CObjectHandler.h"
 #include "../CModHandler.h"
 #include "../JsonNode.h"
-#include "../Terrain.h"
+#include "../TerrainHandler.h"
 
 #include "CRewardableConstructor.h"
 
@@ -157,22 +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() == Terrain::ROCK); // all terrains but rock - counting from 0
-	for(TerrainId i = Terrain::FIRST_REGULAR_TERRAIN; i < Terrain::ROCK; i++)
+	assert(terrStr.size() == TerrainId(ETerrainId::ROCK).getNum()); // all terrains but rock - counting from 0
+	for(TerrainId i = TerrainId(0); i < ETerrainId::ORIGINAL_REGULAR_TERRAIN_COUNT; ++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(Terrain::WATER))
-	{
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
-		{
-			if(terrain.isLand() && terrain.isPassable())
-				allowedTerrains.insert(terrain.id);
-		}
-	}
+	anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER);
 
 	id    = Obj(boost::lexical_cast<int>(strings[5]));
 	subid = boost::lexical_cast<int>(strings[6]);
@@ -231,21 +224,14 @@ 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 = ETerrainId::FIRST_REGULAR_TERRAIN; i < ETerrainId::ORIGINAL_REGULAR_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(Terrain::WATER))
-	{
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
-		{
-			if(terrain.isLand() && terrain.isPassable())
-				allowedTerrains.insert(terrain.id);
-		}
-	}
+	anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER);
 
 	id = Obj(reader.readUInt32());
 	subid = reader.readUInt32();
@@ -288,28 +274,17 @@ 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));
+			});
 		}
+		anyTerrain = false;
 	}
 	else
 	{
-		for(const auto & terrain : VLC->terrainTypeHandler->terrains())
-		{
-			if(!terrain.isPassable() || terrain.isWater())
-				continue;
-			allowedTerrains.insert(terrain.id);
-		}
+		anyTerrain = true;
 	}
 
-	if(withTerrain && allowedTerrains.empty())
-		logGlobal->warn("Loaded template %s without allowed terrains!", animationFile);
-
 	auto charToTile = [&](const char & ch) -> ui8
 	{
 		switch (ch)
@@ -378,14 +353,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)->getJsonKey();
 				data.push_back(value);
 			}
 		}
@@ -565,9 +540,14 @@ void ObjectTemplate::calculateVisitableOffset()
 	visitableOffset = int3(0, 0, 0);
 }
 
-bool ObjectTemplate::canBePlacedAt(TerrainId terrain) const
+bool ObjectTemplate::canBePlacedAt(TerrainId terrainID) const
 {
-	return vstd::contains(allowedTerrains, terrain);
+	if (anyTerrain)
+	{
+		auto const & terrain = VLC->terrainTypeHandler->getById(terrainID);
+		return terrain->isLand() && terrain->isPassable();
+	}
+	return vstd::contains(allowedTerrains, terrainID);
 }
 
 void ObjectTemplate::recalculate()

+ 3 - 0
lib/mapObjects/ObjectTemplate.h

@@ -35,6 +35,9 @@ class DLL_LINKAGE ObjectTemplate
 	/// list of terrains on which this object can be placed
 	std::set<TerrainId> allowedTerrains;
 
+	/// or, allow placing object on any terrain
+	bool anyTerrain;
+
 	void afterLoadFixup();
 
 public:

+ 9 - 6
lib/mapping/CDrawRoadsOperation.cpp

@@ -12,6 +12,9 @@
 #include "CDrawRoadsOperation.h"
 #include "CMap.h"
 
+#include "../RoadHandler.h"
+#include "../RiverHandler.h"
+
 VCMI_LIB_NAMESPACE_BEGIN
 
 const std::vector<CDrawLinesOperation::LinePattern> CDrawLinesOperation::patterns =
@@ -338,12 +341,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.getNum()));
 }
 
 void CDrawRiversOperation::executeTile(TerrainTile & tile)
 {
-	tile.riverType = const_cast<RiverType*>(&VLC->terrainTypeHandler->rivers()[riverType]);
+	tile.riverType = const_cast<RiverType*>(VLC->riverTypeHandler->getByIndex(riverType.getNum()));
 }
 
 bool CDrawRoadsOperation::canApplyPattern(const LinePattern & pattern) const
@@ -358,22 +361,22 @@ bool CDrawRiversOperation::canApplyPattern(const LinePattern & pattern) const
 
 bool CDrawRoadsOperation::needUpdateTile(const TerrainTile & tile) const
 {
-	return tile.roadType->id != Road::NO_ROAD;
+	return tile.roadType->getId() != Road::NO_ROAD;
 }
 
 bool CDrawRiversOperation::needUpdateTile(const TerrainTile & tile) const
 {
-	return tile.riverType->id != River::NO_RIVER;
+	return tile.riverType->getId() != River::NO_RIVER;
 }
 
 bool CDrawRoadsOperation::tileHasSomething(const int3& pos) const
 {
-	return map->getTile(pos).roadType->id != Road::NO_ROAD;
+	return map->getTile(pos).roadType->getId() != Road::NO_ROAD;
 }
 
 bool CDrawRiversOperation::tileHasSomething(const int3& pos) const
 {
-	return map->getTile(pos).riverType->id != River::NO_RIVER;
+	return map->getTile(pos).riverType->getId() != River::NO_RIVER;
 }
 
 void CDrawRoadsOperation::updateTile(TerrainTile & tile, const LinePattern & pattern, const int flip)

+ 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:

+ 5 - 2
lib/mapping/CMap.cpp

@@ -15,6 +15,9 @@
 #include "../CCreatureHandler.h"
 #include "../CTownHandler.h"
 #include "../CHeroHandler.h"
+#include "../RiverHandler.h"
+#include "../RoadHandler.h"
+#include "../TerrainHandler.h"
 #include "../mapObjects/CObjectClassesHandler.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../CGeneralTextHandler.h"
@@ -128,9 +131,9 @@ CCastleEvent::CCastleEvent() : town(nullptr)
 TerrainTile::TerrainTile():
 	terType(nullptr),
 	terView(0),
-	riverType(const_cast<RiverType*>(&VLC->terrainTypeHandler->rivers()[River::NO_RIVER])),
+	riverType(VLC->riverTypeHandler->getById(River::NO_RIVER)),
 	riverDir(0),
-	roadType(const_cast<RoadType*>(&VLC->terrainTypeHandler->roads()[Road::NO_ROAD])),
+	roadType(VLC->roadTypeHandler->getById(Road::NO_ROAD)),
 	roadDir(0),
 	extTileFlags(0),
 	visitable(false),

+ 7 - 3
lib/mapping/CMapDefines.h

@@ -12,6 +12,10 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+class TerrainType;
+class RiverType;
+class RoadType;
+
 /// The map event is an event which e.g. gives or takes resources of a specific
 /// amount to/from players and can appear regularly or once a time.
 class DLL_LINKAGE CMapEvent
@@ -82,11 +86,11 @@ struct DLL_LINKAGE TerrainTile
 	EDiggingStatus getDiggingStatus(const bool excludeTop = true) const;
 	bool hasFavorableWinds() const;
 
-	TerrainType * terType;
+	const TerrainType * terType;
 	ui8 terView;
-	RiverType * riverType;
+	const RiverType * riverType;
 	ui8 riverDir;
-	RoadType * roadType;
+	const RoadType * roadType;
 	ui8 roadDir;
 	/// first two bits - how to rotate terrain graphic (next two - river graphic, next two - road);
 	///	7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favorable Winds effect

+ 0 - 1
lib/mapping/CMapEditManager.h

@@ -12,7 +12,6 @@
 
 #include "../GameConstants.h"
 #include "CMapOperation.h"
-#include "Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 11 - 10
lib/mapping/CMapOperation.cpp

@@ -12,6 +12,7 @@
 #include "CMapOperation.h"
 
 #include "../VCMI_Lib.h"
+#include "../TerrainHandler.h"
 #include "CMap.h"
 #include "MapEditUtils.h"
 
@@ -97,7 +98,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);
 	}
 
@@ -154,7 +155,7 @@ void CDrawTerrainOperation::updateTerrainTypes()
 			rect.forEach([&](const int3& posToTest)
 				{
 					auto & terrainTile = map->getTile(posToTest);
-					if(centerTile.terType->id != terrainTile.terType->id)
+					if(centerTile.terType->getId() != terrainTile.terType->getId())
 					{
 						auto formerTerType = terrainTile.terType;
 						terrainTile.terType = centerTile.terType;
@@ -257,7 +258,7 @@ void CDrawTerrainOperation::updateTerrainViews()
 {
 	for(const auto & pos : invalidatedTerViews)
 	{
-		const auto & patterns = VLC->terviewh->getTerrainViewPatterns(map->getTile(pos).terType->id);
+		const auto & patterns = VLC->terviewh->getTerrainViewPatterns(map->getTile(pos).terType->getId());
 
 		// Detect a pattern which fits best
 		int bestPattern = -1;
@@ -345,7 +346,7 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 		int cy = pos.y + (i / 3) - 1;
 		int3 currentPos(cx, cy, pos.z);
 		bool isAlien = false;
-		TerrainType * terType = nullptr;
+		const TerrainType * terType = nullptr;
 		if(!map->isInTheMap(currentPos))
 		{
 			// position is not in the map, so take the ter type from the neighbor tile
@@ -393,9 +394,9 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 			{
 				if(recDepth == 0 && map->isInTheMap(currentPos))
 				{
-					if(terType->id == centerTerType->id)
+					if(terType->getId() == centerTerType->getId())
 					{
-						const auto & patternForRule = VLC->terviewh->getTerrainViewPatternsById(centerTerType->id, rule.name);
+						const auto & patternForRule = VLC->terviewh->getTerrainViewPatternsById(centerTerType->getId(), rule.name);
 						if(auto p = patternForRule)
 						{
 							auto rslt = validateTerrainView(currentPos, &(*p), 1);
@@ -422,14 +423,14 @@ CDrawTerrainOperation::ValidationResult CDrawTerrainOperation::validateTerrainVi
 			bool nativeTestOk, nativeTestStrongOk;
 			nativeTestOk = nativeTestStrongOk = (rule.isNativeStrong() || rule.isNativeRule()) && !isAlien;
 
-			if(centerTerType->id == Terrain::DIRT)
+			if(centerTerType->getId() == 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 == Terrain::SAND)
+			else if(centerTerType->getId() == ETerrainId::SAND)
 			{
 				applyValidationRslt(true);
 			}
@@ -551,12 +552,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, 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, Terrain::ROCK, gen));
+		addOperation(std::make_unique<CDrawTerrainOperation>(map, terrainSel, ETerrainId::ROCK, gen));
 	}
 }
 

+ 3 - 2
lib/mapping/MapEditUtils.cpp

@@ -13,6 +13,7 @@
 
 #include "../filesystem/Filesystem.h"
 #include "../JsonNode.h"
+#include "../TerrainHandler.h"
 #include "CMap.h"
 #include "CMapOperation.h"
 
@@ -272,7 +273,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;
@@ -357,7 +358,7 @@ void CTerrainViewPatternUtils::printDebuggingInfoAboutTile(const CMap * map, int
 			{
 				auto debugTile = map->getTile(debugPos);
 
-				std::string terType = debugTile.terType->name.substr(0, 6);
+				std::string terType = debugTile.terType->shortIdentifier;
 				line += terType;
 				line.insert(line.end(), PADDED_LENGTH - terType.size(), ' ');
 			}

+ 1 - 1
lib/mapping/MapEditUtils.h

@@ -12,7 +12,7 @@
 
 #include "../int3.h"
 #include "../CRandomGenerator.h"
-#include "Terrain.h"
+#include "../GameConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 9 - 7
lib/mapping/MapFormatH3M.cpp

@@ -24,6 +24,9 @@
 #include "../mapObjects/CObjectClassesHandler.h"
 #include "../mapObjects/MapObjects.h"
 #include "../VCMI_Lib.h"
+#include "../TerrainHandler.h"
+#include "../RoadHandler.h"
+#include "../RiverHandler.h"
 #include "../NetPacksBase.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -923,9 +926,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,15 +937,17 @@ 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.visitable = 0;
+
+				assert(tile.terType->getId() != ETerrainId::NONE);
 			}
 		}
 	}

+ 41 - 7
lib/mapping/MapFormatJson.cpp

@@ -19,6 +19,9 @@
 #include "../CHeroHandler.h"
 #include "../CTownHandler.h"
 #include "../VCMI_Lib.h"
+#include "../RiverHandler.h"
+#include "../RoadHandler.h"
+#include "../TerrainHandler.h"
 #include "../mapObjects/ObjectTemplate.h"
 #include "../mapObjects/CObjectHandler.h"
 #include "../mapObjects/CObjectClassesHandler.h"
@@ -346,6 +349,37 @@ CMapFormatJson::CMapFormatJson():
 
 }
 
+TerrainType * CMapFormatJson::getTerrainByCode( std::string code)
+{
+	for ( auto const & object : VLC->terrainTypeHandler->objects)
+	{
+		if (object->shortIdentifier == code)
+			return const_cast<TerrainType *>(object.get());
+	}
+	return nullptr;
+}
+
+RiverType * CMapFormatJson::getRiverByCode( std::string code)
+{
+	for ( auto const & object : VLC->riverTypeHandler->objects)
+	{
+		if (object->shortIdentifier == code)
+			return const_cast<RiverType *>(object.get());
+	}
+	return nullptr;
+}
+
+RoadType * CMapFormatJson::getRoadByCode( std::string code)
+{
+	for ( auto const & object : VLC->roadTypeHandler->objects)
+	{
+		if (object->shortIdentifier == 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 +983,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 +1013,13 @@ void CMapLoaderJson::readTerrainTile(const std::string & src, TerrainTile & tile
 			startPos += 2;
 			try
 			{
-				tile.roadType = const_cast<RoadType*>(VLC->terrainTypeHandler->getRoadByCode(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->terrainTypeHandler->getRiverByCode(typeCode));
+					tile.riverType = getRiverByCode(typeCode);
 					hasRoad = false;
 				}
 				catch (const std::exception&)
@@ -1021,7 +1055,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 = getRiverByCode(typeCode);
 		}
 		{//river dir
 			int pos = startPos;
@@ -1289,12 +1323,12 @@ std::string CMapSaverJson::writeTerrainTile(const TerrainTile & tile)
 	out.setf(std::ios::dec, std::ios::basefield);
 	out.unsetf(std::ios::showbase);
 
-	out << tile.terType->typeCode << (int)tile.terView << flipCodes[tile.extTileFlags % 4];
+	out << tile.terType->shortIdentifier << (int)tile.terView << flipCodes[tile.extTileFlags % 4];
 
-	if(tile.roadType->id != Road::NO_ROAD)
+	if(tile.roadType->getId() != Road::NO_ROAD)
 		out << tile.roadType << (int)tile.roadDir << flipCodes[(tile.extTileFlags >> 4) % 4];
 
-	if(tile.riverType->id != River::NO_RIVER)
+	if(tile.riverType->getId() != River::NO_RIVER)
 		out << tile.riverType << (int)tile.riverDir << flipCodes[(tile.extTileFlags >> 2) % 4];
 
 	return out.str();

+ 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

+ 3 - 0
lib/registerTypes/TypesLobbyPacks.cpp

@@ -22,6 +22,9 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
+#include "../RoadHandler.h"
+#include "../RiverHandler.h"
+#include "../TerrainHandler.h"
 #include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 #include "../mapObjects/CObjectClassesHandler.h"

+ 13 - 10
lib/rmg/CRmgTemplate.cpp

@@ -16,7 +16,8 @@
 #include "../mapping/CMap.h"
 #include "../VCMI_Lib.h"
 #include "../CTownHandler.h"
-#include "../Terrain.h"
+#include "../CModHandler.h"
+#include "../TerrainHandler.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->terrains();
-		return (index >=0 && index < terrains.size()) ? terrains[index].name : "<INVALID TERRAIN>";
+		return VLC->terrainTypeHandler->getByIndex(index)->getJsonKey();
 	}
 };
 
@@ -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->getId());
 }
 
 ZoneOptions & ZoneOptions::operator=(const ZoneOptions & other)
@@ -224,7 +224,7 @@ const std::set<TerrainId> & ZoneOptions::getTerrainTypes() const
 
 void ZoneOptions::setTerrainTypes(const std::set<TerrainId> & value)
 {
-	//assert(value.find(ETerrainType::WRONG) == value.end() && value.find(ETerrainType::BORDER) == value.end() &&
+	//assert(value.find(ETerrainType::NONE) == value.end() &&
 	//	   value.find(ETerrainType::WATER) == value.end() && value.find(ETerrainType::ROCK) == value.end());
 	terrainTypes = value;
 }
@@ -365,7 +365,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 			for(auto & ttype : terrainTypes)
 			{
 				JsonNode n;
-				n.String() = ttype;
+				n.String() = VLC->terrainTypeHandler->getById(ttype)->getJsonKey();
 				node.Vector().push_back(n);
 			}
 		}
@@ -377,7 +377,10 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 				terrainTypes.clear();
 				for(auto ttype : node.Vector())
 				{
-					terrainTypes.emplace(VLC->terrainTypeHandler->getInfoByName(ttype.String())->id);
+					VLC->modh->identifiers.requestIdentifier("terrain", ttype, [this](int32_t identifier)
+					{
+						terrainTypes.emplace(identifier);
+					});
 				}
 			}
 		}

+ 0 - 1
lib/rmg/CRmgTemplate.h

@@ -13,7 +13,6 @@
 #include "../int3.h"
 #include "../GameConstants.h"
 #include "../ResourceSet.h"
-#include "../Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 5 - 4
lib/rmg/CZonePlacer.cpp

@@ -11,6 +11,7 @@
 #include "StdInc.h"
 #include "../CRandomGenerator.h"
 #include "CZonePlacer.h"
+#include "../TerrainHandler.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "CMapGenOptions.h"
@@ -194,15 +195,15 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
 				else
 				{
 					auto & tt = (*VLC->townh)[faction]->nativeTerrain;
-					if(tt == Terrain::DIRT)
+					if(tt == ETerrainId::NONE)
 					{
 						//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 +581,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, ETerrainId::SUBTERRANEAN);
 		}
 	}
 	logGlobal->info("Finished zone colouring");

+ 6 - 3
lib/rmg/ConnectionsPlacer.cpp

@@ -12,6 +12,7 @@
 #include "ConnectionsPlacer.h"
 #include "CMapGenerator.h"
 #include "RmgMap.h"
+#include "../TerrainHandler.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapObjects/CObjectClassesHandler.h"
@@ -84,9 +85,11 @@ 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 * ourTerrain   = VLC->terrainTypeHandler->getById(zone.getTerrainType());
+	const auto * otherTerrain = VLC->terrainTypeHandler->getById(otherZone->getTerrainType());
+
+	bool directProhibited = vstd::contains(ourTerrain->prohibitTransitions, otherZone->getTerrainType())
+						 || vstd::contains(otherTerrain->prohibitTransitions, zone.getTerrainType());
 	auto directConnectionIterator = dNeighbourZones.find(otherZoneId);
 	if(!directProhibited && directConnectionIterator != dNeighbourZones.end())
 	{

+ 9 - 8
lib/rmg/Functions.cpp

@@ -21,6 +21,7 @@
 #include "RmgMap.h"
 #include "TileInfo.h"
 #include "RmgPath.h"
+#include "../TerrainHandler.h"
 #include "../CTownHandler.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapping/CMap.h"
@@ -119,9 +120,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->getId());
 		
 		zone.setTerrainType(*RandomGeneratorUtil::nextItem(waterTerrains, gen.rand));
 	}
@@ -137,20 +138,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(ETerrainId::SUBTERRANEAN);
 			}
 		}
 		else
 		{
-			if (!terrainType.isSurface())
+			if (!terrainType->isSurface())
 			{
-				zone.setTerrainType(Terrain::DIRT);
+				zone.setTerrainType(ETerrainId::DIRT);
 			}
 		}
 	}

+ 9 - 7
lib/rmg/RiverPlacer.cpp

@@ -13,6 +13,8 @@
 #include "Functions.h"
 #include "CMapGenerator.h"
 #include "RmgMap.h"
+#include "../RiverHandler.h"
+#include "../TerrainHandler.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapObjects/CObjectClassesHandler.h"
@@ -86,7 +88,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 +197,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 +323,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->getById(riverType);
+	if(river->getId() == River::NO_RIVER)
 		return;
 	
 	rmg::Area roads;
@@ -381,9 +383,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->shortIdentifier));
 			
-			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(ETerrainId::GRASS, &generator.rand);
 	
 	auto tmpl = mapGenOptions.getMapTemplate();
 	zones.clear();

+ 4 - 3
lib/rmg/RmgObject.cpp

@@ -18,6 +18,7 @@
 #include "../mapObjects/CommonConstructors.h"
 #include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
 #include "Functions.h"
+#include "../TerrainHandler.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -121,7 +122,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)->getNameTranslated();
 		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();
@@ -293,14 +294,14 @@ void Object::Instance::finalize(RmgMap & map)
 	if (!dObject.appearance)
 	{
 		auto terrainType = map.map().getTile(getPosition(true)).terType;
-		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->id);
+		auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->getId());
 		if (templates.empty())
 		{
 			throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % dObject.ID % dObject.subID % getPosition(true).toString() % terrainType));
 		}
 		else
 		{
-			setTemplate(terrainType->id);
+			setTemplate(terrainType->getId());
 		}
 	}
 

+ 3 - 1
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
@@ -78,8 +79,9 @@ 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->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "road", roadName));
 	map.getEditManager()->drawRoad(roadType, &generator.rand);
 }
 

+ 3 - 2
lib/rmg/RockPlacer.cpp

@@ -17,6 +17,7 @@
 #include "RmgMap.h"
 #include "CMapGenerator.h"
 #include "Functions.h"
+#include "../TerrainHandler.h"
 #include "../CRandomGenerator.h"
 #include "../mapping/CMapEditManager.h"
 
@@ -24,8 +25,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>())

+ 0 - 1
lib/rmg/TileInfo.h

@@ -11,7 +11,6 @@
 #pragma once
 
 #include "../GameConstants.h"
-#include "../Terrain.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 3 - 2
lib/rmg/WaterProxy.cpp

@@ -12,6 +12,7 @@
 #include "WaterProxy.h"
 #include "CMapGenerator.h"
 #include "RmgMap.h"
+#include "../TerrainHandler.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapObjects/CObjectClassesHandler.h"
@@ -44,7 +45,7 @@ void WaterProxy::process()
 	{
 		MAYBE_UNUSED(t);
 		assert(map.isOnMap(t));
-		assert(map.map().getTile(t).terType->id == zone.getTerrainType());
+		assert(map.map().getTile(t).terType->getId() == zone.getTerrainType());
 	}
 	
 	for(auto z : map.getZones())
@@ -54,7 +55,7 @@ void WaterProxy::process()
 		
 		for(auto & t : z.second->area().getTilesVector())
 		{
-			if(map.map().getTile(t).terType->id == zone.getTerrainType())
+			if(map.map().getTile(t).terType->getId() == zone.getTerrainType())
 			{
 				z.second->areaPossible().erase(t);
 				z.second->area().erase(t);

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

+ 2 - 2
lib/serializer/CSerializer.h

@@ -14,8 +14,8 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-const ui32 SERIALIZATION_VERSION = 807;
-const ui32 MINIMAL_SERIALIZATION_VERSION = 805;
+const ui32 SERIALIZATION_VERSION = 810;
+const ui32 MINIMAL_SERIALIZATION_VERSION = 810;
 const std::string SAVEGAME_MAGIC = "VCMISVG";
 
 class CHero;

+ 25 - 16
mapeditor/mainwindow.cpp

@@ -21,12 +21,15 @@
 #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"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMapEditManager.h"
-#include "../lib/Terrain.h"
+#include "../lib/RoadHandler.h"
+#include "../lib/RiverHandler.h"
+#include "../lib/TerrainHandler.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
 #include "../lib/filesystem/CFilesystemLoader.h"
 
@@ -542,32 +545,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->getNameTranslated()));
 		ui->terrainLayout->addWidget(b);
-		connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain.id); });
+		connect(b, &QPushButton::clicked, this, [this, terrain]{ terrainButtonClicked(terrain->getId()); });
 
 		//filter
-		ui->terrainFilterCombo->addItem(QString::fromStdString(terrain));
+		ui->terrainFilterCombo->addItem(QString::fromStdString(terrain->getNameTranslated()));
 	}
 	//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->getNameTranslated()));
 		ui->roadLayout->addWidget(b);
-		connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road.id, true); });
+		connect(b, &QPushButton::clicked, this, [this, road]{ roadOrRiverButtonClicked(road->getIndex(), 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->getNameTranslated()));
 		ui->riverLayout->addWidget(b);
-		connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river.id, false); });
+		connect(b, &QPushButton::clicked, this, [this, river]{ roadOrRiverButtonClicked(river->getIndex(), false); });
 	}
 	//add spacer to keep terrain button on the top
 	ui->riverLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
@@ -930,7 +933,13 @@ 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 = TerrainId(ETerrainId::ANY_TERRAIN);
+	if (!arg1.isEmpty())
+	{
+		for (auto const & terrain : VLC->terrainTypeHandler->objects)
+			if (terrain->getJsonKey() == arg1.toStdString())
+				objectBrowser->terrain = terrain->getId();
+	}
 	objectBrowser->invalidate();
 	objectBrowser->sort(0);
 }
@@ -1122,7 +1131,7 @@ void MainWindow::on_actionUpdate_appearance_triggered()
 		if(handler->isStaticObject())
 		{
 			staticObjects.insert(obj);
-			if(obj->appearance->canBePlacedAt(terrain->id))
+			if(obj->appearance->canBePlacedAt(terrain->getId()))
 			{
 				controller.scene(mapLevel)->selectionObjectsView.deselectObject(obj);
 				continue;
@@ -1133,13 +1142,13 @@ void MainWindow::on_actionUpdate_appearance_triggered()
 		}
 		else
 		{
-			auto app = handler->getOverride(terrain->id, obj);
+			auto app = handler->getOverride(terrain->getId(), obj);
 			if(!app)
 			{
-				if(obj->appearance->canBePlacedAt(terrain->id))
+				if(obj->appearance->canBePlacedAt(terrain->getId()))
 					continue;
 				
-				auto templates = handler->getTemplates(terrain->id);
+				auto templates = handler->getTemplates(terrain->getId());
 				if(templates.empty())
 				{
 					++errors;

+ 0 - 1
mapeditor/mainwindow.h

@@ -4,7 +4,6 @@
 #include <QGraphicsScene>
 #include <QStandardItemModel>
 #include "mapcontroller.h"
-#include "../lib/Terrain.h"
 #include "resourceExtractor/ResourceConverter.h"
 
 class ObjectBrowser;

+ 2 - 2
mapeditor/mapcontroller.cpp

@@ -14,7 +14,7 @@
 #include "../lib/mapping/CMapService.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMapEditManager.h"
-#include "../lib/Terrain.h"
+#include "../lib/TerrainHandler.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
 #include "../lib/rmg/ObstaclePlacer.h"
 #include "../lib/CSkillHandler.h"
@@ -408,7 +408,7 @@ void MapController::commitObstacleFill(int level)
 		if(tl.blocked || tl.visitable)
 			continue;
 		
-		terrainSelected[tl.terType->id].blockedArea.add(t);
+		terrainSelected[tl.terType->getId()].blockedArea.add(t);
 	}
 	
 	for(auto & sel : terrainSelected)

+ 0 - 1
mapeditor/mapcontroller.h

@@ -13,7 +13,6 @@
 #include "maphandler.h"
 #include "mapview.h"
 #include "../lib/mapping/CMap.h"
-#include "../lib/Terrain.h"
 
 class MainWindow;
 class MapController

+ 16 - 14
mapeditor/maphandler.cpp

@@ -12,6 +12,9 @@
 #include "StdInc.h"
 #include "maphandler.h"
 #include "graphics.h"
+#include "../lib/RoadHandler.h"
+#include "../lib/RiverHandler.h"
+#include "../lib/TerrainHandler.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/mapObjects/CObjectClassesHandler.h"
@@ -78,17 +81,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->getJsonKey()] = terrain->tilesFilename;
 	}
-	for(const auto & river : VLC->terrainTypeHandler->rivers())
+	for(const auto & river : VLC->riverTypeHandler->objects)
 	{
-		riverFiles[river.fileName] = river.fileName;
+		riverFiles[river->getJsonKey()] = river->tilesFilename;
 	}
-	for(const auto & road : VLC->terrainTypeHandler->roads())
+	for(const auto & road : VLC->roadTypeHandler->objects)
 	{
-		roadFiles[road.fileName] = road.fileName;
+		roadFiles[road->getJsonKey()] = road->tilesFilename;
 	}
 	
 	loadFlipped(terrainAnimations, terrainImages, terrainFiles);
@@ -101,8 +104,7 @@ void MapHandler::drawTerrainTile(QPainter & painter, int x, int y, int z)
 	auto & tinfo = map->getTile(int3(x, y, z));
 	ui8 rotation = tinfo.extTileFlags % 4;
 	
-	//TODO: use ui8 instead of string key
-	auto terrainName = tinfo.terType->name;
+	auto terrainName = tinfo.terType->getJsonKey();
 	
 	if(terrainImages.at(terrainName).size() <= tinfo.terView)
 		return;
@@ -116,9 +118,9 @@ void MapHandler::drawRoad(QPainter & painter, int x, int y, int z)
 	auto & tinfo = map->getTile(int3(x, y, z));
 	auto * tinfoUpper = map->isInTheMap(int3(x, y - 1, z)) ? &map->getTile(int3(x, y - 1, z)) : nullptr;
 	
-	if(tinfoUpper && tinfoUpper->roadType->id != Road::NO_ROAD)
+	if(tinfoUpper && tinfoUpper->roadType->getId() != Road::NO_ROAD)
 	{
-		auto roadName = tinfoUpper->roadType->fileName;
+		auto roadName = tinfoUpper->roadType->getJsonKey();
 		QRect source(0, tileSize / 2, tileSize, tileSize / 2);
 		ui8 rotation = (tinfoUpper->extTileFlags >> 4) % 4;
 		bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);
@@ -128,9 +130,9 @@ void MapHandler::drawRoad(QPainter & painter, int x, int y, int z)
 		}
 	}
 	
-	if(tinfo.roadType->id != Road::NO_ROAD) //print road from this tile
+	if(tinfo.roadType->getId() != Road::NO_ROAD) //print road from this tile
 	{
-		auto roadName = tinfo.roadType->fileName;
+		auto roadName = tinfo.roadType->getJsonKey();;
 		QRect source(0, 0, tileSize, tileSize / 2);
 		ui8 rotation = (tinfo.extTileFlags >> 4) % 4;
 		bool hflip = (rotation == 1 || rotation == 3), vflip = (rotation == 2 || rotation == 3);
@@ -145,11 +147,11 @@ void MapHandler::drawRiver(QPainter & painter, int x, int y, int z)
 {
 	auto & tinfo = map->getTile(int3(x, y, z));
 
-	if(tinfo.riverType->id == River::NO_RIVER)
+	if(tinfo.riverType->getId() == River::NO_RIVER)
 		return;
 	
 	//TODO: use ui8 instead of string key
-	auto riverName = tinfo.riverType->fileName;
+	auto riverName = tinfo.riverType->getJsonKey();
 
 	if(riverImages.at(riverName).size() <= tinfo.riverDir)
 		return;

+ 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(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 == Terrain::ANY_TERRAIN)
+	if(terrain == ETerrainId::ANY_TERRAIN)
 		return result;
 
 	auto data = item->data().toJsonObject();

+ 1 - 1
mapeditor/objectbrowser.h

@@ -11,7 +11,7 @@
 #pragma once
 
 #include <QSortFilterProxyModel>
-#include "../lib/Terrain.h"
+#include "../lib/GameConstants.h"
 
 class ObjectBrowserProxyModel : public QSortFilterProxyModel
 {

+ 3 - 2
server/CGameHandler.cpp

@@ -38,6 +38,7 @@
 #include "../lib/VCMIDirs.h"
 #include "../lib/ScopeGuard.h"
 #include "../lib/CSoundBase.h"
+#include "../lib/TerrainHandler.h"
 #include "CGameHandler.h"
 #include "CVCMIServer.h"
 #include "../lib/CCreatureSet.h"
@@ -2242,9 +2243,9 @@ void CGameHandler::setupBattle(int3 tile, const CArmedInstance *armies[2], const
 	battleResult.set(nullptr);
 
 	const auto & t = *getTile(tile);
-	TerrainId terrain = t.terType->id;
+	TerrainId terrain = t.terType->getId();
 	if (gs->map->isCoastalTile(tile)) //coastal tile is always ground
-		terrain = Terrain::SAND;
+		terrain = ETerrainId::SAND;
 
 	BattleField terType = gs->battleGetBattlefieldType(tile, getRandomGenerator());
 	if (heroes[0] && heroes[0]->boat && heroes[1] && heroes[1]->boat)