Răsfoiți Sursa

Rmg water support (#751)

* Roads added to shipyard
* Load general rmg parameters from config
* Fix issue with default zone guard
* Move magic numbers related to balance to randomMap.json
Nordsoft91 3 ani în urmă
părinte
comite
5054ee011a

+ 59 - 0
config/randomMap.json

@@ -0,0 +1,59 @@
+{
+  "terrain" :
+  {
+    "undergroundAllow" : ["lava"], //others to be replaced by subterranena
+    "groundProhibit" : ["subterranean"] //to be replaced by dirt
+  },
+  "waterZone" :
+  {
+    "treasure" :
+    [
+        { "min" : 2000, "max" : 6000, "density" : 1 },
+        { "min" : 100, "max" : 1000, "density" : 5 }
+    ],
+    "shipyard" :
+    {
+      "value" : 3500
+    }
+  },
+  "mines" :
+  {
+    "value" :
+    {
+      "wood" : 1500,
+      "ore" : 1500,
+      "gems" : 3500,
+      "crystal" : 3500,
+      "mercury" : 3500,
+      "sulfur" : 3500,
+      "gold" : 7000
+    },
+    "extraResourcesLimit" : 3
+  },
+  "minGuardStrength" : 2000,
+  "defaultRoadType" : "cobblestone_road",
+  "treasureValueLimit" : 20000, //generate pandora with gold for treasure above this limit
+  "prisons" :
+  {
+    "experience" : [0, 5000, 15000, 90000, 500000],
+    "value" : [2500, 5000, 10000, 20000, 30000],
+  },
+  "scrolls" :
+  {
+    "value" : [500, 2000, 3000, 4000, 5000],
+  },
+  "pandoras" :
+  {
+    "valueMultiplierGold" : 5000,
+    "valueMultiplierExperience" : 6000,
+    "valueMultiplierSpells" : 2500,
+    "valueSpellSchool" : 15000,
+    "valueSpell60" : 30000,
+    "creaturesValue" : [5000, 7000, 9000, 12000, 16000, 21000, 27000]
+  },
+  "quests" : 
+  {
+    "value" : [2000, 5333, 8666, 12000],
+    "rewardValue" : [5000, 10000, 15000, 20000]
+  }
+}

+ 10 - 0
config/schemas/template.json

@@ -60,6 +60,12 @@
 					"minimum" : 0
 				}
 			}
+		},
+		"waterContent":
+		{
+			"enum": ["none", "normal", "islands"],
+			"additionalProperties" : false,
+			"type": "string"
 		}
 	},
 
@@ -73,6 +79,10 @@
 			"type": "array",
 			"items":{"$ref" : "#/definitions/connection"}	
 		},
+		"allowedWaterContent": {
+			"type": "array",
+			"items": {"$ref" : "#/definitions/waterContent"}
+		},
 		"required" : ["zones", "connections"],
 		"additionalProperties" : false
 	}

+ 1 - 1
lib/rmg/CMapGenOptions.cpp

@@ -246,7 +246,7 @@ void CMapGenOptions::finalize(CRandomGenerator & rand)
 
 	if(waterContent == EWaterContent::RANDOM)
 	{
-		waterContent = static_cast<EWaterContent::EWaterContent>(rand.nextInt(EWaterContent::NONE, EWaterContent::ISLANDS));
+		waterContent = *RandomGeneratorUtil::nextItem(mapTemplate->getWaterContentAllowed(), rand);
 	}
 	if(monsterStrength == EMonsterStrength::RANDOM)
 	{

+ 83 - 2
lib/rmg/CMapGenerator.cpp

@@ -62,10 +62,89 @@ CMapGenerator::CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed) :
 	zonesTotal(0), tiles(nullptr), prisonsRemaining(0),
     monolithIndex(0)
 {
+	loadConfig();
 	rand.setSeed(this->randomSeed);
 	mapGenOptions.finalize(rand);
 }
 
