浏览代码

It is now possible to copy object settings between zones

Tomasz Zieliński 1 年之前
父节点
当前提交
bfe75a6a02

+ 1 - 0
config/schemas/template.json

@@ -22,6 +22,7 @@
 				"minesLikeZone" : { "type" : "number" },
 				"terrainTypeLikeZone" : { "type" : "number" },
 				"treasureLikeZone" : { "type" : "number" },
+				"customObjectsLikeZone" : { "type" : "number" },
 				
 				"terrainTypes": {"$ref" : "#/definitions/stringArray"},
 				"bannedTerrains": {"$ref" : "#/definitions/stringArray"},

+ 65 - 54
lib/rmg/CRmgTemplate.cpp

@@ -157,7 +157,7 @@ std::optional<int> ZoneOptions::getOwner() const
 	return owner;
 }
 
-const std::set<TerrainId> ZoneOptions::getTerrainTypes() const
+std::set<TerrainId> ZoneOptions::getTerrainTypes() const
 {
 	if (terrainTypes.empty())
 	{
@@ -192,7 +192,7 @@ std::set<FactionID> ZoneOptions::getDefaultTownTypes() const
 	return VLC->townh->getDefaultAllowed();
 }
 
-const std::set<FactionID> ZoneOptions::getTownTypes() const
+std::set<FactionID> ZoneOptions::getTownTypes() const
 {
 	if (townTypes.empty())
 	{
@@ -215,7 +215,7 @@ void ZoneOptions::setMonsterTypes(const std::set<FactionID> & value)
 	monsterTypes = value;
 }
 
-const std::set<FactionID> ZoneOptions::getMonsterTypes() const
+std::set<FactionID> ZoneOptions::getMonsterTypes() const
 {
 	return vstd::difference(monsterTypes, bannedMonsters);
 }
@@ -251,7 +251,7 @@ void ZoneOptions::addTreasureInfo(const CTreasureInfo & value)
 	vstd::amax(maxTreasureValue, value.max);
 }
 
-const std::vector<CTreasureInfo> & ZoneOptions::getTreasureInfo() const
+std::vector<CTreasureInfo> ZoneOptions::getTreasureInfo() const
 {
 	return treasureInfo;
 }
@@ -273,7 +273,22 @@ TRmgTemplateZoneId ZoneOptions::getTerrainTypeLikeZone() const
 
 TRmgTemplateZoneId ZoneOptions::getTreasureLikeZone() const
 {
-    return treasureLikeZone;
+	return treasureLikeZone;
+}
+
+ObjectConfig ZoneOptions::getCustomObjects() const
+{
+	return objectConfig;
+}
+
+void ZoneOptions::setCustomObjects(const ObjectConfig & value)
+{
+	objectConfig = value;
+}
+
+TRmgTemplateZoneId ZoneOptions::getCustomObjectsLikeZone() const
+{
+	return customObjectsLikeZone;
 }
 
 void ZoneOptions::addConnection(const ZoneConnection & connection)
@@ -335,7 +350,7 @@ void ZoneOptions::serializeJson(JsonSerializeFormat & handler)
 	SERIALIZE_ZONE_LINK(minesLikeZone);
 	SERIALIZE_ZONE_LINK(terrainTypeLikeZone);
 	SERIALIZE_ZONE_LINK(treasureLikeZone);
-
+	SERIALIZE_ZONE_LINK(customObjectsLikeZone);
 	#undef SERIALIZE_ZONE_LINK
 
 	if(terrainTypeLikeZone == NO_ZONE)
@@ -751,53 +766,29 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
 	}
 }
 
