Browse Source

Option to define surface or underground level for obstacle set

Tomasz Zieliński 1 year ago
parent
commit
dd55188b77

+ 1 - 0
docs/modders/Entities_Format/Biome_Format.md

@@ -10,6 +10,7 @@ If not enough biomes are defined for [terrain type](Terrain_Format.md), map gene
 "obstacleSetId" : {
 	"biome" : {
 		"terrain" : "grass", // Id or vector of Ids this obstacle set can spawn at
+		"level" : "underground", // or "surface", by default both
 		"faction" : ["castle", "rampart"], //Id or vector of faction Ids. Set will only be used if zone belongs to this faction
 		"alignment" : ["good", "evil", "neutral"], //Alignment of the zone. Set will only be used if zone has this alignment
 		"objectType": "mountain"

+ 56 - 4
lib/mapObjects/ObstacleSetHandler.cpp

@@ -19,13 +19,15 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 ObstacleSet::ObstacleSet():
 	type(INVALID),
-	allowedTerrains({TerrainId::NONE})
+	allowedTerrains({TerrainId::NONE}),
+	level(EMapLevel::ANY)
 {
 }
 
 ObstacleSet::ObstacleSet(EObstacleType type, TerrainId terrain):
 	type(type),
-	allowedTerrains({terrain})
+	allowedTerrains({terrain}),
+	level(EMapLevel::ANY)
 {
 }
 
@@ -47,17 +49,27 @@ void ObstacleSet::removeEmptyTemplates()
 	});
 }
 
-ObstacleSetFilter::ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain = TerrainId::ANY_TERRAIN, FactionID faction = FactionID::ANY, EAlignment alignment = EAlignment::ANY):
+ObstacleSetFilter::ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes,
+	TerrainId terrain = TerrainId::ANY_TERRAIN,
+	ObstacleSet::EMapLevel level = ObstacleSet::EMapLevel::ANY,
+	FactionID faction = FactionID::ANY,
+	EAlignment alignment = EAlignment::ANY):
 	allowedTypes(allowedTypes),
 	terrain(terrain),
+	level(level),
 	faction(faction),
 	alignment(alignment)
 {
 }
 
-ObstacleSetFilter::ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain = TerrainId::ANY_TERRAIN, FactionID faction = FactionID::ANY, EAlignment alignment = EAlignment::ANY):
+ObstacleSetFilter::ObstacleSetFilter(ObstacleSet::EObstacleType allowedType,
+	TerrainId terrain = TerrainId::ANY_TERRAIN,
+	ObstacleSet::EMapLevel level = ObstacleSet::EMapLevel::ANY,
+	FactionID faction = FactionID::ANY,
+	EAlignment alignment = EAlignment::ANY):
 	allowedTypes({allowedType}),
 	terrain(terrain),
+	level(level),
 	faction(faction),
 	alignment(alignment)
 {
@@ -70,6 +82,14 @@ bool ObstacleSetFilter::filter(const ObstacleSet &set) const
 		return false;
 	}
 
+	if (level != ObstacleSet::EMapLevel::ANY && set.getLevel() != ObstacleSet::EMapLevel::ANY)
+	{
+		if (level != set.getLevel())
+		{
+			return false;
+		}
+	}
+
 	if (faction != FactionID::ANY)
 	{
 		auto factions = set.getFactions();
@@ -117,6 +137,16 @@ void ObstacleSet::addTerrain(TerrainId terrain)
 	this->allowedTerrains.insert(terrain);
 }
 
+ObstacleSet::EMapLevel ObstacleSet::getLevel() const
+{
+	return level;
+}
+
+void ObstacleSet::setLevel(ObstacleSet::EMapLevel level)
+{
+	this->level = level;
+}
+
 std::set<FactionID> ObstacleSet::getFactions() const
 {
 	return allowedFactions;
@@ -248,6 +278,22 @@ std::string ObstacleSet::toString() const
 	return OBSTACLE_TYPE_STRINGS.at(type);
 }
 