+void CMapGenerator::loadConfig()
+{
+	static std::map<std::string, ETerrainType> terrainMap
+	{
+		{"dirt", ETerrainType::DIRT},
+		{"sand", ETerrainType::SAND},
+		{"grass", ETerrainType::GRASS},
+		{"snow", ETerrainType::SNOW},
+		{"swamp", ETerrainType::SWAMP},
+		{"subterranean", ETerrainType::SUBTERRANEAN},
+		{"lava", ETerrainType::LAVA},
+		{"rough", ETerrainType::ROUGH}
+	};
+	static const std::map<std::string, Res::ERes> resMap
+	{
+		{"wood", Res::ERes::WOOD},
+		{"ore", Res::ERes::ORE},
+		{"gems", Res::ERes::GEMS},
+		{"crystal", Res::ERes::CRYSTAL},
+		{"mercury", Res::ERes::MERCURY},
+		{"sulfur", Res::ERes::SULFUR},
+		{"gold", Res::ERes::GOLD},
+	};
+	static std::map<std::string, ERoadType::ERoadType> roadTypeMap
+	{
+		{"dirt_road", ERoadType::DIRT_ROAD},
+		{"gravel_road", ERoadType::GRAVEL_ROAD},
+		{"cobblestone_road", ERoadType::COBBLESTONE_ROAD}
+	};
+	static const ResourceID path("config/randomMap.json");
+	JsonNode randomMapJson(path);
+	for(auto& s : randomMapJson["terrain"]["undergroundAllow"].Vector())
+	{
+		if(!s.isNull())
+			config.terrainUndergroundAllowed.push_back(terrainMap[s.String()]);
+	}
+	for(auto& s : randomMapJson["terrain"]["groundProhibit"].Vector())
+	{
+		if(!s.isNull())
+			config.terrainGroundProhibit.push_back(terrainMap[s.String()]);
+	}
+	config.shipyardGuard = randomMapJson["waterZone"]["shipyard"]["value"].Integer();
+	for(auto & treasure : randomMapJson["waterZone"]["treasure"].Vector())
+	{
+		config.waterTreasure.emplace_back(treasure["min"].Integer(), treasure["max"].Integer(), treasure["density"].Integer());
+	}
+	for(auto& s : resMap)
+	{
+		config.mineValues[s.second] = randomMapJson["mines"]["value"][s.first].Integer();
+	}
+	config.mineExtraResources = randomMapJson["mines"]["extraResourcesLimit"].Integer();
+	config.minGuardStrength = randomMapJson["minGuardStrength"].Integer();
+	config.defaultRoadType = roadTypeMap[randomMapJson["defaultRoadType"].String()];
+	config.treasureValueLimit = randomMapJson["treasureValueLimit"].Integer();
+	for(auto & i : randomMapJson["prisons"]["experience"].Vector())
+		config.prisonExperience.push_back(i.Integer());
+	for(auto & i : randomMapJson["prisons"]["value"].Vector())
+		config.prisonValues.push_back(i.Integer());
+	for(auto & i : randomMapJson["scrolls"]["value"].Vector())
+		config.scrollValues.push_back(i.Integer());
+	for(auto & i : randomMapJson["pandoras"]["creaturesValue"].Vector())
+		config.pandoraCreatureValues.push_back(i.Integer());
+	for(auto & i : randomMapJson["quests"]["value"].Vector())
+		config.questValues.push_back(i.Integer());
+	for(auto & i : randomMapJson["quests"]["rewardValue"].Vector())
+		config.questRewardValues.push_back(i.Integer());
+	config.pandoraMultiplierGold = randomMapJson["pandoras"]["valueMultiplierGold"].Integer();
+	config.pandoraMultiplierExperience = randomMapJson["pandoras"]["valueMultiplierExperience"].Integer();
+	config.pandoraMultiplierSpells = randomMapJson["pandoras"]["valueMultiplierSpells"].Integer();
+	config.pandoraSpellSchool = randomMapJson["pandoras"]["valueSpellSchool"].Integer();
+	config.pandoraSpell60 = randomMapJson["pandoras"]["valueSpell60"].Integer();
+}
+
+const CMapGenerator::Config & CMapGenerator::getConfig() const
+{
+	return config;
+}
+
 void CMapGenerator::initTiles()
 {
 	map->initTerrain();
@@ -309,8 +388,10 @@ void CMapGenerator::genZones()
 void CMapGenerator::createWaterTreasures()
 {
 	//add treasures on water
-	getZoneWater().second->addTreasureInfo(CTreasureInfo{100, 1000, 5});
-	getZoneWater().second->addTreasureInfo(CTreasureInfo{2000, 6000, 1});
+	for(auto & treasureInfo : getConfig().waterTreasure)
+	{
+		getZoneWater().second->addTreasureInfo(treasureInfo);
+	}
 }
 
 void CMapGenerator::prepareWaterTiles()

+ 23 - 0
lib/rmg/CMapGenerator.h

@@ -52,11 +52,31 @@ public:
 class DLL_LINKAGE CMapGenerator
 {
 public:
+	struct Config
+	{
+		std::vector<ETerrainType> terrainUndergroundAllowed;
+		std::vector<ETerrainType> terrainGroundProhibit;
+		std::vector<CTreasureInfo> waterTreasure;
+		int shipyardGuard;
+		int mineExtraResources;
+		std::map<Res::ERes, int> mineValues;
+		int minGuardStrength;
+		ERoadType::ERoadType defaultRoadType;
+		int treasureValueLimit;
+		std::vector<int> prisonExperience, prisonValues;
+		std::vector<int> scrollValues;
+		int pandoraMultiplierGold, pandoraMultiplierExperience, pandoraMultiplierSpells, pandoraSpellSchool, pandoraSpell60;
+		std::vector<int> pandoraCreatureValues;
+		std::vector<int> questValues, questRewardValues;
+	};
+	
 	using Zones = std::map<TRmgTemplateZoneId, std::shared_ptr<CRmgTemplateZone>>;
 
 	explicit CMapGenerator(CMapGenOptions& mapGenOptions, int RandomSeed = std::time(nullptr));
 	~CMapGenerator(); // required due to std::unique_ptr
 	
+	const Config & getConfig() const;
+	
 	mutable std::unique_ptr<CMap> map;
 	CRandomGenerator rand;
 	
@@ -111,6 +131,7 @@ public:
 private:
 	int randomSeed;
 	CMapGenOptions& mapGenOptions;
+	Config config;
 	
 	std::vector<rmg::ZoneConnection> connectionsLeft;
 	Zones zones;
@@ -129,6 +150,8 @@ private:
 	void checkIsOnMap(const int3 &tile) const; //throws
 
 	/// Generation methods
+	void loadConfig();
+	
 	std::string getMapDescription() const;
 
 	void initPrisonsRemaining();

+ 33 - 1
lib/rmg/CRmgTemplate.cpp

@@ -10,6 +10,7 @@
 
 #include "StdInc.h"
 #include <vstd/ContainerUtils.h>
+#include <boost/bimap.hpp>
 #include "CRmgTemplate.h"
 
 #include "../mapping/CMap.h"
@@ -359,7 +360,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 			rawStrength = static_cast<decltype(rawStrength)>(zoneMonsterStrength);
 			rawStrength++;
 		}
-		handler.serializeEnum("monsters", rawStrength, STRENGTH);
+		handler.serializeEnum("monsters", rawStrength, EMonsterStrength::ZONE_NORMAL + 1, STRENGTH);
 		if(!handler.saving)
 		{
 			rawStrength--;
@@ -443,6 +444,11 @@ bool CRmgTemplate::isWaterContentAllowed(EWaterContent::EWaterContent waterConte
 	return waterContent == EWaterContent::EWaterContent::RANDOM || allowedWaterContent.count(waterContent);
 }
 
+const std::set<EWaterContent::EWaterContent> & CRmgTemplate::getWaterContentAllowed() const
+{
+	return allowedWaterContent;
+}
+
 void CRmgTemplate::setId(const std::string & value)
 {
 	id = value;
@@ -583,6 +589,32 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
 		auto connectionsData = handler.enterArray("connections");
 		connectionsData.serializeStruct(connections);
 	}
+	
+	{
+		boost::bimap<EWaterContent::EWaterContent, std::string> enc;
+		enc.insert({EWaterContent::NONE, "none"});
+		enc.insert({EWaterContent::NORMAL, "normal"});
+		enc.insert({EWaterContent::ISLANDS, "islands"});
+		JsonNode node;
+		if(handler.saving)
+		{
+			node.setType(JsonNode::JsonType::DATA_VECTOR);
+			for(auto wc : allowedWaterContent)
+			{
+				JsonNode n;
+				n.String() = enc.left.at(wc);
+				node.Vector().push_back(n);
+			}
+		}
+		handler.serializeRaw("allowedWaterContent", node, boost::none);
+		if(!handler.saving)
+		{
+			for(auto wc : node.Vector())
+			{
+				allowedWaterContent.insert(enc.right.at(std::string(wc.String())));
+			}
+		}
+	}
 
 	{
 		auto zonesData = handler.enterStruct("zones");

+ 1 - 0
lib/rmg/CRmgTemplate.h

@@ -181,6 +181,7 @@ public:
 
 	bool matchesSize(const int3 & value) const;
 	bool isWaterContentAllowed(EWaterContent::EWaterContent waterContent) const;
+	const std::set<EWaterContent::EWaterContent> & getWaterContentAllowed() const;
 
 	void setId(const std::string & value);
 	const std::string & getName() const;

+ 113 - 124
lib/rmg/CRmgTemplateZone.cpp

@@ -641,7 +641,7 @@ void CRmgTemplateZone::waterConnection(CRmgTemplateZone& dst)
 			
 			if(dst.getType() == ETemplateZoneType::PLAYER_START || dst.getType() == ETemplateZoneType::CPU_START || zoneTowns)
 			{
-				coastTile = dst.createShipyard(lake.tiles, 3500);
+				coastTile = dst.createShipyard(lake.tiles, gen->getConfig().shipyardGuard);
 				if(!coastTile.valid())
 				{
 					coastTile = makeBoat(dst.getId(), lake.tiles);
@@ -1266,7 +1266,7 @@ bool CRmgTemplateZone::addMonster(int3 &pos, si32 strength, bool clearSurroundin
 	int strength2 = static_cast<int>(std::max(0.f, (strength - value2[monsterStrength]) * multiplier2[monsterStrength]));
 
 	strength = strength1 + strength2;
-	if (strength < 2000)
+	if (strength < gen->getConfig().minGuardStrength)
 		return false; //no guard at all
 
 	CreatureID creId = CreatureID::NONE;
@@ -1742,14 +1742,14 @@ void CRmgTemplateZone::initTerrainType ()
 
 		//TODO: allow new types of terrain?
 		{
-			if (isUnderground())
+			if(isUnderground())
 			{
-				if (terrainType != ETerrainType::LAVA)
+				if(!vstd::contains(gen->getConfig().terrainUndergroundAllowed, terrainType))
 					terrainType = ETerrainType::SUBTERRANEAN;
 			}
 			else
 			{
-				if (terrainType == ETerrainType::SUBTERRANEAN)
+				if(vstd::contains(gen->getConfig().terrainGroundProhibit, terrainType))
 					terrainType = ETerrainType::DIRT;
 			}
 		}
@@ -1767,8 +1767,6 @@ void CRmgTemplateZone::paintZoneTerrain (ETerrainType terrainType)
 bool CRmgTemplateZone::placeMines ()
 {
 	using namespace Res;
-	static const std::map<ERes, int> mineValue{{ERes::WOOD, 1500}, {ERes::ORE, 1500}, {ERes::GEMS, 3500}, {ERes::CRYSTAL, 3500}, {ERes::MERCURY, 3500}, {ERes::SULFUR, 3500}, {ERes::GOLD, 7000}};
-	
 	std::vector<CGMine*> createdMines;
 	
 	for(const auto & mineInfo : mines)
@@ -1783,22 +1781,26 @@ bool CRmgTemplateZone::placeMines ()
 			createdMines.push_back(mine);
 			
 			if(!i && (res == ERes::WOOD || res == ERes::ORE))
-				addCloseObject(mine, mineValue.at(res)); //only first woor&ore mines are close
+				addCloseObject(mine, gen->getConfig().mineValues.at(res)); //only first wood&ore mines are close
 			else
-				addRequiredObject(mine, mineValue.at(res));
+				addRequiredObject(mine, gen->getConfig().mineValues.at(res));
 		}
 	}
 	
 	//create extra resources
-	for(auto * mine : createdMines)
+	if(int extraRes = gen->getConfig().mineExtraResources)
 	{
-		for(int rc = gen->rand.nextInt(1, 3); rc > 0; --rc)
+		for(auto * mine : createdMines)
 		{
-			auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate());
-			resourse->amount = CGResource::RANDOM_AMOUNT;
-			addNearbyObject(resourse, mine);
+			for(int rc = gen->rand.nextInt(1, extraRes); rc > 0; --rc)
+			{
+				auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate());
+				resourse->amount = CGResource::RANDOM_AMOUNT;
+				addNearbyObject(resourse, mine);
+			}
 		}
 	}
+		
 
 	return true;
 }
@@ -2072,69 +2074,62 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
 	shipyard->tempOwner = PlayerColor::NEUTRAL;
 	
 	setTemplateForObject(shipyard);
-	std::vector<int3> offsets;
+	std::vector<int3> outOffsets;
 	auto tilesBlockedByObject = shipyard->getBlockedOffsets();
 	tilesBlockedByObject.insert(shipyard->getVisitableOffset());
-	shipyard->getOutOffsets(offsets);
+	shipyard->getOutOffsets(outOffsets);
 	
 	int3 targetTile(-1, -1, -1);
 	std::set<int3> shipAccessCandidates;
 	
-	for(auto& candidateTile : possibleTiles)
+	for(const auto & outOffset : outOffsets)
 	{
-		bool foundTargetPosition = false;
-		for(const auto & offset : offsets)
+		auto candidateTile = position - outOffset;
+		std::set<int3> tilesBlockedAbsolute;
+		
+		//check space under object
+		bool allClear = true;
+		for(const auto & objectTileOffset : tilesBlockedByObject)
 		{
-			if(candidateTile+offset == position)
+			auto objectTile = candidateTile + objectTileOffset;
+			tilesBlockedAbsolute.insert(objectTile);
+			if(!gen->map->isInTheMap(objectTile) || !gen->isPossible(objectTile) || gen->getZoneID(objectTile)!=id)
 			{
-				std::set<int3> tilesBlockedAbsolute;
-				//check space under object
-				bool allClear = true;
-				for(const auto & objectTileOffset : tilesBlockedByObject)
-				{
-					auto objectTile = candidateTile + objectTileOffset;
-					tilesBlockedAbsolute.insert(objectTile);
-					if(!gen->map->isInTheMap(objectTile) || !gen->isPossible(objectTile) || gen->getZoneID(objectTile)!=id)
-					{
-						allClear = false;
-						break;
-					}
-				}
-				if(!allClear) //cannot place shipyard anyway
-					break;
-				
-				//prepare temporary map
-				for(auto& blockedPos : tilesBlockedAbsolute)
-					gen->setOccupied(blockedPos, ETileType::USED);
-				
-				//check if position is accessible
-				gen->foreach_neighbour(position, [this, &shipAccessCandidates](const int3 & v)
-				{
-					if(!gen->isBlocked(v) && gen->getZoneID(v)==id)
-					{
-						//make sure that it's possible to create path to boarding position
-						if(crunchPath(v, findClosestTile(freePaths, v), false, nullptr))
-							shipAccessCandidates.insert(v);
-					}
-				});
-				
-				//rollback temporary map
-				for(auto& blockedPos : tilesBlockedAbsolute)
-					gen->setOccupied(blockedPos, ETileType::POSSIBLE);
-				
-				if(!shipAccessCandidates.empty())
-				{
-					foundTargetPosition = true;
-				}
-				
-				break; //no need to check other offsets as we already found position
+				allClear = false;
+				break;
 			}
 		}
+		if(!allClear) //cannot place shipyard anyway
+			continue;
+		
+		//prepare temporary map
+		for(auto& blockedPos : tilesBlockedAbsolute)
+			gen->setOccupied(blockedPos, ETileType::USED);
+		
 		
-		if(foundTargetPosition && isAccessibleFromSomewhere(shipyard->appearance, candidateTile))
+		//check if boarding position is accessible
+		gen->foreach_neighbour(position, [this, &shipAccessCandidates](const int3 & v)
+		{
+			if(!gen->isBlocked(v) && gen->getZoneID(v)==id)
+			{
+				//make sure that it's possible to create path to boarding position
+				if(connectWithCenter(v, false, false))
+					shipAccessCandidates.insert(v);
+			}
+		});
+		
+		//check if we can connect shipyard entrance with path
+		if(!connectWithCenter(candidateTile + shipyard->getVisitableOffset(), false))
+			shipAccessCandidates.clear();
+				
+		//rollback temporary map
+		for(auto& blockedPos : tilesBlockedAbsolute)
+			gen->setOccupied(blockedPos, ETileType::POSSIBLE);
+		
+		if(!shipAccessCandidates.empty() && isAccessibleFromSomewhere(shipyard->appearance, candidateTile))
 		{
 			targetTile = candidateTile;
-			break;
+			break; //no need to check other offsets as we already found position
 		}
 		
 		shipAccessCandidates.clear(); //invalidate positions
@@ -2146,19 +2141,24 @@ bool CRmgTemplateZone::createShipyard(const int3 & position, si32 guardStrength)
 		return false;
 	}
 	
-	placeObject(shipyard, targetTile);
-	guardObject(shipyard, guardStrength, false, true);
-	
-	for(auto& accessPosition : shipAccessCandidates)
+	if(tryToPlaceObjectAndConnectToPath(shipyard, targetTile)==EObjectPlacingResult::SUCCESS)
 	{
-		if(connectPath(accessPosition, false))
+		placeObject(shipyard, targetTile);
+		guardObject(shipyard, guardStrength, false, true);
+	
+		for(auto& accessPosition : shipAccessCandidates)
 		{
-			gen->setOccupied(accessPosition, ETileType::FREE);
-			return true;
+			if(connectPath(accessPosition, false))
+			{
+				gen->setOccupied(accessPosition, ETileType::FREE);
+				return true;
+			}
 		}
 	}
 	
-	throw rmgException("Cannot find path to shipyard boarding position");
+	logGlobal->warn("Cannot find path to shipyard boarding position");
+	delete shipyard;
+	return false;
 }
 
 void CRmgTemplateZone::createTreasures()
@@ -2206,7 +2206,12 @@ void CRmgTemplateZone::createTreasures()
 			//optimization - don't check tiles which are not allowed
 			vstd::erase_if(possibleTiles, [this](const int3 &tile) -> bool
 			{
-				return (!gen->isPossible(tile)) || gen->getZoneID(tile)!=getId();
+				//for water area we sholdn't place treasures close to coast
+				for(auto & lake : lakes)
+					if(vstd::contains(lake.distance, tile) && lake.distance[tile] < 2)
+						return true;
+				
+				return !gen->isPossible(tile) || gen->getZoneID(tile)!=getId();
 			});
 
 
@@ -2376,7 +2381,7 @@ void CRmgTemplateZone::drawRoads()
 	}
 
 	gen->getEditManager()->getTerrainSelection().setSelection(tiles);