-std::set<TerrainId> CRmgTemplate::inheritTerrainType(std::shared_ptr<ZoneOptions> zone, uint32_t iteration /* = 0 */)
+template<typename T>
+T CRmgTemplate::inheritZoneProperty(std::shared_ptr<rmg::ZoneOptions> zone, 
+									T (rmg::ZoneOptions::*getter)() const,
+									void (rmg::ZoneOptions::*setter)(const T&),
+									TRmgTemplateZoneId (rmg::ZoneOptions::*inheritFrom)() const,
+									const std::string& propertyString,
+									uint32_t iteration)
 {
 	if (iteration >= 50)
 	{
-		logGlobal->error("Infinite recursion for terrain types detected in template %s", name);
-		return std::set<TerrainId>();
-	}
-	if (zone->getTerrainTypeLikeZone() != ZoneOptions::NO_ZONE)
-	{
-		iteration++;
-		const auto otherZone = zones.at(zone->getTerrainTypeLikeZone());
-		zone->setTerrainTypes(inheritTerrainType(otherZone, iteration));
+		logGlobal->error("Infinite recursion for %s detected in template %s", propertyString, name);
+		return T();
 	}
-	//This implicitly excludes banned terrains
-	return zone->getTerrainTypes();
-}
-
-std::map<TResource, ui16> CRmgTemplate::inheritMineTypes(std::shared_ptr<ZoneOptions> zone, uint32_t iteration /* = 0 */)
-{
-	if (iteration >= 50)
-	{
-		logGlobal->error("Infinite recursion for mine types detected in template %s", name);
-		return std::map<TResource, ui16>();
-	}
-	if (zone->getMinesLikeZone() != ZoneOptions::NO_ZONE)
-	{
-		iteration++;
-		const auto otherZone = zones.at(zone->getMinesLikeZone());
-		zone->setMinesInfo(inheritMineTypes(otherZone, iteration));
-	}
-	return zone->getMinesInfo();
-}
-
-std::vector<CTreasureInfo> CRmgTemplate::inheritTreasureInfo(std::shared_ptr<ZoneOptions> zone, uint32_t iteration /* = 0 */)
-{
-	if (iteration >= 50)
-	{
-		logGlobal->error("Infinite recursion for treasures detected in template %s", name);
-		return std::vector<CTreasureInfo>();
-	}
-	if (zone->getTreasureLikeZone() != ZoneOptions::NO_ZONE)
+	
+	if (((*zone).*inheritFrom)() != rmg::ZoneOptions::NO_ZONE)
 	{
 		iteration++;
-		const auto otherZone = zones.at(zone->getTreasureLikeZone());
-		zone->setTreasureInfo(inheritTreasureInfo(otherZone, iteration));
+		const auto otherZone = zones.at(((*zone).*inheritFrom)());
+		T inheritedValue = inheritZoneProperty(otherZone, getter, setter, inheritFrom, propertyString, iteration);
+		((*zone).*setter)(inheritedValue);
 	}
-	return zone->getTreasureInfo();
+	
+	return ((*zone).*getter)();
 }
 
 void CRmgTemplate::afterLoad()
