Browse Source

Add option to configure zone layer

Tomasz Zieliński 1 week ago
parent
commit
0006f73bb7

+ 4 - 0
config/schemas/template.json

@@ -47,6 +47,10 @@
 				"visiblePositionX" : { "type" : "number" },
 				"visiblePositionY" : { "type" : "number" },
 				"visibleSize" : { "type" : "number" },
+				"forcedLevel" : {
+					"type" : "string",
+					"enum" : ["automatic", "surface", "underground"]
+				},
 				
 				"terrainTypes": {"$ref" : "#/definitions/stringArray"},
 				"bannedTerrains": {"$ref" : "#/definitions/stringArray"},

+ 7 - 1
docs/modders/Random_Map_Template.md

@@ -103,7 +103,13 @@
 	"size" : 2, 
 	
 	// index of player that owns this zone
-	"owner" : 1, 
+	"owner" : 1,
+	
+	// Force zone placement on specific level. Possible values:
+	// "automatic" (default) - Zone level is determined automatically based on faction terrain preferences
+	// "surface" - Force zone to be placed on surface level (level 0)
+	// "underground" - Force zone to be placed on underground level (level 1)
+	"forcedLevel" : "automatic", 
 	
 	// castles and towns owned by player in this zone
 	"playerTowns" : {

+ 25 - 1
lib/rmg/CRmgTemplate.cpp

@@ -155,7 +155,8 @@ ZoneOptions::ZoneOptions():
 	treasureLikeZone(NO_ZONE),
 	customObjectsLikeZone(NO_ZONE),
 	visiblePosition(Point(0, 0)),
-	visibleSize(1.0)
+	visibleSize(1.0),
+	forcedLevel(EZoneLevel::AUTOMATIC)
 {
 }
 
@@ -355,6 +356,16 @@ void ZoneOptions::setVisibleSize(float value)
 	visibleSize = value;
 }
 
+EZoneLevel::EZoneLevel ZoneOptions::getForcedLevel() const
+{
+	return forcedLevel;
+}
+
+void ZoneOptions::setForcedLevel(EZoneLevel::EZoneLevel value)
+{
+	forcedLevel = value;
+}
+
 void ZoneOptions::addConnection(const ZoneConnection & connection)
 {
 	connectedZoneIds.push_back(connection.getOtherZoneId(getId()));
@@ -543,6 +554,19 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 
 	if(!handler.saving && visibleSize < 0.01)
 		visibleSize = 1.0;
+
+	{
+		static const std::vector<std::string> zoneLevels =
+		{
+			"automatic",
+			"surface",
+			"underground"
+		};
+
+		int levelIndex = static_cast<int>(forcedLevel);
+		handler.serializeEnum("forcedLevel", levelIndex, static_cast<int>(EZoneLevel::AUTOMATIC), zoneLevels);
+		forcedLevel = static_cast<EZoneLevel::EZoneLevel>(levelIndex);
+	}
 }
 
 ZoneConnection::ZoneConnection():

+ 14 - 0
lib/rmg/CRmgTemplate.h

@@ -60,6 +60,16 @@ namespace EMonsterStrength // used as int in monster generation procedure and in
 	};
 }
 
+namespace EZoneLevel // used to force zone placement on surface or underground
+{
+	enum EZoneLevel
+	{
+		AUTOMATIC = 0,
+		SURFACE = 1,
+		UNDERGROUND = 2
+	};
+}
+
 class DLL_LINKAGE CTreasureInfo
 {
 public:
@@ -253,6 +263,9 @@ public:
 	float getVisibleSize() const;
 	void setVisibleSize(float value);
 
+	EZoneLevel::EZoneLevel getForcedLevel() const;
+	void setForcedLevel(EZoneLevel::EZoneLevel value);
+
 protected:
 	TRmgTemplateZoneId id;
 	ETemplateZoneType type;
@@ -289,6 +302,7 @@ protected:
 	TRmgTemplateZoneId terrainTypeLikeZone;
 	TRmgTemplateZoneId treasureLikeZone;
 	TRmgTemplateZoneId customObjectsLikeZone;
+	EZoneLevel::EZoneLevel forcedLevel;
 };
 
 }