-	gen->getEditManager()->drawRoad(ERoadType::COBBLESTONE_ROAD, &gen->rand);
+	gen->getEditManager()->drawRoad(gen->getConfig().defaultRoadType, &gen->rand);
 }
 
 
@@ -2386,26 +2391,13 @@ bool CRmgTemplateZone::fill()
 	
 	addAllPossibleObjects();
 	
-	if(type==ETemplateZoneType::WATER)
-	{
-		initFreeTiles();
-		connectLater();
-		createRequiredObjects();
-		fractalize();
-		createTreasures();
-	}
-	else
-	{
-		//zone center should be always clear to allow other tiles to connect
-		initFreeTiles();
-		connectLater(); //ideally this should work after fractalize, but fails
-		fractalize();
-		placeMines();
-		createRequiredObjects();
-		createTreasures();
-	}
-	
-	gen->dump(false);
+	//zone center should be always clear to allow other tiles to connect
+	initFreeTiles();
+	connectLater(); //ideally this should work after fractalize, but fails
+	fractalize();
+	placeMines();
+	createRequiredObjects();
+	createTreasures();
 
 	logGlobal->info("Zone %d filled successfully", id);
 	return true;
@@ -2613,6 +2605,7 @@ void CRmgTemplateZone::placeObject(CGObjectInstance* object, const int3 &pos, bo
 	case Obj::MONOLITH_ONE_WAY_ENTRANCE:
 	case Obj::MONOLITH_ONE_WAY_EXIT:
 	case Obj::SUBTERRANEAN_GATE:
+	case Obj::SHIPYARD:
 		{
 			addRoadNode(object->visitablePos());
 		}
@@ -2831,11 +2824,11 @@ ObjectInfo CRmgTemplateZone::getRandomObject(CTreasurePileInfo &info, ui32 desir
 		}
 	}
 
