From bfe75a6a024285aed02148d2cc632de39dd8b699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Zieli=C5=84ski?= Date: Sat, 24 Aug 2024 20:18:36 +0200 Subject: [PATCH] It is now possible to copy object settings between zones --- config/schemas/template.json | 1 + lib/rmg/CRmgTemplate.cpp | 117 +++++++++++++----------- lib/rmg/CRmgTemplate.h | 28 ++++-- lib/rmg/ObjectInfo.cpp | 8 +- lib/rmg/ObjectInfo.h | 10 +- lib/rmg/modificators/TreasurePlacer.cpp | 58 +++++++++--- lib/rmg/modificators/TreasurePlacer.h | 2 +- 7 files changed, 139 insertions(+), 85 deletions(-) diff --git a/config/schemas/template.json b/config/schemas/template.json index 77aaefafa..655bcbb62 100644 --- a/config/schemas/template.json +++ b/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"}, diff --git a/lib/rmg/CRmgTemplate.cpp b/lib/rmg/CRmgTemplate.cpp index f97cc6c94..4247d48d1 100644 --- a/lib/rmg/CRmgTemplate.cpp +++ b/lib/rmg/CRmgTemplate.cpp @@ -157,7 +157,7 @@ std::optional ZoneOptions::getOwner() const return owner; } -const std::set ZoneOptions::getTerrainTypes() const +std::set ZoneOptions::getTerrainTypes() const { if (terrainTypes.empty()) { @@ -192,7 +192,7 @@ std::set ZoneOptions::getDefaultTownTypes() const return VLC->townh->getDefaultAllowed(); } -const std::set ZoneOptions::getTownTypes() const +std::set ZoneOptions::getTownTypes() const { if (townTypes.empty()) { @@ -215,7 +215,7 @@ void ZoneOptions::setMonsterTypes(const std::set & value) monsterTypes = value; } -const std::set ZoneOptions::getMonsterTypes() const +std::set 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 & ZoneOptions::getTreasureInfo() const +std::vector 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 CRmgTemplate::inheritTerrainType(std::shared_ptr zone, uint32_t iteration /* = 0 */) +template +T CRmgTemplate::inheritZoneProperty(std::shared_ptr 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(); + logGlobal->error("Infinite recursion for %s detected in template %s", propertyString, name); + return T(); } - if (zone->getTerrainTypeLikeZone() != ZoneOptions::NO_ZONE) + + if (((*zone).*inheritFrom)() != rmg::ZoneOptions::NO_ZONE) { iteration++; - const auto otherZone = zones.at(zone->getTerrainTypeLikeZone()); - zone->setTerrainTypes(inheritTerrainType(otherZone, iteration)); + const auto otherZone = zones.at(((*zone).*inheritFrom)()); + T inheritedValue = inheritZoneProperty(otherZone, getter, setter, inheritFrom, propertyString, iteration); + ((*zone).*setter)(inheritedValue); } - //This implicitly excludes banned terrains - return zone->getTerrainTypes(); -} - -std::map CRmgTemplate::inheritMineTypes(std::shared_ptr zone, uint32_t iteration /* = 0 */) -{ - if (iteration >= 50) - { - logGlobal->error("Infinite recursion for mine types detected in template %s", name); - return std::map(); - } - if (zone->getMinesLikeZone() != ZoneOptions::NO_ZONE) - { - iteration++; - const auto otherZone = zones.at(zone->getMinesLikeZone()); - zone->setMinesInfo(inheritMineTypes(otherZone, iteration)); - } - return zone->getMinesInfo(); -} - -std::vector CRmgTemplate::inheritTreasureInfo(std::shared_ptr zone, uint32_t iteration /* = 0 */) -{ - if (iteration >= 50) - { - logGlobal->error("Infinite recursion for treasures detected in template %s", name); - return std::vector(); - } - if (zone->getTreasureLikeZone() != ZoneOptions::NO_ZONE) - { - iteration++; - const auto otherZone = zones.at(zone->getTreasureLikeZone()); - zone->setTreasureInfo(inheritTreasureInfo(otherZone, iteration)); - } - 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); + // 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"); - //TODO: Inherit monster types as well + 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 & ZoneOptions::getBannedObjectC return objectConfig.getBannedObjectCategories(); } -const std::vector & ZoneOptions::getCustomObjects() const +const std::vector & ZoneOptions::getConfiguredObjects() const { - return objectConfig.getCustomObjects(); + return objectConfig.getConfiguredObjects(); } VCMI_LIB_NAMESPACE_END diff --git a/lib/rmg/CRmgTemplate.h b/lib/rmg/CRmgTemplate.h index 786207fae..95c23d906 100644 --- a/lib/rmg/CRmgTemplate.h +++ b/lib/rmg/CRmgTemplate.h @@ -151,15 +151,15 @@ public: void setSize(int value); std::optional getOwner() const; - const std::set getTerrainTypes() const; + std::set getTerrainTypes() const; void setTerrainTypes(const std::set & value); std::set getDefaultTerrainTypes() const; const CTownInfo & getPlayerTowns() const; const CTownInfo & getNeutralTowns() const; std::set getDefaultTownTypes() const; - const std::set getTownTypes() const; - const std::set getMonsterTypes() const; + std::set getTownTypes() const; + std::set getMonsterTypes() const; void setTownTypes(const std::set & value); void setMonsterTypes(const std::set & value); @@ -169,7 +169,7 @@ public: void setTreasureInfo(const std::vector & value); void addTreasureInfo(const CTreasureInfo & value); - const std::vector & getTreasureInfo() const; + std::vector 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 & getBannedObjects() const; const std::vector & getBannedObjectCategories() const; - const std::vector & getCustomObjects() const; + const std::vector & 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 + T inheritZoneProperty(std::shared_ptr 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 \ No newline at end of file diff --git a/lib/rmg/ObjectInfo.cpp b/lib/rmg/ObjectInfo.cpp index 1e469db08..fda32a8b0 100644 --- a/lib/rmg/ObjectInfo.cpp +++ b/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 & ObjectConfig::getCustomObjects() const +const std::vector & ObjectConfig::getConfiguredObjects() const { return customObjects; } diff --git a/lib/rmg/ObjectInfo.h b/lib/rmg/ObjectInfo.h index d421baff3..cee3cf10b 100644 --- a/lib/rmg/ObjectInfo.h +++ b/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 & getBannedObjects() const; const std::vector & getBannedObjectCategories() const; - const std::vector & getCustomObjects() const; + const std::vector & getConfiguredObjects() const; - // TODO: Separate serializer void serializeJson(JsonSerializeFormat & handler); private: // TODO: Add convenience method for banning objects by name diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 5ba5992b7..8e30b5cff 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/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 == "questArtifact") + { + // TODO: There are no dedicated quest artifacts, needs extra logic + return ObjectConfig::EObjectCategory::QUEST_ARTIFACT; + } else if (name == "seerHut") { - // quest artifacts are configurable, but what about seer huts? - return ObjectConfig::EObjectCategory::QUEST_ARTIFACT; + 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; diff --git a/lib/rmg/modificators/TreasurePlacer.h b/lib/rmg/modificators/TreasurePlacer.h index f9ecf082e..c96733eb0 100644 --- a/lib/rmg/modificators/TreasurePlacer.h +++ b/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: