Merge pull request #5919 from IvanSavenko/rmg_banned_entities

Support for banned game entities in random map templates
This commit is contained in:
Ivan Savenko
2025-07-17 16:03:08 +03:00
committed by GitHub
21 changed files with 242 additions and 78 deletions
+9 -1
View File
@@ -182,6 +182,9 @@ void RandomMapTab::updateMapInfoByHost()
mapInfo->mapHeader->name.appendLocalString(EMetaText::GENERAL_TXT, 740);
mapInfo->mapHeader->description.appendLocalString(EMetaText::GENERAL_TXT, 741);
if(mapGenOptions->getWaterContent() != EWaterContent::RANDOM)
mapInfo->mapHeader->banWaterHeroes(mapGenOptions->getWaterContent() != EWaterContent::NONE);
const auto * temp = mapGenOptions->getMapTemplate();
if (temp)
{
@@ -191,6 +194,9 @@ void RandomMapTab::updateMapInfoByHost()
auto description = std::string("\n\n") + randomTemplateDescription;
mapInfo->mapHeader->description.appendRawString(description);
}
for (const auto & hero : temp->getBannedHeroes())
mapInfo->mapHeader->allowedHeroes.erase(hero);
}
mapInfo->mapHeader->difficulty = EMapDifficulty::NORMAL;
@@ -590,9 +596,11 @@ void RandomMapTab::saveOptions(const CMapGenOptions & options)
void RandomMapTab::loadOptions()
{
auto rmgSettings = persistentStorage["rmg"]["rmg"];
JsonNode rmgSettings = persistentStorage["rmg"]["rmg"];
if (!rmgSettings.Struct().empty())
{
rmgSettings.setModScope(ModScope::scopeGame());
mapGenOptions.reset(new CMapGenOptions());
JsonDeserializer handler(nullptr, rmgSettings);
handler.serializeStruct("lastSettings", *mapGenOptions);
+21
View File
@@ -206,6 +206,27 @@
"type" : "object",
"$ref" : "gameSettings.json"
},
"bannedSpells": {
"description" : "List of spells that are banned on this template",
"$ref" : "#/definitions/stringArray"
},
"bannedArtifacts": {
"description" : "List of artifacts that are banned on this template",
"$ref" : "#/definitions/stringArray"
},
"bannedSkills": {
"description" : "List of skills that are banned on this template",
"$ref" : "#/definitions/stringArray"
},
"bannedHeroes": {
"description" : "List of heroes that are banned on this template",
"$ref" : "#/definitions/stringArray"
},
"name" : {
"description" : "Optional name - useful to have several template variations with same name",
"type": "string"
@@ -228,19 +228,26 @@ Each town requires a set of buildings (Around 30-45 buildings)
// Chance of specific hero class to appear in this town
// Mirrored version of field "tavern" from hero class format
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"tavern" :
{
"knight" : 5,
"druid" : 6
"druid" : 6,
"modID:classFromMod" : 4
},
// Chance of specific spell to appear in mages guild of this town
// If spell is missing or set to 0 it will not appear unless set as "always present" in editor
// Spells from unavailable levels are not required to be in this list
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"guildSpells" :
{
"magicArrow" : 30,
"bless" : 10
"bless" : 10,
"modID:spellFromMod" : 20
},
// Which tiers in this town have creature hordes. Set to -1 to disable horde(s)
@@ -81,13 +81,16 @@ In order to make functional hero class you also need:
// Chance to get specific secondary skill on level-up
// All missing skills are considered to be banned, including universities
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"secondarySkills" :
{
"pathfinding" : 3.
"archery" : 6.
...
"resistance" : 5,
"firstAid" : 4
"firstAid" : 4,
"modName:skillName" : 9
},
// Chance for a this hero class to appear in a town, creates pair with same field in town format
@@ -99,11 +102,14 @@ In order to make functional hero class you also need:
// Reversed version of field "tavern" from town format
// If faction-class pair is not listed in any of them
// chance set to 0 and the class won't appear in tavern of this town
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"tavern" :
{
"castle" : 4,
...
"conflux" : 6
"conflux" : 6,
"modID:factionFromMod" : 5
}
}
```
@@ -32,6 +32,8 @@
],
// Chance for the skill to be offered on level-up (heroClass may override)
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"gainChance" : {
// Chance for hero classes with might affinity
"might" : 4,
@@ -41,6 +43,7 @@
"knight" : 2,
"cleric" : 8,
...
"modName:heroClassName" : 5
},
// This skill is major obligatory (like H3 Wisdom) and is guaranteed to be offered once per specific number of levels
+4 -1
View File
@@ -39,9 +39,12 @@
// Chance for this spell to appear in Mage Guild of a specific faction
// Symmetric property of "guildSpells" property in towns
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"gainChance":
{
"factionName" : 3
"factionName" : 3,
"modID:anotherFactionName" : 5
},
"animation":{<Animation format>},
+48
View File
@@ -35,6 +35,38 @@
"perPlayerOnMapCap" : 1
}
},
/// List of spells that are banned on this map.
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedSpells": [
"townPortal",
"modID:spellFromMod"
],
/// List of artifacts that are banned on this map.
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedArtifacts": [
"armageddonsBlade",
"modID:artifactFromMod"
],
/// List of secondary skills that are banned on this map.
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedSkills": [
"diplomacy",
"modID:secondarySkillFromMod"
],
/// List of heroes that are banned on this map.
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedHeroes": [
"lordHaart",
"modID:heroFromMod"
]
/// List of named zones, see below for format description
"zones" :
@@ -92,6 +124,8 @@
"monsters" : "normal",
//possible terrain types. All terrains will be available if not specified
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"terrainTypes" : [ "sand" ],
// List of type hints for every town, in the order of placement. First present hint if used for each town
@@ -111,6 +145,8 @@
],
//optional, list of explicitly banned terrain types
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedTerrains" : ["lava", "asphalt"]
// if true, terrain for this zone will match native terrain of player faction. Used only in owned zones
@@ -129,15 +165,27 @@
"customObjectsLikeZone" : 1,
// factions of monsters allowed on this zone
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"allowedMonsters" : ["inferno", "necropolis"]
// These monsers will never appear in the zone
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedMonsters" : ["fortress", "stronghold", "conflux"]
// towns allowed on this terrain
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"allowedTowns" : ["castle", "tower", "rampart"]
// towns will never spawn on this terrain
/// Identifier without modID specifier MUST exist in base game or in one of dependencies
/// Identifier with explicit modID specifier will be silently skipped if corresponding mod is not loaded
"bannedTowns" : ["necropolis"]
// List of mines that will be added to this zone
+24
View File
@@ -16,6 +16,7 @@
#include "GameLibrary.h"
#include "bonuses/Updaters.h"
#include "constants/StringConstants.h"
#include "entities/hero/CHeroClassHandler.h"
#include "filesystem/Filesystem.h"
#include "modding/IdentifierStorage.h"
#include "texts/CGeneralTextHandler.h"
@@ -221,6 +222,29 @@ std::shared_ptr<CSkill> CSkillHandler::loadFromJson(const std::string & scope, c
skill->special = json["special"].Bool();
LIBRARY->generaltexth->registerString(scope, skill->getNameTextID(), json["name"]);
for(auto skillPair : json["gainChance"].Struct())
{
int probability = static_cast<int>(skillPair.second.Integer());
if (skillPair.first == "might")
{
skill->gainChance[0] = probability;
continue;
}
if (skillPair.first == "magic")
{
skill->gainChance[1] = probability;
continue;
}
LIBRARY->identifiers()->requestIdentifierIfFound(skillPair.second.getModScope(), "heroClass", skillPair.first, [skill, probability](si32 classID)
{
LIBRARY->heroclassesh->objects[classID]->secSkillProbability[skill->id] = probability;
});
}
switch(json["gainChance"].getType())
{
case JsonNode::JsonType::DATA_INTEGER:
+6 -6
View File
@@ -300,17 +300,17 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
const JsonNode & fortifications = source["fortifications"];
if (!fortifications.isNull())
{
LIBRARY->identifiers()->requestIdentifierOptional("creature", fortifications["citadelShooter"], [=](si32 identifier)
LIBRARY->identifiers()->requestIdentifierIfNotNull("creature", fortifications["citadelShooter"], [=](si32 identifier)
{
ret->fortifications.citadelShooter = CreatureID(identifier);
});
LIBRARY->identifiers()->requestIdentifierOptional("creature", fortifications["upperTowerShooter"], [=](si32 identifier)
LIBRARY->identifiers()->requestIdentifierIfNotNull("creature", fortifications["upperTowerShooter"], [=](si32 identifier)
{
ret->fortifications.upperTowerShooter = CreatureID(identifier);
});
LIBRARY->identifiers()->requestIdentifierOptional("creature", fortifications["lowerTowerShooter"], [=](si32 identifier)
LIBRARY->identifiers()->requestIdentifierIfNotNull("creature", fortifications["lowerTowerShooter"], [=](si32 identifier)
{
ret->fortifications.lowerTowerShooter = CreatureID(identifier);
});
@@ -326,7 +326,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
if(!source["mapObjectLikeBonuses"].isNull())
{
LIBRARY->identifiers()->requestIdentifierOptional("object", source["mapObjectLikeBonuses"], [ret](si32 identifier)
LIBRARY->identifiers()->requestIdentifierIfNotNull("object", source["mapObjectLikeBonuses"], [ret](si32 identifier)
{
ret->mapObjectLikeBonuses = MapObjectID(identifier);
});
@@ -684,7 +684,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
{
int chance = static_cast<int>(node.second.Float());
LIBRARY->identifiers()->requestIdentifier(node.second.getModScope(), "heroClass",node.first, [=](si32 classID)
LIBRARY->identifiers()->requestIdentifierIfFound(node.second.getModScope(), "heroClass", node.first, [=](si32 classID)
{
LIBRARY->heroclassesh->objects[classID]->selectionProbability[town->faction->getId()] = chance;
});
@@ -694,7 +694,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
{
int chance = static_cast<int>(node.second.Float());
LIBRARY->identifiers()->requestIdentifier(node.second.getModScope(), "spell", node.first, [=](si32 spellID)
LIBRARY->identifiers()->requestIdentifierIfFound(node.second.getModScope(), "spell", node.first, [=](si32 spellID)
{
LIBRARY->spellh->objects.at(spellID)->probabilities[town->faction->getId()] = chance;
});
+20 -28
View File
@@ -97,45 +97,37 @@ std::shared_ptr<CHeroClass> CHeroClassHandler::loadFromJson(const std::string &
for(auto skillPair : node["secondarySkills"].Struct())
{
int probability = static_cast<int>(skillPair.second.Integer());
LIBRARY->identifiers()->requestIdentifier(skillPair.second.getModScope(), "skill", skillPair.first, [heroClass, probability](si32 skillID)
{
heroClass->secSkillProbability[skillID] = probability;
});
LIBRARY->identifiers()->requestIdentifierIfFound(skillPair.second.getModScope(), "skill", skillPair.first, [heroClass, probability](si32 skillID) {
heroClass->secSkillProbability[skillID] = probability;
});
}
LIBRARY->identifiers()->requestIdentifier ("creature", node["commander"],
[=](si32 commanderID)
{
heroClass->commander = CreatureID(commanderID);
});
LIBRARY->identifiers()->requestIdentifier ("creature", node["commander"], [=](si32 commanderID) {
heroClass->commander = CreatureID(commanderID);
});
heroClass->defaultTavernChance = static_cast<ui32>(node["defaultTavern"].Float());
for(const auto & tavern : node["tavern"].Struct())
{
int value = static_cast<int>(tavern.second.Float());
LIBRARY->identifiers()->requestIdentifier(tavern.second.getModScope(), "faction", tavern.first,
[=](si32 factionID)
{
heroClass->selectionProbability[FactionID(factionID)] = value;
});
LIBRARY->identifiers()->requestIdentifierIfFound(tavern.second.getModScope(), "faction", tavern.first, [=](si32 factionID) {
heroClass->selectionProbability[FactionID(factionID)] = value;
});
}
LIBRARY->identifiers()->requestIdentifier("faction", node["faction"],
[=](si32 factionID)
{
heroClass->faction.setNum(factionID);
});
LIBRARY->identifiers()->requestIdentifier("faction", node["faction"], [=](si32 factionID) {
heroClass->faction.setNum(factionID);
});
LIBRARY->identifiers()->requestIdentifier(scope, "object", "hero", [=](si32 index)
{
JsonNode classConf = node["mapObject"];
classConf["heroClass"].String() = identifier;
if (!node["compatibilityIdentifiers"].isNull())
classConf["compatibilityIdentifiers"] = node["compatibilityIdentifiers"];
classConf.setModScope(scope);
LIBRARY->objtypeh->loadSubObject(identifier, classConf, index, heroClass->getIndex());
});
LIBRARY->identifiers()->requestIdentifier(scope, "object", "hero", [=](si32 index) {
JsonNode classConf = node["mapObject"];
classConf["heroClass"].String() = identifier;
if (!node["compatibilityIdentifiers"].isNull())
classConf["compatibilityIdentifiers"] = node["compatibilityIdentifiers"];
classConf.setModScope(scope);
LIBRARY->objtypeh->loadSubObject(identifier, classConf, index, heroClass->getIndex());
});
return heroClass;
}
@@ -51,7 +51,7 @@ void ResourceInstanceConstructor::initTypeData(const JsonNode & input)
config = input;
resourceType = GameResID::GOLD; //set up fallback
LIBRARY->identifiers()->requestIdentifierOptional("resource", input["resource"], [&](si32 index)
LIBRARY->identifiers()->requestIdentifierIfNotNull("resource", input["resource"], [&](si32 index)
{
resourceType = GameResID(index);
});
+4 -4
View File
@@ -687,7 +687,7 @@ bool CMap::calculateWaterContent()
void CMap::banWaterContent()
{
banWaterHeroes();
banWaterHeroes(isWaterMap());
banWaterArtifacts();
banWaterSpells();
banWaterSkills();
@@ -717,16 +717,16 @@ void CMap::banWaterSkills()
});
}
void CMap::banWaterHeroes()
void CMapHeader::banWaterHeroes(bool isWaterMap)
{
vstd::erase_if(allowedHeroes, [&](HeroTypeID hero)
{
return hero.toHeroType()->onlyOnWaterMap && !isWaterMap();
return hero.toHeroType()->onlyOnWaterMap && !isWaterMap;
});
vstd::erase_if(allowedHeroes, [&](HeroTypeID hero)
{
return hero.toHeroType()->onlyOnMapWithoutWater && isWaterMap();
return hero.toHeroType()->onlyOnMapWithoutWater && isWaterMap;
});
}
-1
View File
@@ -222,7 +222,6 @@ public:
bool isWaterMap() const;
bool calculateWaterContent();
void banWaterArtifacts();
void banWaterHeroes();
void banHero(const HeroTypeID& id);
void unbanHero(const HeroTypeID & id);
void banWaterSpells();
+2
View File
@@ -239,6 +239,8 @@ public:
ui8 levels() const;
void banWaterHeroes(bool isWaterMap);
EMapFormat version; /// The default value is EMapFormat::SOD.
ModCompatibilityInfo mods; /// set of mods required to play a map
+42 -15
View File
@@ -123,7 +123,7 @@ CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameW
return result;
}
CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional)
CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional, bool bypassDependenciesCheck)
{
assert(!scope.empty());
@@ -148,13 +148,14 @@ CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameA
result.name = typeAndName.second;
result.callback = callback;
result.optional = optional;
result.bypassDependenciesCheck = bypassDependenciesCheck;
result.dynamicType = false;
return result;
}
void CIdentifierStorage::requestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const
{
requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, false));
requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, false, false));
}
void CIdentifierStorage::requestIdentifier(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback) const
@@ -164,7 +165,7 @@ void CIdentifierStorage::requestIdentifier(const std::string & scope, const std:
void CIdentifierStorage::requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
{
requestIdentifier(ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), callback, false));
requestIdentifier(ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), callback, false, false));
}
void CIdentifierStorage::requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback) const
@@ -172,27 +173,37 @@ void CIdentifierStorage::requestIdentifier(const JsonNode & name, const std::fun
requestIdentifier(ObjectCallback::fromNameWithType(name.getModScope(), name.String(), callback, false));
}
void CIdentifierStorage::requestIdentifierOptional(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
void CIdentifierStorage::requestIdentifierIfNotNull(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
{
if (!name.isNull())
requestIdentifier(type, name, callback);
}
void CIdentifierStorage::requestIdentifierIfFound(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
{
requestIdentifier(ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), callback, false, true));
}
void CIdentifierStorage::requestIdentifierIfFound(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const
{
requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, false, true));
}
void CIdentifierStorage::tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const
{
requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, true));
requestIdentifier(ObjectCallback::fromNameAndType(scope, type, name, callback, true, false));
}
void CIdentifierStorage::tryRequestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const
{
requestIdentifier(ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), callback, true));
requestIdentifier(ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), callback, true, false));
}
std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & scope, const std::string & type, const std::string & name, bool silent) const
{
//assert(state != ELoadingState::LOADING);
auto options = ObjectCallback::fromNameAndType(scope, type, name, std::function<void(si32)>(), silent);
auto options = ObjectCallback::fromNameAndType(scope, type, name, std::function<void(si32)>(), silent, false);
return getIdentifierImpl(options, silent);
}
@@ -200,7 +211,7 @@ std::optional<si32> CIdentifierStorage::getIdentifier(const std::string & type,
{
assert(state != ELoadingState::LOADING);
auto options = ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), std::function<void(si32)>(), silent);
auto options = ObjectCallback::fromNameAndType(name.getModScope(), type, name.String(), std::function<void(si32)>(), silent, false);
return getIdentifierImpl(options, silent);
}
@@ -269,26 +280,28 @@ void CIdentifierStorage::showIdentifierResolutionErrorDetails(const ObjectCallba
}
else
{
// such identifiers exists, but were not picked for some reason
if (options.remoteScope.empty())
// such identifier(s) exists, but were not picked for some reason
for (auto const & testOption : testList)
{
// attempt to access identifier from mods that is not dependency
for (auto const & testOption : testList)
bool isValidScope = true;
const auto & dependencies = LIBRARY->modh->getModDependencies(options.localScope, isValidScope);
if (!vstd::contains(dependencies, testOption.scope))
{
logMod->error("Identifier '%s' exists in mod %s", options.name, testOption.scope);
logMod->error("Please add mod '%s' as dependency of mod '%s' to access this identifier", testOption.scope, options.localScope);
continue;
}
}
else
{
// attempt to access identifier in form 'modName:object', but identifier is only present in different mod
for (auto const & testOption : testList)
if (options.remoteScope.empty())
{
logMod->error("Identifier '%s' exists in mod '%s' but identifier was explicitly requested from mod '%s'!", options.name, testOption.scope, options.remoteScope);
if (options.dynamicType)
logMod->error("Please use form '%s.%s' or '%s:%s.%s' to access this identifier", options.type, options.name, testOption.scope, options.type, options.name);
else
logMod->error("Please use form '%s' or '%s:%s' to access this identifier", options.name, testOption.scope, options.name);
continue;
}
}
}
@@ -377,6 +390,11 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent
// allow self-access
allowedScopes.insert(request.remoteScope);
}
else if (request.bypassDependenciesCheck)
{
// this is request for an identifier that bypasses mod dependencies check
allowedScopes.insert(request.remoteScope);
}
else
{
// allow access only if mod is in our dependencies
@@ -423,6 +441,15 @@ bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request) const
return true;
}
if (request.bypassDependenciesCheck)
{
if (!vstd::contains(LIBRARY->modh->getActiveMods(), request.remoteScope))
{
logMod->debug("Mod '%s' requested identifier '%s' from not loaded mod '%s'. Ignoring.", request.localScope, request.remoteScope, request.name);
return true; // mod was not loaded - ignore
}
}
// error found. Try to generate some debug info
failedRequests.push_back(request);
showIdentifierResolutionErrorDetails(request);
+5 -2
View File
@@ -32,13 +32,14 @@ class DLL_LINKAGE CIdentifierStorage
std::string name; /// string ID
std::function<void(si32)> callback;
bool optional;
bool bypassDependenciesCheck;
bool dynamicType;
/// Builds callback from identifier in form "targetMod:type.name"
static ObjectCallback fromNameWithType(const std::string & scope, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
/// Builds callback from identifier in form "targetMod:name"
static ObjectCallback fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional);
static ObjectCallback fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function<void(si32)> & callback, bool optional, bool bypassDependenciesCheck);
private:
ObjectCallback() = default;
@@ -87,7 +88,9 @@ public:
void requestIdentifier(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
void requestIdentifier(const JsonNode & name, const std::function<void(si32)> & callback) const;
void requestIdentifierOptional(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
void requestIdentifierIfNotNull(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
void requestIdentifierIfFound(const std::string & type, const JsonNode & name, const std::function<void(si32)> & callback) const;
void requestIdentifierIfFound(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const;
/// try to request ID. If ID with such name won't be loaded, callback function will not be called
void tryRequestIdentifier(const std::string & scope, const std::string & type, const std::string & name, const std::function<void(si32)> & callback) const;
+12
View File
@@ -466,6 +466,18 @@ void CMapGenerator::addHeaderInfo()
m.waterMap = (mapGenOptions.getWaterContent() != EWaterContent::EWaterContent::NONE);
m.banWaterContent();
m.overrideGameSettings(mapGenOptions.getMapTemplate()->getMapSettings());
for (const auto & spell : mapGenOptions.getMapTemplate()->getBannedSpells())
m.allowedSpells.erase(spell);
for (const auto & artifact : mapGenOptions.getMapTemplate()->getBannedArtifacts())
m.allowedArtifact.erase(artifact);
for (const auto & skill : mapGenOptions.getMapTemplate()->getBannedSkills())
m.allowedAbilities.erase(skill);
for (const auto & hero : mapGenOptions.getMapTemplate()->getBannedHeroes())
m.allowedHeroes.erase(hero);
}
int CMapGenerator::getNextMonlithIndex()
+5
View File
@@ -835,6 +835,11 @@ void CRmgTemplate::serializeJson(JsonSerializeFormat & handler)
serializePlayers(handler, players, "players");
serializePlayers(handler, humanPlayers, "humans"); // TODO: Rename this parameter
handler.serializeIdArray("bannedSpells", bannedSpells);
handler.serializeIdArray("bannedArtifacts", bannedArtifacts);
handler.serializeIdArray("bannedSkills", bannedSkills);
handler.serializeIdArray("bannedHeroes", bannedHeroes);
*mapSettings = handler.getCurrent()["settings"];
{
+11 -1
View File
@@ -338,6 +338,11 @@ public:
const JsonNode & getMapSettings() const;
const std::vector<rmg::ZoneConnection> & getConnectedZoneIds() const;
const std::set<SpellID> & getBannedSpells() const { return bannedSpells; }
const std::set<ArtifactID> & getBannedArtifacts() const { return bannedArtifacts; }
const std::set<SecondarySkill> & getBannedSkills() const { return bannedSkills; }
const std::set<HeroTypeID> & getBannedHeroes() const { return bannedHeroes; }
void validate() const; /// Tests template on validity and throws exception on failure
void serializeJson(JsonSerializeFormat & handler);
@@ -356,6 +361,11 @@ private:
std::set<EWaterContent::EWaterContent> allowedWaterContent;
std::unique_ptr<JsonNode> mapSettings;
std::set<SpellID> bannedSpells;
std::set<ArtifactID> bannedArtifacts;
std::set<SecondarySkill> bannedSkills;
std::set<HeroTypeID> bannedHeroes;
std::set<TerrainId> inheritTerrainType(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
std::map<TResource, ui16> inheritMineTypes(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
std::vector<CTreasureInfo> inheritTreasureInfo(std::shared_ptr<rmg::ZoneOptions> zone, uint32_t iteration = 0);
@@ -375,4 +385,4 @@ private:
};
VCMI_LIB_NAMESPACE_END
VCMI_LIB_NAMESPACE_END
+7 -13
View File
@@ -316,12 +316,11 @@ public:
}
else
{
std::string fieldValue;
serializeString(fieldName, fieldValue);
const JsonNode & fieldValue = getCurrent()[fieldName];
if (!fieldValue.empty())
if (!fieldValue.String().empty())
{
LIBRARY->identifiers()->requestIdentifier(ModScope::scopeGame(), IdentifierType::entityType(), fieldValue, [&value](int32_t index){
LIBRARY->identifiers()->requestIdentifier(IdentifierType::entityType(), fieldValue, [&value](int32_t index){
value = IdentifierType(index);
});
}
@@ -347,14 +346,12 @@ public:
}
else
{
std::vector<std::string> fieldValue;
serializeInternal(fieldName, fieldValue);
const JsonVector & fieldValue = getCurrent()[fieldName].Vector();
value.resize(fieldValue.size());
for(size_t i = 0; i < fieldValue.size(); ++i)
{
LIBRARY->identifiers()->requestIdentifier(ModScope::scopeGame(), E::entityType(), fieldValue[i], [&value, i](int32_t index){
LIBRARY->identifiers()->requestIdentifier(E::entityType(), fieldValue[i], [&value, i](int32_t index){
value[i] = T(index);
});
}
@@ -376,12 +373,9 @@ public:
}
else
{
std::vector<std::string> fieldValue;
serializeInternal(fieldName, fieldValue);
for(size_t i = 0; i < fieldValue.size(); ++i)
for (const auto & element : getCurrent()[fieldName].Vector())
{
LIBRARY->identifiers()->requestIdentifier(ModScope::scopeGame(), U::entityType(), fieldValue[i], [&value](int32_t index){
LIBRARY->identifiers()->requestIdentifierIfFound(U::entityType(), element, [&value](int32_t index){
value.insert(T(index));
});
}
+1 -1
View File
@@ -761,7 +761,7 @@ std::shared_ptr<CSpell> CSpellHandler::loadFromJson(const std::string & scope, c
{
const int chance = static_cast<int>(node.second.Integer());
LIBRARY->identifiers()->requestIdentifier(node.second.getModScope(), "faction", node.first, [=](si32 factionID)
LIBRARY->identifiers()->requestIdentifierIfFound(node.second.getModScope(), "faction", node.first, [=](si32 factionID)
{
spell->probabilities[FactionID(factionID)] = chance;
});