-	if (thresholds.empty())
+	if(thresholds.empty())
 	{
 		ObjectInfo oi;
 		//Generate pandora Box with gold if the value is extremely high
-		if (minValue > 20000) //we don't have object valuable enough
+		if(minValue > gen->getConfig().treasureValueLimit) //we don't have object valuable enough
 		{
 			oi.generateObject = [minValue]() -> CGObjectInstance *
 			{
@@ -2913,17 +2906,15 @@ void CRmgTemplateZone::addAllPossibleObjects()
 
 	//prisons
 	//levels 1, 5, 10, 20, 30
-	static int prisonExp[] = { 0, 5000, 15000, 90000, 500000 };
-	static int prisonValues[] = { 2500, 5000, 10000, 20000, 30000 };
-
-	for (int i = 0; i < 5; i++)
+	static int prisonsLevels = std::min(gen->getConfig().prisonExperience.size(), gen->getConfig().prisonValues.size());
+	for(int i = 0; i < prisonsLevels; i++)
 	{
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
 			std::vector<ui32> possibleHeroes;
-			for (int j = 0; j < gen->map->allowedHeroes.size(); j++)
+			for(int j = 0; j < gen->map->allowedHeroes.size(); j++)
 			{
-				if (gen->map->allowedHeroes[j])
+				if(gen->map->allowedHeroes[j])
 					possibleHeroes.push_back(j);
 			}
 
@@ -2933,7 +2924,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 
 
 			obj->subID = hid; //will be initialized later
-			obj->exp = prisonExp[i];
+			obj->exp = gen->getConfig().prisonExperience[i];
 			obj->setOwner(PlayerColor::NEUTRAL);
 			gen->map->allowedHeroes[hid] = false; //ban this hero
 			gen->decreasePrisonsRemaining();
@@ -2942,7 +2933,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return obj;
 		};
 		oi.setTemplate(Obj::PRISON, 0, terrainType);
-		oi.value = prisonValues[i];
+		oi.value = gen->getConfig().prisonValues[i];
 		oi.probability = 30;
 		oi.maxPerZone = gen->getPrisonsRemaning() / 5; //probably not perfect, but we can't generate more prisons than hereos.
 		possibleObjects.push_back(oi);
@@ -3008,9 +2999,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 		}
 	}
 
-	static const int scrollValues[] = { 500, 2000, 3000, 4000, 5000 };
-
-	for (int i = 0; i < 5; i++)
+	for(int i = 0; i < gen->getConfig().scrollValues.size(); i++)
 	{
 		oi.generateObject = [i, this]() -> CGObjectInstance *
 		{
@@ -3030,13 +3019,13 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return obj;
 		};
 		oi.setTemplate(Obj::SPELL_SCROLL, 0, terrainType);
-		oi.value = scrollValues[i];
+		oi.value = gen->getConfig().scrollValues[i];
 		oi.probability = 30;
 		possibleObjects.push_back(oi);
 	}
 
 	//pandora box with gold
-	for (int i = 1; i < 5; i++)
+	for(int i = 1; i < 5; i++)
 	{
 		oi.generateObject = [i]() -> CGObjectInstance *
 		{
@@ -3046,7 +3035,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return obj;
 		};
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
-		oi.value = i * 5000;
+		oi.value = i * gen->getConfig().pandoraMultiplierGold;
 		oi.probability = 5;
 		possibleObjects.push_back(oi);
 	}
@@ -3062,20 +3051,22 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return obj;
 		};
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
-		oi.value = i * 6000;
+		oi.value = i * gen->getConfig().pandoraMultiplierExperience;
 		oi.probability = 20;
 		possibleObjects.push_back(oi);
 	}
 
 	//pandora box with creatures
