2024-02-14 15:48:06 +02:00
/*
* JsonUtils . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
# include "JsonBonus.h"
# include "JsonValidator.h"
2024-07-20 14:55:17 +02:00
# include "../texts/CGeneralTextHandler.h"
2024-02-13 22:19:24 +02:00
# include "../VCMI_Lib.h"
2024-02-14 15:48:06 +02:00
# include "../bonuses/BonusParams.h"
# include "../bonuses/Limiters.h"
# include "../bonuses/Propagators.h"
# include "../bonuses/Updaters.h"
# include "../constants/StringConstants.h"
2024-02-13 22:19:24 +02:00
# include "../modding/IdentifierStorage.h"
2024-02-14 15:48:06 +02:00
2024-02-14 20:45:14 +02:00
VCMI_LIB_USING_NAMESPACE
template < typename T >
const T parseByMap ( const std : : map < std : : string , T > & map , const JsonNode * val , const std : : string & err )
{
if ( ! val - > isNull ( ) )
{
const std : : string & type = val - > String ( ) ;
auto it = map . find ( type ) ;
if ( it = = map . end ( ) )
{
logMod - > error ( " Error: invalid %s%s. " , err , type ) ;
return { } ;
}
else
{
return it - > second ;
}
}
else
return { } ;
}
template < typename T >
const T parseByMapN ( const std : : map < std : : string , T > & map , const JsonNode * val , const std : : string & err )
{
if ( val - > isNumber ( ) )
return static_cast < T > ( val - > Integer ( ) ) ;
else
return parseByMap < T > ( map , val , err ) ;
}
2024-02-14 15:48:06 +02:00
static void loadBonusSubtype ( BonusSubtypeID & subtype , BonusType type , const JsonNode & node )
{
if ( node . isNull ( ) )
{
subtype = BonusSubtypeID ( ) ;
return ;
}
if ( node . isNumber ( ) ) // Compatibility code for 1.3 or older
{
2024-02-13 14:34:16 +02:00
logMod - > warn ( " Bonus subtype must be string! (%s) " , node . getModScope ( ) ) ;
2024-02-14 15:48:06 +02:00
subtype = BonusCustomSubtype ( node . Integer ( ) ) ;
return ;
}
if ( ! node . isString ( ) )
{
2024-02-13 14:34:16 +02:00
logMod - > warn ( " Bonus subtype must be string! (%s) " , node . getModScope ( ) ) ;
2024-02-14 15:48:06 +02:00
subtype = BonusSubtypeID ( ) ;
return ;
}
switch ( type )
{
case BonusType : : MAGIC_SCHOOL_SKILL :
case BonusType : : SPELL_DAMAGE :
case BonusType : : SPELLS_OF_SCHOOL :
case BonusType : : SPELL_DAMAGE_REDUCTION :
case BonusType : : SPELL_SCHOOL_IMMUNITY :
case BonusType : : NEGATIVE_EFFECTS_IMMUNITY :
{
VLC - > identifiers ( ) - > requestIdentifier ( " spellSchool " , node , [ & subtype ] ( int32_t identifier )
{
subtype = SpellSchool ( identifier ) ;
} ) ;
break ;
}
case BonusType : : NO_TERRAIN_PENALTY :
{
VLC - > identifiers ( ) - > requestIdentifier ( " terrain " , node , [ & subtype ] ( int32_t identifier )
{
subtype = TerrainId ( identifier ) ;
} ) ;
break ;
}
case BonusType : : PRIMARY_SKILL :
{
VLC - > identifiers ( ) - > requestIdentifier ( " primarySkill " , node , [ & subtype ] ( int32_t identifier )
{
subtype = PrimarySkill ( identifier ) ;
} ) ;
break ;
}
case BonusType : : IMPROVED_NECROMANCY :
case BonusType : : HERO_GRANTS_ATTACKS :
case BonusType : : BONUS_DAMAGE_CHANCE :
case BonusType : : BONUS_DAMAGE_PERCENTAGE :
case BonusType : : SPECIAL_UPGRADE :
case BonusType : : HATE :
case BonusType : : SUMMON_GUARDIANS :
case BonusType : : MANUAL_CONTROL :
{
VLC - > identifiers ( ) - > requestIdentifier ( " creature " , node , [ & subtype ] ( int32_t identifier )
{
subtype = CreatureID ( identifier ) ;
} ) ;
break ;
}
case BonusType : : SPELL_IMMUNITY :
case BonusType : : SPELL_DURATION :
case BonusType : : SPECIAL_ADD_VALUE_ENCHANT :
case BonusType : : SPECIAL_FIXED_VALUE_ENCHANT :
case BonusType : : SPECIAL_PECULIAR_ENCHANT :
case BonusType : : SPECIAL_SPELL_LEV :
case BonusType : : SPECIFIC_SPELL_DAMAGE :
case BonusType : : SPELL :
case BonusType : : OPENING_BATTLE_SPELL :
case BonusType : : SPELL_LIKE_ATTACK :
case BonusType : : CATAPULT :
case BonusType : : CATAPULT_EXTRA_SHOTS :
case BonusType : : HEALER :
case BonusType : : SPELLCASTER :
case BonusType : : ENCHANTER :
case BonusType : : SPELL_AFTER_ATTACK :
case BonusType : : SPELL_BEFORE_ATTACK :
case BonusType : : SPECIFIC_SPELL_POWER :
case BonusType : : ENCHANTED :
case BonusType : : MORE_DAMAGE_FROM_SPELL :
case BonusType : : NOT_ACTIVE :
{
VLC - > identifiers ( ) - > requestIdentifier ( " spell " , node , [ & subtype ] ( int32_t identifier )
{
subtype = SpellID ( identifier ) ;
} ) ;
break ;
}
case BonusType : : GENERATE_RESOURCE :
2024-04-15 21:18:45 +02:00
case BonusType : : RESOURCES_CONSTANT_BOOST :
case BonusType : : RESOURCES_TOWN_MULTIPLYING_BOOST :
2024-02-14 15:48:06 +02:00
{
VLC - > identifiers ( ) - > requestIdentifier ( " resource " , node , [ & subtype ] ( int32_t identifier )
{
subtype = GameResID ( identifier ) ;
} ) ;
break ;
}
case BonusType : : MOVEMENT :
case BonusType : : WATER_WALKING :
case BonusType : : FLYING_MOVEMENT :
case BonusType : : NEGATE_ALL_NATURAL_IMMUNITIES :
case BonusType : : CREATURE_DAMAGE :
case BonusType : : FLYING :
case BonusType : : FIRST_STRIKE :
case BonusType : : GENERAL_DAMAGE_REDUCTION :
case BonusType : : PERCENTAGE_DAMAGE_BOOST :
case BonusType : : SOUL_STEAL :
case BonusType : : TRANSMUTATION :
case BonusType : : DESTRUCTION :
case BonusType : : DEATH_STARE :
case BonusType : : REBIRTH :
case BonusType : : VISIONS :
case BonusType : : SPELLS_OF_LEVEL : // spell level
case BonusType : : CREATURE_GROWTH : // creature level
{
VLC - > identifiers ( ) - > requestIdentifier ( " bonusSubtype " , node , [ & subtype ] ( int32_t identifier )
{
subtype = BonusCustomSubtype ( identifier ) ;
} ) ;
break ;
}
default :
for ( const auto & i : bonusNameMap )
if ( i . second = = type )
logMod - > warn ( " Bonus type %s does not supports subtypes! " , i . first ) ;
subtype = BonusSubtypeID ( ) ;
}
}
static void loadBonusSourceInstance ( BonusSourceID & sourceInstance , BonusSource sourceType , const JsonNode & node )
{
if ( node . isNull ( ) )
{
sourceInstance = BonusCustomSource ( ) ;
return ;
}
if ( node . isNumber ( ) ) // Compatibility code for 1.3 or older
{
logMod - > warn ( " Bonus source must be string! " ) ;
sourceInstance = BonusCustomSource ( node . Integer ( ) ) ;
return ;
}
if ( ! node . isString ( ) )
{
logMod - > warn ( " Bonus source must be string! " ) ;
sourceInstance = BonusCustomSource ( ) ;
return ;
}
switch ( sourceType )
{
case BonusSource : : ARTIFACT :
case BonusSource : : ARTIFACT_INSTANCE :
{
VLC - > identifiers ( ) - > requestIdentifier ( " artifact " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = ArtifactID ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : OBJECT_TYPE :
{
VLC - > identifiers ( ) - > requestIdentifier ( " object " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = Obj ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : OBJECT_INSTANCE :
case BonusSource : : HERO_BASE_SKILL :
sourceInstance = ObjectInstanceID ( ObjectInstanceID : : decode ( node . String ( ) ) ) ;
break ;
case BonusSource : : CREATURE_ABILITY :
{
VLC - > identifiers ( ) - > requestIdentifier ( " creature " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = CreatureID ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : TERRAIN_OVERLAY :
{
VLC - > identifiers ( ) - > requestIdentifier ( " spell " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = BattleField ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : SPELL_EFFECT :
{
VLC - > identifiers ( ) - > requestIdentifier ( " spell " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = SpellID ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : TOWN_STRUCTURE :
assert ( 0 ) ; // TODO
sourceInstance = BuildingTypeUniqueID ( ) ;
break ;
case BonusSource : : SECONDARY_SKILL :
{
VLC - > identifiers ( ) - > requestIdentifier ( " secondarySkill " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = SecondarySkill ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : HERO_SPECIAL :
{
VLC - > identifiers ( ) - > requestIdentifier ( " hero " , node , [ & sourceInstance ] ( int32_t identifier )
{
sourceInstance = HeroTypeID ( identifier ) ;
} ) ;
break ;
}
case BonusSource : : CAMPAIGN_BONUS :
sourceInstance = CampaignScenarioID ( CampaignScenarioID : : decode ( node . String ( ) ) ) ;
break ;
case BonusSource : : ARMY :
case BonusSource : : STACK_EXPERIENCE :
case BonusSource : : COMMANDER :
case BonusSource : : GLOBAL :
case BonusSource : : TERRAIN_NATIVE :
case BonusSource : : OTHER :
default :
sourceInstance = BonusSourceID ( ) ;
break ;
}
}
2024-02-14 20:45:14 +02:00
static BonusParams convertDeprecatedBonus ( const JsonNode & ability )
{
if ( vstd : : contains ( deprecatedBonusSet , ability [ " type " ] . String ( ) ) )
{
logMod - > warn ( " There is deprecated bonus found: \n %s \n Trying to convert... " , ability . toString ( ) ) ;
auto params = BonusParams ( ability [ " type " ] . String ( ) ,
ability [ " subtype " ] . isString ( ) ? ability [ " subtype " ] . String ( ) : " " ,
ability [ " subtype " ] . isNumber ( ) ? ability [ " subtype " ] . Integer ( ) : - 1 ) ;
if ( params . isConverted )
{
if ( ability [ " type " ] . String ( ) = = " SECONDARY_SKILL_PREMY " & & bonusValueMap . find ( ability [ " valueType " ] . String ( ) ) - > second = = BonusValueType : : PERCENT_TO_BASE ) //assume secondary skill special
{
params . valueType = BonusValueType : : PERCENT_TO_TARGET_TYPE ;
params . targetType = BonusSource : : SECONDARY_SKILL ;
}
logMod - > warn ( " Please, use this bonus: \n %s \n Converted successfully! " , params . toJson ( ) . toString ( ) ) ;
return params ;
}
else
logMod - > error ( " Cannot convert bonus! \n %s " , ability . toString ( ) ) ;
}
BonusParams ret ;
ret . isConverted = false ;
return ret ;
}
static TUpdaterPtr parseUpdater ( const JsonNode & updaterJson )
{
2024-05-28 18:43:28 +02:00
const std : : map < std : : string , std : : function < TUpdaterPtr ( ) > > bonusUpdaterMap =
{
{ " TIMES_HERO_LEVEL " , std : : make_shared < TimesHeroLevelUpdater > } ,
{ " TIMES_STACK_LEVEL " , std : : make_shared < TimesStackLevelUpdater > } ,
{ " ARMY_MOVEMENT " , std : : make_shared < ArmyMovementUpdater > } ,
{ " BONUS_OWNER_UPDATER " , std : : make_shared < OwnerUpdater > }
} ;
2024-02-14 20:45:14 +02:00
switch ( updaterJson . getType ( ) )
{
case JsonNode : : JsonType : : DATA_STRING :
2024-05-28 18:43:28 +02:00
{
auto it = bonusUpdaterMap . find ( updaterJson . String ( ) ) ;
if ( it ! = bonusUpdaterMap . end ( ) )
return it - > second ( ) ;
logGlobal - > error ( " Unknown bonus updater type '%s' " , updaterJson . String ( ) ) ;
return nullptr ;
}
2024-02-14 20:45:14 +02:00
case JsonNode : : JsonType : : DATA_STRUCT :
if ( updaterJson [ " type " ] . String ( ) = = " GROWS_WITH_LEVEL " )
{
auto updater = std : : make_shared < GrowsWithLevelUpdater > ( ) ;
const JsonVector param = updaterJson [ " parameters " ] . Vector ( ) ;
updater - > valPer20 = static_cast < int > ( param [ 0 ] . Integer ( ) ) ;
if ( param . size ( ) > 1 )
updater - > stepSize = static_cast < int > ( param [ 1 ] . Integer ( ) ) ;
return updater ;
}
else if ( updaterJson [ " type " ] . String ( ) = = " ARMY_MOVEMENT " )
{
auto updater = std : : make_shared < ArmyMovementUpdater > ( ) ;
if ( updaterJson [ " parameters " ] . isVector ( ) )
{
const auto & param = updaterJson [ " parameters " ] . Vector ( ) ;
if ( param . size ( ) < 4 )
logMod - > warn ( " Invalid ARMY_MOVEMENT parameters, using default! " ) ;
else
{
updater - > base = static_cast < si32 > ( param . at ( 0 ) . Integer ( ) ) ;
updater - > divider = static_cast < si32 > ( param . at ( 1 ) . Integer ( ) ) ;
updater - > multiplier = static_cast < si32 > ( param . at ( 2 ) . Integer ( ) ) ;
updater - > max = static_cast < si32 > ( param . at ( 3 ) . Integer ( ) ) ;
}
return updater ;
}
}
else
logMod - > warn ( " Unknown updater type \" %s \" " , updaterJson [ " type " ] . String ( ) ) ;
break ;
}
return nullptr ;
}
VCMI_LIB_NAMESPACE_BEGIN
2024-02-14 15:48:06 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonVector & ability_vec )
{
auto b = std : : make_shared < Bonus > ( ) ;
std : : string type = ability_vec [ 0 ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
if ( it = = bonusNameMap . end ( ) )
{
logMod - > error ( " Error: invalid ability type %s. " , type ) ;
return b ;
}
b - > type = it - > second ;
b - > val = static_cast < si32 > ( ability_vec [ 1 ] . Float ( ) ) ;
loadBonusSubtype ( b - > subtype , b - > type , ability_vec [ 2 ] ) ;
b - > additionalInfo = static_cast < si32 > ( ability_vec [ 3 ] . Float ( ) ) ;
b - > duration = BonusDuration : : PERMANENT ; //TODO: handle flags (as integer)
b - > turnsRemain = 0 ;
return b ;
}
void JsonUtils : : resolveAddInfo ( CAddInfo & var , const JsonNode & node )
{
const JsonNode & value = node [ " addInfo " ] ;
if ( ! value . isNull ( ) )
{
switch ( value . getType ( ) )
{
case JsonNode : : JsonType : : DATA_INTEGER :
var = static_cast < si32 > ( value . Integer ( ) ) ;
break ;
case JsonNode : : JsonType : : DATA_FLOAT :
var = static_cast < si32 > ( value . Float ( ) ) ;
break ;
case JsonNode : : JsonType : : DATA_STRING :
VLC - > identifiers ( ) - > requestIdentifier ( value , [ & ] ( si32 identifier )
{
var = identifier ;
} ) ;
break ;
case JsonNode : : JsonType : : DATA_VECTOR :
{
const JsonVector & vec = value . Vector ( ) ;
var . resize ( vec . size ( ) ) ;
for ( int i = 0 ; i < vec . size ( ) ; i + + )
{
switch ( vec [ i ] . getType ( ) )
{
case JsonNode : : JsonType : : DATA_INTEGER :
var [ i ] = static_cast < si32 > ( vec [ i ] . Integer ( ) ) ;
break ;
case JsonNode : : JsonType : : DATA_FLOAT :
var [ i ] = static_cast < si32 > ( vec [ i ] . Float ( ) ) ;
break ;
case JsonNode : : JsonType : : DATA_STRING :
VLC - > identifiers ( ) - > requestIdentifier ( vec [ i ] , [ & var , i ] ( si32 identifier )
{
var [ i ] = identifier ;
} ) ;
break ;
default :
logMod - > error ( " Error! Wrong identifier used for value of addInfo[%d]. " , i ) ;
}
}
break ;
}
default :
logMod - > error ( " Error! Wrong identifier used for value of addInfo. " ) ;
}
}
}
std : : shared_ptr < ILimiter > JsonUtils : : parseLimiter ( const JsonNode & limiter )
{
switch ( limiter . getType ( ) )
{
case JsonNode : : JsonType : : DATA_VECTOR :
{
const JsonVector & subLimiters = limiter . Vector ( ) ;
if ( subLimiters . empty ( ) )
{
logMod - > warn ( " Warning: empty limiter list " ) ;
return std : : make_shared < AllOfLimiter > ( ) ;
}
std : : shared_ptr < AggregateLimiter > result ;
int offset = 1 ;
// determine limiter type and offset for sub-limiters
if ( subLimiters [ 0 ] . getType ( ) = = JsonNode : : JsonType : : DATA_STRING )
{
const std : : string & aggregator = subLimiters [ 0 ] . String ( ) ;
if ( aggregator = = AllOfLimiter : : aggregator )
result = std : : make_shared < AllOfLimiter > ( ) ;
else if ( aggregator = = AnyOfLimiter : : aggregator )
result = std : : make_shared < AnyOfLimiter > ( ) ;
else if ( aggregator = = NoneOfLimiter : : aggregator )
result = std : : make_shared < NoneOfLimiter > ( ) ;
}
if ( ! result )
{
// collapse for single limiter without explicit aggregate operator
if ( subLimiters . size ( ) = = 1 )
return parseLimiter ( subLimiters [ 0 ] ) ;
// implicit aggregator must be allOf
result = std : : make_shared < AllOfLimiter > ( ) ;
offset = 0 ;
}
if ( subLimiters . size ( ) = = offset )
logMod - > warn ( " Warning: empty sub-limiter list " ) ;
for ( int sl = offset ; sl < subLimiters . size ( ) ; + + sl )
result - > add ( parseLimiter ( subLimiters [ sl ] ) ) ;
return result ;
}
break ;
case JsonNode : : JsonType : : DATA_STRING : //pre-defined limiters
return parseByMap ( bonusLimiterMap , & limiter , " limiter type " ) ;
break ;
case JsonNode : : JsonType : : DATA_STRUCT : //customizable limiters
{
std : : string limiterType = limiter [ " type " ] . String ( ) ;
const JsonVector & parameters = limiter [ " parameters " ] . Vector ( ) ;
if ( limiterType = = " CREATURE_TYPE_LIMITER " )
{
auto creatureLimiter = std : : make_shared < CCreatureTypeLimiter > ( ) ;
VLC - > identifiers ( ) - > requestIdentifier ( " creature " , parameters [ 0 ] , [ = ] ( si32 creature )
{
creatureLimiter - > setCreature ( CreatureID ( creature ) ) ;
} ) ;
auto includeUpgrades = false ;
if ( parameters . size ( ) > 1 )
{
bool success = true ;
includeUpgrades = parameters [ 1 ] . TryBoolFromString ( success ) ;
if ( ! success )
logMod - > error ( " Second parameter of '%s' limiter should be Bool " , limiterType ) ;
}
creatureLimiter - > includeUpgrades = includeUpgrades ;
return creatureLimiter ;
}
else if ( limiterType = = " HAS_ANOTHER_BONUS_LIMITER " )
{
std : : string anotherBonusType = parameters [ 0 ] . String ( ) ;
auto it = bonusNameMap . find ( anotherBonusType ) ;
if ( it = = bonusNameMap . end ( ) )
{
logMod - > error ( " Error: invalid ability type %s. " , anotherBonusType ) ;
}
else
{
auto bonusLimiter = std : : make_shared < HasAnotherBonusLimiter > ( ) ;
bonusLimiter - > type = it - > second ;
auto findSource = [ & ] ( const JsonNode & parameter )
{
if ( parameter . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
{
auto sourceIt = bonusSourceMap . find ( parameter [ " type " ] . String ( ) ) ;
if ( sourceIt ! = bonusSourceMap . end ( ) )
{
bonusLimiter - > source = sourceIt - > second ;
bonusLimiter - > isSourceRelevant = true ;
if ( ! parameter [ " id " ] . isNull ( ) ) {
loadBonusSourceInstance ( bonusLimiter - > sid , bonusLimiter - > source , parameter [ " id " ] ) ;
bonusLimiter - > isSourceIDRelevant = true ;
}
}
}
return false ;
} ;
if ( parameters . size ( ) > 1 )
{
if ( findSource ( parameters [ 1 ] ) & & parameters . size ( ) = = 2 )
return bonusLimiter ;
else
{
loadBonusSubtype ( bonusLimiter - > subtype , bonusLimiter - > type , parameters [ 1 ] ) ;
bonusLimiter - > isSubtypeRelevant = true ;
if ( parameters . size ( ) > 2 )
findSource ( parameters [ 2 ] ) ;
}
}
return bonusLimiter ;
}
}
else if ( limiterType = = " CREATURE_ALIGNMENT_LIMITER " )
{
int alignment = vstd : : find_pos ( GameConstants : : ALIGNMENT_NAMES , parameters [ 0 ] . String ( ) ) ;
if ( alignment = = - 1 )
logMod - > error ( " Error: invalid alignment %s. " , parameters [ 0 ] . String ( ) ) ;
else
return std : : make_shared < CreatureAlignmentLimiter > ( static_cast < EAlignment > ( alignment ) ) ;
}
else if ( limiterType = = " FACTION_LIMITER " | | limiterType = = " CREATURE_FACTION_LIMITER " ) //Second name is deprecated, 1.2 compat
{
auto factionLimiter = std : : make_shared < FactionLimiter > ( ) ;
VLC - > identifiers ( ) - > requestIdentifier ( " faction " , parameters [ 0 ] , [ = ] ( si32 faction )
{
factionLimiter - > faction = FactionID ( faction ) ;
} ) ;
return factionLimiter ;
}
else if ( limiterType = = " CREATURE_LEVEL_LIMITER " )
{
auto levelLimiter = std : : make_shared < CreatureLevelLimiter > ( ) ;
if ( ! parameters . empty ( ) ) //If parameters is empty, level limiter works as CREATURES_ONLY limiter
{
levelLimiter - > minLevel = parameters [ 0 ] . Integer ( ) ;
if ( parameters [ 1 ] . isNumber ( ) )
levelLimiter - > maxLevel = parameters [ 1 ] . Integer ( ) ;
}
return levelLimiter ;
}
else if ( limiterType = = " CREATURE_TERRAIN_LIMITER " )
{
auto terrainLimiter = std : : make_shared < CreatureTerrainLimiter > ( ) ;
if ( ! parameters . empty ( ) )
{
2024-06-07 14:24:36 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( " terrain " , parameters [ 0 ] , [ terrainLimiter ] ( si32 terrain )
2024-02-14 15:48:06 +02:00
{
2024-06-07 14:24:36 +02:00
terrainLimiter - > terrainType = terrain ;
2024-02-14 15:48:06 +02:00
} ) ;
}
return terrainLimiter ;
}
else if ( limiterType = = " UNIT_ON_HEXES " ) {
auto hexLimiter = std : : make_shared < UnitOnHexLimiter > ( ) ;
if ( ! parameters . empty ( ) )
{
for ( const auto & parameter : parameters ) {
if ( parameter . isNumber ( ) )
hexLimiter - > applicableHexes . insert ( BattleHex ( parameter . Integer ( ) ) ) ;
}
}
return hexLimiter ;
}
else
{
logMod - > error ( " Error: invalid customizable limiter type %s. " , limiterType ) ;
}
}
break ;
default :
break ;
}
return nullptr ;
}
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonNode & ability )
{
auto b = std : : make_shared < Bonus > ( ) ;
if ( ! parseBonus ( ability , b . get ( ) ) )
{
// caller code can not handle this case and presumes that returned bonus is always valid
2024-02-12 01:22:16 +02:00
logGlobal - > error ( " Failed to parse bonus! Json config was %S " , ability . toString ( ) ) ;
2024-02-14 15:48:06 +02:00
b - > type = BonusType : : NONE ;
return b ;
}
return b ;
}
bool JsonUtils : : parseBonus ( const JsonNode & ability , Bonus * b )
{
const JsonNode * value = nullptr ;
std : : string type = ability [ " type " ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
auto params = std : : make_unique < BonusParams > ( false ) ;
if ( it = = bonusNameMap . end ( ) )
{
params = std : : make_unique < BonusParams > ( convertDeprecatedBonus ( ability ) ) ;
if ( ! params - > isConverted )
{
logMod - > error ( " Error: invalid ability type %s. " , type ) ;
return false ;
}
b - > type = params - > type ;
b - > val = params - > val . value_or ( 0 ) ;
b - > valType = params - > valueType . value_or ( BonusValueType : : ADDITIVE_VALUE ) ;
if ( params - > targetType )
b - > targetSourceType = params - > targetType . value ( ) ;
}
else
b - > type = it - > second ;
loadBonusSubtype ( b - > subtype , b - > type , params - > isConverted ? params - > toJson ( ) [ " subtype " ] : ability [ " subtype " ] ) ;
if ( ! params - > isConverted )
{
b - > val = static_cast < si32 > ( ability [ " val " ] . Float ( ) ) ;
value = & ability [ " valueType " ] ;
if ( ! value - > isNull ( ) )
b - > valType = static_cast < BonusValueType > ( parseByMapN ( bonusValueMap , value , " value type " ) ) ;
}
b - > stacking = ability [ " stacking " ] . String ( ) ;
resolveAddInfo ( b - > additionalInfo , ability ) ;
b - > turnsRemain = static_cast < si32 > ( ability [ " turns " ] . Float ( ) ) ;
if ( ! ability [ " description " ] . isNull ( ) )
{
if ( ability [ " description " ] . isString ( ) )
2024-04-07 18:57:49 +02:00
b - > description . appendTextID ( ability [ " description " ] . String ( ) ) ;
2024-02-14 15:48:06 +02:00
if ( ability [ " description " ] . isNumber ( ) )
2024-04-07 18:57:49 +02:00
b - > description . appendTextID ( " core.arraytxt. " + std : : to_string ( ability [ " description " ] . Integer ( ) ) ) ;
2024-02-14 15:48:06 +02:00
}
value = & ability [ " effectRange " ] ;
if ( ! value - > isNull ( ) )
b - > effectRange = static_cast < BonusLimitEffect > ( parseByMapN ( bonusLimitEffect , value , " effect range " ) ) ;
value = & ability [ " duration " ] ;
if ( ! value - > isNull ( ) )
{
switch ( value - > getType ( ) )
{
case JsonNode : : JsonType : : DATA_STRING :
b - > duration = parseByMap ( bonusDurationMap , value , " duration type " ) ;
break ;
case JsonNode : : JsonType : : DATA_VECTOR :
{
BonusDuration : : Type dur = 0 ;
for ( const JsonNode & d : value - > Vector ( ) )
dur | = parseByMapN ( bonusDurationMap , & d , " duration type " ) ;
b - > duration = dur ;
}
break ;
default :
logMod - > error ( " Error! Wrong bonus duration format. " ) ;
}
}
value = & ability [ " sourceType " ] ;
if ( ! value - > isNull ( ) )
b - > source = static_cast < BonusSource > ( parseByMap ( bonusSourceMap , value , " source type " ) ) ;
if ( ! ability [ " sourceID " ] . isNull ( ) )
loadBonusSourceInstance ( b - > sid , b - > source , ability [ " sourceID " ] ) ;
value = & ability [ " targetSourceType " ] ;
if ( ! value - > isNull ( ) )
b - > targetSourceType = static_cast < BonusSource > ( parseByMap ( bonusSourceMap , value , " target type " ) ) ;
value = & ability [ " limiters " ] ;
if ( ! value - > isNull ( ) )
b - > limiter = parseLimiter ( * value ) ;
value = & ability [ " propagator " ] ;
if ( ! value - > isNull ( ) )
{
//ALL_CREATURES old propagator compatibility
if ( value - > String ( ) = = " ALL_CREATURES " )
{
logMod - > warn ( " ALL_CREATURES propagator is deprecated. Use GLOBAL_EFFECT propagator with CREATURES_ONLY limiter " ) ;
b - > addLimiter ( std : : make_shared < CreatureLevelLimiter > ( ) ) ;
b - > propagator = bonusPropagatorMap . at ( " GLOBAL_EFFECT " ) ;
}
else
b - > propagator = parseByMap ( bonusPropagatorMap , value , " propagator type " ) ;
}
value = & ability [ " updater " ] ;
if ( ! value - > isNull ( ) )
b - > addUpdater ( parseUpdater ( * value ) ) ;
value = & ability [ " propagationUpdater " ] ;
if ( ! value - > isNull ( ) )
b - > propagationUpdater = parseUpdater ( * value ) ;
return true ;
}
CSelector JsonUtils : : parseSelector ( const JsonNode & ability )
{
CSelector ret = Selector : : all ;
// Recursive parsers for anyOf, allOf, noneOf
const auto * value = & ability [ " allOf " ] ;
if ( value - > isVector ( ) )
{
for ( const auto & andN : value - > Vector ( ) )
ret = ret . And ( parseSelector ( andN ) ) ;
}
value = & ability [ " anyOf " ] ;
if ( value - > isVector ( ) )
{
CSelector base = Selector : : none ;
for ( const auto & andN : value - > Vector ( ) )
base = base . Or ( parseSelector ( andN ) ) ;
ret = ret . And ( base ) ;
}
value = & ability [ " noneOf " ] ;
if ( value - > isVector ( ) )
{
CSelector base = Selector : : none ;
for ( const auto & andN : value - > Vector ( ) )
base = base . Or ( parseSelector ( andN ) ) ;
ret = ret . And ( base . Not ( ) ) ;
}
BonusType type = BonusType : : NONE ;
// Actual selector parser
value = & ability [ " type " ] ;
if ( value - > isString ( ) )
{
auto it = bonusNameMap . find ( value - > String ( ) ) ;
if ( it ! = bonusNameMap . end ( ) )
{
type = it - > second ;
ret = ret . And ( Selector : : type ( ) ( it - > second ) ) ;
}
}
value = & ability [ " subtype " ] ;
if ( ! value - > isNull ( ) & & type ! = BonusType : : NONE )
{
BonusSubtypeID subtype ;
loadBonusSubtype ( subtype , type , ability ) ;
ret = ret . And ( Selector : : subtype ( ) ( subtype ) ) ;
}
value = & ability [ " sourceType " ] ;
std : : optional < BonusSource > src = std : : nullopt ; //Fixes for GCC false maybe-uninitialized
std : : optional < BonusSourceID > id = std : : nullopt ;
if ( value - > isString ( ) )
{
auto it = bonusSourceMap . find ( value - > String ( ) ) ;
if ( it ! = bonusSourceMap . end ( ) )
src = it - > second ;
}
value = & ability [ " sourceID " ] ;
if ( ! value - > isNull ( ) & & src . has_value ( ) )
{
loadBonusSourceInstance ( * id , * src , ability ) ;
}
if ( src & & id )
ret = ret . And ( Selector : : source ( * src , * id ) ) ;
else if ( src )
ret = ret . And ( Selector : : sourceTypeSel ( * src ) ) ;
value = & ability [ " targetSourceType " ] ;
if ( value - > isString ( ) )
{
auto it = bonusSourceMap . find ( value - > String ( ) ) ;
if ( it ! = bonusSourceMap . end ( ) )
ret = ret . And ( Selector : : targetSourceType ( ) ( it - > second ) ) ;
}
value = & ability [ " valueType " ] ;
if ( value - > isString ( ) )
{
auto it = bonusValueMap . find ( value - > String ( ) ) ;
if ( it ! = bonusValueMap . end ( ) )
ret = ret . And ( Selector : : valueType ( it - > second ) ) ;
}
CAddInfo info ;
value = & ability [ " addInfo " ] ;
if ( ! value - > isNull ( ) )
{
resolveAddInfo ( info , ability [ " addInfo " ] ) ;
ret = ret . And ( Selector : : info ( ) ( info ) ) ;
}
value = & ability [ " effectRange " ] ;
if ( value - > isString ( ) )
{
auto it = bonusLimitEffect . find ( value - > String ( ) ) ;
if ( it ! = bonusLimitEffect . end ( ) )
ret = ret . And ( Selector : : effectRange ( ) ( it - > second ) ) ;
}
value = & ability [ " lastsTurns " ] ;
if ( value - > isNumber ( ) )
ret = ret . And ( Selector : : turns ( value - > Integer ( ) ) ) ;
value = & ability [ " lastsDays " ] ;
if ( value - > isNumber ( ) )
ret = ret . And ( Selector : : days ( value - > Integer ( ) ) ) ;
return ret ;
}
VCMI_LIB_NAMESPACE_END