+ 55 - 34
lib/rmg/CZonePlacer.cpp

@@ -480,54 +480,75 @@ void CZonePlacer::prepareZones(TZoneMap &zones, TZoneVector &zonesVector, const
 	for(const auto & zone : zonesVector)
 	{
 		if (mapLevels == 1) //this step is ignored
+		{
 			zonesToPlace.push_back(zone);
-		else //place players depending on their factions
+			continue;
+		}
+
+		// Check if zone has forced level assignment
+		auto forcedLevel = zone.second->getForcedLevel();
+		if (forcedLevel == EZoneLevel::SURFACE)
+		{
+			// Force to surface (level 0)
+			levels[zone.first] = 0;
+			zonesOnLevel[0]++;
+			continue;
+		}
+		else if (forcedLevel == EZoneLevel::UNDERGROUND)
+		{
+			// Force to underground (level 1)
+			// mapLevels > 1 is guaranteed here since mapLevels == 1 was handled above
+			levels[zone.first] = 1;
+			zonesOnLevel[1]++;
+			continue;
+		}
+		// forcedLevel == AUTOMATIC - continue with normal logic
+
+		//place players depending on their factions
+		if(std::optional<int> owner = zone.second->getOwner())
 		{
-			if(std::optional<int> owner = zone.second->getOwner())
+			auto player = PlayerColor(*owner - 1);
+			auto playerSettings = map.getMapGenOptions().getPlayersSettings();
+			FactionID faction = FactionID::RANDOM;
+			if (playerSettings.size() > player.getNum())
 			{
-				auto player = PlayerColor(*owner - 1);
-				auto playerSettings = map.getMapGenOptions().getPlayersSettings();
-				FactionID faction = FactionID::RANDOM;
-				if (playerSettings.size() > player.getNum())
-				{
-					faction = std::next(playerSettings.begin(), player.getNum())->second.getStartingTown();
-				}
-				else
-				{
-					logGlobal->trace("Player %d (starting zone %d) does not participate in game", player.getNum(), zone.first);
-				}
+				faction = std::next(playerSettings.begin(), player.getNum())->second.getStartingTown();
+			}
+			else
+			{
+				logGlobal->trace("Player %d (starting zone %d) does not participate in game", player.getNum(), zone.first);
+			}
 
-				if (faction == FactionID::RANDOM) //TODO: check this after a town has already been randomized
+			if (faction == FactionID::RANDOM) //TODO: check this after a town has already been randomized
+				zonesToPlace.push_back(zone);
+			else
+			{
+				auto & tt = (*LIBRARY->townh)[faction]->nativeTerrain;
+				if(tt == ETerrainId::NONE)
+				{
+					//any / random
 					zonesToPlace.push_back(zone);
+				}
 				else
 				{
-					auto & tt = (*LIBRARY->townh)[faction]->nativeTerrain;
-					if(tt == ETerrainId::NONE)
+					const auto & terrainType = LIBRARY->terrainTypeHandler->getById(tt);
+					if(terrainType->isUnderground() && !terrainType->isSurface())
 					{
-						//any / random
-						zonesToPlace.push_back(zone);
+						//underground only
+						zonesOnLevel[1]++;
+						levels[zone.first] = 1;
 					}
 					else
 					{
-						const auto & terrainType = LIBRARY->terrainTypeHandler->getById(tt);
-						if(terrainType->isUnderground() && !terrainType->isSurface())
-						{
-							//underground only
-							zonesOnLevel[1]++;
-							levels[zone.first] = 1;
-						}
-						else
-						{
-							//surface
-							addZoneEqually(zone, true);
-						}
+						//surface
+						addZoneEqually(zone, true);
 					}
 				}
 			}
-			else //no starting zone or no underground altogether
-			{
-				zonesToPlace.push_back(zone);
-			}
+		}
+		else //no starting zone or no underground altogether
+		{
+			zonesToPlace.push_back(zone);
 		}
 	}
 	for(const auto & zone : zonesToPlace)