+ObstacleSet::EMapLevel ObstacleSet::levelFromString(const std::string &str)
+{
+	static const std::map<std::string, EMapLevel> LEVEL_NAMES =
+	{
+		{"surface", SURFACE},
+		{"underground", UNDERGROUND}
+	};
+
+	if (LEVEL_NAMES.find(str) != LEVEL_NAMES.end())
+	{
+		return LEVEL_NAMES.at(str);
+	}
+
+	throw std::runtime_error("Invalid map level: " + str);
+}
+
 std::vector<ObstacleSet::EObstacleType> ObstacleSetFilter::getAllowedTypes() const
 {
 	return allowedTypes;
@@ -325,6 +371,12 @@ std::shared_ptr<ObstacleSet> ObstacleSetHandler::loadFromJson(const std::string
 		logMod->error("No terrain specified for obstacle set %s", name);
 	}
 
+	if (biome["level"].isString())
+	{
+		auto level = biome["level"].String();
+		os->setLevel(ObstacleSet::levelFromString(level));
+	}
+
 	auto handleFaction = [os, scope](const std::string & str)
 	{
 		VLC->identifiers()->requestIdentifier(scope, "faction", str, [os](si32 id)

+ 15 - 2
lib/mapObjects/ObstacleSetHandler.h

@@ -37,6 +37,14 @@ public:
 		ANIMALS, // Living, or bones
 		OTHER // Crystals, shipwrecks, barrels, etc.
 	};
+
+	enum EMapLevel // TODO: Move somewhere to map definitions
+	{
+		ANY = -1,
+		SURFACE = 0,
+		UNDERGROUND = 1
+	};
+
 	ObstacleSet();
 	explicit ObstacleSet(EObstacleType type, TerrainId terrain);
 
@@ -51,6 +59,8 @@ public:
 	void setTerrain(TerrainId terrain);
 	void setTerrains(const std::set<TerrainId> & terrains);
 	void addTerrain(TerrainId terrain);
+	EMapLevel getLevel() const;
+	void setLevel(EMapLevel level);
 	std::set<EAlignment> getAlignments() const;
 	void addAlignment(EAlignment alignment);
 	std::set<FactionID> getFactions() const;
@@ -58,12 +68,14 @@ public:
 
 	static EObstacleType typeFromString(const std::string &str);
 	std::string toString() const;
+	static EMapLevel levelFromString(const std::string &str);
 
 	si32 id;
 
 private:
 
 	EObstacleType type;
+	EMapLevel level;
 	std::set<TerrainId> allowedTerrains; // Empty means all terrains
 	std::set<FactionID> allowedFactions; // Empty means all factions
 	std::set<EAlignment> allowedAlignments; // Empty means all alignments
@@ -75,8 +87,8 @@ typedef std::vector<std::shared_ptr<ObstacleSet>> TObstacleTypes;
 class DLL_LINKAGE ObstacleSetFilter
 {
 public:
-	ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain, FactionID faction, EAlignment alignment);
-	ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain, FactionID faction, EAlignment alignment);
+	ObstacleSetFilter(ObstacleSet::EObstacleType allowedType, TerrainId terrain, ObstacleSet::EMapLevel level, FactionID faction, EAlignment alignment);
+	ObstacleSetFilter(std::vector<ObstacleSet::EObstacleType> allowedTypes, TerrainId terrain, ObstacleSet::EMapLevel level, FactionID faction, EAlignment alignment);
 
 	bool filter(const ObstacleSet &set) const;
 
@@ -93,6 +105,7 @@ private:
 	EAlignment alignment;
 // TODO: Filter by faction,  surface/underground, etc.
 	const TerrainId terrain;
+	ObstacleSet::EMapLevel level;
 };
 
 // TODO: Instantiate ObstacleSetHandler

+ 5 - 1
lib/rmg/modificators/ObstaclePlacer.cpp

@@ -38,7 +38,11 @@ void ObstaclePlacer::process()
 
 	auto faction = zone.getTownType().toFaction();
 
-	ObstacleSetFilter filter(ObstacleSet::EObstacleType::INVALID, zone.getTerrainType(), faction->getId(), faction->alignment);
+	ObstacleSetFilter filter(ObstacleSet::EObstacleType::INVALID,
+							zone.getTerrainType(),
+							static_cast<ObstacleSet::EMapLevel>(zone.isUnderground()),
+							faction->getId(),
+							faction->alignment);
 
 	if (!prepareBiome(filter, zone.getRand()))
 	{