@@ -806,12 +797,32 @@ void CRmgTemplate::afterLoad()
 	{
 		auto zone = idAndZone.second;
 
-		//Inherit properties recursively.
-		inheritTerrainType(zone);
-		inheritMineTypes(zone);
-		inheritTreasureInfo(zone);
-
-		//TODO: Inherit monster types as well
+		// Inherit properties recursively
+		inheritZoneProperty(zone, 
+							&rmg::ZoneOptions::getTerrainTypes, 
+							&rmg::ZoneOptions::setTerrainTypes, 
+							&rmg::ZoneOptions::getTerrainTypeLikeZone,
+							"terrain types");
+		
+		inheritZoneProperty(zone, 
+							&rmg::ZoneOptions::getMinesInfo, 
+							&rmg::ZoneOptions::setMinesInfo, 
+							&rmg::ZoneOptions::getMinesLikeZone,
+							"mine types");
+		
+		inheritZoneProperty(zone, 
+							&rmg::ZoneOptions::getTreasureInfo, 
+							&rmg::ZoneOptions::setTreasureInfo, 
+							&rmg::ZoneOptions::getTreasureLikeZone,
+							"treasure info");
+
+		inheritZoneProperty(zone, 
+							&rmg::ZoneOptions::getCustomObjects, 
+							&rmg::ZoneOptions::setCustomObjects, 
+							&rmg::ZoneOptions::getCustomObjectsLikeZone,
+							"custom objects");
+
+				//TODO: Inherit monster types as well
 		auto monsterTypes = zone->getMonsterTypes();
 		if (monsterTypes.empty())
 		{
@@ -919,9 +930,9 @@ const std::vector<ObjectConfig::EObjectCategory> & ZoneOptions::getBannedObjectC
 	return objectConfig.getBannedObjectCategories();
 }
 
-const std::vector<ObjectInfo> & ZoneOptions::getCustomObjects() const
+const std::vector<ObjectInfo> & ZoneOptions::getConfiguredObjects() const
 {
-	return objectConfig.getCustomObjects();
+	return objectConfig.getConfiguredObjects();
 }
 
 VCMI_LIB_NAMESPACE_END

+ 22 - 6
lib/rmg/CRmgTemplate.h

@@ -151,15 +151,15 @@ public:
 	void setSize(int value);
 	std::optional<int> getOwner() const;
 
-	const std::set<TerrainId> getTerrainTypes() const;
+	std::set<TerrainId> getTerrainTypes() const;
 	void setTerrainTypes(const std::set<TerrainId> & value);
 	std::set<TerrainId> getDefaultTerrainTypes() const;
 
 	const CTownInfo & getPlayerTowns() const;
 	const CTownInfo & getNeutralTowns() const;
 	std::set<FactionID> getDefaultTownTypes() const;
-	const std::set<FactionID> getTownTypes() const;
-	const std::set<FactionID> getMonsterTypes() const;
+	std::set<FactionID> getTownTypes() const;
+	std::set<FactionID> getMonsterTypes() const;
 
 	void setTownTypes(const std::set<FactionID> & value);
 	void setMonsterTypes(const std::set<FactionID> & value);
@@ -169,7 +169,7 @@ public:
 
 	void setTreasureInfo(const std::vector<CTreasureInfo> & value);
 	void addTreasureInfo(const CTreasureInfo & value);
-	const std::vector<CTreasureInfo> & getTreasureInfo() const;
+	std::vector<CTreasureInfo> getTreasureInfo() const;
 	ui32 getMaxTreasureValue() const;
 	void recalculateMaxTreasureValue();
 
@@ -188,9 +188,15 @@ public:
 	bool areTownsSameType() const;
 	bool isMatchTerrainToTown() const;
 
+	// Get a group of configured objects
 	const std::vector<CompoundMapObjectID> & getBannedObjects() const;
 	const std::vector<ObjectConfig::EObjectCategory> & getBannedObjectCategories() const;
-	const std::vector<ObjectInfo> & getCustomObjects() const;
+	const std::vector<ObjectInfo> & getConfiguredObjects() const;
+
+	// Copy whole custom object config from another zone
+	ObjectConfig getCustomObjects() const;
+	void setCustomObjects(const ObjectConfig & value);
+	TRmgTemplateZoneId	getCustomObjectsLikeZone() const;
 
 protected:
 	TRmgTemplateZoneId id;
@@ -222,6 +228,7 @@ protected:
 	TRmgTemplateZoneId minesLikeZone;
 	TRmgTemplateZoneId terrainTypeLikeZone;
 	TRmgTemplateZoneId treasureLikeZone;
+	TRmgTemplateZoneId customObjectsLikeZone;
 };
 
 }
@@ -294,6 +301,15 @@ private:
 
 	void serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName);
 	void serializePlayers(JsonSerializeFormat & handler, CPlayerCountRange & value, const std::string & fieldName);
+
+	template<typename T>
+	T inheritZoneProperty(std::shared_ptr<rmg::ZoneOptions> zone, 
+						  T (rmg::ZoneOptions::*getter)() const,
+						  void (rmg::ZoneOptions::*setter)(const T&),
+						  TRmgTemplateZoneId (rmg::ZoneOptions::*inheritFrom)() const,
+						  const std::string& propertyString,
+						  uint32_t iteration = 0);
+
 };
 
-VCMI_LIB_NAMESPACE_END
+VCMI_LIB_NAMESPACE_END

+ 4 - 4
lib/rmg/ObjectInfo.cpp