-	static const int tierValues[] = { 5000, 7000, 9000, 12000, 16000, 21000, 27000 };
+	const std::vector<int> & tierValues = gen->getConfig().pandoraCreatureValues;
 
-	auto creatureToCount = [](CCreature * creature) -> int
+	auto creatureToCount = [&tierValues](CCreature * creature) -> int
 	{
 		if (!creature->AIValue) //bug #2681
 			return 0; //this box won't be generated
 
-		int actualTier = creature->level > 7 ? 6 : creature->level - 1;
+		int actualTier = creature->level > tierValues.size() ?
+						 tierValues.size() - 1 :
+						 creature->level - 1;
 		float creaturesAmount = ((float)tierValues[actualTier]) / creature->AIValue;
 		if (creaturesAmount <= 5)
 		{
@@ -3142,7 +3133,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return obj;
 		};
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
-		oi.value = (i + 1) * 2500; //5000 - 15000
+		oi.value = (i + 1) * gen->getConfig().pandoraMultiplierSpells; //5000 - 15000
 		oi.probability = 2;
 		possibleObjects.push_back(oi);
 	}
@@ -3171,7 +3162,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return obj;
 		};
 		oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
-		oi.value = 15000;
+		oi.value = gen->getConfig().pandoraSpellSchool;
 		oi.probability = 2;
 		possibleObjects.push_back(oi);
 	}
