mirror of
https://github.com/vcmi/vcmi.git
synced 2026-06-19 22:57:37 +02:00
Merge pull request #5919 from IvanSavenko/rmg_banned_entities
Support for banned game entities in random map templates
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user