@@ -77,15 +77,15 @@ void ObjectConfig::serializeJson(JsonSerializeFormat & handler)
 		(EObjectCategory::ALL, "all")
 		(EObjectCategory::NONE, "none")
 		(EObjectCategory::CREATURE_BANK, "creatureBank")
-		(EObjectCategory::PERMANENT_BONUS, "permanentBonus")
-		(EObjectCategory::NEXT_BATTLE_BONUS, "nextBattleBonus")
+		(EObjectCategory::BONUS, "bonus")
 		(EObjectCategory::DWELLING, "dwelling")
 		(EObjectCategory::RESOURCE, "resource")
 		(EObjectCategory::RESOURCE_GENERATOR, "resourceGenerator")
 		(EObjectCategory::SPELL_SCROLL, "spellScroll")
 		(EObjectCategory::RANDOM_ARTIFACT, "randomArtifact")
 		(EObjectCategory::PANDORAS_BOX, "pandorasBox")
-		(EObjectCategory::QUEST_ARTIFACT, "questArtifact");
+		(EObjectCategory::QUEST_ARTIFACT, "questArtifact")
+		(EObjectCategory::SEER_HUT, "seerHut");
 
 	auto categories = handler.enterArray("bannedCategories");
 	if (handler.saving)
@@ -161,7 +161,7 @@ void ObjectConfig::serializeJson(JsonSerializeFormat & handler)
 	}
 }
 
-const std::vector<ObjectInfo> & ObjectConfig::getCustomObjects() const
+const std::vector<ObjectInfo> & ObjectConfig::getConfiguredObjects() const
 {
 	return customObjects;
 }

+ 4 - 6
lib/rmg/ObjectInfo.h

@@ -49,16 +49,15 @@ public:
 		ALL = -1,
 		NONE = 0,
 		CREATURE_BANK = 1,
-		PERMANENT_BONUS,
-		NEXT_BATTLE_BONUS,
+		BONUS,
 		DWELLING,
 		RESOURCE,
 		RESOURCE_GENERATOR,
 		SPELL_SCROLL,
 		RANDOM_ARTIFACT,
 		PANDORAS_BOX,
-		QUEST_ARTIFACT
-		// TODO: Seer huts?
+		QUEST_ARTIFACT,
+		SEER_HUT
 	};
 
 	void addBannedObject(const CompoundMapObjectID & objid);
@@ -67,9 +66,8 @@ public:
 	void clearCustomObjects();
 	const std::vector<CompoundMapObjectID> & getBannedObjects() const;
 	const std::vector<EObjectCategory> & getBannedObjectCategories() const;
-	const std::vector<ObjectInfo> & getCustomObjects() const;
+	const std::vector<ObjectInfo> & getConfiguredObjects() const;
 
-	// TODO: Separate serializer
 	void serializeJson(JsonSerializeFormat & handler);
 private:
 	// TODO: Add convenience method for banning objects by name

+ 43 - 15
lib/rmg/modificators/TreasurePlacer.cpp

@@ -25,6 +25,7 @@
 #include "../../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../../mapObjectConstructors/DwellingInstanceConstructor.h"
+#include "../../rewardable/Info.h"
 #include "../../mapObjects/CGHeroInstance.h"
 #include "../../mapObjects/CGPandoraBox.h"
 #include "../../mapObjects/CQuest.h"
