瀏覽代碼

Merge pull request #6136 from Laserlicht/resource

Configurable resources
Ivan Savenko 2 月之前
父節點
當前提交
c469c6c31e
共有 96 個文件被更改,包括 673 次插入313 次删除
  1. 2 1
      AI/Nullkiller/AIUtility.cpp
  2. 4 2
      AI/Nullkiller/Engine/PriorityEvaluator.cpp
  3. 3 2
      AI/Nullkiller/Goals/AbstractGoal.cpp
  4. 3 2
      AI/VCAI/Goals/AbstractGoal.cpp
  5. 2 1
      AI/VCAI/Goals/CollectRes.cpp
  6. 5 3
      client/adventureMap/AdventureMapWidget.cpp
  7. 15 0
      client/adventureMap/CResDataBar.cpp
  8. 1 0
      client/adventureMap/CResDataBar.h
  9. 2 3
      client/lobby/OptionsTab.cpp
  10. 14 13
      client/mainmenu/CStatisticScreen.cpp
  11. 0 1
      client/mapView/MapViewCache.cpp
  12. 2 0
      client/renderSDL/RenderHandler.cpp
  13. 1 1
      client/widgets/MiscWidgets.cpp
  14. 2 1
      client/widgets/markets/TradePanels.cpp
  15. 6 4
      client/windows/CCastleInterface.cpp
  16. 2 2
      client/windows/GUIClasses.cpp
  17. 1 1
      client/windows/GUIClasses.h
  18. 10 10
      config/difficulty.json
  19. 4 0
      config/gameConfig.json
  20. 34 2
      config/resources.json
  21. 4 11
      config/schemas/creature.json
  22. 3 10
      config/schemas/flaggable.json
  23. 5 1
      config/schemas/mod.json
  24. 44 0
      config/schemas/resources.json
  25. 2 9
      config/schemas/template.json
  26. 8 22
      config/schemas/townBuilding.json
  27. 1 1
      docs/modders/Difficulty.md
  28. 20 0
      docs/modders/Entities_Format/Resource_Format.md
  29. 0 1
      docs/modders/Game_Identifiers.md
  30. 1 1
      docs/modders/Mod_File_Format.md
  31. 0 4
      docs/players/Game_Mechanics.md
  32. 25 0
      include/vcmi/ResourceType.h
  33. 6 8
      include/vcmi/ResourceTypeService.h
  34. 2 0
      include/vcmi/Services.h
  35. 1 0
      launcher/modManager/modstateitemmodel_moc.cpp
  36. 2 2
      lib/CCreatureHandler.cpp
  37. 4 2
      lib/CMakeLists.txt
  38. 1 1
      lib/CPlayerState.cpp
  39. 7 2
      lib/GameLibrary.cpp
  40. 3 3
      lib/GameLibrary.h
  41. 30 15
      lib/ResourceSet.cpp
  42. 26 3
      lib/ResourceSet.h
  43. 14 16
      lib/constants/EntityIdentifiers.cpp
  44. 5 2
      lib/constants/EntityIdentifiers.h
  45. 1 1
      lib/constants/NumericConstants.h
  46. 1 1
      lib/constants/StringConstants.h
  47. 83 0
      lib/entities/ResourceTypeHandler.cpp
  48. 66 0
      lib/entities/ResourceTypeHandler.h
  49. 3 6
      lib/entities/faction/CTownHandler.cpp
  50. 1 1
      lib/gameState/CGameState.cpp
  51. 10 9
      lib/gameState/GameStatistics.cpp
  52. 4 3
      lib/json/JsonRandom.cpp
  53. 2 1
      lib/mapObjectConstructors/CommonConstructors.cpp
  54. 1 1
      lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp
  55. 2 1
      lib/mapObjects/CGCreature.cpp
  56. 2 1
      lib/mapObjects/CGHeroInstance.cpp
  57. 1 1
      lib/mapObjects/CGObjectInstance.cpp
  58. 3 2
      lib/mapObjects/CGResource.cpp
  59. 2 1
      lib/mapObjects/CGTownInstance.cpp
  60. 0 31
      lib/mapObjects/CObjectHandler.cpp
  61. 5 6
      lib/mapObjects/CQuest.cpp
  62. 7 7
      lib/mapObjects/IMarket.cpp
  63. 0 2
      lib/mapObjects/MapObjects.h
  64. 9 5
      lib/mapObjects/MiscObjects.cpp
  65. 2 0
      lib/modding/ContentTypeHandler.cpp
  66. 2 1
      lib/rewardable/Info.cpp
  67. 4 8
      lib/rmg/CRmgTemplate.cpp
  68. 4 4
      lib/rmg/CRmgTemplate.h
  69. 10 0
      lib/serializer/JsonDeserializer.cpp
  70. 1 0
      lib/serializer/JsonDeserializer.h
  71. 30 0
      lib/serializer/JsonSerializeFormat.h
  72. 11 0
      lib/serializer/JsonSerializer.cpp
  73. 1 0
      lib/serializer/JsonSerializer.h
  74. 5 0
      lib/serializer/JsonUpdater.cpp
  75. 1 0
      lib/serializer/JsonUpdater.h
  76. 3 2
      lib/texts/MetaString.cpp
  77. 2 0
      mapeditor/graphics.cpp
  78. 4 5
      mapeditor/inspector/questwidget.cpp
  79. 5 6
      mapeditor/inspector/rewardswidget.cpp
  80. 7 5
      mapeditor/inspector/towneventdialog.cpp
  81. 7 3
      mapeditor/mapsettings/eventsettings.cpp
  82. 7 5
      mapeditor/mapsettings/timedevent.cpp
  83. 4 2
      mapeditor/mapsettings/victoryconditions.cpp
  84. 4 2
      mapeditor/templateeditor/graphicelements/CardItem.cpp
  85. 11 9
      mapeditor/templateeditor/mineselector.cpp
  86. 3 3
      mapeditor/templateeditor/mineselector.h
  87. 0 2
      scripting/lua/api/ObjectInstance.h
  88. 1 1
      scripting/lua/api/netpacks/SetResources.cpp
  89. 1 1
      scripts/lib/erm/MA.lua
  90. 2 1
      server/CGameHandler.cpp
  91. 3 2
      server/CVCMIServer.cpp
  92. 5 4
      server/processors/NewTurnProcessor.cpp
  93. 2 1
      server/processors/PlayerMessageProcessor.cpp
  94. 0 2
      test/erm/ERM_OB_T.cpp
  95. 1 0
      test/mock/mock_Services.h
  96. 0 1
      test/vcai/ResourceManagerTest.cpp

+ 2 - 1
AI/Nullkiller/AIUtility.cpp

@@ -15,6 +15,7 @@
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/entities/artifact/CArtifact.h"
 #include "../../lib/entities/artifact/CArtifact.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/mapObjects/MapObjects.h"
 #include "../../lib/mapObjects/CQuest.h"
 #include "../../lib/mapObjects/CQuest.h"
 #include "../../lib/mapping/TerrainTile.h"
 #include "../../lib/mapping/TerrainTile.h"
@@ -470,7 +471,7 @@ int32_t getArtifactBonusScoreImpl(const std::shared_ptr<Bonus> & bonus)
 		case BonusType::UNDEAD_RAISE_PERCENTAGE:
 		case BonusType::UNDEAD_RAISE_PERCENTAGE:
 			return bonus->val * 400;
 			return bonus->val * 400;
 		case BonusType::GENERATE_RESOURCE:
 		case BonusType::GENERATE_RESOURCE:
-			return bonus->val * LIBRARY->objh->resVals.at(bonus->subtype.as<GameResID>().getNum()) * 10;
+			return bonus->val * bonus->subtype.as<GameResID>().toResource()->getPrice() * 10;
 		case BonusType::SPELL_DURATION:
 		case BonusType::SPELL_DURATION:
 			return bonus->val * 200;
 			return bonus->val * 200;
 		case BonusType::MAGIC_RESISTANCE:
 		case BonusType::MAGIC_RESISTANCE:

+ 4 - 2
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -12,6 +12,7 @@
 
 
 #include "Nullkiller.h"
 #include "Nullkiller.h"
 #include "../../../lib/entities/artifact/CArtifact.h"
 #include "../../../lib/entities/artifact/CArtifact.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h"
 #include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h"
 #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h"
 #include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h"
 #include "../../../lib/mapObjects/CGResource.h"
 #include "../../../lib/mapObjects/CGResource.h"
@@ -22,6 +23,7 @@
 #include "../../../lib/StartInfo.h"
 #include "../../../lib/StartInfo.h"
 #include "../../../lib/GameSettings.h"
 #include "../../../lib/GameSettings.h"
 #include "../../../lib/filesystem/Filesystem.h"
 #include "../../../lib/filesystem/Filesystem.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 #include "../Goals/ExecuteHeroChain.h"
 #include "../Goals/ExecuteHeroChain.h"
 #include "../Goals/BuildThis.h"
 #include "../Goals/BuildThis.h"
 #include "../Goals/StayAtTown.h"
 #include "../Goals/StayAtTown.h"
@@ -136,7 +138,7 @@ int32_t getResourcesGoldReward(const TResources & res)
 {
 {
 	int32_t result = 0;
 	int32_t result = 0;
 
 
-	for(auto r : GameResID::ALL_RESOURCES())
+	for(auto r : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		if(res[r] > 0)
 		if(res[r] > 0)
 			result += r == EGameResID::GOLD ? res[r] : res[r] * 100;
 			result += r == EGameResID::GOLD ? res[r] : res[r] * 100;
@@ -1589,7 +1591,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task, int priorityTier)
 						needed.positive();
 						needed.positive();
 						int turnsTo = needed.maxPurchasableCount(income);
 						int turnsTo = needed.maxPurchasableCount(income);
 						bool haveEverythingButGold = true;
 						bool haveEverythingButGold = true;
-						for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
+						for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 						{
 						{
 							if (i != GameResID::GOLD && resourcesAvailable[i] < evaluationContext.buildingCost[i])
 							if (i != GameResID::GOLD && resourcesAvailable[i] < evaluationContext.buildingCost[i])
 								haveEverythingButGold = false;
 								haveEverythingButGold = false;

+ 3 - 2
AI/Nullkiller/Goals/AbstractGoal.cpp

@@ -12,6 +12,7 @@
 #include "../AIGateway.h"
 #include "../AIGateway.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/entities/artifact/CArtifact.h"
 #include "../../../lib/entities/artifact/CArtifact.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 
 
 namespace NKAI
 namespace NKAI
 {
 {
@@ -43,13 +44,13 @@ std::string AbstractGoal::toString() const
 	switch(goalType)
 	switch(goalType)
 	{
 	{
 	case COLLECT_RES:
 	case COLLECT_RES:
-		desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + std::to_string(value) + ")";
+		desc = "COLLECT RESOURCE " + GameResID(resID).toResource()->getJsonKey() + " (" + std::to_string(value) + ")";
 		break;
 		break;
 	case TRADE:
 	case TRADE:
 	{
 	{
 		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
 		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
 		if (obj)
 		if (obj)
-			desc = (boost::format("TRADE %d of %s at %s") % value % GameConstants::RESOURCE_NAMES[resID] % obj->getObjectName()).str();
+			desc = (boost::format("TRADE %d of %s at %s") % value % GameResID(resID).toResource()->getJsonKey() % obj->getObjectName()).str();
 	}
 	}
 	break;
 	break;
 	case GATHER_TROOPS:
 	case GATHER_TROOPS:

+ 3 - 2
AI/VCAI/Goals/AbstractGoal.cpp

@@ -16,6 +16,7 @@
 #include "../BuildingManager.h"
 #include "../BuildingManager.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/entities/artifact/CArtifact.h"
 #include "../../../lib/entities/artifact/CArtifact.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 
 
 using namespace Goals;
 using namespace Goals;
 
 
@@ -56,13 +57,13 @@ std::string AbstractGoal::name() const //TODO: virtualize
 	case BUILD_STRUCTURE:
 	case BUILD_STRUCTURE:
 		return "BUILD STRUCTURE";
 		return "BUILD STRUCTURE";
 	case COLLECT_RES:
 	case COLLECT_RES:
-		desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + std::to_string(value) + ")";
+		desc = "COLLECT RESOURCE " + GameResID(resID).toResource()->getJsonKey() + " (" + std::to_string(value) + ")";
 		break;
 		break;
 	case TRADE:
 	case TRADE:
 	{
 	{
 		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
 		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
 		if (obj)
 		if (obj)
-			desc = (boost::format("TRADE %d of %s at %s") % value % GameConstants::RESOURCE_NAMES[resID] % obj->getObjectName()).str();
+			desc = (boost::format("TRADE %d of %s at %s") % value % GameResID(resID).toResource()->getJsonKey() % obj->getObjectName()).str();
 	}
 	}
 	break;
 	break;
 	case GATHER_TROOPS:
 	case GATHER_TROOPS:

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

@@ -18,6 +18,7 @@
 #include "../../../lib/mapObjects/CGMarket.h"
 #include "../../../lib/mapObjects/CGMarket.h"
 #include "../../../lib/mapObjects/CGResource.h"
 #include "../../../lib/mapObjects/CGResource.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/constants/StringConstants.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 
 
 using namespace Goals;
 using namespace Goals;
 
 
@@ -171,7 +172,7 @@ TSubgoal CollectRes::whatToDoToTrade()
 		const IMarket * m = markets.back();
 		const IMarket * m = markets.back();
 		//attempt trade at back (best prices)
 		//attempt trade at back (best prices)
 		int howManyCanWeBuy = 0;
 		int howManyCanWeBuy = 0;
-		for (GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i)
+		for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 		{
 		{
 			if (i.getNum() == resID)
 			if (i.getNum() == resID)
 				continue;
 				continue;

+ 5 - 3
client/adventureMap/AdventureMapWidget.cpp

@@ -31,10 +31,12 @@
 #include "../CPlayerInterface.h"
 #include "../CPlayerInterface.h"
 #include "../PlayerLocalState.h"
 #include "../PlayerLocalState.h"
 
 
+#include "../../lib/GameLibrary.h"
 #include "../../lib/callback/CCallback.h"
 #include "../../lib/callback/CCallback.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/mapping/CMapHeader.h"
 #include "../../lib/mapping/CMapHeader.h"
 #include "../../lib/filesystem/ResourcePath.h"
 #include "../../lib/filesystem/ResourcePath.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 
 
 AdventureMapWidget::AdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts )
 AdventureMapWidget::AdventureMapWidget( std::shared_ptr<AdventureMapShortcuts> shortcuts )
 	: shortcuts(shortcuts)
 	: shortcuts(shortcuts)
@@ -294,14 +296,14 @@ std::shared_ptr<CIntObject> AdventureMapWidget::buildResourceDateBar(const JsonN
 
 
 	auto result = std::make_shared<CResDataBar>(image, area.topLeft());
 	auto result = std::make_shared<CResDataBar>(image, area.topLeft());
 
 
-	for(auto i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
+	for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
-		const auto & node = input[GameConstants::RESOURCE_NAMES[i]];
+		const auto & node = input[i.toResource()->getJsonKey()];
 
 
 		if(node.isNull())
 		if(node.isNull())
 			continue;
 			continue;
 
 
-		result->setResourcePosition(GameResID(i), Point(node["x"].Integer(), node["y"].Integer()));
+		result->setResourcePosition(i, Point(node["x"].Integer(), node["y"].Integer()));
 	}
 	}
 
 
 	result->setDatePosition(Point(input["date"]["x"].Integer(), input["date"]["y"].Integer()));
 	result->setDatePosition(Point(input["date"]["x"].Integer(), input["date"]["y"].Integer()));

+ 15 - 0
client/adventureMap/CResDataBar.cpp

@@ -18,15 +18,21 @@
 #include "../GameInstance.h"
 #include "../GameInstance.h"
 #include "../gui/TextAlignment.h"
 #include "../gui/TextAlignment.h"
 #include "../widgets/Images.h"
 #include "../widgets/Images.h"
+#include "../widgets/CComponent.h"
+#include "../windows/InfoWindows.h"
 
 
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/CConfigHandler.h"
 #include "../../lib/callback/CCallback.h"
 #include "../../lib/callback/CCallback.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/ResourceSet.h"
 #include "../../lib/ResourceSet.h"
 #include "../../lib/GameLibrary.h"
 #include "../../lib/GameLibrary.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
+#include "../../lib/networkPacks/Component.h"
 
 
 CResDataBar::CResDataBar(const ImagePath & imageName, const Point & position)
 CResDataBar::CResDataBar(const ImagePath & imageName, const Point & position)
 {
 {
+	addUsedEvents(SHOW_POPUP);
+
 	pos.x += position.x;
 	pos.x += position.x;
 	pos.y += position.y;
 	pos.y += position.y;
 
 
@@ -89,3 +95,12 @@ void CResDataBar::setPlayerColor(PlayerColor player)
 {
 {
 	background->setPlayerColor(player);
 	background->setPlayerColor(player);
 }
 }
+
+void CResDataBar::showPopupWindow(const Point & cursorPosition)
+{
+	std::vector<std::shared_ptr<CComponent>> comp;
+	for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
+		comp.push_back(std::make_shared<CComponent>(ComponentType::RESOURCE, i, GAME->interface()->cb->getResourceAmount(i)));
+	
+	CRClickPopup::createAndPush(LIBRARY->generaltexth->translate("core.genrltxt.270"), comp);
+}

+ 1 - 0
client/adventureMap/CResDataBar.h

@@ -35,6 +35,7 @@ public:
 	void setResourcePosition(const GameResID & resource, const Point & position);
 	void setResourcePosition(const GameResID & resource, const Point & position);
 
 
 	void setPlayerColor(PlayerColor player);
 	void setPlayerColor(PlayerColor player);
+	void showPopupWindow(const Point & cursorPosition) override;
 	void showAll(Canvas & to) override;
 	void showAll(Canvas & to) override;
 };
 };
 
 

+ 2 - 3
client/lobby/OptionsTab.cpp

@@ -41,6 +41,7 @@
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/hero/CHeroHandler.h"
 #include "../../lib/entities/hero/CHeroHandler.h"
 #include "../../lib/entities/hero/CHeroClass.h"
 #include "../../lib/entities/hero/CHeroClass.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/filesystem/Filesystem.h"
 #include "../../lib/networkPacks/PacksForLobby.h"
 #include "../../lib/networkPacks/PacksForLobby.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
@@ -161,8 +162,6 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex(bool big)
 				return GEM;
 				return GEM;
 			case EGameResID::GOLD:
 			case EGameResID::GOLD:
 				return GOLD;
 				return GOLD;
-			case EGameResID::MITHRIL:
-				return MITHRIL;
 			}
 			}
 		}
 		}
 		}
 		}
@@ -1057,7 +1056,7 @@ OptionsTab::PlayerOptionsEntry::PlayerOptionsEntry(const PlayerSettings & S, con
 		{
 		{
 			auto str = MetaString::createFromTextID("vcmi.lobby.handicap");
 			auto str = MetaString::createFromTextID("vcmi.lobby.handicap");
 			str.appendRawString(":\n");
 			str.appendRawString(":\n");
-			for(auto & res : EGameResID::ALL_RESOURCES())
+			for(auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
 				if(s->handicap.startBonus[res] != 0)
 				if(s->handicap.startBonus[res] != 0)
 				{
 				{
 					str.appendRawString("\n");
 					str.appendRawString("\n");

+ 14 - 13
client/mainmenu/CStatisticScreen.cpp

@@ -28,6 +28,7 @@
 #include "../windows/InfoWindows.h"
 #include "../windows/InfoWindows.h"
 #include "../widgets/Slider.h"
 #include "../widgets/Slider.h"
 
 
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/gameState/GameStatistics.h"
 #include "../../lib/gameState/GameStatistics.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
@@ -76,10 +77,10 @@ void CStatisticScreen::onSelectButton()
 		else
 		else
 		{
 		{
 			auto content = static_cast<Content>(selectedIndex);
 			auto content = static_cast<Content>(selectedIndex);
-			auto possibleRes = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS};
+			auto possibleRes = LIBRARY->resourceTypeHandler->getAllObjects();
 			std::vector<std::string> resourceText;
 			std::vector<std::string> resourceText;
 			for(const auto & res : possibleRes)
 			for(const auto & res : possibleRes)
-				resourceText.emplace_back(LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", res.getNum()).get()));
+				resourceText.emplace_back(res.toResource()->getNameTranslated());
 			
 			
 			ENGINE->windows().createAndPushWindow<StatisticSelector>(resourceText, [this, content, possibleRes](int index)
 			ENGINE->windows().createAndPushWindow<StatisticSelector>(resourceText, [this, content, possibleRes](int index)
 			{
 			{
@@ -169,7 +170,7 @@ std::shared_ptr<CIntObject> CStatisticScreen::getContent(Content c, EGameResID r
 	
 	
 	case CHART_RESOURCES:
 	case CHART_RESOURCES:
 		plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.resources[res]; });
 		plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.resources[res]; });
-		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", res.getNum()).get()), plotData, icons, 0);
+		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + res.toResource()->getNameTranslated(), plotData, icons, 0);
 	
 	
 	case CHART_INCOME:
 	case CHART_INCOME:
 		plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.income; });
 		plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.income; });
@@ -193,7 +194,7 @@ std::shared_ptr<CIntObject> CStatisticScreen::getContent(Content c, EGameResID r
 	
 	
 	case CHART_NUMBER_OF_MINES:
 	case CHART_NUMBER_OF_MINES:
 		plotData = extractData(statistic, [res](StatisticDataSetEntry val) -> float { return val.numMines[res]; });
 		plotData = extractData(statistic, [res](StatisticDataSetEntry val) -> float { return val.numMines[res]; });
-		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", res.getNum()).get()), plotData, icons, 0);
+		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + res.toResource()->getNameTranslated(), plotData, icons, 0);
 	
 	
 	case CHART_ARMY_STRENGTH:
 	case CHART_ARMY_STRENGTH:
 		plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.armyStrength; });
 		plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.armyStrength; });
@@ -205,11 +206,11 @@ std::shared_ptr<CIntObject> CStatisticScreen::getContent(Content c, EGameResID r
 	
 	
 	case CHART_RESOURCES_SPENT_ARMY:
 	case CHART_RESOURCES_SPENT_ARMY:
 		plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.spentResourcesForArmy[res]; });
 		plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.spentResourcesForArmy[res]; });
-		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", res.getNum()).get()), plotData, icons, 0);
+		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + res.toResource()->getNameTranslated(), plotData, icons, 0);
 	
 	
 	case CHART_RESOURCES_SPENT_BUILDINGS:
 	case CHART_RESOURCES_SPENT_BUILDINGS:
 		plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.spentResourcesForBuildings[res]; });
 		plotData = extractData(statistic, [res](const StatisticDataSetEntry & val) -> float { return val.spentResourcesForBuildings[res]; });
-		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", res.getNum()).get()), plotData, icons, 0);
+		return std::make_shared<LineChart>(contentArea.resize(-5), LIBRARY->generaltexth->translate(std::get<0>(contentInfo[c])) + " - " + res.toResource()->getNameTranslated(), plotData, icons, 0);
 	
 	
 	case CHART_MAP_EXPLORED:
 	case CHART_MAP_EXPLORED:
 		plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.mapExploredRatio; });
 		plotData = extractData(statistic, [](const StatisticDataSetEntry & val) -> float { return val.mapExploredRatio; });
@@ -330,43 +331,43 @@ OverviewPanel::OverviewPanel(Rect position, std::string title, const StatisticDa
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::GOLD).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::GOLD).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::GOLD]);
 				return std::to_string(val.tradeVolume[EGameResID::GOLD]);
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::WOOD).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::WOOD).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::WOOD]);
 				return std::to_string(val.tradeVolume[EGameResID::WOOD]);
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::MERCURY).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::MERCURY).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::MERCURY]);
 				return std::to_string(val.tradeVolume[EGameResID::MERCURY]);
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::ORE).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::ORE).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::ORE]);
 				return std::to_string(val.tradeVolume[EGameResID::ORE]);
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::SULFUR).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::SULFUR).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::SULFUR]);
 				return std::to_string(val.tradeVolume[EGameResID::SULFUR]);
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::CRYSTAL).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::CRYSTAL).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::CRYSTAL]);
 				return std::to_string(val.tradeVolume[EGameResID::CRYSTAL]);
 			}
 			}
 		},
 		},
 		{
 		{
-			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", EGameResID::GEMS).get()), [this](PlayerColor color){
+			LIBRARY->generaltexth->translate("vcmi.statisticWindow.param.tradeVolume") + " - " + GameResID(EGameResID::GEMS).toResource()->getNameTranslated(), [this](PlayerColor color){
 				auto val = playerDataFilter(color).back();
 				auto val = playerDataFilter(color).back();
 				return std::to_string(val.tradeVolume[EGameResID::GEMS]);
 				return std::to_string(val.tradeVolume[EGameResID::GEMS]);
 			}
 			}

+ 0 - 1
client/mapView/MapViewCache.cpp

@@ -25,7 +25,6 @@
 #include "../GameEngine.h"
 #include "../GameEngine.h"
 #include "../widgets/TextControls.h"
 #include "../widgets/TextControls.h"
 
 
-#include "../../lib/mapObjects/CObjectHandler.h"
 #include "../../lib/int3.h"
 #include "../../lib/int3.h"
 
 
 MapViewCache::~MapViewCache() = default;
 MapViewCache::~MapViewCache() = default;

+ 2 - 0
client/renderSDL/RenderHandler.cpp

@@ -43,6 +43,7 @@
 #include <vcmi/Services.h>
 #include <vcmi/Services.h>
 #include <vcmi/SkillService.h>
 #include <vcmi/SkillService.h>
 #include <vcmi/spells/Service.h>
 #include <vcmi/spells/Service.h>
+#include <vcmi/ResourceTypeService.h>
 
 
 RenderHandler::RenderHandler()
 RenderHandler::RenderHandler()
 	:assetGenerator(std::make_unique<AssetGenerator>())
 	:assetGenerator(std::make_unique<AssetGenerator>())
@@ -494,6 +495,7 @@ void RenderHandler::onLibraryLoadingFinished(const Services * services)
 	addImageListEntries(services->factions());
 	addImageListEntries(services->factions());
 	addImageListEntries(services->spells());
 	addImageListEntries(services->spells());
 	addImageListEntries(services->skills());
 	addImageListEntries(services->skills());
+	addImageListEntries(services->resources());
 
 
 	if (settings["mods"]["validation"].String() == "full")
 	if (settings["mods"]["validation"].String() == "full")
 	{
 	{

+ 1 - 1
client/widgets/MiscWidgets.cpp

@@ -227,7 +227,7 @@ void CMinorResDataBar::showAll(Canvas & to)
 {
 {
 	CIntObject::showAll(to);
 	CIntObject::showAll(to);
 
 
-	for (GameResID i=EGameResID::WOOD; i<=EGameResID::GOLD; ++i)
+	for (GameResID i=EGameResID::WOOD; i<=EGameResID::GOLD; ++i) //todo: configurable resource support
 	{
 	{
 		std::string text = std::to_string(GAME->interface()->cb->getResourceAmount(i));
 		std::string text = std::to_string(GAME->interface()->cb->getResourceAmount(i));
 
 

+ 2 - 1
client/widgets/markets/TradePanels.cpp

@@ -23,6 +23,7 @@
 #include "../../../lib/entities/artifact/CArtHandler.h"
 #include "../../../lib/entities/artifact/CArtHandler.h"
 #include "../../../lib/texts/CGeneralTextHandler.h"
 #include "../../../lib/texts/CGeneralTextHandler.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 
 
 CTradeableItem::CTradeableItem(const Rect & area, EType Type, int32_t ID, int32_t serial)
 CTradeableItem::CTradeableItem(const Rect & area, EType Type, int32_t ID, int32_t serial)
 	: SelectableSlot(area, Point(1, 1))
 	: SelectableSlot(area, Point(1, 1))
@@ -175,7 +176,7 @@ void CTradeableItem::hover(bool on)
 			ENGINE->statusbar()->write(LIBRARY->artifacts()->getByIndex(id)->getNameTranslated());
 			ENGINE->statusbar()->write(LIBRARY->artifacts()->getByIndex(id)->getNameTranslated());
 		break;
 		break;
 	case EType::RESOURCE:
 	case EType::RESOURCE:
-		ENGINE->statusbar()->write(LIBRARY->generaltexth->restypes[id]);
+		ENGINE->statusbar()->write(GameResID(id).toResource()->getNameTranslated());
 		break;
 		break;
 	case EType::PLAYER:
 	case EType::PLAYER:
 		ENGINE->statusbar()->write(LIBRARY->generaltexth->capColors[id]);
 		ENGINE->statusbar()->write(LIBRARY->generaltexth->capColors[id]);

+ 6 - 4
client/windows/CCastleInterface.cpp

@@ -58,6 +58,7 @@
 #include "../../lib/campaign/CampaignState.h"
 #include "../../lib/campaign/CampaignState.h"
 #include "../../lib/entities/artifact/CArtifact.h"
 #include "../../lib/entities/artifact/CArtifact.h"
 #include "../../lib/entities/building/CBuilding.h"
 #include "../../lib/entities/building/CBuilding.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapObjects/TownBuildingInstance.h"
 #include "../../lib/mapObjects/TownBuildingInstance.h"
@@ -292,7 +293,7 @@ CDwellingInfoBox::CDwellingInfoBox(int centerX, int centerY, const CGTownInstanc
 	available = std::make_shared<CLabel>(80,190, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[217] + text);
 	available = std::make_shared<CLabel>(80,190, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[217] + text);
 	costPerTroop = std::make_shared<CLabel>(80, 227, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[346]);
 	costPerTroop = std::make_shared<CLabel>(80, 227, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->allTexts[346]);
 
 
-	for(int i = 0; i<GameConstants::RESOURCE_QUANTITY; i++)
+	for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		auto res = static_cast<EGameResID>(i);
 		auto res = static_cast<EGameResID>(i);
 		if(creature->getRecruitCost(res))
 		if(creature->getRecruitCost(res))
@@ -1126,7 +1127,7 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID:
 		else //Mystic Pond produced something;
 		else //Mystic Pond produced something;
 		{
 		{
 			descr += "\n\n" + hasProduced;
 			descr += "\n\n" + hasProduced;
-			boost::algorithm::replace_first(descr,"%s",LIBRARY->generaltexth->restypes[town->bonusValue.first]);
+			boost::algorithm::replace_first(descr,"%s",GameResID(town->bonusValue.first).toResource()->getNameTranslated());
 			boost::algorithm::replace_first(descr,"%d",std::to_string(town->bonusValue.second));
 			boost::algorithm::replace_first(descr,"%d",std::to_string(town->bonusValue.second));
 		}
 		}
 	}
 	}
@@ -1795,7 +1796,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 	//Create components for all required resources
 	//Create components for all required resources
 	std::vector<std::shared_ptr<CComponent>> components;
 	std::vector<std::shared_ptr<CComponent>> components;
 
 
-	for(GameResID i : GameResID::ALL_RESOURCES())
+	for(GameResID i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		if(building->resources[i])
 		if(building->resources[i])
 		{
 		{
@@ -2211,7 +2212,8 @@ void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition)
 			return;
 			return;
 		}
 		}
 
 
-		auto costBase = TResources(GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
+		ResourceSet costBase;
+		costBase.resolveFromJson(GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
 		auto costExponent = GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
 		auto costExponent = GAME->interface()->cb->getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
 		auto cost = costBase * std::pow(town->spellResearchAcceptedCounter + 1, costExponent);
 		auto cost = costBase * std::pow(town->spellResearchAcceptedCounter + 1, costExponent);
 
 

+ 2 - 2
client/windows/GUIClasses.cpp

@@ -46,6 +46,7 @@
 #include "../lib/entities/building/CBuilding.h"
 #include "../lib/entities/building/CBuilding.h"
 #include "../lib/entities/faction/CTownHandler.h"
 #include "../lib/entities/faction/CTownHandler.h"
 #include "../lib/entities/hero/CHeroHandler.h"
 #include "../lib/entities/hero/CHeroHandler.h"
+#include "../lib/entities/ResourceTypeHandler.h"
 #include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
 #include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
 #include "../lib/mapObjectConstructors/CommonConstructors.h"
 #include "../lib/mapObjectConstructors/CommonConstructors.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
@@ -830,7 +831,7 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat
 	build = std::make_shared<CButton>(Point(42, 312), AnimationPath::builtin("IBUY30"), CButton::tooltip(LIBRARY->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_ACCEPT);
 	build = std::make_shared<CButton>(Point(42, 312), AnimationPath::builtin("IBUY30"), CButton::tooltip(LIBRARY->generaltexth->allTexts[598]), std::bind(&CShipyardWindow::close, this), EShortcut::GLOBAL_ACCEPT);
 	build->addCallback(onBuy);
 	build->addCallback(onBuy);
 
 
-	for(GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i)
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		if(cost[i] > GAME->interface()->cb->getResourceAmount(i))
 		if(cost[i] > GAME->interface()->cb->getResourceAmount(i))
 		{
 		{
@@ -1215,7 +1216,6 @@ void CHillFortWindow::updateGarrisons()
 
 
 	for(int i=0; i<slotsCount; i++)
 	for(int i=0; i<slotsCount; i++)
 	{
 	{
-		std::fill(costs[i].begin(), costs[i].end(), 0);
 		State newState = getState(SlotID(i));
 		State newState = getState(SlotID(i));
 		if(newState != State::EMPTY)
 		if(newState != State::EMPTY)
 		{
 		{

+ 1 - 1
client/windows/GUIClasses.h

@@ -464,7 +464,7 @@ private:
 
 
 	enum class State { UNAFFORDABLE, ALREADY_UPGRADED, MAKE_UPGRADE, EMPTY, UNAVAILABLE };
 	enum class State { UNAFFORDABLE, ALREADY_UPGRADED, MAKE_UPGRADE, EMPTY, UNAVAILABLE };
 	static constexpr std::size_t slotsCount = 7;
 	static constexpr std::size_t slotsCount = 7;
-	//todo: mithril support
+	//todo: configurable resource support
 	static constexpr std::size_t resCount = 7;
 	static constexpr std::size_t resCount = 7;
 
 
 	const CGObjectInstance * fort;
 	const CGObjectInstance * fort;

+ 10 - 10
config/difficulty.json

@@ -4,31 +4,31 @@
 	{
 	{
 		"pawn":
 		"pawn":
 		{
 		{
-			"resources": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000, "mithril": 0 },
+			"resources": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"knight":
 		"knight":
 		{
 		{
-			"resources": { "wood" : 20, "mercury": 10, "ore": 20, "sulfur": 10, "crystal": 10, "gems": 10, "gold": 20000, "mithril": 0 },
+			"resources": { "wood" : 20, "mercury": 10, "ore": 20, "sulfur": 10, "crystal": 10, "gems": 10, "gold": 20000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"rook":
 		"rook":
 		{
 		{
-			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 15000, "mithril": 0 },
+			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 15000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"queen":
 		"queen":
 		{
 		{
-			"resources": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 10000, "mithril": 0 },
+			"resources": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 10000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"king":
 		"king":
 		{
 		{
-			"resources": { "wood" : 0, "mercury": 0, "ore": 0, "sulfur": 0, "crystal": 0, "gems": 0, "gold": 0, "mithril": 0 },
+			"resources": { "wood" : 0, "mercury": 0, "ore": 0, "sulfur": 0, "crystal": 0, "gems": 0, "gold": 0 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		}
 		}
@@ -37,31 +37,31 @@
 	{
 	{
 		"pawn":
 		"pawn":
 		{
 		{
-			"resources": { "wood" : 5, "mercury": 2, "ore": 5, "sulfur": 2, "crystal": 2, "gems": 2, "gold": 5000, "mithril": 0 },
+			"resources": { "wood" : 5, "mercury": 2, "ore": 5, "sulfur": 2, "crystal": 2, "gems": 2, "gold": 5000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"knight":
 		"knight":
 		{
 		{
-			"resources": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 7500, "mithril": 0 },
+			"resources": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 7500 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"rook":
 		"rook":
 		{
 		{
-			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 },
+			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"queen":
 		"queen":
 		{
 		{
-			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 },
+			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		},
 		},
 		"king":
 		"king":
 		{
 		{
-			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 },
+			"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000 },
 			"globalBonuses": [],
 			"globalBonuses": [],
 			"battleBonuses": []
 			"battleBonuses": []
 		}
 		}

+ 4 - 0
config/gameConfig.json

@@ -111,6 +111,10 @@
 	[
 	[
 		"config/terrains.json"
 		"config/terrains.json"
 	],
 	],
+	"resources" :
+	[
+		"config/resources.json"
+	],
 	"roads":
 	"roads":
 	[
 	[
 		"config/roads.json"
 		"config/roads.json"

+ 34 - 2
config/resources.json

@@ -1,4 +1,36 @@
 {
 {
-	// Price of each resource in gold, in usual resource order
-	"resources_prices": [ 250, 500, 250, 500, 500, 500, 1, 0 ]
+	"wood": {
+		"index" : 0,
+		"price": 250
+	},
+	
+	"mercury": {
+		"index" : 1,
+		"price": 500
+	},
+	
+	"ore": {
+		"index" : 2,
+		"price": 250
+	},
+	
+	"sulfur": {
+		"index" : 3,
+		"price": 500
+	},
+	
+	"crystal": {
+		"index" : 4,
+		"price": 500
+	},
+	
+	"gems": {
+		"index" : 5,
+		"price": 500
+	},
+	
+	"gold": {
+		"index" : 6,
+		"price": 1
+	}
 }
 }

+ 4 - 11
config/schemas/creature.json

@@ -93,17 +93,10 @@
 		},
 		},
 		"cost" : {
 		"cost" : {
 			"type" : "object",
 			"type" : "object",
-			"additionalProperties" : false,
-			"description" : "Cost to recruit this creature",
-			"properties" : {
-				"gold" :    { "type" : "number"},
-				"wood" :    { "type" : "number"},
-				"ore" :     { "type" : "number"},
-				"mercury" : { "type" : "number"},
-				"sulfur" :  { "type" : "number"},
-				"crystal" : { "type" : "number"},
-				"gems" :    { "type" : "number"}
-			}
+			"additionalProperties" : {
+				"type" : "number"
+			},
+			"description" : "Cost to recruit this creature"
 		},
 		},
 		"speed" :     { "type" : "number" },
 		"speed" :     { "type" : "number" },
 		"hitPoints" : { "type" : "number" },
 		"hitPoints" : { "type" : "number" },

+ 3 - 10
config/schemas/flaggable.json

@@ -35,17 +35,10 @@
 
 
 		"dailyIncome" : {
 		"dailyIncome" : {
 			"type" : "object",
 			"type" : "object",
-			"additionalProperties" : false,
+			"additionalProperties" : {
+				"type" : "number"
+			},
 			"description" : "Daily income that this building provides to owner, if any",
 			"description" : "Daily income that this building provides to owner, if any",
-			"properties" : {
-				"gold" :    { "type" : "number"},
-				"wood" :    { "type" : "number"},
-				"ore" :     { "type" : "number"},
-				"mercury" : { "type" : "number"},
-				"sulfur" :  { "type" : "number"},
-				"crystal" : { "type" : "number"},
-				"gems" :    { "type" : "number"}
-			}
 		},
 		},
 
 
 		// Properties that might appear since this node is shared with object config
 		// Properties that might appear since this node is shared with object config

+ 5 - 1
config/schemas/mod.json

@@ -65,7 +65,7 @@
 		},
 		},
 		"modType" : {
 		"modType" : {
 			"type" : "string",
 			"type" : "string",
-			"enum" : [ "Translation", "Town", "Test", "Templates", "Spells", "Music", "Maps", "Sounds", "Skills", "Other", "Objects", "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Compatibility", "Campaigns", "Artifacts", "AI" ],
+			"enum" : [ "Translation", "Town", "Test", "Templates", "Spells", "Music", "Maps", "Sounds", "Skills", "Other", "Objects", "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Compatibility", "Campaigns", "Artifacts", "AI", "Resources" ],
 			"description" : "Type of mod, e.g. Town, Artifacts, Graphical."
 			"description" : "Type of mod, e.g. Town, Artifacts, Graphical."
 		},
 		},
 		"author" : {
 		"author" : {
@@ -303,6 +303,10 @@
 			"description" : "List of configuration files for terrains",
 			"description" : "List of configuration files for terrains",
 			"$ref" : "#/definitions/fileListOrObject"
 			"$ref" : "#/definitions/fileListOrObject"
 		},
 		},
+		"resources" : {
+			"description" : "List of configuration files for resources",
+			"$ref" : "#/definitions/fileListOrObject"
+		},
 		"roads" : {
 		"roads" : {
 			"description" : "List of configuration files for roads",
 			"description" : "List of configuration files for roads",
 			"$ref" : "#/definitions/fileListOrObject"
 			"$ref" : "#/definitions/fileListOrObject"

+ 44 - 0
config/schemas/resources.json

@@ -0,0 +1,44 @@
+{
+	"type" : "object",
+	"$schema" : "http://json-schema.org/draft-04/schema",
+	"title" : "VCMI resources format",
+	"description" : "Format used to define new resources in VCMI",
+	"required" : [ "name", "price" ],
+	"additionalProperties" : false,
+	"properties" : {
+		"index" : {
+			"type" : "number",
+			"description" : "numeric id of h3 resource, prohibited for new resources"
+		},
+		"name" : {
+			"type" : "string",
+			"description" : "Localizable name of this resource"
+		},
+		"images" : {
+			"type" : "object",
+			"description" : "Resource icons of varying size",
+			"required" : [ "small", "medium", "large"],
+			"properties" : {
+				"small" : {
+					"type" : "string",
+					"description" : "20x18 resource icon",
+					"format" : "imageFile"
+				},
+				"medium" : {
+					"type" : "string",
+					"description" : "32x32 resource icon",
+					"format" : "imageFile"
+				},
+				"large" : {
+					"type" : "string",
+					"description" : "82x93 resource icon",
+					"format" : "imageFile"
+				}
+			}
+		},
+		"price" : {
+			"type" : "number",
+			"description" : "Price of resource in gold"
+		}
+	}
+}

+ 2 - 9
config/schemas/template.json

@@ -139,15 +139,8 @@
 		},
 		},
 		"mines" : {
 		"mines" : {
 			"type" : "object",
 			"type" : "object",
-			"additionalProperties" : false,
-			"properties" : {
-				"gold" :    { "type" : "number"},
-				"wood" :    { "type" : "number"},
-				"ore" :     { "type" : "number"},
-				"mercury" : { "type" : "number"},
-				"sulfur" :  { "type" : "number"},
-				"crystal" : { "type" : "number"},
-				"gems" :    { "type" : "number"}
+			"additionalProperties" : {
+				"type" : "number"
 			}
 			}
 		},
 		},
 		"connection" :
 		"connection" :

+ 8 - 22
config/schemas/townBuilding.json

@@ -86,31 +86,17 @@
 		},
 		},
 		"cost" : {
 		"cost" : {
 			"type" : "object",
 			"type" : "object",
-			"additionalProperties" : false,
-			"description" : "Resources needed to build building",
-			"properties" : {
-				"gold" :    { "type" : "number"},
-				"wood" :    { "type" : "number"},
-				"ore" :     { "type" : "number"},
-				"mercury" : { "type" : "number"},
-				"sulfur" :  { "type" : "number"},
-				"crystal" : { "type" : "number"},
-				"gems" :    { "type" : "number"}
-			}
+			"additionalProperties" : {
+				"type" : "number"
+			},
+			"description" : "Resources needed to build building"
 		},
 		},
 		"produce" : {
 		"produce" : {
 			"type" : "object",
 			"type" : "object",
-			"additionalProperties" : false,
-			"description" : "Resources produced each day by this building",
-			"properties" : {
-				"gold" :    { "type" : "number"},
-				"wood" :    { "type" : "number"},
-				"ore" :     { "type" : "number"},
-				"mercury" : { "type" : "number"},
-				"sulfur" :  { "type" : "number"},
-				"crystal" : { "type" : "number"},
-				"gems" :    { "type" : "number"}
-			}
+			"additionalProperties" : {
+				"type" : "number"
+			},
+			"description" : "Resources produced each day by this building"
 		},
 		},
 		"warMachine" : {
 		"warMachine" : {
 			"type" : "string",
 			"type" : "string",

+ 1 - 1
docs/modders/Difficulty.md

@@ -14,7 +14,7 @@ Difficulty configuration is located in [config/difficulty.json](../../config/dif
 		"pawn": //parameters for specific difficulty
 		"pawn": //parameters for specific difficulty
 		{
 		{
 			//starting resources
 			//starting resources
-			"resources": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000, "mithril": 0 },
+			"resources": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000 },
 			//bonuses will be given to player globally
 			//bonuses will be given to player globally
 			"globalBonuses": [],
 			"globalBonuses": [],
 			//bonuses will be given to player every battle
 			//bonuses will be given to player every battle

+ 20 - 0
docs/modders/Entities_Format/Resource_Format.md

@@ -0,0 +1,20 @@
+# Resource Format
+
+```json
+	// Internal field for H3 resources. Do not use for mods
+	"index" : "",
+
+	// displayed name of the resource
+	"name" : "",
+	
+	// Resource icons of varying size
+	"images" : {
+		// 20x18 resource icon
+		"small" : "",
+		// 32x32 resource icon
+		"medium" : "",
+		// 82x93 resource icon
+		"large" : ""
+	}
+
+```

+ 0 - 1
docs/modders/Game_Identifiers.md

@@ -568,7 +568,6 @@ Deprecated, please use primarySkill instead
 - resource.gems
 - resource.gems
 - resource.gold
 - resource.gold
 - resource.mercury
 - resource.mercury
-- resource.mithril
 - resource.ore
 - resource.ore
 - resource.sulfur
 - resource.sulfur
 - resource.wood
 - resource.wood

+ 1 - 1
docs/modders/Mod_File_Format.md

@@ -31,7 +31,7 @@
 
 
 	// Type of mod, list of all possible values:
 	// Type of mod, list of all possible values:
 	// "Translation", "Town", "Test", "Templates", "Spells", "Music", "Maps", "Sounds", "Skills", "Other", "Objects", 
 	// "Translation", "Town", "Test", "Templates", "Spells", "Music", "Maps", "Sounds", "Skills", "Other", "Objects", 
-	// "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Compatibility", "Campaigns", "Artifacts", "AI"
+	// "Mechanics", "Interface", "Heroes", "Graphical", "Expansion", "Creatures", "Compatibility", "Campaigns", "Artifacts", "AI", "Resources"
 	//
 	//
 	// Some mod types have additional effects on your mod:
 	// Some mod types have additional effects on your mod:
 	// Translation: mod of this type is only active if player uses base language of this mod. See "language" property. 
 	// Translation: mod of this type is only active if player uses base language of this mod. See "language" property. 

+ 0 - 4
docs/players/Game_Mechanics.md

@@ -41,10 +41,6 @@ Stack experience interface has been merged with regular creature window. Among o
 
 
 VCMI offers native support for Commanders. Commanders are part of WoG mod for VCMI and require it to be enabled. However, once this is done, any new faction can use its own Commander, too.
 VCMI offers native support for Commanders. Commanders are part of WoG mod for VCMI and require it to be enabled. However, once this is done, any new faction can use its own Commander, too.
 
 
-### Mithril module
-
-VCMI natively supports Mithril resource known from WoG. However, it is not currently used by any mod.
-
 ### Stack Artifact module
 ### Stack Artifact module
 
 
 In original WoG, there is one available Stack Artifact - Warlord's Banner, which is related directly to stack experience. VCMI natively supports any number of Stack Artifacts regardless if of Stack Experience module is enabled or not. However, currently no mods make use of this feature and it hasn't been tested for many years.
 In original WoG, there is one available Stack Artifact - Warlord's Banner, which is related directly to stack experience. VCMI natively supports any number of Stack Artifacts regardless if of Stack Experience module is enabled or not. However, currently no mods make use of this feature and it hasn't been tested for many years.

+ 25 - 0
include/vcmi/ResourceType.h

@@ -0,0 +1,25 @@
+/*
+ * ResourceType.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 "Entity.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class GameResID;
+
+class DLL_LINKAGE ResourceType : public EntityT<GameResID>
+{
+	virtual int getPrice() const = 0;
+};
+
+
+VCMI_LIB_NAMESPACE_END

+ 6 - 8
lib/mapObjects/CObjectHandler.h → include/vcmi/ResourceTypeService.h

@@ -1,5 +1,5 @@
 /*
 /*
- * CObjectHandler.h, part of VCMI engine
+ * ResourceTypeService.h, part of VCMI engine
  *
  *
  * Authors: listed in file AUTHORS in main folder
  * Authors: listed in file AUTHORS in main folder
  *
  *
@@ -7,21 +7,19 @@
  * Full text of license available in license.txt file, in main folder
  * Full text of license available in license.txt file, in main folder
  *
  *
  */
  */
+
 #pragma once
 #pragma once
 
 
-#include "../GameConstants.h"
+#include "EntityService.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-class CGObjectInstance;
-class int3;
+class GameResID;
+class ResourceType;
 
 
-class DLL_LINKAGE CObjectHandler
+class DLL_LINKAGE ResourceTypeService : public EntityServiceT<GameResID, ResourceType>
 {
 {
 public:
 public:
-	std::vector<ui32> resVals; //default values of resources in gold
-
-	CObjectHandler();
 };
 };
 
 
 VCMI_LIB_NAMESPACE_END
 VCMI_LIB_NAMESPACE_END

+ 2 - 0
include/vcmi/Services.h

@@ -19,6 +19,7 @@ class CreatureService;
 class FactionService;
 class FactionService;
 class HeroClassService;
 class HeroClassService;
 class HeroTypeService;
 class HeroTypeService;
+class ResourceTypeService;
 class SkillService;
 class SkillService;
 class JsonNode;
 class JsonNode;
 class BattleFieldService;
 class BattleFieldService;
@@ -52,6 +53,7 @@ public:
 	virtual const FactionService * factions() const = 0;
 	virtual const FactionService * factions() const = 0;
 	virtual const HeroClassService * heroClasses() const = 0;
 	virtual const HeroClassService * heroClasses() const = 0;
 	virtual const HeroTypeService * heroTypes() const = 0;
 	virtual const HeroTypeService * heroTypes() const = 0;
+	virtual const ResourceTypeService * resources() const = 0;
 #if SCRIPTING_ENABLED
 #if SCRIPTING_ENABLED
 	virtual const scripting::Service * scripts() const = 0;
 	virtual const scripting::Service * scripts() const = 0;
 #endif
 #endif

+ 1 - 0
launcher/modManager/modstateitemmodel_moc.cpp

@@ -54,6 +54,7 @@ QString ModStateItemModel::modTypeName(QString modTypeID) const
 		QT_TR_NOOP("Campaigns"),
 		QT_TR_NOOP("Campaigns"),
 		QT_TR_NOOP("Artifacts"),
 		QT_TR_NOOP("Artifacts"),
 		QT_TR_NOOP("AI"),
 		QT_TR_NOOP("AI"),
+		QT_TR_NOOP("Resources"),
 	};
 	};
 
 
 	if (modTypes.contains(modTypeID))
 	if (modTypes.contains(modTypeID))

+ 2 - 2
lib/CCreatureHandler.cpp

@@ -568,7 +568,7 @@ std::vector<JsonNode> CCreatureHandler::loadLegacyData()
 
 
 		data["name"]["plural"].String() =  parser.readString();
 		data["name"]["plural"].String() =  parser.readString();
 
 
-		for(int v=0; v<7; ++v)
+		for(int v=0; v<GameConstants::RESOURCE_QUANTITY; ++v)
 			data["cost"][GameConstants::RESOURCE_NAMES[v]].Float() = parser.readNumber();
 			data["cost"][GameConstants::RESOURCE_NAMES[v]].Float() = parser.readNumber();
 
 
 		data["fightValue"].Float() = parser.readNumber();
 		data["fightValue"].Float() = parser.readNumber();
@@ -623,7 +623,7 @@ std::shared_ptr<CCreature> CCreatureHandler::loadFromJson(const std::string & sc
 	JsonDeserializer handler(nullptr, node);
 	JsonDeserializer handler(nullptr, node);
 	cre->serializeJson(handler);
 	cre->serializeJson(handler);
 
 
-	cre->cost = ResourceSet(node["cost"]);
+	cre->cost.resolveFromJson(node["cost"]);
 
 
 	LIBRARY->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"]);
 	LIBRARY->generaltexth->registerString(scope, cre->getNameSingularTextID(), node["name"]["singular"]);
 	LIBRARY->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"]);
 	LIBRARY->generaltexth->registerString(scope, cre->getNamePluralTextID(), node["name"]["plural"]);

+ 4 - 2
lib/CMakeLists.txt

@@ -111,6 +111,7 @@ set(lib_MAIN_SRCS
 	entities/hero/CHeroClass.cpp
 	entities/hero/CHeroClass.cpp
 	entities/hero/CHeroClassHandler.cpp
 	entities/hero/CHeroClassHandler.cpp
 	entities/hero/CHeroHandler.cpp
 	entities/hero/CHeroHandler.cpp
+	entities/ResourceTypeHandler.cpp
 
 
 	events/ApplyDamage.cpp
 	events/ApplyDamage.cpp
 	events/GameResumed.cpp
 	events/GameResumed.cpp
@@ -152,7 +153,6 @@ set(lib_MAIN_SRCS
 	mapObjects/CGResource.cpp
 	mapObjects/CGResource.cpp
 	mapObjects/TownBuildingInstance.cpp
 	mapObjects/TownBuildingInstance.cpp
 	mapObjects/CGTownInstance.cpp
 	mapObjects/CGTownInstance.cpp
-	mapObjects/CObjectHandler.cpp
 	mapObjects/CQuest.cpp
 	mapObjects/CQuest.cpp
 	mapObjects/CRewardableObject.cpp
 	mapObjects/CRewardableObject.cpp
 	mapObjects/FlaggableMapObject.cpp
 	mapObjects/FlaggableMapObject.cpp
@@ -428,6 +428,8 @@ set(lib_MAIN_HEADERS
 	../include/vcmi/HeroClassService.h
 	../include/vcmi/HeroClassService.h
 	../include/vcmi/HeroType.h
 	../include/vcmi/HeroType.h
 	../include/vcmi/HeroTypeService.h
 	../include/vcmi/HeroTypeService.h
+	../include/vcmi/ResourceType.h
+	../include/vcmi/ResourceTypeService.h
 	../include/vcmi/Metatype.h
 	../include/vcmi/Metatype.h
 	../include/vcmi/Player.h
 	../include/vcmi/Player.h
 	../include/vcmi/ServerCallback.h
 	../include/vcmi/ServerCallback.h
@@ -534,6 +536,7 @@ set(lib_MAIN_HEADERS
 	entities/hero/CHeroClassHandler.h
 	entities/hero/CHeroClassHandler.h
 	entities/hero/CHeroHandler.h
 	entities/hero/CHeroHandler.h
 	entities/hero/EHeroGender.h
 	entities/hero/EHeroGender.h
+	entities/ResourceTypeHandler.h
 
 
 	events/ApplyDamage.h
 	events/ApplyDamage.h
 	events/GameResumed.h
 	events/GameResumed.h
@@ -581,7 +584,6 @@ set(lib_MAIN_HEADERS
 	mapObjects/TownBuildingInstance.h
 	mapObjects/TownBuildingInstance.h
 	mapObjects/CGResource.h
 	mapObjects/CGResource.h
 	mapObjects/CGTownInstance.h
 	mapObjects/CGTownInstance.h
-	mapObjects/CObjectHandler.h
 	mapObjects/CQuest.h
 	mapObjects/CQuest.h
 	mapObjects/CRewardableObject.h
 	mapObjects/CRewardableObject.h
 	mapObjects/FlaggableMapObject.h
 	mapObjects/FlaggableMapObject.h

+ 1 - 1
lib/CPlayerState.cpp

@@ -98,7 +98,7 @@ const IBonusBearer * PlayerState::getBonusBearer() const
 
 
 int PlayerState::getResourceAmount(int type) const
 int PlayerState::getResourceAmount(int type) const
 {
 {
-	return vstd::atOrDefault(resources, static_cast<size_t>(type), 0);
+	return resources[type];
 }
 }
 
 
 template<typename T>
 template<typename T>

+ 7 - 2
lib/GameLibrary.cpp

@@ -25,6 +25,7 @@
 #include "entities/faction/CTownHandler.h"
 #include "entities/faction/CTownHandler.h"
 #include "entities/hero/CHeroClassHandler.h"
 #include "entities/hero/CHeroClassHandler.h"
 #include "entities/hero/CHeroHandler.h"
 #include "entities/hero/CHeroHandler.h"
+#include "entities/ResourceTypeHandler.h"
 #include "texts/CGeneralTextHandler.h"
 #include "texts/CGeneralTextHandler.h"
 #include "campaign/CampaignRegionsHandler.h"
 #include "campaign/CampaignRegionsHandler.h"
 #include "mapping/MapFormatSettings.h"
 #include "mapping/MapFormatSettings.h"
@@ -36,7 +37,6 @@
 #include "filesystem/Filesystem.h"
 #include "filesystem/Filesystem.h"
 #include "rmg/CRmgTemplateStorage.h"
 #include "rmg/CRmgTemplateStorage.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
-#include "mapObjects/CObjectHandler.h"
 #include "mapObjects/ObstacleSetHandler.h"
 #include "mapObjects/ObstacleSetHandler.h"
 #include "mapping/CMapEditManager.h"
 #include "mapping/CMapEditManager.h"
 #include "ScriptHandler.h"
 #include "ScriptHandler.h"
@@ -75,6 +75,11 @@ const HeroTypeService * GameLibrary::heroTypes() const
 	return heroh.get();
 	return heroh.get();
 }
 }
 
 
+const ResourceTypeService * GameLibrary::resources() const
+{
+	return resourceTypeHandler.get();
+}
+
 #if SCRIPTING_ENABLED
 #if SCRIPTING_ENABLED
 const scripting::Service * GameLibrary::scripts() const
 const scripting::Service * GameLibrary::scripts() const
 {
 {
@@ -171,6 +176,7 @@ void GameLibrary::initializeLibrary()
 
 
 	createHandler(generaltexth);
 	createHandler(generaltexth);
 	createHandler(bth);
 	createHandler(bth);
+	createHandler(resourceTypeHandler);
 	createHandler(roadTypeHandler);
 	createHandler(roadTypeHandler);
 	createHandler(riverTypeHandler);
 	createHandler(riverTypeHandler);
 	createHandler(terrainTypeHandler);
 	createHandler(terrainTypeHandler);
@@ -180,7 +186,6 @@ void GameLibrary::initializeLibrary()
 	createHandler(creh);
 	createHandler(creh);
 	createHandler(townh);
 	createHandler(townh);
 	createHandler(biomeHandler);
 	createHandler(biomeHandler);
-	createHandler(objh);
 	createHandler(objtypeh);
 	createHandler(objtypeh);
 	createHandler(spellSchoolHandler);
 	createHandler(spellSchoolHandler);
 	createHandler(spellh);
 	createHandler(spellh);

+ 3 - 3
lib/GameLibrary.h

@@ -20,7 +20,6 @@ class CHeroClassHandler;
 class CCreatureHandler;
 class CCreatureHandler;
 class CSpellHandler;
 class CSpellHandler;
 class CSkillHandler;
 class CSkillHandler;
-class CObjectHandler;
 class CObjectClassesHandler;
 class CObjectClassesHandler;
 class ObstacleSetHandler;
 class ObstacleSetHandler;
 class CTownHandler;
 class CTownHandler;
@@ -31,6 +30,7 @@ class BattleFieldHandler;
 class IBonusTypeHandler;
 class IBonusTypeHandler;
 class CBonusTypeHandler;
 class CBonusTypeHandler;
 class TerrainTypeHandler;
 class TerrainTypeHandler;
+class ResourceTypeHandler;
 class RoadTypeHandler;
 class RoadTypeHandler;
 class RiverTypeHandler;
 class RiverTypeHandler;
 class ObstacleHandler;
 class ObstacleHandler;
@@ -60,6 +60,7 @@ public:
 	const FactionService * factions() const override;
 	const FactionService * factions() const override;
 	const HeroClassService * heroClasses() const override;
 	const HeroClassService * heroClasses() const override;
 	const HeroTypeService * heroTypes() const override;
 	const HeroTypeService * heroTypes() const override;
+	const ResourceTypeService * resources() const override;
 #if SCRIPTING_ENABLED
 #if SCRIPTING_ENABLED
 	const scripting::Service * scripts() const override;
 	const scripting::Service * scripts() const override;
 #endif
 #endif
@@ -83,13 +84,12 @@ public:
 	std::unique_ptr<CSpellHandler> spellh;
 	std::unique_ptr<CSpellHandler> spellh;
 	std::unique_ptr<SpellSchoolHandler> spellSchoolHandler;
 	std::unique_ptr<SpellSchoolHandler> spellSchoolHandler;
 	std::unique_ptr<CSkillHandler> skillh;
 	std::unique_ptr<CSkillHandler> skillh;
-	// TODO: Remove ObjectHandler altogether?
-	std::unique_ptr<CObjectHandler> objh;
 	std::unique_ptr<CObjectClassesHandler> objtypeh;
 	std::unique_ptr<CObjectClassesHandler> objtypeh;
 	std::unique_ptr<CTownHandler> townh;
 	std::unique_ptr<CTownHandler> townh;
 	std::unique_ptr<CGeneralTextHandler> generaltexth;
 	std::unique_ptr<CGeneralTextHandler> generaltexth;
 	std::unique_ptr<CModHandler> modh;
 	std::unique_ptr<CModHandler> modh;
 	std::unique_ptr<TerrainTypeHandler> terrainTypeHandler;
 	std::unique_ptr<TerrainTypeHandler> terrainTypeHandler;
+	std::unique_ptr<ResourceTypeHandler> resourceTypeHandler;
 	std::unique_ptr<RoadTypeHandler> roadTypeHandler;
 	std::unique_ptr<RoadTypeHandler> roadTypeHandler;
 	std::unique_ptr<RiverTypeHandler> riverTypeHandler;
 	std::unique_ptr<RiverTypeHandler> riverTypeHandler;
 	std::unique_ptr<CIdentifierStorage> identifiersHandler;
 	std::unique_ptr<CIdentifierStorage> identifiersHandler;

+ 30 - 15
lib/ResourceSet.cpp

@@ -13,17 +13,34 @@
 #include "ResourceSet.h"
 #include "ResourceSet.h"
 #include "constants/StringConstants.h"
 #include "constants/StringConstants.h"
 #include "serializer/JsonSerializeFormat.h"
 #include "serializer/JsonSerializeFormat.h"
-#include "mapObjects/CObjectHandler.h"
+#include "entities/ResourceTypeHandler.h"
 #include "GameLibrary.h"
 #include "GameLibrary.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
-ResourceSet::ResourceSet() = default;
+ResourceSet::ResourceSet()
+{
+	resizeContainer();
+};
+
+ResourceSet::ResourceSet(const ResourceSet& rhs)
+	: container(rhs.container) // vector copy constructor
+{
+	resizeContainer();
+}
+
+void ResourceSet::resizeContainer()
+{
+	container.resize(std::max(static_cast<int>(LIBRARY->resourceTypeHandler->getAllObjects().size()), GameConstants::RESOURCE_QUANTITY));
+}
 
 
-ResourceSet::ResourceSet(const JsonNode & node)
+void ResourceSet::resolveFromJson(const JsonNode & node)
 {
 {
-	for(auto i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
-		container[i] = static_cast<int>(node[GameConstants::RESOURCE_NAMES[i]].Float());
+	for(auto & n : node.Struct())
+		LIBRARY->identifiers()->requestIdentifier(n.second.getModScope(), "resource", n.first, [n, this](int32_t identifier)
+		{
+			(*this)[identifier] = static_cast<int>(n.second.Float());
+		});
 }
 }
 
 
 void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
 void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
@@ -32,9 +49,8 @@ void ResourceSet::serializeJson(JsonSerializeFormat & handler, const std::string
 		return;
 		return;
 	auto s = handler.enterStruct(fieldName);
 	auto s = handler.enterStruct(fieldName);
 
 
-	//TODO: add proper support for mithril to map format
-	for(int idx = 0; idx < GameConstants::RESOURCE_QUANTITY - 1; idx ++)
-		handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], this->operator[](idx), 0);
+	for(auto & idx : LIBRARY->resourceTypeHandler->getAllObjects())
+		handler.serializeInt(idx.toResource()->getJsonKey(), this->operator[](idx), 0);
 }
 }
 
 
 bool ResourceSet::nonZero() const
 bool ResourceSet::nonZero() const
@@ -76,8 +92,7 @@ void ResourceSet::applyHandicap(int percentage)
 
 
 static bool canAfford(const ResourceSet &res, const ResourceSet &price)
 static bool canAfford(const ResourceSet &res, const ResourceSet &price)
 {
 {
-	assert(res.size() == price.size() && price.size() == GameConstants::RESOURCE_QUANTITY);
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 		if(price[i] > res[i])
 		if(price[i] > res[i])
 			return false;
 			return false;
 
 
@@ -97,8 +112,8 @@ bool ResourceSet::canAfford(const ResourceSet &price) const
 TResourceCap ResourceSet::marketValue() const
 TResourceCap ResourceSet::marketValue() const
 {
 {
 	TResourceCap total = 0;
 	TResourceCap total = 0;
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; i++)
-		total += static_cast<TResourceCap>(LIBRARY->objh->resVals[i]) * static_cast<TResourceCap>(operator[](i));
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
+		total += static_cast<TResourceCap>(i.toResource()->getPrice()) * static_cast<TResourceCap>(operator[](i));
 	return total;
 	return total;
 }
 }
 
 
@@ -117,7 +132,7 @@ std::string ResourceSet::toString() const
 
 
 bool ResourceSet::nziterator::valid() const
 bool ResourceSet::nziterator::valid() const
 {
 {
-	return cur.resType < GameResID::COUNT && cur.resVal;
+	return static_cast<int>(cur.resType) < LIBRARY->resourceTypeHandler->getAllObjects().size() && cur.resVal;
 }
 }
 
 
 ResourceSet::nziterator ResourceSet::nziterator::operator++()
 ResourceSet::nziterator ResourceSet::nziterator::operator++()
@@ -148,9 +163,9 @@ void ResourceSet::nziterator::advance()
 	do
 	do
 	{
 	{
 		++cur.resType;
 		++cur.resType;
-	} while(cur.resType < GameResID::COUNT && !(cur.resVal=rs[cur.resType]));
+	} while(static_cast<int>(cur.resType) < LIBRARY->resourceTypeHandler->getAllObjects().size() && !(cur.resVal=rs[cur.resType]));
 
 
-	if(cur.resType >= GameResID::COUNT)
+	if(static_cast<int>(cur.resType) >= LIBRARY->resourceTypeHandler->getAllObjects().size())
 		cur.resVal = -1;
 		cur.resVal = -1;
 }
 }
 
 

+ 26 - 3
lib/ResourceSet.h

@@ -26,16 +26,19 @@ class ResourceSet;
 class ResourceSet
 class ResourceSet
 {
 {
 private:
 private:
-	std::array<TResource, GameConstants::RESOURCE_QUANTITY> container = {};
+	std::vector<TResource> container = {};
+	DLL_LINKAGE void resizeContainer();
 public:
 public:
-	// read resources set from json. Format example: { "gold": 500, "wood":5 }
-	DLL_LINKAGE ResourceSet(const JsonNode & node);
 	DLL_LINKAGE ResourceSet();
 	DLL_LINKAGE ResourceSet();
+	DLL_LINKAGE ResourceSet(const ResourceSet& rhs);
+
+	DLL_LINKAGE void resolveFromJson(const JsonNode & node);
 
 
 
 
 #define scalarOperator(OPSIGN)									\
 #define scalarOperator(OPSIGN)									\
 	ResourceSet& operator OPSIGN ## =(const TResource &rhs) \
 	ResourceSet& operator OPSIGN ## =(const TResource &rhs) \
 	{														\
 	{														\
+		resizeContainer(); \
 		for(auto i = 0; i < container.size(); i++)						\
 		for(auto i = 0; i < container.size(); i++)						\
 			container.at(i) OPSIGN ## = rhs;						\
 			container.at(i) OPSIGN ## = rhs;						\
 															\
 															\
@@ -45,6 +48,7 @@ public:
 #define vectorOperator(OPSIGN)										\
 #define vectorOperator(OPSIGN)										\
 	ResourceSet& operator OPSIGN ## =(const ResourceSet &rhs)	\
 	ResourceSet& operator OPSIGN ## =(const ResourceSet &rhs)	\
 	{															\
 	{															\
+		resizeContainer(); \
 		for(auto i = 0; i < container.size(); i++)							\
 		for(auto i = 0; i < container.size(); i++)							\
 			container.at(i) OPSIGN ## = rhs[i];						\
 			container.at(i) OPSIGN ## = rhs[i];						\
 																\
 																\
@@ -84,21 +88,31 @@ public:
 	// Array-like interface
 	// Array-like interface
 	TResource & operator[](GameResID index)
 	TResource & operator[](GameResID index)
 	{
 	{
+		resizeContainer();
 		return operator[](index.getNum());
 		return operator[](index.getNum());
 	}
 	}
 
 
 	const TResource & operator[](GameResID index) const 
 	const TResource & operator[](GameResID index) const 
 	{
 	{
+		if (index.getNum() >= container.size()) {
+			static const TResource defaultValue{};
+			return defaultValue;
+		}
 		return operator[](index.getNum());
 		return operator[](index.getNum());
 	}
 	}
 
 
 	TResource & operator[](size_t index)
 	TResource & operator[](size_t index)
 	{
 	{
+		resizeContainer();
 		return container.at(index);
 		return container.at(index);
 	}
 	}
 
 
 	const TResource & operator[](size_t index) const 
 	const TResource & operator[](size_t index) const 
 	{
 	{
+		if (index >= container.size()) {
+			static const TResource defaultValue{};
+			return defaultValue;
+		}
 		return container.at(index);
 		return container.at(index);
 	}
 	}
 
 
@@ -176,6 +190,15 @@ public:
 		return *this;
 		return *this;
 	}
 	}
 
 
+	ResourceSet& operator=(const ResourceSet& rhs)
+	{
+		if (this != &rhs)
+		{
+			container = rhs.container;
+		}
+		return *this;
+	}
+
 	ResourceSet operator-() const
 	ResourceSet operator-() const
 	{
 	{
 		ResourceSet ret;
 		ResourceSet ret;

+ 14 - 16
lib/constants/EntityIdentifiers.cpp

@@ -21,6 +21,8 @@
 #include <vcmi/HeroTypeService.h>
 #include <vcmi/HeroTypeService.h>
 #include <vcmi/HeroClass.h>
 #include <vcmi/HeroClass.h>
 #include <vcmi/HeroClassService.h>
 #include <vcmi/HeroClassService.h>
+#include <vcmi/ResourceType.h>
+#include <vcmi/ResourceTypeService.h>
 #include <vcmi/Services.h>
 #include <vcmi/Services.h>
 
 
 #include <vcmi/spells/Spell.h>
 #include <vcmi/spells/Spell.h>
@@ -37,6 +39,7 @@
 #include "entities/faction/CFaction.h"
 #include "entities/faction/CFaction.h"
 #include "entities/hero/CHero.h"
 #include "entities/hero/CHero.h"
 #include "entities/hero/CHeroClass.h"
 #include "entities/hero/CHeroClass.h"
+#include "entities/ResourceTypeHandler.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "constants/StringConstants.h"
 #include "constants/StringConstants.h"
 #include "texts/CGeneralTextHandler.h"
 #include "texts/CGeneralTextHandler.h"
@@ -398,6 +401,16 @@ const HeroType * HeroTypeID::toEntity(const Services * services) const
 	return services->heroTypes()->getByIndex(num);
 	return services->heroTypes()->getByIndex(num);
 }
 }
 
 
+const Resource * GameResID::toResource() const
+{
+	return dynamic_cast<const Resource*>(toEntity(LIBRARY));
+}
+
+const ResourceType * GameResID::toEntity(const Services * services) const
+{
+	return services->resources()->getByIndex(num);
+}
+
 si32 SpellID::decode(const std::string & identifier)
 si32 SpellID::decode(const std::string & identifier)
 {
 {
 	if (identifier == "preset")
 	if (identifier == "preset")
@@ -628,7 +641,7 @@ si32 GameResID::decode(const std::string & identifier)
 
 
 std::string GameResID::encode(const si32 index)
 std::string GameResID::encode(const si32 index)
 {
 {
-	return GameConstants::RESOURCE_NAMES[index];
+	return GameResID(index).toResource()->getJsonKey();
 }
 }
 
 
 si32 BuildingTypeUniqueID::decode(const std::string & identifier)
 si32 BuildingTypeUniqueID::decode(const std::string & identifier)
@@ -676,21 +689,6 @@ const std::array<PrimarySkill, 4> & PrimarySkill::ALL_SKILLS()
 	return allSkills;
 	return allSkills;
 }
 }
 
 
-const std::array<GameResID, 7> & GameResID::ALL_RESOURCES()
-{
-	static const std::array allResources = {
-		GameResID(WOOD),
-		GameResID(MERCURY),
-		GameResID(ORE),
-		GameResID(SULFUR),
-		GameResID(CRYSTAL),
-		GameResID(GEMS),
-		GameResID(GOLD)
-	};
-
-	return allResources;
-}
-
 std::string SecondarySkill::entityType()
 std::string SecondarySkill::entityType()
 {
 {
 	return "secondarySkill";
 	return "secondarySkill";

+ 5 - 2
lib/constants/EntityIdentifiers.h

@@ -25,6 +25,9 @@ class CHero;
 class CHeroClass;
 class CHeroClass;
 class HeroClass;
 class HeroClass;
 class HeroTypeService;
 class HeroTypeService;
+class Resource;
+class ResourceType;
+class ResourceTypeService;
 class CFaction;
 class CFaction;
 class Faction;
 class Faction;
 class Skill;
 class Skill;
@@ -1061,7 +1064,6 @@ public:
 		CRYSTAL,
 		CRYSTAL,
 		GEMS,
 		GEMS,
 		GOLD,
 		GOLD,
-		MITHRIL,
 		COUNT,
 		COUNT,
 
 
 		WOOD_AND_ORE = -4,  // special case for town bonus resource
 		WOOD_AND_ORE = -4,  // special case for town bonus resource
@@ -1080,7 +1082,8 @@ public:
 	static std::string encode(const si32 index);
 	static std::string encode(const si32 index);
 	static std::string entityType();
 	static std::string entityType();
 
 
-	static const std::array<GameResID, 7> & ALL_RESOURCES();
+	const Resource * toResource() const;
+	const ResourceType * toEntity(const Services * services) const;
 };
 };
 
 
 class DLL_LINKAGE BuildingTypeUniqueID : public Identifier<BuildingTypeUniqueID>
 class DLL_LINKAGE BuildingTypeUniqueID : public Identifier<BuildingTypeUniqueID>

+ 1 - 1
lib/constants/NumericConstants.h

@@ -37,7 +37,7 @@ namespace GameConstants
 
 
 	constexpr int SKILL_QUANTITY=28;
 	constexpr int SKILL_QUANTITY=28;
 	constexpr int PRIMARY_SKILLS=4;
 	constexpr int PRIMARY_SKILLS=4;
-	constexpr int RESOURCE_QUANTITY=8;
+	constexpr int RESOURCE_QUANTITY=7;
 	constexpr int HEROES_PER_TYPE=8; //amount of heroes of each type
 	constexpr int HEROES_PER_TYPE=8; //amount of heroes of each type
 
 
 	// amounts of OH3 objects. Can be changed by mods, should be used only during H3 loading phase
 	// amounts of OH3 objects. Can be changed by mods, should be used only during H3 loading phase

+ 1 - 1
lib/constants/StringConstants.h

@@ -19,7 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
 namespace GameConstants
 namespace GameConstants
 {
 {
 	const std::string RESOURCE_NAMES [RESOURCE_QUANTITY] = {
 	const std::string RESOURCE_NAMES [RESOURCE_QUANTITY] = {
-		"wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold", "mithril"
+		"wood", "mercury", "ore", "sulfur", "crystal", "gems", "gold"
 	};
 	};
 
 
 	const std::string PLAYER_COLOR_NAMES [PlayerColor::PLAYER_LIMIT_I] = {
 	const std::string PLAYER_COLOR_NAMES [PlayerColor::PLAYER_LIMIT_I] = {

+ 83 - 0
lib/entities/ResourceTypeHandler.cpp

@@ -0,0 +1,83 @@
+/*
+ * ResourceTypeHandler.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 "ResourceTypeHandler.h"
+
+#include "../GameLibrary.h"
+#include "../json/JsonNode.h"
+#include "../texts/CGeneralTextHandler.h"
+#include "../texts/TextIdentifier.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+std::string Resource::getNameTextID() const
+{
+	if(id.getNum() < GameConstants::RESOURCE_QUANTITY) // OH3 resources
+		return TextIdentifier("core.restypes", id).get();
+	return TextIdentifier( "resources", modScope, identifier, "name" ).get();
+}
+
+std::string Resource::getNameTranslated() const
+{
+	return LIBRARY->generaltexth->translate(getNameTextID());
+}
+
+void Resource::registerIcons(const IconRegistar & cb) const
+{
+	cb(getIconIndex(), 0, "SMALRES", iconSmall);
+	cb(getIconIndex(), 0, "RESOURCE", iconMedium);
+	cb(getIconIndex(), 0, "RESOUR82", iconLarge);
+}
+
+std::vector<JsonNode> ResourceTypeHandler::loadLegacyData()
+{
+	objects.resize(GameConstants::RESOURCE_QUANTITY);
+
+	return std::vector<JsonNode>(GameConstants::RESOURCE_QUANTITY, JsonNode(JsonMap()));
+}
+
+std::shared_ptr<Resource> ResourceTypeHandler::loadFromJson(const std::string & scope, const JsonNode & json, const std::string & identifier, size_t index)
+{
+	auto ret = std::make_shared<Resource>();
+
+	ret->id = GameResID(index);
+	ret->modScope = scope;
+	ret->identifier = identifier;
+
+	ret->price = json["price"].Integer();
+	ret->iconSmall = json["images"]["small"].String();
+	ret->iconMedium = json["images"]["medium"].String();
+	ret->iconLarge = json["images"]["large"].String();
+
+	if(ret->id.getNum() >= GameConstants::RESOURCE_QUANTITY) // not OH3 resources
+		LIBRARY->generaltexth->registerString(scope, ret->getNameTextID(), json["name"]);
+
+	return ret;
+}
+
+const std::vector<std::string> & ResourceTypeHandler::getTypeNames() const
+{
+	static const std::vector<std::string> types = { "resource" };
+	return types;
+}
+
+std::vector<GameResID> ResourceTypeHandler::getAllObjects() const
+{
+	std::vector<GameResID> result;
+
+	for (const auto & resource : objects)
+		if(resource)
+			result.push_back(resource->getId());
+
+	return result;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 66 - 0
lib/entities/ResourceTypeHandler.h

@@ -0,0 +1,66 @@
+/*
+ * ResourceTypeHandler.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 <vcmi/ResourceType.h>
+#include <vcmi/ResourceTypeService.h>
+#include "../constants/EntityIdentifiers.h"
+#include "../IHandlerBase.h"
+#include "../filesystem/ResourcePath.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class ResourceTypeHandler;
+
+class DLL_LINKAGE Resource : public ResourceType
+{
+	friend class ResourceTypeHandler;
+
+	GameResID id; //backlink
+
+	int price;
+	std::string iconSmall;
+	std::string iconMedium;
+	std::string iconLarge;
+
+	std::string identifier;
+	std::string modScope;
+
+public:
+	int getPrice() const override { return price; }
+
+	std::string getJsonKey() const override { return identifier; }
+	int32_t getIndex() const override { return id.getNum(); }
+	GameResID getId() const override { return id;}
+	int32_t getIconIndex() const override { return id.getNum(); }
+	std::string getModScope() const override { return modScope; };
+	void registerIcons(const IconRegistar & cb) const override;
+	std::string getNameTextID() const override;
+	std::string getNameTranslated() const override;
+};
+
+class DLL_LINKAGE ResourceTypeHandler : public CHandlerBase<GameResID, ResourceType, Resource, ResourceTypeService>
+{
+public:
+	std::shared_ptr<Resource> loadFromJson(const std::string & scope,
+										const JsonNode & json,
+										const std::string & identifier,
+										size_t index) override;
+	
+	const std::vector<std::string> & getTypeNames() const override;
+	std::vector<JsonNode> loadLegacyData() override;
+
+	std::vector<GameResID> getAllObjects() const;
+};
+
+VCMI_LIB_NAMESPACE_END

+ 3 - 6
lib/entities/faction/CTownHandler.cpp

@@ -53,12 +53,9 @@ JsonNode readBuilding(CLegacyConfigParser & parser)
 	JsonNode ret;
 	JsonNode ret;
 	JsonNode & cost = ret["cost"];
 	JsonNode & cost = ret["cost"];
 
 
-	//note: this code will try to parse mithril as well but wil always return 0 for it
 	for(const std::string & resID : GameConstants::RESOURCE_NAMES)
 	for(const std::string & resID : GameConstants::RESOURCE_NAMES)
 		cost[resID].Float() = parser.readNumber();
 		cost[resID].Float() = parser.readNumber();
-
-	cost.Struct().erase("mithril"); // erase mithril to avoid confusing validator
-
+	
 	parser.endLine();
 	parser.endLine();
 
 
 	return ret;
 	return ret;
@@ -284,8 +281,8 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 	LIBRARY->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"]);
 	LIBRARY->generaltexth->registerString(source.getModScope(), ret->getDescriptionTextID(), source["description"]);
 
 
 	ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
 	ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
-	ret->resources = TResources(source["cost"]);
-	ret->produce =   TResources(source["produce"]);
+	ret->resources.resolveFromJson(source["cost"]);
+	ret->produce.resolveFromJson(source["produce"]);
 
 
 	ret->manualHeroVisit = source["manualHeroVisit"].Bool();
 	ret->manualHeroVisit = source["manualHeroVisit"].Bool();
 	ret->upgradeReplacesBonuses = source["upgradeReplacesBonuses"].Bool();
 	ret->upgradeReplacesBonuses = source["upgradeReplacesBonuses"].Bool();

+ 1 - 1
lib/gameState/CGameState.cpp

@@ -397,7 +397,7 @@ void CGameState::initDifficulty()
 	auto setDifficulty = [this](PlayerState & state, const JsonNode & json)
 	auto setDifficulty = [this](PlayerState & state, const JsonNode & json)
 	{
 	{
 		//set starting resources
 		//set starting resources
-		state.resources = TResources(json["resources"]);
+		state.resources.resolveFromJson(json["resources"]);
 
 
 		//handicap
 		//handicap
 		const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(state.color);
 		const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(state.color);

+ 10 - 9
lib/gameState/GameStatistics.cpp

@@ -24,6 +24,7 @@
 #include "../entities/building/CBuilding.h"
 #include "../entities/building/CBuilding.h"
 #include "../serializer/JsonDeserializer.h"
 #include "../serializer/JsonDeserializer.h"
 #include "../serializer/JsonUpdater.h"
 #include "../serializer/JsonUpdater.h"
+#include "../entities/ResourceTypeHandler.h"
 
 
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
@@ -105,8 +106,8 @@ void StatisticDataSetEntry::serializeJson(JsonSerializeFormat & handler)
 	handler.serializeBool("hasGrail", hasGrail);
 	handler.serializeBool("hasGrail", hasGrail);
 	{
 	{
 		auto zonesData = handler.enterStruct("numMines");
 		auto zonesData = handler.enterStruct("numMines");
-		for(TResource idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
-			handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], numMines[idx], 0);
+		for(auto & idx : LIBRARY->resourceTypeHandler->getAllObjects())
+			handler.serializeInt(idx.toResource()->getJsonKey(), numMines[idx], 0);
 	}
 	}
 	handler.serializeInt("score", score);
 	handler.serializeInt("score", score);
 	handler.serializeInt("maxHeroLevel", maxHeroLevel);
 	handler.serializeInt("maxHeroLevel", maxHeroLevel);
@@ -158,7 +159,7 @@ std::string StatisticDataSet::toCsv(std::string sep) const
 {
 {
 	std::stringstream ss;
 	std::stringstream ss;
 
 
-	auto resources = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS};
+	auto resources = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS}; //todo: configurable resource support
 
 
 	ss << "Map" << sep;
 	ss << "Map" << sep;
 	ss << "Timestamp" << sep;
 	ss << "Timestamp" << sep;
@@ -191,15 +192,15 @@ std::string StatisticDataSet::toCsv(std::string sep) const
 	ss << "EventDefeatedStrongestHero" << sep;
 	ss << "EventDefeatedStrongestHero" << sep;
 	ss << "MovementPointsUsed";
 	ss << "MovementPointsUsed";
 	for(auto & resource : resources)
 	for(auto & resource : resources)
-		ss << sep << GameConstants::RESOURCE_NAMES[resource];
+		ss << sep << resource.toResource()->getJsonKey();
 	for(auto & resource : resources)
 	for(auto & resource : resources)
-		ss << sep << GameConstants::RESOURCE_NAMES[resource] + "Mines";
+		ss << sep << resource.toResource()->getJsonKey() + "Mines";
 	for(auto & resource : resources)
 	for(auto & resource : resources)
-		ss << sep << GameConstants::RESOURCE_NAMES[resource] + "SpentResourcesForArmy";
+		ss << sep << resource.toResource()->getJsonKey() + "SpentResourcesForArmy";
 	for(auto & resource : resources)
 	for(auto & resource : resources)
-		ss << sep << GameConstants::RESOURCE_NAMES[resource] + "SpentResourcesForBuildings";
+		ss << sep << resource.toResource()->getJsonKey() + "SpentResourcesForBuildings";
 	for(auto & resource : resources)
 	for(auto & resource : resources)
-		ss << sep << GameConstants::RESOURCE_NAMES[resource] + "TradeVolume";
+		ss << sep << resource.toResource()->getJsonKey() + "TradeVolume";
 	ss << "\r\n";
 	ss << "\r\n";
 
 
 	for(auto & entry : data)
 	for(auto & entry : data)
@@ -403,7 +404,7 @@ std::map<EGameResID, int> Statistic::getNumMines(const CGameState * gs, const Pl
 {
 {
 	std::map<EGameResID, int> tmp;
 	std::map<EGameResID, int> tmp;
 
 
-	for(auto & res : EGameResID::ALL_RESOURCES())
+	for(auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
 		tmp[res] = 0;
 		tmp[res] = 0;
 
 
 	for(const auto * object : ps->getOwnedObjects())
 	for(const auto * object : ps->getOwnedObjects())

+ 4 - 3
lib/json/JsonRandom.cpp

@@ -28,6 +28,7 @@
 #include "../entities/artifact/CArtHandler.h"
 #include "../entities/artifact/CArtHandler.h"
 #include "../entities/hero/CHero.h"
 #include "../entities/hero/CHero.h"
 #include "../entities/hero/CHeroClass.h"
 #include "../entities/hero/CHeroClass.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../gameState/CGameState.h"
 #include "../gameState/CGameState.h"
 #include "../mapObjects/army/CStackBasicDescriptor.h"
 #include "../mapObjects/army/CStackBasicDescriptor.h"
 #include "../mapObjects/IObjectInterface.h"
 #include "../mapObjects/IObjectInterface.h"
@@ -298,9 +299,9 @@ JsonRandom::JsonRandom(IGameInfoCallback * cb, IGameRandomizer & gameRandomizer)
 			return ret;
 			return ret;
 		}
 		}
 
 
-		for (size_t i=0; i<GameConstants::RESOURCE_QUANTITY; i++)
+		for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 		{
 		{
-			ret[i] = loadValue(value[GameConstants::RESOURCE_NAMES[i]], variables);
+			ret[i] = loadValue(value[i.toResource()->getJsonKey()], variables);
 		}
 		}
 		return ret;
 		return ret;
 	}
 	}
@@ -315,7 +316,7 @@ JsonRandom::JsonRandom(IGameInfoCallback * cb, IGameRandomizer & gameRandomizer)
 			GameResID::CRYSTAL,
 			GameResID::CRYSTAL,
 			GameResID::GEMS,
 			GameResID::GEMS,
 			GameResID::GOLD
 			GameResID::GOLD
-		};
+		}; //todo: configurable resource support
 
 
 		std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
 		std::set<GameResID> potentialPicks = filterKeys(value, defaultResources, variables);
 		GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);
 		GameResID resourceID = *RandomGeneratorUtil::nextItem(potentialPicks, rng);

+ 2 - 1
lib/mapObjectConstructors/CommonConstructors.cpp

@@ -17,6 +17,7 @@
 #include "../callback/IGameInfoCallback.h"
 #include "../callback/IGameInfoCallback.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/hero/CHeroClass.h"
 #include "../entities/hero/CHeroClass.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGTownInstance.h"
 #include "../mapObjects/CGTownInstance.h"
 #include "../mapObjects/MiscObjects.h"
 #include "../mapObjects/MiscObjects.h"
@@ -60,7 +61,7 @@ bool ResourceInstanceConstructor::hasNameTextID() const
 
 
 std::string ResourceInstanceConstructor::getNameTextID() const
 std::string ResourceInstanceConstructor::getNameTextID() const
 {
 {
-	return TextIdentifier("core", "restypes", resourceType.getNum()).get();
+	return resourceType.toResource()->getNameTextID();
 }
 }
 
 
 GameResID ResourceInstanceConstructor::getResourceType() const
 GameResID ResourceInstanceConstructor::getResourceType() const

+ 1 - 1
lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp

@@ -40,7 +40,7 @@ void FlaggableInstanceConstructor::initTypeData(const JsonNode & config)
 		}
 		}
 	}
 	}
 
 
-	dailyIncome = ResourceSet(config["dailyIncome"]);
+	dailyIncome.resolveFromJson(config["dailyIncome"]);
 }
 }
 
 
 void FlaggableInstanceConstructor::initializeObject(FlaggableMapObject * flaggable) const
 void FlaggableInstanceConstructor::initializeObject(FlaggableMapObject * flaggable) const

+ 2 - 1
lib/mapObjects/CGCreature.cpp

@@ -25,6 +25,7 @@
 #include "../networkPacks/StackLocation.h"
 #include "../networkPacks/StackLocation.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 
 
 #include <vstd/RNG.h>
 #include <vstd/RNG.h>
 
 
@@ -630,7 +631,7 @@ void CGCreature::giveReward(IGameEventCallback & gameEvents, const CGHeroInstanc
 	if(!resources.empty())
 	if(!resources.empty())
 	{
 	{
 		gameEvents.giveResources(h->tempOwner, resources);
 		gameEvents.giveResources(h->tempOwner, resources);
-		for(const auto & res : GameResID::ALL_RESOURCES())
+		for(const auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
 		{
 		{
 			if(resources[res] > 0)
 			if(resources[res] > 0)
 				iw.components.emplace_back(ComponentType::RESOURCE, res, resources[res]);
 				iw.components.emplace_back(ComponentType::RESOURCE, res, resources[res]);

+ 2 - 1
lib/mapObjects/CGHeroInstance.cpp

@@ -38,6 +38,7 @@
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/hero/CHeroHandler.h"
 #include "../entities/hero/CHeroHandler.h"
 #include "../entities/hero/CHeroClass.h"
 #include "../entities/hero/CHeroClass.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../battle/CBattleInfoEssentials.h"
 #include "../battle/CBattleInfoEssentials.h"
 #include "../campaign/CampaignState.h"
 #include "../campaign/CampaignState.h"
 #include "../json/JsonBonus.h"
 #include "../json/JsonBonus.h"
@@ -1816,7 +1817,7 @@ ResourceSet CGHeroInstance::dailyIncome() const
 {
 {
 	ResourceSet income;
 	ResourceSet income;
 
 
-	for (GameResID k : GameResID::ALL_RESOURCES())
+	for (GameResID k : LIBRARY->resourceTypeHandler->getAllObjects())
 		income[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 		income[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 
 
 	const auto & playerSettings = cb->getPlayerSettings(getOwner());
 	const auto & playerSettings = cb->getPlayerSettings(getOwner());

+ 1 - 1
lib/mapObjects/CGObjectInstance.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * CObjectHandler.cpp, part of VCMI engine
+ * CGObjectInstance.cpp, part of VCMI engine
  *
  *
  * Authors: listed in file AUTHORS in main folder
  * Authors: listed in file AUTHORS in main folder
  *
  *

+ 3 - 2
lib/mapObjects/CGResource.cpp

@@ -21,6 +21,7 @@
 #include "../gameState/CGameState.h"
 #include "../gameState/CGameState.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../CSoundBase.h"
 #include "../CSoundBase.h"
+#include "../entities/ResourceTypeHandler.h"
 
 
 #include <vstd/RNG.h>
 #include <vstd/RNG.h>
 
 
@@ -50,7 +51,7 @@ GameResID CGResource::resourceID() const
 
 
 std::string CGResource::getHoverText(PlayerColor player) const
 std::string CGResource::getHoverText(PlayerColor player) const
 {
 {
-	return LIBRARY->generaltexth->restypes[resourceID().getNum()];
+	return resourceID().toResource()->getNameTranslated();
 }
 }
 
 
 void CGResource::pickRandomObject(IGameRandomizer & gameRandomizer)
 void CGResource::pickRandomObject(IGameRandomizer & gameRandomizer)
@@ -60,7 +61,7 @@ void CGResource::pickRandomObject(IGameRandomizer & gameRandomizer)
 	if (ID == Obj::RANDOM_RESOURCE)
 	if (ID == Obj::RANDOM_RESOURCE)
 	{
 	{
 		ID = Obj::RESOURCE;
 		ID = Obj::RESOURCE;
-		subID = gameRandomizer.getDefault().nextInt(EGameResID::WOOD, EGameResID::GOLD);
+		subID = gameRandomizer.getDefault().nextInt(EGameResID::WOOD, EGameResID::GOLD); //todo: configurable resource support
 		setType(ID, subID);
 		setType(ID, subID);
 
 
 		amount *= getAmountMultiplier();
 		amount *= getAmountMultiplier();

+ 2 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -32,6 +32,7 @@
 #include "../callback/IGameRandomizer.h"
 #include "../callback/IGameRandomizer.h"
 #include "../entities/building/CBuilding.h"
 #include "../entities/building/CBuilding.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGHeroInstance.h"
@@ -209,7 +210,7 @@ TResources CGTownInstance::dailyIncome() const
 {
 {
 	ResourceSet ret;
 	ResourceSet ret;
 
 
-	for (GameResID k : GameResID::ALL_RESOURCES())
+	for (GameResID k : LIBRARY->resourceTypeHandler->getAllObjects())
 		ret[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 		ret[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 
 
 	for(const auto & p : getTown()->buildings)
 	for(const auto & p : getTown()->buildings)

+ 0 - 31
lib/mapObjects/CObjectHandler.cpp

@@ -1,31 +0,0 @@
-/*
- * CObjectHandler.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 "CObjectHandler.h"
-
-#include "CGObjectInstance.h"
-#include "../filesystem/ResourcePath.h"
-#include "../json/JsonNode.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-CObjectHandler::CObjectHandler()
-{
-	logGlobal->trace("\t\tReading resources prices ");
-	const JsonNode config2(JsonPath::builtin("config/resources.json"));
-	for(const JsonNode &price : config2["resources_prices"].Vector())
-	{
-		resVals.push_back(static_cast<ui32>(price.Float()));
-	}
-	logGlobal->trace("\t\tDone loading resource prices!");
-}
-
-VCMI_LIB_NAMESPACE_END

+ 5 - 6
lib/mapObjects/CQuest.cpp

@@ -22,6 +22,7 @@
 #include "../callback/IGameRandomizer.h"
 #include "../callback/IGameRandomizer.h"
 #include "../entities/artifact/CArtifact.h"
 #include "../entities/artifact/CArtifact.h"
 #include "../entities/hero/CHeroHandler.h"
 #include "../entities/hero/CHeroHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../GameConstants.h"
 #include "../GameConstants.h"
@@ -233,7 +234,7 @@ void CQuest::addTextReplacements(const IGameInfoCallback * cb, MetaString & text
 	if(mission.resources.nonZero())
 	if(mission.resources.nonZero())
 	{
 	{
 		MetaString loot;
 		MetaString loot;
-		for(auto i : GameResID::ALL_RESOURCES())
+		for(auto i : LIBRARY->resourceTypeHandler->getAllObjects())
 		{
 		{
 			if(mission.resources[i])
 			if(mission.resources[i])
 			{
 			{
@@ -372,11 +373,9 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
 		if(missionType == "Resources")
 		if(missionType == "Resources")
 		{
 		{
 			auto r = handler.enterStruct("resources");
 			auto r = handler.enterStruct("resources");
-
-			for(size_t idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
-			{
-				handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], mission.resources[idx], 0);
-			}
+			
+			for(auto & idx : LIBRARY->resourceTypeHandler->getAllObjects())
+				handler.serializeInt(idx.toResource()->getJsonKey(), mission.resources[idx], 0);
 		}
 		}
 		
 		
 		if(missionType == "Hero")
 		if(missionType == "Hero")

+ 7 - 7
lib/mapObjects/IMarket.cpp

@@ -13,10 +13,10 @@
 
 
 #include "CCreatureHandler.h"
 #include "CCreatureHandler.h"
 #include "CGObjectInstance.h"
 #include "CGObjectInstance.h"
-#include "CObjectHandler.h"
 
 
 #include "../GameLibrary.h"
 #include "../GameLibrary.h"
 #include "../entities/artifact/CArtHandler.h"
 #include "../entities/artifact/CArtHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 
 
 VCMI_LIB_NAMESPACE_BEGIN
 VCMI_LIB_NAMESPACE_BEGIN
 
 
@@ -33,8 +33,8 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 		{
 		{
 			double effectiveness = std::min((getMarketEfficiency() + 1.0) / 20.0, 0.5);
 			double effectiveness = std::min((getMarketEfficiency() + 1.0) / 20.0, 0.5);
 
 
-			double r = LIBRARY->objh->resVals[id1]; //value of given resource
-			double g = LIBRARY->objh->resVals[id2] / effectiveness; //value of wanted resource
+			double r = GameResID(id1).toResource()->getPrice(); //value of given resource
+			double g = GameResID(id2).toResource()->getPrice() / effectiveness; //value of wanted resource
 
 
 			if(r>g) //if given resource is more expensive than wanted
 			if(r>g) //if given resource is more expensive than wanted
 			{
 			{
@@ -54,7 +54,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 			double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)];
 			double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)];
 
 
 			double r = LIBRARY->creatures()->getByIndex(id1)->getRecruitCost(EGameResID::GOLD); //value of given creature in gold
 			double r = LIBRARY->creatures()->getByIndex(id1)->getRecruitCost(EGameResID::GOLD); //value of given creature in gold
-			double g = LIBRARY->objh->resVals[id2] / effectiveness; //value of wanted resource
+			double g = GameResID(id2).toResource()->getPrice() / effectiveness; //value of wanted resource
 
 
 			if(r>g) //if given resource is more expensive than wanted
 			if(r>g) //if given resource is more expensive than wanted
 			{
 			{
@@ -75,7 +75,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 	case EMarketMode::RESOURCE_ARTIFACT:
 	case EMarketMode::RESOURCE_ARTIFACT:
 		{
 		{
 			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
 			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
-			double r = LIBRARY->objh->resVals[id1]; //value of offered resource
+			double r = GameResID(id1).toResource()->getPrice(); //value of offered resource
 			double g = LIBRARY->artifacts()->getByIndex(id2)->getPrice() / effectiveness; //value of bought artifact in gold
 			double g = LIBRARY->artifacts()->getByIndex(id2)->getPrice() / effectiveness; //value of bought artifact in gold
 
 
 			if(id1 != 6) //non-gold prices are doubled
 			if(id1 != 6) //non-gold prices are doubled
@@ -89,7 +89,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
 		{
 		{
 			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
 			double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6);
 			double r = LIBRARY->artifacts()->getByIndex(id1)->getPrice() * effectiveness;
 			double r = LIBRARY->artifacts()->getByIndex(id1)->getPrice() * effectiveness;
-			double g = LIBRARY->objh->resVals[id2];
+			double g = GameResID(id2).toResource()->getPrice();
 
 
 // 			if(id2 != 6) //non-gold prices are doubled
 // 			if(id2 != 6) //non-gold prices are doubled
 // 				r /= 2;
 // 				r /= 2;
@@ -163,7 +163,7 @@ std::vector<TradeItemBuy> IMarket::availableItemsIds(const EMarketMode mode) con
 	case EMarketMode::RESOURCE_RESOURCE:
 	case EMarketMode::RESOURCE_RESOURCE:
 	case EMarketMode::ARTIFACT_RESOURCE:
 	case EMarketMode::ARTIFACT_RESOURCE:
 	case EMarketMode::CREATURE_RESOURCE:
 	case EMarketMode::CREATURE_RESOURCE:
-		for(const auto & res : GameResID::ALL_RESOURCES())
+		for(const auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
 			ret.push_back(res);
 			ret.push_back(res);
 	}
 	}
 	return ret;
 	return ret;

+ 0 - 2
lib/mapObjects/MapObjects.h

@@ -10,8 +10,6 @@
 #pragma once
 #pragma once
 
 
 // Helper header that includes all map objects, similar to old CObjectHandler.h
 // Helper header that includes all map objects, similar to old CObjectHandler.h
-// Possible TODO - remove this header after CObjectHandler.cpp will be fully split into smaller files
-#include "CObjectHandler.h"
 
 
 #include "CGDwelling.h"
 #include "CGDwelling.h"
 #include "CGHeroInstance.h"
 #include "CGHeroInstance.h"

+ 9 - 5
lib/mapObjects/MiscObjects.cpp

@@ -18,6 +18,7 @@
 #include "../constants/StringConstants.h"
 #include "../constants/StringConstants.h"
 #include "../entities/artifact/ArtifactUtils.h"
 #include "../entities/artifact/ArtifactUtils.h"
 #include "../entities/artifact/CArtifact.h"
 #include "../entities/artifact/CArtifact.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../CConfigHandler.h"
 #include "../CConfigHandler.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../CSkillHandler.h"
 #include "../CSkillHandler.h"
@@ -143,7 +144,7 @@ ResourceSet CGMine::dailyIncome() const
 {
 {
 	ResourceSet result;
 	ResourceSet result;
 
 
-	for (GameResID k : GameResID::ALL_RESOURCES())
+	for (GameResID k : LIBRARY->resourceTypeHandler->getAllObjects())
 		result[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 		result[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 
 
 	result[producedResource] += defaultResProduction();
 	result[producedResource] += defaultResProduction();
@@ -164,7 +165,7 @@ std::string CGMine::getHoverText(PlayerColor player) const
 	std::string hoverName = CArmedInstance::getHoverText(player);
 	std::string hoverName = CArmedInstance::getHoverText(player);
 
 
 	if (tempOwner != PlayerColor::NEUTRAL)
 	if (tempOwner != PlayerColor::NEUTRAL)
-		hoverName += "\n(" + LIBRARY->generaltexth->restypes[producedResource.getNum()] + ")";
+		hoverName += "\n(" + producedResource.toResource()->getNameTranslated() + ")";
 
 
 	if(stacksCount())
 	if(stacksCount())
 	{
 	{
@@ -238,7 +239,7 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler)
 		{
 		{
 			JsonNode node;
 			JsonNode node;
 			for(const auto & resID : abandonedMineResources)
 			for(const auto & resID : abandonedMineResources)
-				node.Vector().emplace_back(GameConstants::RESOURCE_NAMES[resID.getNum()]);
+				node.Vector().emplace_back(resID.toResource()->getJsonKey());
 
 
 			handler.serializeRaw("possibleResources", node, std::nullopt);
 			handler.serializeRaw("possibleResources", node, std::nullopt);
 		}
 		}
@@ -251,7 +252,10 @@ void CGMine::serializeJsonOptions(JsonSerializeFormat & handler)
 
 
 			for(const std::string & s : names)
 			for(const std::string & s : names)
 			{
 			{
-				int raw_res = vstd::find_pos(GameConstants::RESOURCE_NAMES, s);
+				std::vector<std::string> resNames;
+				for(auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
+					resNames.push_back(res.toResource()->getJsonKey());
+				int raw_res = vstd::find_pos(resNames, s);
 				if(raw_res < 0)
 				if(raw_res < 0)
 					logGlobal->error("Invalid resource name: %s", s);
 					logGlobal->error("Invalid resource name: %s", s);
 				else
 				else
@@ -872,7 +876,7 @@ const IOwnableObject * CGGarrison::asOwnable() const
 ResourceSet CGGarrison::dailyIncome() const
 ResourceSet CGGarrison::dailyIncome() const
 {
 {
 	ResourceSet result;
 	ResourceSet result;
-	for (GameResID k : GameResID::ALL_RESOURCES())
+	for (GameResID k : LIBRARY->resourceTypeHandler->getAllObjects())
 		result[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 		result[k] += valOfBonuses(BonusType::GENERATE_RESOURCE, BonusSubtypeID(k));
 
 
 	return result;
 	return result;

+ 2 - 0
lib/modding/ContentTypeHandler.cpp

@@ -23,6 +23,7 @@
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/hero/CHeroClassHandler.h"
 #include "../entities/hero/CHeroClassHandler.h"
 #include "../entities/hero/CHeroHandler.h"
 #include "../entities/hero/CHeroHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../CBonusTypeHandler.h"
 #include "../CBonusTypeHandler.h"
 #include "../CSkillHandler.h"
 #include "../CSkillHandler.h"
@@ -263,6 +264,7 @@ void CContentHandler::init()
 	handlers.insert(std::make_pair("roads", ContentTypeHandler(LIBRARY->roadTypeHandler.get(), "road")));
 	handlers.insert(std::make_pair("roads", ContentTypeHandler(LIBRARY->roadTypeHandler.get(), "road")));
 	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(LIBRARY->obstacleHandler.get(), "obstacle")));
 	handlers.insert(std::make_pair("obstacles", ContentTypeHandler(LIBRARY->obstacleHandler.get(), "obstacle")));
 	handlers.insert(std::make_pair("biomes", ContentTypeHandler(LIBRARY->biomeHandler.get(), "biome")));
 	handlers.insert(std::make_pair("biomes", ContentTypeHandler(LIBRARY->biomeHandler.get(), "biome")));
+	handlers.insert(std::make_pair("resources", ContentTypeHandler(LIBRARY->resourceTypeHandler.get(), "resources")));
 }
 }
 
 
 bool CContentHandler::preloadData(const ModDescription & mod, bool validate)
 bool CContentHandler::preloadData(const ModDescription & mod, bool validate)

+ 2 - 1
lib/rewardable/Info.cpp

@@ -22,6 +22,7 @@
 #include "../mapObjects/IObjectInterface.h"
 #include "../mapObjects/IObjectInterface.h"
 #include "../modding/IdentifierStorage.h"
 #include "../modding/IdentifierStorage.h"
 #include "../texts/CGeneralTextHandler.h"
 #include "../texts/CGeneralTextHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 
 
 #include <vstd/RNG.h>
 #include <vstd/RNG.h>
 
 
@@ -296,7 +297,7 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
 
 
 		MetaString loot;
 		MetaString loot;
 
 
-		for (GameResID it : GameResID::ALL_RESOURCES())
+		for (GameResID it : LIBRARY->resourceTypeHandler->getAllObjects())
 		{
 		{
 			if (info.reward.resources[it] != 0)
 			if (info.reward.resources[it] != 0)
 			{
 			{

+ 4 - 8
lib/rmg/CRmgTemplate.cpp

@@ -18,6 +18,7 @@
 #include "../GameLibrary.h"
 #include "../GameLibrary.h"
 #include "../constants/StringConstants.h"
 #include "../constants/StringConstants.h"
 #include "../entities/faction/CTownHandler.h"
 #include "../entities/faction/CTownHandler.h"
+#include "../entities/ResourceTypeHandler.h"
 #include "../modding/ModScope.h"
 #include "../modding/ModScope.h"
 #include "../serializer/JsonSerializeFormat.h"
 #include "../serializer/JsonSerializeFormat.h"
 
 
@@ -258,12 +259,12 @@ std::set<FactionID> ZoneOptions::getMonsterTypes() const
 	return vstd::difference(monsterTypes, bannedMonsters);
 	return vstd::difference(monsterTypes, bannedMonsters);
 }
 }
 
 
-void ZoneOptions::setMinesInfo(const std::map<TResource, ui16> & value)
+void ZoneOptions::setMinesInfo(const std::map<GameResID, ui16> & value)
 {
 {
 	mines = value;
 	mines = value;
 }
 }
 
 
-std::map<TResource, ui16> ZoneOptions::getMinesInfo() const
+std::map<GameResID, ui16> ZoneOptions::getMinesInfo() const
 {
 {
 	return mines;
 	return mines;
 }
 }
@@ -532,12 +533,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 
 
 	if((minesLikeZone == NO_ZONE) && (!handler.saving || !mines.empty()))
 	if((minesLikeZone == NO_ZONE) && (!handler.saving || !mines.empty()))
 	{
 	{
-		auto minesData = handler.enterStruct("mines");
-
-		for(TResource idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
-		{
-			handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], mines[idx], 0);
-		}
+		handler.serializeIdMap<GameResID, ui16>("mines", mines);
 	}
 	}
 
 
 	handler.serializeStruct("customObjects", objectConfig);
 	handler.serializeStruct("customObjects", objectConfig);

+ 4 - 4
lib/rmg/CRmgTemplate.h

@@ -209,8 +209,8 @@ public:
 
 
 	void setMonsterTypes(const std::set<FactionID> & value);
 	void setMonsterTypes(const std::set<FactionID> & value);
 
 
-	void setMinesInfo(const std::map<TResource, ui16> & value);
-	std::map<TResource, ui16> getMinesInfo() const;
+	void setMinesInfo(const std::map<GameResID, ui16> & value);
+	std::map<GameResID, ui16> getMinesInfo() const;
 
 
 	void setTreasureInfo(const std::vector<CTreasureInfo> & value);
 	void setTreasureInfo(const std::vector<CTreasureInfo> & value);
 	void addTreasureInfo(const CTreasureInfo & value);
 	void addTreasureInfo(const CTreasureInfo & value);
@@ -277,7 +277,7 @@ protected:
 	std::set<FactionID> monsterTypes;
 	std::set<FactionID> monsterTypes;
 	std::set<FactionID> bannedMonsters;
 	std::set<FactionID> bannedMonsters;
 
 
-	std::map<TResource, ui16> mines; //obligatory mines to spawn in this zone
+	std::map<GameResID, ui16> mines; //obligatory mines to spawn in this zone
 
 
 	std::vector<CTreasureInfo> treasureInfo;
 	std::vector<CTreasureInfo> treasureInfo;
 
 
@@ -373,7 +373,7 @@ private:
 	std::set<HeroTypeID> bannedHeroes;
 	std::set<HeroTypeID> bannedHeroes;
 
 
 	std::set<TerrainId> inheritTerrainType(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
 	std::set<TerrainId> inheritTerrainType(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
-	std::map<TResource, ui16> inheritMineTypes(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
+	std::map<GameResID, ui16> inheritMineTypes(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
 	std::vector<CTreasureInfo> inheritTreasureInfo(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
 	std::vector<CTreasureInfo> inheritTreasureInfo(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
 
 
 	void inheritTownProperties(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
 	void inheritTownProperties(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);

+ 10 - 0
lib/serializer/JsonDeserializer.cpp

@@ -119,6 +119,16 @@ void JsonDeserializer::serializeInternal(const std::string & fieldName, std::vec
 	}
 	}
 }
 }
 
 
+void JsonDeserializer::serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value)
+{
+	const JsonMap & data = currentObject->operator[](fieldName).Struct();
+
+	value.clear();
+
+	for(const auto & [id, elem] : data)
+		value[id] = elem.Integer();
+}
+
 void JsonDeserializer::serializeInternal(std::string & value)
 void JsonDeserializer::serializeInternal(std::string & value)
 {
 {
 	value = currentObject->String();
 	value = currentObject->String();

+ 1 - 0
lib/serializer/JsonDeserializer.h

@@ -32,6 +32,7 @@ protected:
 	void serializeInternal(const std::string & fieldName, si64 & value, const std::optional<si64> & defaultValue) override;
 	void serializeInternal(const std::string & fieldName, si64 & value, const std::optional<si64> & defaultValue) override;
 	void serializeInternal(const std::string & fieldName, si32 & value, const std::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) override;
 	void serializeInternal(const std::string & fieldName, si32 & value, const std::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) override;
 	void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) override;
 	void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) override;
+	void serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value) override;
 
 
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(int64_t & value) override;
 	void serializeInternal(int64_t & value) override;

+ 30 - 0
lib/serializer/JsonSerializeFormat.h

@@ -331,6 +331,33 @@ public:
 		}
 		}
 	}
 	}
 
 
+	/// si32-convertible identifier map <-> Json object of {key: string}
+	template <typename Key, typename T, typename E = T>
+	void serializeIdMap(const std::string & fieldName, std::map<Key, T> & value)
+	{
+		if (saving)
+		{
+			std::map<std::string, T> fieldValue;
+
+			for (const auto & [key, val] : value)
+				fieldValue[Key::encode(key.getNum())] = val;
+
+			serializeInternal(fieldName, fieldValue);
+		}
+		else
+		{
+			const JsonNode & node = getCurrent()[fieldName];
+			for (const auto & [keyStr, jsonVal] : node.Struct())
+			{
+				Key key = Key::decode(keyStr);
+
+				LIBRARY->identifiers()->requestIdentifier(node.getModScope(), Key::entityType(), keyStr, [&value, key](int32_t index) {
+					value[key] = T(index);
+				});
+			}
+		}
+	}
+
 	///si32-convertible identifier vector <-> Json array of string
 	///si32-convertible identifier vector <-> Json array of string
 	template <typename T, typename E = T>
 	template <typename T, typename E = T>
 	void serializeIdArray(const std::string & fieldName, std::vector<T> & value)
 	void serializeIdArray(const std::string & fieldName, std::vector<T> & value)
@@ -443,6 +470,9 @@ protected:
 	///String vector <-> Json string vector
 	///String vector <-> Json string vector
 	virtual void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) = 0;
 	virtual void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) = 0;
 
 
+	///String map <-> Json map of int
+	virtual void serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value) = 0;
+
 	virtual void pop() = 0;
 	virtual void pop() = 0;
 	virtual void pushStruct(const std::string & fieldName) = 0;
 	virtual void pushStruct(const std::string & fieldName) = 0;
 	virtual void pushArray(const std::string & fieldName) = 0;
 	virtual void pushArray(const std::string & fieldName) = 0;

+ 11 - 0
lib/serializer/JsonSerializer.cpp

@@ -75,6 +75,17 @@ void JsonSerializer::serializeInternal(const std::string & fieldName, std::vecto
 		data.emplace_back(rawId);
 		data.emplace_back(rawId);
 }
 }
 
 
+void JsonSerializer::serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value)
+{
+	if(value.empty())
+		return;
+
+	JsonMap & data = currentObject->operator[](fieldName).Struct();
+
+	for(const auto & [rawId, val] : value)
+		data[rawId].Integer() = val;
+}
+
 void JsonSerializer::serializeInternal(std::string & value)
 void JsonSerializer::serializeInternal(std::string & value)
 {
 {
 	currentObject->String() = value;
 	currentObject->String() = value;

+ 1 - 0
lib/serializer/JsonSerializer.h

@@ -32,6 +32,7 @@ protected:
 	void serializeInternal(const std::string & fieldName, si64 & value, const std::optional<si64> & defaultValue) override;
 	void serializeInternal(const std::string & fieldName, si64 & value, const std::optional<si64> & defaultValue) override;
 	void serializeInternal(const std::string & fieldName, si32 & value, const std::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) override;
 	void serializeInternal(const std::string & fieldName, si32 & value, const std::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) override;
 	void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) override;
 	void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) override;
+	void serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value) override;
 
 
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(int64_t & value) override;
 	void serializeInternal(int64_t & value) override;

+ 5 - 0
lib/serializer/JsonUpdater.cpp

@@ -65,6 +65,11 @@ void JsonUpdater::serializeInternal(const std::string & fieldName, std::vector<s
 	// TODO
 	// TODO
 }
 }
 
 
+void JsonUpdater::serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value)
+{
+	// TODO
+}
+
 void JsonUpdater::serializeInternal(const std::string & fieldName, double & value, const std::optional<double> & defaultValue)
 void JsonUpdater::serializeInternal(const std::string & fieldName, double & value, const std::optional<double> & defaultValue)
 {
 {
 	const JsonNode & data = currentObject->operator[](fieldName);
 	const JsonNode & data = currentObject->operator[](fieldName);

+ 1 - 0
lib/serializer/JsonUpdater.h

@@ -36,6 +36,7 @@ protected:
 	void serializeInternal(const std::string & fieldName, si64 & value, const std::optional<si64> & defaultValue) override;
 	void serializeInternal(const std::string & fieldName, si64 & value, const std::optional<si64> & defaultValue) override;
 	void serializeInternal(const std::string & fieldName, si32 & value, const std::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) override;
 	void serializeInternal(const std::string & fieldName, si32 & value, const std::optional<si32> & defaultValue, const std::vector<std::string> & enumMap) override;
 	void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) override;
 	void serializeInternal(const std::string & fieldName, std::vector<std::string> & value) override;
+	void serializeInternal(const std::string & fieldName, std::map<std::string, uint16_t> & value) override;
 
 
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(std::string & value) override;
 	void serializeInternal(int64_t & value) override;
 	void serializeInternal(int64_t & value) override;

+ 3 - 2
lib/texts/MetaString.cpp

@@ -14,6 +14,7 @@
 #include "entities/artifact/CArtifact.h"
 #include "entities/artifact/CArtifact.h"
 #include "entities/faction/CFaction.h"
 #include "entities/faction/CFaction.h"
 #include "entities/hero/CHero.h"
 #include "entities/hero/CHero.h"
+#include "entities/ResourceTypeHandler.h"
 #include "texts/CGeneralTextHandler.h"
 #include "texts/CGeneralTextHandler.h"
 #include "CSkillHandler.h"
 #include "CSkillHandler.h"
 #include "GameConstants.h"
 #include "GameConstants.h"
@@ -378,7 +379,7 @@ void MetaString::appendName(const CreatureID & id, TQuantity count)
 
 
 void MetaString::appendName(const GameResID& id)
 void MetaString::appendName(const GameResID& id)
 {
 {
-	appendTextID(TextIdentifier("core.restypes", id.getNum()).get());
+	appendTextID(id.toResource()->getNameTextID());
 }
 }
 
 
 void MetaString::appendNameSingular(const CreatureID & id)
 void MetaString::appendNameSingular(const CreatureID & id)
@@ -423,7 +424,7 @@ void MetaString::replaceName(const SpellID & id)
 
 
 void MetaString::replaceName(const GameResID& id)
 void MetaString::replaceName(const GameResID& id)
 {
 {
-	replaceTextID(TextIdentifier("core.restypes", id.getNum()).get());
+	replaceTextID(id.toResource()->getNameTextID());
 }
 }
 
 
 void MetaString::replaceNameSingular(const CreatureID & id)
 void MetaString::replaceNameSingular(const CreatureID & id)

+ 2 - 0
mapeditor/graphics.cpp

@@ -19,6 +19,7 @@
 #include <vcmi/HeroTypeService.h>
 #include <vcmi/HeroTypeService.h>
 #include <vcmi/SkillService.h>
 #include <vcmi/SkillService.h>
 #include <vcmi/spells/Service.h>
 #include <vcmi/spells/Service.h>
+#include <vcmi/ResourceTypeService.h>
 
 
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/CBinaryReader.h"
 #include "../lib/filesystem/CBinaryReader.h"
@@ -342,4 +343,5 @@ void Graphics::initializeImageLists()
 	addImageListEntries(LIBRARY->factions());
 	addImageListEntries(LIBRARY->factions());
 	addImageListEntries(LIBRARY->spells());
 	addImageListEntries(LIBRARY->spells());
 	addImageListEntries(LIBRARY->skills());
 	addImageListEntries(LIBRARY->skills());
+	addImageListEntries(LIBRARY->resources());
 }
 }

+ 4 - 5
mapeditor/inspector/questwidget.cpp

@@ -17,6 +17,7 @@
 #include "../lib/CCreatureHandler.h"
 #include "../lib/CCreatureHandler.h"
 #include "../lib/constants/StringConstants.h"
 #include "../lib/constants/StringConstants.h"
 #include "../lib/entities/artifact/CArtHandler.h"
 #include "../lib/entities/artifact/CArtHandler.h"
+#include "../lib/entities/ResourceTypeHandler.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/mapObjects/CGHeroInstance.h"
 #include "../lib/mapObjects/CGCreature.h"
 #include "../lib/mapObjects/CGCreature.h"
@@ -40,13 +41,13 @@ QuestWidget::QuestWidget(MapController & _controller, CQuest & _sh, QWidget *par
 		ui->lDayOfWeek->addItem(tr("Day %1").arg(i));
 		ui->lDayOfWeek->addItem(tr("Day %1").arg(i));
 	
 	
 	//fill resources
 	//fill resources
-	ui->lResources->setRowCount(GameConstants::RESOURCE_QUANTITY - 1);
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY - 1; ++i)
+	ui->lResources->setRowCount(LIBRARY->resourceTypeHandler->getAllObjects().size() - 1);
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		MetaString str;
 		MetaString str;
 		str.appendName(GameResID(i));
 		str.appendName(GameResID(i));
 		auto * item = new QTableWidgetItem(QString::fromStdString(str.toString()));
 		auto * item = new QTableWidgetItem(QString::fromStdString(str.toString()));
-		item->setData(Qt::UserRole, QVariant::fromValue(i));
+		item->setData(Qt::UserRole, QVariant::fromValue(i.getNum()));
 		ui->lResources->setItem(i, 0, item);
 		ui->lResources->setItem(i, 0, item);
 		auto * spinBox = new QSpinBox;
 		auto * spinBox = new QSpinBox;
 		spinBox->setMaximum(i == GameResID::GOLD ? 999999 : 999);
 		spinBox->setMaximum(i == GameResID::GOLD ? 999999 : 999);
@@ -455,8 +456,6 @@ void QuestDelegate::updateModelData(QAbstractItemModel * model, const QModelInde
 	QStringList resourcesList;
 	QStringList resourcesList;
 	for(GameResID resource = GameResID::WOOD; resource < GameResID::COUNT ; resource++)
 	for(GameResID resource = GameResID::WOOD; resource < GameResID::COUNT ; resource++)
 	{
 	{
-		if(resource == GameResID::MITHRIL)
-			continue;
 		if(quest.mission.resources[resource] == 0)
 		if(quest.mission.resources[resource] == 0)
 			continue;
 			continue;
 		MetaString str;
 		MetaString str;

+ 5 - 6
mapeditor/inspector/rewardswidget.cpp

@@ -17,6 +17,7 @@
 #include "../lib/CCreatureHandler.h"
 #include "../lib/CCreatureHandler.h"
 #include "../lib/constants/StringConstants.h"
 #include "../lib/constants/StringConstants.h"
 #include "../lib/entities/artifact/CArtifact.h"
 #include "../lib/entities/artifact/CArtifact.h"
+#include "../lib/entities/ResourceTypeHandler.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/mapping/CMap.h"
 #include "../lib/modding/IdentifierStorage.h"
 #include "../lib/modding/IdentifierStorage.h"
 #include "../lib/modding/ModScope.h"
 #include "../lib/modding/ModScope.h"
@@ -55,16 +56,16 @@ RewardsWidget::RewardsWidget(CMap & m, CRewardableObject & p, QWidget *parent) :
 		ui->lDayOfWeek->addItem(tr("Day %1").arg(i));
 		ui->lDayOfWeek->addItem(tr("Day %1").arg(i));
 	
 	
 	//fill resources
 	//fill resources
-	ui->rResources->setRowCount(GameConstants::RESOURCE_QUANTITY - 1);
-	ui->lResources->setRowCount(GameConstants::RESOURCE_QUANTITY - 1);
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY - 1; ++i)
+	ui->rResources->setRowCount(LIBRARY->resourceTypeHandler->getAllObjects().size() - 1);
+	ui->lResources->setRowCount(LIBRARY->resourceTypeHandler->getAllObjects().size() - 1);
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		MetaString str;
 		MetaString str;
 		str.appendName(GameResID(i));
 		str.appendName(GameResID(i));
 		for(auto * w : {ui->rResources, ui->lResources})
 		for(auto * w : {ui->rResources, ui->lResources})
 		{
 		{
 			auto * item = new QTableWidgetItem(QString::fromStdString(str.toString()));
 			auto * item = new QTableWidgetItem(QString::fromStdString(str.toString()));
-			item->setData(Qt::UserRole, QVariant::fromValue(i));
+			item->setData(Qt::UserRole, QVariant::fromValue(i.getNum()));
 			w->setItem(i, 0, item);
 			w->setItem(i, 0, item);
 			auto * spinBox = new QSpinBox;
 			auto * spinBox = new QSpinBox;
 			spinBox->setMaximum(i == GameResID::GOLD ? 999999 : 999);
 			spinBox->setMaximum(i == GameResID::GOLD ? 999999 : 999);
@@ -779,8 +780,6 @@ void RewardsDelegate::updateModelData(QAbstractItemModel * model, const QModelIn
 		QStringList resourcesList;
 		QStringList resourcesList;
 		for(GameResID resource = GameResID::WOOD; resource < GameResID::COUNT ; resource++)
 		for(GameResID resource = GameResID::WOOD; resource < GameResID::COUNT ; resource++)
 		{
 		{
-			if(resource == GameResID::MITHRIL)
-				continue; // translated as "Abandoned"?
 			if(vinfo.reward.resources[resource] == 0)
 			if(vinfo.reward.resources[resource] == 0)
 				continue;
 				continue;
 			MetaString str;
 			MetaString str;

+ 7 - 5
mapeditor/inspector/towneventdialog.cpp

@@ -18,6 +18,8 @@
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/constants/NumericConstants.h"
 #include "../../lib/constants/NumericConstants.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/constants/StringConstants.h"
+#include "../../lib/GameLibrary.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 
 
 static const int FIRST_DAY_FOR_EVENT = 1;
 static const int FIRST_DAY_FOR_EVENT = 1;
 static const int LAST_DAY_FOR_EVENT = 999;
 static const int LAST_DAY_FOR_EVENT = 999;
@@ -79,9 +81,9 @@ void TownEventDialog::initPlayers()
 
 
 void TownEventDialog::initResources()
 void TownEventDialog::initResources()
 {
 {
-	ui->resourcesTable->setRowCount(GameConstants::RESOURCE_QUANTITY);
+	ui->resourcesTable->setRowCount(LIBRARY->resourceTypeHandler->getAllObjects().size());
 	auto resourcesMap = params.value("resources").toMap();
 	auto resourcesMap = params.value("resources").toMap();
-	for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		MetaString str;
 		MetaString str;
 		str.appendName(GameResID(i));
 		str.appendName(GameResID(i));
@@ -91,7 +93,7 @@ void TownEventDialog::initResources()
 		item->setText(name);
 		item->setText(name);
 		ui->resourcesTable->setItem(i, 0, item);
 		ui->resourcesTable->setItem(i, 0, item);
 
 
-		int val = resourcesMap.value(QString::fromStdString(GameConstants::RESOURCE_NAMES[i])).toInt();
+		int val = resourcesMap.value(QString::fromStdString(i.toResource()->getJsonKey())).toInt();
 		auto * edit = new QSpinBox(ui->resourcesTable);
 		auto * edit = new QSpinBox(ui->resourcesTable);
 		edit->setMaximum(i == GameResID::GOLD ? MAXIMUM_GOLD_CHANGE : MAXIMUM_RESOURCE_CHANGE);
 		edit->setMaximum(i == GameResID::GOLD ? MAXIMUM_GOLD_CHANGE : MAXIMUM_RESOURCE_CHANGE);
 		edit->setMinimum(i == GameResID::GOLD ? -MAXIMUM_GOLD_CHANGE : -MAXIMUM_RESOURCE_CHANGE);
 		edit->setMinimum(i == GameResID::GOLD ? -MAXIMUM_GOLD_CHANGE : -MAXIMUM_RESOURCE_CHANGE);
@@ -228,9 +230,9 @@ QVariant TownEventDialog::playersToVariant()
 QVariantMap TownEventDialog::resourcesToVariant()
 QVariantMap TownEventDialog::resourcesToVariant()
 {
 {
 	auto res = params.value("resources").toMap();
 	auto res = params.value("resources").toMap();
-	for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
-		auto itemType = QString::fromStdString(GameConstants::RESOURCE_NAMES[i]);
+		auto itemType = QString::fromStdString(i.toResource()->getJsonKey());
 		auto * itemQty = static_cast<QSpinBox *> (ui->resourcesTable->cellWidget(i, 1));
 		auto * itemQty = static_cast<QSpinBox *> (ui->resourcesTable->cellWidget(i, 1));
 
 
 		res[itemType] = QVariant::fromValue(itemQty->value());
 		res[itemType] = QVariant::fromValue(itemQty->value());

+ 7 - 3
mapeditor/mapsettings/eventsettings.cpp

@@ -14,6 +14,8 @@
 #include "../mapcontroller.h"
 #include "../mapcontroller.h"
 #include "../../lib/constants/NumericConstants.h"
 #include "../../lib/constants/NumericConstants.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/constants/StringConstants.h"
+#include "../../lib/GameLibrary.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 
 
 QString toQString(const PlayerColor & player)
 QString toQString(const PlayerColor & player)
 {
 {
@@ -41,8 +43,8 @@ std::set<PlayerColor> playersFromVariant(const QVariant & v)
 QVariant toVariant(const TResources & resources)
 QVariant toVariant(const TResources & resources)
 {
 {
 	QVariantMap result;
 	QVariantMap result;
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
-		result[QString::fromStdString(GameConstants::RESOURCE_NAMES[i])] = QVariant::fromValue(resources[i]);
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
+		result[QString::fromStdString(i.toResource()->getJsonKey())] = QVariant::fromValue(resources[i]);
 	return result;
 	return result;
 }
 }
 
 
@@ -51,7 +53,9 @@ TResources resourcesFromVariant(const QVariant & v)
 	JsonNode vJson;
 	JsonNode vJson;
 	for(auto r : v.toMap().keys())
 	for(auto r : v.toMap().keys())
 		vJson[r.toStdString()].Integer() = v.toMap().value(r).toInt();
 		vJson[r.toStdString()].Integer() = v.toMap().value(r).toInt();
-	return TResources(vJson);
+	ResourceSet res;
+	res.resolveFromJson(vJson);
+	return res;
 }
 }
 
 
 QVariant toVariant(std::vector<ObjectInstanceID> objects)
 QVariant toVariant(std::vector<ObjectInstanceID> objects)

+ 7 - 5
mapeditor/mapsettings/timedevent.cpp

@@ -14,6 +14,8 @@
 #include "../mapeditorroles.h"
 #include "../mapeditorroles.h"
 #include "../../lib/constants/EntityIdentifiers.h"
 #include "../../lib/constants/EntityIdentifiers.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/constants/StringConstants.h"
+#include "../../lib/GameLibrary.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 
 
 TimedEvent::TimedEvent(MapController & c, QListWidgetItem * t, QWidget *parent) : 
 TimedEvent::TimedEvent(MapController & c, QListWidgetItem * t, QWidget *parent) : 
 	controller(c),
 	controller(c),
@@ -45,13 +47,13 @@ TimedEvent::TimedEvent(MapController & c, QListWidgetItem * t, QWidget *parent)
 		ui->playersAffected->addItem(item);
 		ui->playersAffected->addItem(item);
 	}
 	}
 
 
-	ui->resources->setRowCount(GameConstants::RESOURCE_QUANTITY);
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
+	ui->resources->setRowCount(LIBRARY->resourceTypeHandler->getAllObjects().size());
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
 		MetaString str;
 		MetaString str;
 		str.appendName(GameResID(i));
 		str.appendName(GameResID(i));
 		auto name = QString::fromStdString(str.toString());
 		auto name = QString::fromStdString(str.toString());
-		int val = params.value("resources").toMap().value(QString::fromStdString(GameConstants::RESOURCE_NAMES[i])).toInt();
+		int val = params.value("resources").toMap().value(QString::fromStdString(i.toResource()->getJsonKey())).toInt();
 		ui->resources->setItem(i, 0, new QTableWidgetItem(name));
 		ui->resources->setItem(i, 0, new QTableWidgetItem(name));
 		auto nval = new QTableWidgetItem(QString::number(val));
 		auto nval = new QTableWidgetItem(QString::number(val));
 		nval->setFlags(nval->flags() | Qt::ItemIsEditable);
 		nval->setFlags(nval->flags() | Qt::ItemIsEditable);
@@ -94,9 +96,9 @@ void TimedEvent::on_TimedEvent_finished(int result)
 	descriptor["players"] = QVariant::fromValue(players);
 	descriptor["players"] = QVariant::fromValue(players);
 
 
 	auto res = target->data(Qt::UserRole).toMap().value("resources").toMap();
 	auto res = target->data(Qt::UserRole).toMap().value("resources").toMap();
-	for(int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
+	for(auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 	{
 	{
-		auto itemType = QString::fromStdString(GameConstants::RESOURCE_NAMES[i]);
+		auto itemType = QString::fromStdString(i.toResource()->getJsonKey());
 		auto * itemQty = ui->resources->item(i, 1);
 		auto * itemQty = ui->resources->item(i, 1);
 		res[itemType] = QVariant::fromValue(itemQty->text().toInt());
 		res[itemType] = QVariant::fromValue(itemQty->text().toInt());
 	}
 	}

+ 4 - 2
mapeditor/mapsettings/victoryconditions.cpp

@@ -12,9 +12,11 @@
 #include "ui_victoryconditions.h"
 #include "ui_victoryconditions.h"
 #include "../mapcontroller.h"
 #include "../mapcontroller.h"
 
 
+#include "../../lib/GameLibrary.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/entities/artifact/CArtHandler.h"
 #include "../../lib/entities/artifact/CArtHandler.h"
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/faction/CTownHandler.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/mapObjects/CGCreature.h"
 #include "../../lib/mapObjects/CGCreature.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 
 
@@ -406,12 +408,12 @@ void VictoryConditions::on_victoryComboBox_currentIndexChanged(int index)
 			victoryTypeWidget = new QComboBox;
 			victoryTypeWidget = new QComboBox;
 			ui->victoryParamsLayout->addWidget(victoryTypeWidget);
 			ui->victoryParamsLayout->addWidget(victoryTypeWidget);
 			{
 			{
-				for(int resType = 0; resType < GameConstants::RESOURCE_QUANTITY; ++resType)
+				for(auto & resType : LIBRARY->resourceTypeHandler->getAllObjects())
 				{
 				{
 					MetaString str;
 					MetaString str;
 					str.appendName(GameResID(resType));
 					str.appendName(GameResID(resType));
 					auto resName = QString::fromStdString(str.toString());
 					auto resName = QString::fromStdString(str.toString());
-					victoryTypeWidget->addItem(resName, QVariant::fromValue(resType));
+					victoryTypeWidget->addItem(resName, QVariant::fromValue(resType.getNum()));
 				}
 				}
 			}
 			}
 
 

+ 4 - 2
mapeditor/templateeditor/graphicelements/CardItem.cpp

@@ -16,6 +16,8 @@
 #include "../../../lib/constants/EntityIdentifiers.h"
 #include "../../../lib/constants/EntityIdentifiers.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/constants/StringConstants.h"
 #include "../../../lib/rmg/CRmgTemplate.h"
 #include "../../../lib/rmg/CRmgTemplate.h"
+#include "../../../lib/GameLibrary.h"
+#include "../../../lib/entities/ResourceTypeHandler.h"
 
 
 QDomElement CardItem::getElementById(const QDomDocument& doc, const QString& id)
 QDomElement CardItem::getElementById(const QDomDocument& doc, const QString& id)
 {
 {
@@ -149,11 +151,11 @@ int CardItem::getId()
 
 
 void CardItem::setResAmount(GameResID res, int val)
 void CardItem::setResAmount(GameResID res, int val)
 {
 {
-	auto textElem = getElementById(doc, "text" + QString::fromStdString(GameConstants::RESOURCE_NAMES[res]));
+	auto textElem = getElementById(doc, "text" + QString::fromStdString(res.toResource()->getJsonKey()));
 	textElem.setAttribute("style", textElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + QColor(useBlackText ? Qt::black : Qt::white).name() + ";"));
 	textElem.setAttribute("style", textElem.attribute("style").replace(QRegularExpression("fill:.*?;"), "fill:" + QColor(useBlackText ? Qt::black : Qt::white).name() + ";"));
 	textElem.firstChild().setNodeValue(val ? QString::number(val) : "");
 	textElem.firstChild().setNodeValue(val ? QString::number(val) : "");
 
 
-	auto iconElem = getElementById(doc, "icon" + QString::fromStdString(GameConstants::RESOURCE_NAMES[res]));
+	auto iconElem = getElementById(doc, "icon" + QString::fromStdString(res.toResource()->getJsonKey()));
 	iconElem.setAttribute("opacity", val ? "1.0" : "0.1");
 	iconElem.setAttribute("opacity", val ? "1.0" : "0.1");
 }
 }
 
 

+ 11 - 9
mapeditor/templateeditor/mineselector.cpp

@@ -15,10 +15,12 @@
 
 
 #include "../../lib/GameLibrary.h"
 #include "../../lib/GameLibrary.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
 #include "../../lib/texts/CGeneralTextHandler.h"
+#include "../../lib/texts/MetaString.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 
 
-auto resources = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS};
+auto resourcesToShow = std::vector<EGameResID>{EGameResID::GOLD, EGameResID::WOOD, EGameResID::MERCURY, EGameResID::ORE, EGameResID::SULFUR, EGameResID::CRYSTAL, EGameResID::GEMS}; //todo: configurable resource support
 
 
-MineSelector::MineSelector(std::map<TResource, ui16> & mines) :
+MineSelector::MineSelector(std::map<GameResID, ui16> & mines) :
 	ui(new Ui::MineSelector),
 	ui(new Ui::MineSelector),
 	minesSelected(mines)
 	minesSelected(mines)
 {
 {
@@ -29,18 +31,18 @@ MineSelector::MineSelector(std::map<TResource, ui16> & mines) :
 	setWindowModality(Qt::ApplicationModal);
 	setWindowModality(Qt::ApplicationModal);
 
 
 	ui->tableWidgetMines->setColumnCount(2);
 	ui->tableWidgetMines->setColumnCount(2);
-	ui->tableWidgetMines->setRowCount(resources.size());
+	ui->tableWidgetMines->setRowCount(resourcesToShow.size());
 	ui->tableWidgetMines->setHorizontalHeaderLabels({tr("Resource"), tr("Mines")});
 	ui->tableWidgetMines->setHorizontalHeaderLabels({tr("Resource"), tr("Mines")});
-	for (int row = 0; row < resources.size(); ++row)
+	for (int row = 0; row < resourcesToShow.size(); ++row)
 	{
 	{
-		auto name = LIBRARY->generaltexth->translate(TextIdentifier("core.restypes", resources[row].getNum()).get());
+		auto name = resourcesToShow[row].toResource()->getNameTranslated();
 		auto label = new QLabel(QString::fromStdString(name));
 		auto label = new QLabel(QString::fromStdString(name));
 		label->setAlignment(Qt::AlignCenter);
 		label->setAlignment(Qt::AlignCenter);
 		ui->tableWidgetMines->setCellWidget(row, 0, label);
 		ui->tableWidgetMines->setCellWidget(row, 0, label);
 
 
 		auto spinBox = new QSpinBox();
 		auto spinBox = new QSpinBox();
 		spinBox->setRange(0, 100);
 		spinBox->setRange(0, 100);
-		spinBox->setValue(mines[resources[row]]);
+		spinBox->setValue(mines[resourcesToShow[row]]);
 		ui->tableWidgetMines->setCellWidget(row, 1, spinBox);
 		ui->tableWidgetMines->setCellWidget(row, 1, spinBox);
 	}
 	}
 	ui->tableWidgetMines->resizeColumnsToContents();
 	ui->tableWidgetMines->resizeColumnsToContents();
@@ -48,7 +50,7 @@ MineSelector::MineSelector(std::map<TResource, ui16> & mines) :
 	show();
 	show();
 }
 }
 
 
-void MineSelector::showMineSelector(std::map<TResource, ui16> & mines)
+void MineSelector::showMineSelector(std::map<GameResID, ui16> & mines)
 {
 {
 	auto * dialog = new MineSelector(mines);
 	auto * dialog = new MineSelector(mines);
 	dialog->setAttribute(Qt::WA_DeleteOnClose);
 	dialog->setAttribute(Qt::WA_DeleteOnClose);
@@ -57,8 +59,8 @@ void MineSelector::showMineSelector(std::map<TResource, ui16> & mines)
 
 
 void MineSelector::on_buttonBoxResult_accepted()
 void MineSelector::on_buttonBoxResult_accepted()
 {
 {
-	for (int row = 0; row < resources.size(); ++row)
-		minesSelected[resources[row]] = static_cast<QSpinBox *>(ui->tableWidgetMines->cellWidget(row, 1))->value();
+	for (int row = 0; row < resourcesToShow.size(); ++row)
+		minesSelected[resourcesToShow[row]] = static_cast<QSpinBox *>(ui->tableWidgetMines->cellWidget(row, 1))->value();
 
 
 	close();
 	close();
 }
 }

+ 3 - 3
mapeditor/templateeditor/mineselector.h

@@ -22,9 +22,9 @@ class MineSelector : public QDialog
 	Q_OBJECT
 	Q_OBJECT
 
 
 public:
 public:
-	explicit MineSelector(std::map<TResource, ui16> & mines);
+	explicit MineSelector(std::map<GameResID, ui16> & mines);
 
 
-	static void showMineSelector(std::map<TResource, ui16> & mines);
+	static void showMineSelector(std::map<GameResID, ui16> & mines);
 
 
 private slots:
 private slots:
 	void on_buttonBoxResult_accepted();
 	void on_buttonBoxResult_accepted();
@@ -33,5 +33,5 @@ private slots:
 private:
 private:
 	Ui::MineSelector *ui;
 	Ui::MineSelector *ui;
 
 
-	std::map<TResource, ui16> & minesSelected;
+	std::map<GameResID, ui16> & minesSelected;
 };
 };

+ 0 - 2
scripting/lua/api/ObjectInstance.h

@@ -14,8 +14,6 @@
 
 
 #include "../LuaWrapper.h"
 #include "../LuaWrapper.h"
 
 
-#include "../../../lib/mapObjects/CObjectHandler.h"
-
 namespace scripting
 namespace scripting
 {
 {
 namespace api
 namespace api

+ 1 - 1
scripting/lua/api/netpacks/SetResources.cpp

@@ -82,7 +82,7 @@ int SetResourcesProxy::getAmount(lua_State * L)
 
 
 	S.clear();
 	S.clear();
 
 
-	const TQuantity amount = vstd::atOrDefault(object->res, static_cast<size_t>(type), 0);
+	const TQuantity amount = object->res[type];
 	S.push(amount);
 	S.push(amount);
 	return 1;
 	return 1;
 }
 }

+ 1 - 1
scripts/lib/erm/MA.lua

@@ -8,7 +8,7 @@ local Bonus = require("Bonus")
 local BonusBearer = require("BonusBearer")
 local BonusBearer = require("BonusBearer")
 local BonusList = require("BonusList")
 local BonusList = require("BonusList")
 
 
-local RES = {[0] = "wood", [1] = "mercury", [2] = "ore", [3] = "sulfur", [4] = "crystal", [5] = "gems", [6] = "gold", [7] = "mithril"}
+local RES = {[0] = "wood", [1] = "mercury", [2] = "ore", [3] = "sulfur", [4] = "crystal", [5] = "gems", [6] = "gold"}
 
 
 local SERVICES = SERVICES
 local SERVICES = SERVICES
 local creatures = SERVICES:creatures()
 local creatures = SERVICES:creatures()

+ 2 - 1
server/CGameHandler.cpp

@@ -2293,7 +2293,8 @@ bool CGameHandler::spellResearch(ObjectInstanceID tid, SpellID spellAtSlot, bool
 		return true;
 		return true;
 	}
 	}
 
 
-	auto costBase = TResources(gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
+	ResourceSet costBase;
+	costBase.resolveFromJson(gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST).Vector()[level]);
 	auto costExponent = gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
 	auto costExponent = gameInfo().getSettings().getValue(EGameSettings::TOWNS_SPELL_RESEARCH_COST_EXPONENT_PER_RESEARCH).Vector()[level].Float();
 	auto cost = costBase * std::pow(t->spellResearchAcceptedCounter + 1, costExponent);
 	auto cost = costBase * std::pow(t->spellResearchAcceptedCounter + 1, costExponent);
 
 

+ 3 - 2
server/CVCMIServer.cpp

@@ -20,6 +20,7 @@
 #include "../lib/campaign/CampaignState.h"
 #include "../lib/campaign/CampaignState.h"
 #include "../lib/entities/hero/CHeroHandler.h"
 #include "../lib/entities/hero/CHeroHandler.h"
 #include "../lib/entities/hero/CHeroClass.h"
 #include "../lib/entities/hero/CHeroClass.h"
+#include "../lib/entities/ResourceTypeHandler.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/mapping/CMapInfo.h"
 #include "../lib/mapping/CMapInfo.h"
 #include "../lib/mapping/CMapHeader.h"
 #include "../lib/mapping/CMapHeader.h"
@@ -709,7 +710,7 @@ void CVCMIServer::setPlayerHandicap(PlayerColor color, Handicap handicap)
 		return;
 		return;
 	}
 	}
 
 
-	for(auto & res : EGameResID::ALL_RESOURCES())
+	for(auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
 		if(handicap.startBonus[res] != 0)
 		if(handicap.startBonus[res] != 0)
 		{
 		{
 			str.appendRawString(" ");
 			str.appendRawString(" ");
@@ -1008,7 +1009,7 @@ void CVCMIServer::multiplayerWelcomeMessage()
 			str.appendRawString(" ");
 			str.appendRawString(" ");
 			str.appendName(pi.first);
 			str.appendName(pi.first);
 			str.appendRawString(":");
 			str.appendRawString(":");
-			for(auto & res : EGameResID::ALL_RESOURCES())
+			for(auto & res : LIBRARY->resourceTypeHandler->getAllObjects())
 				if(pi.second.handicap.startBonus[res] != 0)
 				if(pi.second.handicap.startBonus[res] != 0)
 				{
 				{
 					str.appendRawString(" ");
 					str.appendRawString(" ");

+ 5 - 4
server/processors/NewTurnProcessor.cpp

@@ -21,6 +21,7 @@
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/constants/StringConstants.h"
 #include "../../lib/entities/building/CBuilding.h"
 #include "../../lib/entities/building/CBuilding.h"
 #include "../../lib/entities/faction/CTownHandler.h"
 #include "../../lib/entities/faction/CTownHandler.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/gameState/SThievesGuildInfo.h"
 #include "../../lib/gameState/SThievesGuildInfo.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
@@ -58,7 +59,7 @@ void NewTurnProcessor::handleTimeEvents(PlayerColor color)
 		if (!event.resources.empty())
 		if (!event.resources.empty())
 		{
 		{
 			gameHandler->giveResources(color, event.resources);
 			gameHandler->giveResources(color, event.resources);
-			for (GameResID i : GameResID::ALL_RESOURCES())
+			for (GameResID i : LIBRARY->resourceTypeHandler->getAllObjects())
 				if (event.resources[i])
 				if (event.resources[i])
 					iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
 					iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
 		}
 		}
@@ -94,7 +95,7 @@ void NewTurnProcessor::handleTownEvents(const CGTownInstance * town)
 		{
 		{
 			gameHandler->giveResources(player, event.resources);
 			gameHandler->giveResources(player, event.resources);
 
 
-			for (GameResID i : GameResID::ALL_RESOURCES())
+			for (GameResID i : LIBRARY->resourceTypeHandler->getAllObjects())
 				if (event.resources[i])
 				if (event.resources[i])
 					iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
 					iw.components.emplace_back(ComponentType::RESOURCE, i, event.resources[i]);
 		}
 		}
@@ -256,9 +257,9 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
 		const JsonNode & difficultyConfig = weeklyBonusesConfig[difficultyName];
 		const JsonNode & difficultyConfig = weeklyBonusesConfig[difficultyName];
 
 
 		// Distribute weekly bonuses over 7 days, depending on the current day of the week
 		// Distribute weekly bonuses over 7 days, depending on the current day of the week
-		for (GameResID i : GameResID::ALL_RESOURCES())
+		for (GameResID i : LIBRARY->resourceTypeHandler->getAllObjects())
 		{
 		{
-			const std::string & name = GameConstants::RESOURCE_NAMES[i.getNum()];
+			const std::string & name = i.toResource()->getJsonKey();
 			int64_t weeklyBonus = difficultyConfig[name].Integer();
 			int64_t weeklyBonus = difficultyConfig[name].Integer();
 			int64_t dayOfWeek = gameHandler->gameState().getDate(Date::DAY_OF_WEEK);
 			int64_t dayOfWeek = gameHandler->gameState().getDate(Date::DAY_OF_WEEK);
 			int64_t dailyIncome = incomeHandicapped[i];
 			int64_t dailyIncome = incomeHandicapped[i];

+ 2 - 1
server/processors/PlayerMessageProcessor.cpp

@@ -23,6 +23,7 @@
 #include "../../lib/entities/artifact/CArtHandler.h"
 #include "../../lib/entities/artifact/CArtHandler.h"
 #include "../../lib/entities/building/CBuilding.h"
 #include "../../lib/entities/building/CBuilding.h"
 #include "../../lib/entities/hero/CHeroHandler.h"
 #include "../../lib/entities/hero/CHeroHandler.h"
+#include "../../lib/entities/ResourceTypeHandler.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/gameState/CGameState.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
@@ -593,7 +594,7 @@ void PlayerMessageProcessor::cheatResources(PlayerColor player, std::vector<std:
 
 
 	TResources resources;
 	TResources resources;
 	resources[EGameResID::GOLD] = baseResourceAmount * 1000;
 	resources[EGameResID::GOLD] = baseResourceAmount * 1000;
-	for (GameResID i = EGameResID::WOOD; i < EGameResID::GOLD; ++i)
+	for (auto & i : LIBRARY->resourceTypeHandler->getAllObjects())
 		resources[i] = baseResourceAmount;
 		resources[i] = baseResourceAmount;
 
 
 	gameHandler->giveResources(player, resources);
 	gameHandler->giveResources(player, resources);

+ 0 - 2
test/erm/ERM_OB_T.cpp

@@ -13,8 +13,6 @@
 
 
 #include "../scripting/ScriptFixture.h"
 #include "../scripting/ScriptFixture.h"
 
 
-#include "../../lib/mapObjects/CObjectHandler.h"
-
 namespace test
 namespace test
 {
 {
 namespace scripting
 namespace scripting

+ 1 - 0
test/mock/mock_Services.h

@@ -21,6 +21,7 @@ public:
 	MOCK_CONST_METHOD0(factions, const FactionService *());
 	MOCK_CONST_METHOD0(factions, const FactionService *());
 	MOCK_CONST_METHOD0(heroClasses, const HeroClassService *());
 	MOCK_CONST_METHOD0(heroClasses, const HeroClassService *());
 	MOCK_CONST_METHOD0(heroTypes, const HeroTypeService *());
 	MOCK_CONST_METHOD0(heroTypes, const HeroTypeService *());
+	MOCK_CONST_METHOD0(resources, const ResourceTypeService *());
 #if SCRIPTING_ENABLED
 #if SCRIPTING_ENABLED
 	MOCK_CONST_METHOD0(scripts, const scripting::Service *());
 	MOCK_CONST_METHOD0(scripts, const scripting::Service *());
 #endif
 #endif

+ 0 - 1
test/vcai/ResourceManagerTest.cpp

@@ -214,7 +214,6 @@ TEST_F(ResourceManagerTest, freeResources)
 	ASSERT_GE(res[EGameResID::CRYSTAL], 0);
 	ASSERT_GE(res[EGameResID::CRYSTAL], 0);
 	ASSERT_GE(res[EGameResID::GEMS], 0);
 	ASSERT_GE(res[EGameResID::GEMS], 0);
 	ASSERT_GE(res[EGameResID::GOLD], 0);
 	ASSERT_GE(res[EGameResID::GOLD], 0);
-	ASSERT_GE(res[EGameResID::MITHRIL], 0);
 }
 }
 
 
 TEST_F(ResourceManagerTest, freeResourcesWithManyGoals)
 TEST_F(ResourceManagerTest, freeResourcesWithManyGoals)