@@ -3199,7 +3190,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 		return obj;
 	};
 	oi.setTemplate(Obj::PANDORAS_BOX, 0, terrainType);
-	oi.value = 30000;
+	oi.value = gen->getConfig().pandoraSpell60;
 	oi.probability = 2;
 	possibleObjects.push_back(oi);
 
@@ -3240,7 +3231,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			return artInfo;
 		};
 
-		for (int i = 0; i < std::min((int)creatures.size(), questArtsRemaining - genericSeerHuts); i++)
+		for(int i = 0; i < std::min((int)creatures.size(), questArtsRemaining - genericSeerHuts); i++)
 		{
 			auto creature = creatures[i];
 			int creaturesAmount = creatureToCount(creature);
@@ -3276,15 +3267,13 @@ void CRmgTemplateZone::addAllPossibleObjects()
 			possibleObjects.push_back(oi);
 		}
 
-		static int seerExpGold[] = { 5000, 10000, 15000, 20000 };
-		static int seerValues[] = { 2000, 5333, 8666, 12000 };
-
-		for (int i = 0; i < 4; i++) //seems that code for exp and gold reward is similiar
+		static int seerLevels = std::min(gen->getConfig().questValues.size(), gen->getConfig().questRewardValues.size());
+		for(int i = 0; i < seerLevels; i++) //seems that code for exp and gold reward is similiar
 		{
 			int randomAppearance = *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::SEER_HUT), gen->rand);
 
 			oi.setTemplate(Obj::SEER_HUT, randomAppearance, terrainType);
-			oi.value = seerValues[i];
+			oi.value = gen->getConfig().questValues[i];
 			oi.probability = 10;
 
 			oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance *
@@ -3294,7 +3283,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 
 				obj->rewardType = CGSeerHut::EXPERIENCE;
 				obj->rID = 0; //unitialized?
-				obj->rVal = seerExpGold[i];
+				obj->rVal = gen->getConfig().questRewardValues[i];
 
 				obj->quest->missionType = CQuest::MISSION_ART;
 				ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);
@@ -3317,7 +3306,7 @@ void CRmgTemplateZone::addAllPossibleObjects()
 				auto obj = (CGSeerHut *) factory->create(ObjectTemplate());
 				obj->rewardType = CGSeerHut::RESOURCES;
 				obj->rID = Res::GOLD;
-				obj->rVal = seerExpGold[i];
+				obj->rVal = gen->getConfig().questRewardValues[i];
 
 				obj->quest->missionType = CQuest::MISSION_ART;
 				ArtifactID artid = *RandomGeneratorUtil::nextItem(gen->getQuestArtsRemaning(), gen->rand);