@@ -1139,7 +1140,8 @@ void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone)
 
 	vstd::erase_if(possibleObjects, [this, &categoriesSet](const ObjectInfo & oi) -> bool
 	{
-		auto category = getObjectCategory(oi.templates.front()->id);
+		auto temp = oi.templates.front();
+		auto category = getObjectCategory(CompoundMapObjectID(temp->id, temp->subid));
 		if (categoriesSet.count(category))
 		{
 			logGlobal->info("Removing object %s from possible objects", oi.templates.front()->stringID);
@@ -1161,10 +1163,11 @@ void TreasurePlacer::ObjectPool::patchWithZoneConfig(const Zone & zone)
 		return false;
 	});
 
-	// Now copy back modified list
+	auto configuredObjects = zone.getConfiguredObjects();
+	// TODO: Overwrite or add to possibleObjects
+
 	// FIXME: Protect with mutex as well?
 	/*
-	possibleObjects.clear();
 	for (const auto & customObject : customObjects)
 	{
 		addObject(customObject.second);
@@ -1194,15 +1197,35 @@ void TreasurePlacer::ObjectPool::discardObjectsAboveValue(ui32 value)
 	});
 }
 
-ObjectConfig::EObjectCategory TreasurePlacer::ObjectPool::getObjectCategory(MapObjectID id)
+ObjectConfig::EObjectCategory TreasurePlacer::ObjectPool::getObjectCategory(CompoundMapObjectID id)
 {
-	auto name = VLC->objtypeh->getObjectHandlerName(id);
+	auto name = VLC->objtypeh->getObjectHandlerName(id.primaryID);
 
 	if (name == "configurable")
 	{
-		// TODO: Need to check configuration here.
-		// Possible otions: PERMANENT_BONUS, NEXT_BATTLE_BONUS, RESOURCE
-		return ObjectConfig::EObjectCategory::RESOURCE;
+		// TODO: Access Rewardable::Info by ID
+
+		auto handler = VLC->objtypeh->getHandlerFor(id.primaryID, id.secondaryID);
+		if (!handler)
+		{
+			return ObjectConfig::EObjectCategory::NONE;
+		}
+		auto temp = handler->getTemplates().front();
+		auto info = handler->getObjectInfo(temp);
+		if (info->givesResources())
+		{
+			return ObjectConfig::EObjectCategory::RESOURCE;
+		}
+		else if (info->givesArtifacts())
+		{
+			return ObjectConfig::EObjectCategory::RANDOM_ARTIFACT;
+		}
+		else if (info->givesBonuses())
+		{
+			return ObjectConfig::EObjectCategory::BONUS;
+		}
+
+		return ObjectConfig::EObjectCategory::OTHER;
 	}
 	else if (name == "dwelling" || name == "randomDwelling")
 	{
@@ -1223,28 +1246,33 @@ ObjectConfig::EObjectCategory TreasurePlacer::ObjectPool::getObjectCategory(MapO
 		return ObjectConfig::EObjectCategory::OTHER;
 	else if (name == "lighthouse")
 	{
-		// TODO: So far Lighthouse is not generated
-		// Also, it gives global bonus as long as owned
-		return ObjectConfig::EObjectCategory::PERMANENT_BONUS;
+		return ObjectConfig::EObjectCategory::BONUS;
 	}
 	else if (name == "magi")
+	{
+		// TODO: By default, both eye and hut are banned in every zone
 		return ObjectConfig::EObjectCategory::OTHER;
+	}
 	else if (name == "mine")
 		return ObjectConfig::EObjectCategory::RESOURCE_GENERATOR;
 	else if (name == "pandora")
 		return ObjectConfig::EObjectCategory::PANDORAS_BOX;
 	else if (name == "prison")
 	{
-		// TODO: Prisons are configurable
+		// TODO: Prisons should be configurable
 		return ObjectConfig::EObjectCategory::OTHER;
 	}
-	else if (name == "seerHut")
+	else if (name == "questArtifact")
 	{
-		// quest artifacts are configurable, but what about seer huts?
+		// TODO: There are no dedicated quest artifacts, needs extra logic
 		return ObjectConfig::EObjectCategory::QUEST_ARTIFACT;
 	}
+	else if (name == "seerHut")
+	{
+		return ObjectConfig::EObjectCategory::SEER_HUT;
+	}
 	else if (name == "siren")
-		return ObjectConfig::EObjectCategory::NEXT_BATTLE_BONUS;
+		return ObjectConfig::EObjectCategory::BONUS;
 	else if (name == "obelisk")
 		return ObjectConfig::EObjectCategory::OTHER;
 

+ 1 - 1
lib/rmg/modificators/TreasurePlacer.h

@@ -73,7 +73,7 @@ protected:
 		void sortPossibleObjects();
 		void discardObjectsAboveValue(ui32 value);
 
-		ObjectConfig::EObjectCategory getObjectCategory(MapObjectID id);
+		ObjectConfig::EObjectCategory getObjectCategory(CompoundMapObjectID id);
 
 	private: