1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

It is now possible to copy object settings between zones

This commit is contained in:
Tomasz Zieliński 2024-08-24 20:18:36 +02:00
parent 64fc2e5ed0
commit bfe75a6a02
7 changed files with 139 additions and 85 deletions

View File

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

View File

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

View File

@ -151,15 +151,15 @@ public:
void setSize(int value); void setSize(int value);
std::optional<int> getOwner() const; std::optional<int> getOwner() const;
const std::set<TerrainId> getTerrainTypes() const; std::set<TerrainId> getTerrainTypes() const;
void setTerrainTypes(const std::set<TerrainId> & value); void setTerrainTypes(const std::set<TerrainId> & value);
std::set<TerrainId> getDefaultTerrainTypes() const; std::set<TerrainId> getDefaultTerrainTypes() const;
const CTownInfo & getPlayerTowns() const; const CTownInfo & getPlayerTowns() const;
const CTownInfo & getNeutralTowns() const; const CTownInfo & getNeutralTowns() const;
std::set<FactionID> getDefaultTownTypes() const; std::set<FactionID> getDefaultTownTypes() const;
const std::set<FactionID> getTownTypes() const; std::set<FactionID> getTownTypes() const;
const std::set<FactionID> getMonsterTypes() const; std::set<FactionID> getMonsterTypes() const;
void setTownTypes(const std::set<FactionID> & value); void setTownTypes(const std::set<FactionID> & value);
void setMonsterTypes(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 setTreasureInfo(const std::vector<CTreasureInfo> & value);
void addTreasureInfo(const CTreasureInfo & value); void addTreasureInfo(const CTreasureInfo & value);
const std::vector<CTreasureInfo> & getTreasureInfo() const; std::vector<CTreasureInfo> getTreasureInfo() const;
ui32 getMaxTreasureValue() const; ui32 getMaxTreasureValue() const;
void recalculateMaxTreasureValue(); void recalculateMaxTreasureValue();
@ -188,9 +188,15 @@ public:
bool areTownsSameType() const; bool areTownsSameType() const;
bool isMatchTerrainToTown() const; bool isMatchTerrainToTown() const;
// Get a group of configured objects
const std::vector<CompoundMapObjectID> & getBannedObjects() const; const std::vector<CompoundMapObjectID> & getBannedObjects() const;
const std::vector<ObjectConfig::EObjectCategory> & getBannedObjectCategories() 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: protected:
TRmgTemplateZoneId id; TRmgTemplateZoneId id;
@ -222,6 +228,7 @@ protected:
TRmgTemplateZoneId minesLikeZone; TRmgTemplateZoneId minesLikeZone;
TRmgTemplateZoneId terrainTypeLikeZone; TRmgTemplateZoneId terrainTypeLikeZone;
TRmgTemplateZoneId treasureLikeZone; TRmgTemplateZoneId treasureLikeZone;
TRmgTemplateZoneId customObjectsLikeZone;
}; };
} }
@ -294,6 +301,15 @@ private:
void serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName); void serializeSize(JsonSerializeFormat & handler, int3 & value, const std::string & fieldName);
void serializePlayers(JsonSerializeFormat & handler, CPlayerCountRange & 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

View File

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

View File

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

View File

@ -25,6 +25,7 @@
#include "../../mapObjectConstructors/AObjectTypeHandler.h" #include "../../mapObjectConstructors/AObjectTypeHandler.h"
#include "../../mapObjectConstructors/CObjectClassesHandler.h" #include "../../mapObjectConstructors/CObjectClassesHandler.h"
#include "../../mapObjectConstructors/DwellingInstanceConstructor.h" #include "../../mapObjectConstructors/DwellingInstanceConstructor.h"
#include "../../rewardable/Info.h"
#include "../../mapObjects/CGHeroInstance.h" #include "../../mapObjects/CGHeroInstance.h"
#include "../../mapObjects/CGPandoraBox.h" #include "../../mapObjects/CGPandoraBox.h"
#include "../../mapObjects/CQuest.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 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)) if (categoriesSet.count(category))
{ {
logGlobal->info("Removing object %s from possible objects", oi.templates.front()->stringID); 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; return false;
}); });
// Now copy back modified list auto configuredObjects = zone.getConfiguredObjects();
// TODO: Overwrite or add to possibleObjects
// FIXME: Protect with mutex as well? // FIXME: Protect with mutex as well?
/* /*
possibleObjects.clear();
for (const auto & customObject : customObjects) for (const auto & customObject : customObjects)
{ {
addObject(customObject.second); 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") if (name == "configurable")
{ {
// TODO: Need to check configuration here. // TODO: Access Rewardable::Info by ID
// Possible otions: PERMANENT_BONUS, NEXT_BATTLE_BONUS, RESOURCE
return ObjectConfig::EObjectCategory::RESOURCE; 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") else if (name == "dwelling" || name == "randomDwelling")
{ {
@ -1223,28 +1246,33 @@ ObjectConfig::EObjectCategory TreasurePlacer::ObjectPool::getObjectCategory(MapO
return ObjectConfig::EObjectCategory::OTHER; return ObjectConfig::EObjectCategory::OTHER;
else if (name == "lighthouse") else if (name == "lighthouse")
{ {
// TODO: So far Lighthouse is not generated return ObjectConfig::EObjectCategory::BONUS;
// Also, it gives global bonus as long as owned
return ObjectConfig::EObjectCategory::PERMANENT_BONUS;
} }
else if (name == "magi") else if (name == "magi")
{
// TODO: By default, both eye and hut are banned in every zone
return ObjectConfig::EObjectCategory::OTHER; return ObjectConfig::EObjectCategory::OTHER;
}
else if (name == "mine") else if (name == "mine")
return ObjectConfig::EObjectCategory::RESOURCE_GENERATOR; return ObjectConfig::EObjectCategory::RESOURCE_GENERATOR;
else if (name == "pandora") else if (name == "pandora")
return ObjectConfig::EObjectCategory::PANDORAS_BOX; return ObjectConfig::EObjectCategory::PANDORAS_BOX;
else if (name == "prison") else if (name == "prison")
{ {
// TODO: Prisons are configurable // TODO: Prisons should be configurable
return ObjectConfig::EObjectCategory::OTHER; 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") else if (name == "seerHut")
{ {
// quest artifacts are configurable, but what about seer huts? return ObjectConfig::EObjectCategory::SEER_HUT;
return ObjectConfig::EObjectCategory::QUEST_ARTIFACT;
} }
else if (name == "siren") else if (name == "siren")
return ObjectConfig::EObjectCategory::NEXT_BATTLE_BONUS; return ObjectConfig::EObjectCategory::BONUS;
else if (name == "obelisk") else if (name == "obelisk")
return ObjectConfig::EObjectCategory::OTHER; return ObjectConfig::EObjectCategory::OTHER;

View File

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