2013-01-16 16:02:01 +03:00
/*
* JsonNode . 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 "JsonNode.h"
2013-04-02 20:06:43 +03:00
# include "ScopeGuard.h"
2023-04-30 16:35:15 +02:00
# include "bonuses/BonusParams.h"
2023-05-01 19:29:53 +02:00
# include "bonuses/Bonus.h"
2023-04-30 15:52:48 +02:00
# include "bonuses/Limiters.h"
2023-04-30 16:35:15 +02:00
# include "bonuses/Propagators.h"
2023-04-30 15:52:48 +02:00
# include "bonuses/Updaters.h"
2013-07-28 17:49:50 +03:00
# include "filesystem/Filesystem.h"
2023-07-30 19:12:25 +02:00
# include "modding/IdentifierStorage.h"
2013-01-16 16:02:01 +03:00
# include "VCMI_Lib.h" //for identifier resolution
2013-10-26 00:45:14 +03:00
# include "CGeneralTextHandler.h"
2013-10-26 22:33:34 +03:00
# include "JsonDetail.h"
2018-04-17 14:59:30 +02:00
# include "StringConstants.h"
2023-03-28 01:14:18 +02:00
# include "battle/BattleHex.h"
2013-01-16 16:02:01 +03:00
2022-07-26 15:07:42 +02:00
namespace
{
// to avoid duplicating const and non-const code
template < typename Node >
Node & resolvePointer ( Node & in , const std : : string & pointer )
{
2022-09-23 18:22:37 +02:00
if ( pointer . empty ( ) )
2022-07-26 15:07:42 +02:00
return in ;
assert ( pointer [ 0 ] = = ' / ' ) ;
size_t splitPos = pointer . find ( ' / ' , 1 ) ;
2022-08-17 13:32:32 +02:00
std : : string entry = pointer . substr ( 1 , splitPos - 1 ) ;
2022-07-26 15:07:42 +02:00
std : : string remainer = splitPos = = std : : string : : npos ? " " : pointer . substr ( splitPos ) ;
2022-09-23 18:22:37 +02:00
if ( in . getType ( ) = = VCMI_LIB_WRAP_NAMESPACE ( JsonNode ) : : JsonType : : DATA_VECTOR )
2022-07-26 15:07:42 +02:00
{
2022-09-23 18:22:37 +02:00
if ( entry . find_first_not_of ( " 0123456789 " ) ! = std : : string : : npos ) // non-numbers in string
2022-07-26 15:07:42 +02:00
throw std : : runtime_error ( " Invalid Json pointer " ) ;
2022-09-23 18:22:37 +02:00
if ( entry . size ( ) > 1 & & entry [ 0 ] = = ' 0 ' ) // leading zeros are not allowed
2022-07-26 15:07:42 +02:00
throw std : : runtime_error ( " Invalid Json pointer " ) ;
2023-03-13 23:26:44 +02:00
auto index = boost : : lexical_cast < size_t > ( entry ) ;
2022-07-26 15:07:42 +02:00
if ( in . Vector ( ) . size ( ) > index )
return in . Vector ( ) [ index ] . resolvePointer ( remainer ) ;
}
return in [ entry ] . resolvePointer ( remainer ) ;
}
}
VCMI_LIB_NAMESPACE_BEGIN
2013-01-16 16:02:01 +03:00
using namespace JsonDetail ;
class LibClasses ;
class CModHandler ;
static const JsonNode nullNode ;
JsonNode : : JsonNode ( JsonType Type ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
setType ( Type ) ;
}
JsonNode : : JsonNode ( const char * data , size_t datasize ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
2013-03-09 16:16:35 +03:00
JsonParser parser ( data , datasize ) ;
* this = parser . parse ( " <unknown> " ) ;
2013-01-16 16:02:01 +03:00
}
JsonNode : : JsonNode ( ResourceID & & fileURI ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
2013-07-28 17:49:50 +03:00
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
2013-01-16 16:02:01 +03:00
2013-03-09 16:16:35 +03:00
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
2013-01-16 16:02:01 +03:00
}
2016-01-27 10:38:35 +02:00
JsonNode : : JsonNode ( const ResourceID & fileURI ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2016-01-27 10:38:35 +02:00
{
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
}
2022-06-20 16:39:50 +02:00
JsonNode : : JsonNode ( const std : : string & idx , const ResourceID & fileURI ) :
type ( JsonType : : DATA_NULL )
{
auto file = CResourceHandler : : get ( idx ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
}
2013-11-08 23:36:26 +03:00
JsonNode : : JsonNode ( ResourceID & & fileURI , bool & isValidSyntax ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL )
2013-11-08 23:36:26 +03:00
{
auto file = CResourceHandler : : get ( ) - > load ( fileURI ) - > readAll ( ) ;
JsonParser parser ( reinterpret_cast < char * > ( file . first . get ( ) ) , file . second ) ;
* this = parser . parse ( fileURI . getName ( ) ) ;
isValidSyntax = parser . isValid ( ) ;
}
2013-01-16 16:02:01 +03:00
JsonNode : : JsonNode ( const JsonNode & copy ) :
2017-11-26 23:18:18 +02:00
type ( JsonType : : DATA_NULL ) ,
2018-03-05 05:30:10 +02:00
meta ( copy . meta ) ,
flags ( copy . flags )
2013-01-16 16:02:01 +03:00
{
setType ( copy . getType ( ) ) ;
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_NULL :
break ; case JsonType : : DATA_BOOL : Bool ( ) = copy . Bool ( ) ;
break ; case JsonType : : DATA_FLOAT : Float ( ) = copy . Float ( ) ;
break ; case JsonType : : DATA_STRING : String ( ) = copy . String ( ) ;
break ; case JsonType : : DATA_VECTOR : Vector ( ) = copy . Vector ( ) ;
break ; case JsonType : : DATA_STRUCT : Struct ( ) = copy . Struct ( ) ;
break ; case JsonType : : DATA_INTEGER : Integer ( ) = copy . Integer ( ) ;
2013-01-16 16:02:01 +03:00
}
}
JsonNode : : ~ JsonNode ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_NULL ) ;
2013-01-16 16:02:01 +03:00
}
void JsonNode : : swap ( JsonNode & b )
{
using std : : swap ;
2013-04-25 17:03:35 +03:00
swap ( meta , b . meta ) ;
2013-01-16 16:02:01 +03:00
swap ( data , b . data ) ;
swap ( type , b . type ) ;
2018-03-05 05:30:10 +02:00
swap ( flags , b . flags ) ;
2013-01-16 16:02:01 +03:00
}
JsonNode & JsonNode : : operator = ( JsonNode node )
{
swap ( node ) ;
return * this ;
}
bool JsonNode : : operator = = ( const JsonNode & other ) const
{
if ( getType ( ) = = other . getType ( ) )
{
switch ( type )
{
2017-11-26 23:18:18 +02:00
case JsonType : : DATA_NULL : return true ;
case JsonType : : DATA_BOOL : return Bool ( ) = = other . Bool ( ) ;
case JsonType : : DATA_FLOAT : return Float ( ) = = other . Float ( ) ;
case JsonType : : DATA_STRING : return String ( ) = = other . String ( ) ;
case JsonType : : DATA_VECTOR : return Vector ( ) = = other . Vector ( ) ;
case JsonType : : DATA_STRUCT : return Struct ( ) = = other . Struct ( ) ;
case JsonType : : DATA_INTEGER : return Integer ( ) = = other . Integer ( ) ;
2013-01-16 16:02:01 +03:00
}
}
return false ;
}
bool JsonNode : : operator ! = ( const JsonNode & other ) const
{
return ! ( * this = = other ) ;
}
JsonNode : : JsonType JsonNode : : getType ( ) const
{
return type ;
}
2023-03-13 23:26:44 +02:00
void JsonNode : : setMeta ( const std : : string & metadata , bool recursive )
2013-04-25 17:03:35 +03:00
{
meta = metadata ;
if ( recursive )
{
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_VECTOR :
2013-04-25 17:03:35 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto & node : Vector ( ) )
2013-04-25 17:03:35 +03:00
{
node . setMeta ( metadata ) ;
}
}
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_STRUCT :
2013-04-25 17:03:35 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto & node : Struct ( ) )
2013-04-25 17:03:35 +03:00
{
node . second . setMeta ( metadata ) ;
}
}
}
}
}
2013-01-16 16:02:01 +03:00
void JsonNode : : setType ( JsonType Type )
{
if ( type = = Type )
return ;
2016-11-13 12:38:42 +02:00
//float<->int conversion
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_FLOAT & & Type = = JsonType : : DATA_INTEGER )
2016-11-13 12:38:42 +02:00
{
2020-10-01 10:38:06 +02:00
si64 converted = static_cast < si64 > ( data . Float ) ;
2016-11-13 12:38:42 +02:00
type = Type ;
data . Integer = converted ;
return ;
}
2017-11-26 23:18:18 +02:00
else if ( type = = JsonType : : DATA_INTEGER & & Type = = JsonType : : DATA_FLOAT )
2016-11-13 12:38:42 +02:00
{
2023-03-13 23:26:44 +02:00
auto converted = static_cast < double > ( data . Integer ) ;
2016-11-13 12:38:42 +02:00
type = Type ;
data . Float = converted ;
return ;
}
2013-06-26 14:18:27 +03:00
//Reset node to nullptr
2017-11-26 23:18:18 +02:00
if ( Type ! = JsonType : : DATA_NULL )
setType ( JsonType : : DATA_NULL ) ;
2013-01-16 16:02:01 +03:00
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_STRING : delete data . String ;
break ; case JsonType : : DATA_VECTOR : delete data . Vector ;
break ; case JsonType : : DATA_STRUCT : delete data . Struct ;
2013-01-16 16:02:01 +03:00
break ; default :
break ;
}
//Set new node type
type = Type ;
switch ( type )
{
2017-11-26 23:18:18 +02:00
break ; case JsonType : : DATA_NULL :
break ; case JsonType : : DATA_BOOL : data . Bool = false ;
break ; case JsonType : : DATA_FLOAT : data . Float = 0 ;
break ; case JsonType : : DATA_STRING : data . String = new std : : string ( ) ;
break ; case JsonType : : DATA_VECTOR : data . Vector = new JsonVector ( ) ;
break ; case JsonType : : DATA_STRUCT : data . Struct = new JsonMap ( ) ;
break ; case JsonType : : DATA_INTEGER : data . Integer = 0 ;
2013-01-16 16:02:01 +03:00
}
}
bool JsonNode : : isNull ( ) const
{
2017-11-26 23:18:18 +02:00
return type = = JsonType : : DATA_NULL ;
2013-01-16 16:02:01 +03:00
}
2016-11-13 12:38:42 +02:00
bool JsonNode : : isNumber ( ) const
{
2017-11-26 23:18:18 +02:00
return type = = JsonType : : DATA_INTEGER | | type = = JsonType : : DATA_FLOAT ;
2016-11-13 12:38:42 +02:00
}
2023-01-22 18:17:26 +02:00
bool JsonNode : : isString ( ) const
{
return type = = JsonType : : DATA_STRING ;
}
bool JsonNode : : isVector ( ) const
{
return type = = JsonType : : DATA_VECTOR ;
}
bool JsonNode : : isStruct ( ) const
{
return type = = JsonType : : DATA_STRUCT ;
}
2017-09-16 00:23:31 +02:00
bool JsonNode : : containsBaseData ( ) const
2017-09-13 08:58:14 +02:00
{
switch ( type )
{
2017-09-15 13:32:32 +02:00
case JsonType : : DATA_NULL :
2017-09-16 00:23:31 +02:00
return false ;
2017-09-15 13:32:32 +02:00
case JsonType : : DATA_STRUCT :
2023-03-13 23:26:44 +02:00
for ( const auto & elem : * data . Struct )
2017-09-15 13:32:32 +02:00
{
2017-09-16 00:23:31 +02:00
if ( elem . second . containsBaseData ( ) )
return true ;
2017-09-15 13:32:32 +02:00
}
return false ;
2017-09-16 00:23:31 +02:00
default :
//other types (including vector) cannot be extended via merge
return true ;
2017-09-13 08:58:14 +02:00
}
}
2017-09-14 03:03:44 +02:00
bool JsonNode : : isCompact ( ) const
{
switch ( type )
{
2017-09-16 00:30:57 +02:00
case JsonType : : DATA_VECTOR :
for ( JsonNode & elem : * data . Vector )
{
if ( ! elem . isCompact ( ) )
return false ;
}
return true ;
case JsonType : : DATA_STRUCT :
{
2020-10-01 10:38:06 +02:00
auto propertyCount = data . Struct - > size ( ) ;
2017-09-16 00:30:57 +02:00
if ( propertyCount = = 0 )
return true ;
else if ( propertyCount = = 1 )
return data . Struct - > begin ( ) - > second . isCompact ( ) ;
}
return false ;
default :
return true ;
2017-09-14 03:03:44 +02:00
}
}
2021-04-25 14:07:06 +02:00
bool JsonNode : : TryBoolFromString ( bool & success ) const
{
success = true ;
if ( type = = JsonNode : : JsonType : : DATA_BOOL )
return Bool ( ) ;
success = type = = JsonNode : : JsonType : : DATA_STRING ;
if ( success )
{
auto boolParamStr = String ( ) ;
boost : : algorithm : : trim ( boolParamStr ) ;
boost : : algorithm : : to_lower ( boolParamStr ) ;
success = boolParamStr = = " true " ;
if ( success )
return true ;
success = boolParamStr = = " false " ;
}
return false ;
}
2013-04-02 20:06:43 +03:00
void JsonNode : : clear ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_NULL ) ;
2013-04-02 20:06:43 +03:00
}
2013-01-16 16:02:01 +03:00
bool & JsonNode : : Bool ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_BOOL ) ;
2013-01-16 16:02:01 +03:00
return data . Bool ;
}
double & JsonNode : : Float ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_FLOAT ) ;
2013-01-16 16:02:01 +03:00
return data . Float ;
}
2016-11-13 12:38:42 +02:00
si64 & JsonNode : : Integer ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_INTEGER ) ;
2016-11-13 12:38:42 +02:00
return data . Integer ;
}
2013-01-16 16:02:01 +03:00
std : : string & JsonNode : : String ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_STRING ) ;
2013-01-16 16:02:01 +03:00
return * data . String ;
}
JsonVector & JsonNode : : Vector ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_VECTOR ) ;
2013-01-16 16:02:01 +03:00
return * data . Vector ;
}
JsonMap & JsonNode : : Struct ( )
{
2017-11-26 23:18:18 +02:00
setType ( JsonType : : DATA_STRUCT ) ;
2013-01-16 16:02:01 +03:00
return * data . Struct ;
}
const bool boolDefault = false ;
2016-11-13 12:38:42 +02:00
bool JsonNode : : Bool ( ) const
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return boolDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_BOOL ) ;
2013-01-16 16:02:01 +03:00
return data . Bool ;
}
const double floatDefault = 0 ;
2016-11-13 12:38:42 +02:00
double JsonNode : : Float ( ) const
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return floatDefault ;
2017-11-26 23:18:18 +02:00
else if ( type = = JsonType : : DATA_INTEGER )
2020-10-01 10:38:06 +02:00
return static_cast < double > ( data . Integer ) ;
2016-11-13 12:38:42 +02:00
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_FLOAT ) ;
2013-01-16 16:02:01 +03:00
return data . Float ;
}
2016-11-13 12:38:42 +02:00
const si64 integetDefault = 0 ;
si64 JsonNode : : Integer ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2016-11-13 12:38:42 +02:00
return integetDefault ;
2017-11-26 23:18:18 +02:00
else if ( type = = JsonType : : DATA_FLOAT )
2020-10-01 10:38:06 +02:00
return static_cast < si64 > ( data . Float ) ;
2016-11-13 12:38:42 +02:00
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_INTEGER ) ;
2016-11-13 12:38:42 +02:00
return data . Integer ;
}
2013-01-16 16:02:01 +03:00
const std : : string stringDefault = std : : string ( ) ;
const std : : string & JsonNode : : String ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return stringDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_STRING ) ;
2013-01-16 16:02:01 +03:00
return * data . String ;
}
const JsonVector vectorDefault = JsonVector ( ) ;
const JsonVector & JsonNode : : Vector ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return vectorDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_VECTOR ) ;
2013-01-16 16:02:01 +03:00
return * data . Vector ;
}
const JsonMap mapDefault = JsonMap ( ) ;
const JsonMap & JsonNode : : Struct ( ) const
{
2017-11-26 23:18:18 +02:00
if ( type = = JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
return mapDefault ;
2017-11-26 23:18:18 +02:00
assert ( type = = JsonType : : DATA_STRUCT ) ;
2013-01-16 16:02:01 +03:00
return * data . Struct ;
}
2023-03-13 23:26:44 +02:00
JsonNode & JsonNode : : operator [ ] ( const std : : string & child )
2013-01-16 16:02:01 +03:00
{
return Struct ( ) [ child ] ;
}
2023-03-13 23:26:44 +02:00
const JsonNode & JsonNode : : operator [ ] ( const std : : string & child ) const
2013-01-16 16:02:01 +03:00
{
2013-06-29 16:05:48 +03:00
auto it = Struct ( ) . find ( child ) ;
2013-01-16 16:02:01 +03:00
if ( it ! = Struct ( ) . end ( ) )
return it - > second ;
return nullNode ;
}
2013-04-02 20:06:43 +03:00
2023-07-19 21:25:52 +02:00
JsonNode & JsonNode : : operator [ ] ( size_t child )
{
if ( child > = Vector ( ) . size ( ) )
Vector ( ) . resize ( child + 1 ) ;
return Vector ( ) [ child ] ;
}
const JsonNode & JsonNode : : operator [ ] ( size_t child ) const
{
if ( child < Vector ( ) . size ( ) )
return Vector ( ) [ child ] ;
return nullNode ;
}
2013-04-02 20:06:43 +03:00
const JsonNode & JsonNode : : resolvePointer ( const std : : string & jsonPointer ) const
{
return : : resolvePointer ( * this , jsonPointer ) ;
}
JsonNode & JsonNode : : resolvePointer ( const std : : string & jsonPointer )
{
return : : resolvePointer ( * this , jsonPointer ) ;
}
2017-09-14 03:03:44 +02:00
std : : string JsonNode : : toJson ( bool compact ) const
2017-08-11 15:27:42 +02:00
{
std : : ostringstream out ;
2017-09-14 03:03:44 +02:00
JsonWriter writer ( out , compact ) ;
2017-08-11 15:27:42 +02:00
writer . writeNode ( * this ) ;
return out . str ( ) ;
}
2013-02-25 15:18:45 +03:00
///JsonUtils
2023-03-13 23:26:44 +02:00
void JsonUtils : : parseTypedBonusShort ( const JsonVector & source , const std : : shared_ptr < Bonus > & dest )
2013-02-25 15:18:45 +03:00
{
2020-10-01 10:38:06 +02:00
dest - > val = static_cast < si32 > ( source [ 1 ] . Float ( ) ) ;
2013-02-25 15:18:45 +03:00
resolveIdentifier ( source [ 2 ] , dest - > subtype ) ;
2020-10-01 10:38:06 +02:00
dest - > additionalInfo = static_cast < si32 > ( source [ 3 ] . Float ( ) ) ;
2023-05-01 00:20:01 +02:00
dest - > duration = BonusDuration : : PERMANENT ; //TODO: handle flags (as integer)
2016-01-27 10:38:35 +02:00
dest - > turnsRemain = 0 ;
2013-02-25 15:18:45 +03:00
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonVector & ability_vec )
2013-01-16 16:02:01 +03:00
{
2016-09-19 23:36:35 +02:00
auto b = std : : make_shared < Bonus > ( ) ;
2013-01-16 16:02:01 +03:00
std : : string type = ability_vec [ 0 ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
if ( it = = bonusNameMap . end ( ) )
{
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error: invalid ability type %s. " , type ) ;
2013-01-16 16:02:01 +03:00
return b ;
}
b - > type = it - > second ;
2016-01-27 10:38:35 +02:00
2013-02-25 15:18:45 +03:00
parseTypedBonusShort ( ability_vec , b ) ;
2013-01-16 16:02:01 +03:00
return b ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
2013-01-16 16:02:01 +03:00
template < typename T >
2023-03-13 23:26:44 +02:00
const T parseByMap ( const std : : map < std : : string , T > & map , const JsonNode * val , const std : : string & err )
2013-01-16 16:02:01 +03:00
{
2013-04-21 15:49:26 +03:00
static T defaultValue = T ( ) ;
2013-01-16 16:02:01 +03:00
if ( ! val - > isNull ( ) )
{
2023-03-13 23:26:44 +02:00
const std : : string & type = val - > String ( ) ;
2013-01-16 16:02:01 +03:00
auto it = map . find ( type ) ;
if ( it = = map . end ( ) )
{
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error: invalid %s%s. " , err , type ) ;
2013-01-16 16:02:01 +03:00
return defaultValue ;
}
else
{
return it - > second ;
}
}
else
return defaultValue ;
2013-04-21 15:49:26 +03:00
}
2013-01-16 16:02:01 +03:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
template < typename T >
2023-03-13 23:26:44 +02:00
const T parseByMapN ( const std : : map < std : : string , T > & map , const JsonNode * val , const std : : string & err )
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
{
if ( val - > isNumber ( ) )
return static_cast < T > ( val - > Integer ( ) ) ;
else
return parseByMap < T > ( map , val , err ) ;
}
2023-03-13 23:26:44 +02:00
void JsonUtils : : resolveIdentifier ( si32 & var , const JsonNode & node , const std : : string & name )
2013-01-16 16:02:01 +03:00
{
2013-04-25 17:03:35 +03:00
const JsonNode & value = node [ name ] ;
if ( ! value . isNull ( ) )
2013-01-16 16:02:01 +03:00
{
2013-04-25 17:03:35 +03:00
switch ( value . getType ( ) )
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_INTEGER :
2020-10-01 10:38:06 +02:00
var = static_cast < si32 > ( value . Integer ( ) ) ;
2016-11-13 12:38:42 +02:00
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_FLOAT :
2020-10-01 10:38:06 +02:00
var = static_cast < si32 > ( value . Float ( ) ) ;
2013-01-16 16:02:01 +03:00
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING :
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( value , [ & ] ( si32 identifier )
2013-01-16 16:02:01 +03:00
{
var = identifier ;
} ) ;
break ;
default :
2018-02-06 08:22:28 +02:00
logMod - > error ( " Error! Wrong identifier used for value of %s. " , name ) ;
2013-01-16 16:02:01 +03:00
}
}
}
2018-03-12 07:20:18 +02:00
void JsonUtils : : resolveAddInfo ( CAddInfo & var , const JsonNode & node )
{
const JsonNode & value = node [ " addInfo " ] ;
if ( ! value . isNull ( ) )
{
switch ( value . getType ( ) )
{
case JsonNode : : JsonType : : DATA_INTEGER :
2020-10-01 10:38:06 +02:00
var = static_cast < si32 > ( value . Integer ( ) ) ;
2018-03-12 07:20:18 +02:00
break ;
case JsonNode : : JsonType : : DATA_FLOAT :
2020-10-01 10:38:06 +02:00
var = static_cast < si32 > ( value . Float ( ) ) ;
2018-03-12 07:20:18 +02:00
break ;
case JsonNode : : JsonType : : DATA_STRING :
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( value , [ & ] ( si32 identifier )
2018-03-12 07:20:18 +02:00
{
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 :
2020-10-01 10:38:06 +02:00
var [ i ] = static_cast < si32 > ( vec [ i ] . Integer ( ) ) ;
2018-03-12 07:20:18 +02:00
break ;
case JsonNode : : JsonType : : DATA_FLOAT :
2020-10-01 10:38:06 +02:00
var [ i ] = static_cast < si32 > ( vec [ i ] . Float ( ) ) ;
2018-03-12 07:20:18 +02:00
break ;
case JsonNode : : JsonType : : DATA_STRING :
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( vec [ i ] , [ & var , i ] ( si32 identifier )
2018-03-12 07:20:18 +02:00
{
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. " ) ;
}
}
}
2016-10-22 10:00:55 +02:00
void JsonUtils : : resolveIdentifier ( const JsonNode & node , si32 & var )
2013-01-16 16:02:01 +03:00
{
switch ( node . getType ( ) )
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_INTEGER :
2020-10-01 10:38:06 +02:00
var = static_cast < si32 > ( node . Integer ( ) ) ;
2016-11-13 12:38:42 +02:00
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_FLOAT :
2020-10-01 10:38:06 +02:00
var = static_cast < si32 > ( node . Float ( ) ) ;
2013-01-16 16:02:01 +03:00
break ;
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING :
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( node , [ & ] ( si32 identifier )
2013-01-16 16:02:01 +03:00
{
var = identifier ;
} ) ;
break ;
default :
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error! Wrong identifier used for identifier! " ) ;
2013-01-16 16:02:01 +03:00
}
}
2018-04-01 13:17:34 +02:00
std : : shared_ptr < ILimiter > JsonUtils : : parseLimiter ( const JsonNode & limiter )
{
switch ( limiter . getType ( ) )
{
case JsonNode : : JsonType : : DATA_VECTOR :
{
const JsonVector & subLimiters = limiter . Vector ( ) ;
2023-03-13 23:26:44 +02:00
if ( subLimiters . empty ( ) )
2018-04-01 13:17:34 +02:00
{
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 " )
{
std : : shared_ptr < CCreatureTypeLimiter > creatureLimiter = std : : make_shared < CCreatureTypeLimiter > ( ) ;
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( " creature " , parameters [ 0 ] , [ = ] ( si32 creature )
2018-04-01 13:17:34 +02:00
{
creatureLimiter - > setCreature ( CreatureID ( creature ) ) ;
} ) ;
2021-04-25 14:07:06 +02:00
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 ;
2018-04-01 13:17:34 +02:00
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
{
std : : shared_ptr < HasAnotherBonusLimiter > bonusLimiter = std : : make_shared < HasAnotherBonusLimiter > ( ) ;
bonusLimiter - > type = it - > second ;
2023-03-01 23:54:10 +02:00
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 ( ) ) {
resolveIdentifier ( parameter [ " id " ] , bonusLimiter - > sid ) ;
bonusLimiter - > isSourceIDRelevant = true ;
}
}
}
return false ;
} ;
2018-04-01 13:17:34 +02:00
if ( parameters . size ( ) > 1 )
{
2023-03-01 23:54:10 +02:00
if ( findSource ( parameters [ 1 ] ) & & parameters . size ( ) = = 2 )
return bonusLimiter ;
else
{
resolveIdentifier ( parameters [ 1 ] , bonusLimiter - > subtype ) ;
bonusLimiter - > isSubtypeRelevant = true ;
if ( parameters . size ( ) > 2 )
findSource ( parameters [ 2 ] ) ;
}
2018-04-01 13:17:34 +02:00
}
return bonusLimiter ;
}
}
2018-04-17 14:59:30 +02:00
else if ( limiterType = = " CREATURE_ALIGNMENT_LIMITER " )
{
2023-04-04 12:36:42 +02:00
int alignment = vstd : : find_pos ( GameConstants : : ALIGNMENT_NAMES , parameters [ 0 ] . String ( ) ) ;
2018-04-17 14:59:30 +02:00
if ( alignment = = - 1 )
logMod - > error ( " Error: invalid alignment %s. " , parameters [ 0 ] . String ( ) ) ;
else
2023-04-04 12:36:42 +02:00
return std : : make_shared < CreatureAlignmentLimiter > ( static_cast < EAlignment > ( alignment ) ) ;
2018-04-17 14:59:30 +02:00
}
2023-04-12 22:14:38 +02:00
else if ( limiterType = = " FACTION_LIMITER " | | limiterType = = " CREATURE_FACTION_LIMITER " ) //Second name is deprecated, 1.2 compat
2018-04-17 14:59:30 +02:00
{
2023-04-09 03:36:16 +02:00
std : : shared_ptr < FactionLimiter > factionLimiter = std : : make_shared < FactionLimiter > ( ) ;
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( " faction " , parameters [ 0 ] , [ = ] ( si32 faction )
2018-04-17 14:59:30 +02:00
{
2023-04-06 19:03:17 +02:00
factionLimiter - > faction = FactionID ( faction ) ;
2018-04-17 14:59:30 +02:00
} ) ;
return factionLimiter ;
}
2023-04-06 17:28:02 +02:00
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 ;
}
2018-04-17 14:59:30 +02:00
else if ( limiterType = = " CREATURE_TERRAIN_LIMITER " )
{
std : : shared_ptr < CreatureTerrainLimiter > terrainLimiter = std : : make_shared < CreatureTerrainLimiter > ( ) ;
2023-03-13 23:26:44 +02:00
if ( ! parameters . empty ( ) )
2018-04-17 14:59:30 +02:00
{
2023-07-30 19:12:25 +02:00
VLC - > identifiers ( ) - > requestIdentifier ( " terrain " , parameters [ 0 ] , [ = ] ( si32 terrain )
2018-04-17 14:59:30 +02:00
{
2022-06-20 16:39:50 +02:00
//TODO: support limiters
//terrainLimiter->terrainType = terrain;
2018-04-17 14:59:30 +02:00
} ) ;
}
return terrainLimiter ;
}
2023-03-28 01:14:18 +02:00
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 ;
}
2018-04-01 13:17:34 +02:00
else
{
logMod - > error ( " Error: invalid customizable limiter type %s. " , limiterType ) ;
}
}
break ;
default :
break ;
}
return nullptr ;
}
2016-09-19 23:36:35 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBonus ( const JsonNode & ability )
2013-01-16 16:02:01 +03:00
{
2016-09-19 23:36:35 +02:00
auto b = std : : make_shared < Bonus > ( ) ;
2016-10-22 10:00:55 +02:00
if ( ! parseBonus ( ability , b . get ( ) ) )
{
2023-08-05 11:49:02 +02:00
// caller code can not handle this case and presumes that returned bonus is always valid
logGlobal - > error ( " Failed to parse bonus! Json config was %S " , ability . toJson ( ) ) ;
b - > type = BonusType : : NONE ;
assert ( 0 ) ; // or throw? Game *should* work with dummy bonus
return b ;
2016-10-22 10:00:55 +02:00
}
return b ;
}
2023-03-13 23:26:44 +02:00
std : : shared_ptr < Bonus > JsonUtils : : parseBuildingBonus ( const JsonNode & ability , const BuildingID & building , const std : : string & description )
2021-01-14 00:02:13 +02:00
{
2023-05-01 00:20:01 +02:00
/* duration = BonusDuration::PERMANENT
source = BonusSource : : TOWN_STRUCTURE
2021-01-14 00:02:13 +02:00
bonusType , val , subtype - get from json
*/
2023-05-01 00:20:01 +02:00
auto b = std : : make_shared < Bonus > ( BonusDuration : : PERMANENT , BonusType : : NONE , BonusSource : : TOWN_STRUCTURE , 0 , building , description , - 1 ) ;
2021-01-14 00:02:13 +02:00
if ( ! parseBonus ( ability , b . get ( ) ) )
return nullptr ;
return b ;
}
2023-03-05 02:16:05 +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 . toJson ( ) ) ;
auto params = BonusParams ( ability [ " type " ] . String ( ) ,
ability [ " subtype " ] . isString ( ) ? ability [ " subtype " ] . String ( ) : " " ,
ability [ " subtype " ] . isNumber ( ) ? ability [ " subtype " ] . Integer ( ) : - 1 ) ;
if ( params . isConverted )
{
2023-05-05 18:08:36 +02:00
if ( ability [ " type " ] . String ( ) = = " SECONDARY_SKILL_PREMY " & & bonusValueMap . find ( ability [ " valueType " ] . String ( ) ) - > second = = BonusValueType : : PERCENT_TO_BASE ) //assume secondary skill special
2023-03-05 02:16:05 +02:00
{
2023-05-01 00:20:01 +02:00
params . valueType = BonusValueType : : PERCENT_TO_TARGET_TYPE ;
params . targetType = BonusSource : : SECONDARY_SKILL ;
2023-03-05 02:16:05 +02:00
}
logMod - > warn ( " Please, use this bonus: \n %s \n Converted sucessfully! " , params . toJson ( ) . toJson ( ) ) ;
return params ;
}
else
logMod - > error ( " Cannot convert bonus! \n %s " , ability . toJson ( ) ) ;
}
BonusParams ret ;
ret . isConverted = false ;
return ret ;
}
2023-03-09 12:05:47 +02:00
static TUpdaterPtr parseUpdater ( const JsonNode & updaterJson )
{
switch ( updaterJson . getType ( ) )
{
case JsonNode : : JsonType : : DATA_STRING :
return parseByMap ( bonusUpdaterMap , & updaterJson , " updater type " ) ;
break ;
case JsonNode : : JsonType : : DATA_STRUCT :
if ( updaterJson [ " type " ] . String ( ) = = " GROWS_WITH_LEVEL " )
{
std : : shared_ptr < GrowsWithLevelUpdater > 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 ;
}
2023-03-11 22:09:16 +02:00
else if ( updaterJson [ " type " ] . String ( ) = = " ARMY_MOVEMENT " )
{
std : : shared_ptr < ArmyMovementUpdater > 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 ;
}
}
2023-03-09 12:05:47 +02:00
else
logMod - > warn ( " Unknown updater type \" %s \" " , updaterJson [ " type " ] . String ( ) ) ;
break ;
}
return nullptr ;
}
2016-10-22 10:00:55 +02:00
bool JsonUtils : : parseBonus ( const JsonNode & ability , Bonus * b )
{
2023-03-13 23:26:44 +02:00
const JsonNode * value = nullptr ;
2013-01-16 16:02:01 +03:00
std : : string type = ability [ " type " ] . String ( ) ;
auto it = bonusNameMap . find ( type ) ;
2023-03-05 02:16:05 +02:00
auto params = std : : make_unique < BonusParams > ( false ) ;
2013-01-16 16:02:01 +03:00
if ( it = = bonusNameMap . end ( ) )
{
2023-03-05 02:16:05 +02:00
params = std : : make_unique < BonusParams > ( convertDeprecatedBonus ( ability ) ) ;
if ( ! params - > isConverted )
{
logMod - > error ( " Error: invalid ability type %s. " , type ) ;
return false ;
}
b - > type = params - > type ;
2023-05-05 18:08:36 +02:00
b - > val = params - > val . value_or ( 0 ) ;
b - > valType = params - > valueType . value_or ( BonusValueType : : ADDITIVE_VALUE ) ;
if ( params - > targetType )
b - > targetSourceType = params - > targetType . value ( ) ;
2013-01-16 16:02:01 +03:00
}
2023-03-05 02:16:05 +02:00
else
b - > type = it - > second ;
2013-01-16 16:02:01 +03:00
2023-03-05 02:16:05 +02:00
resolveIdentifier ( b - > subtype , params - > isConverted ? params - > toJson ( ) : ability , " subtype " ) ;
2013-01-16 16:02:01 +03:00
2023-03-05 02:16:05 +02:00
if ( ! params - > isConverted )
{
b - > val = static_cast < si32 > ( ability [ " val " ] . Float ( ) ) ;
2013-01-16 16:02:01 +03:00
2023-03-05 02:16:05 +02:00
value = & ability [ " valueType " ] ;
if ( ! value - > isNull ( ) )
2023-05-01 00:20:01 +02:00
b - > valType = static_cast < BonusValueType > ( parseByMapN ( bonusValueMap , value , " value type " ) ) ;
2023-03-05 02:16:05 +02:00
}
2013-01-16 16:02:01 +03:00
2018-03-27 09:54:58 +02:00
b - > stacking = ability [ " stacking " ] . String ( ) ;
2018-03-12 07:20:18 +02:00
resolveAddInfo ( b - > additionalInfo , ability ) ;
2013-01-16 16:02:01 +03:00
2020-10-01 10:38:06 +02:00
b - > turnsRemain = static_cast < si32 > ( ability [ " turns " ] . Float ( ) ) ;
2013-01-16 16:02:01 +03:00
2020-10-01 10:38:06 +02:00
b - > sid = static_cast < si32 > ( ability [ " sourceID " ] . Float ( ) ) ;
2013-01-16 16:02:01 +03:00
2021-01-14 00:02:13 +02:00
if ( ! ability [ " description " ] . isNull ( ) )
2023-01-24 17:34:48 +02:00
{
if ( ability [ " description " ] . isString ( ) )
b - > description = ability [ " description " ] . String ( ) ;
if ( ability [ " description " ] . isNumber ( ) )
b - > description = VLC - > generaltexth - > translate ( " core.arraytxt " , ability [ " description " ] . Integer ( ) ) ;
}
2013-01-16 16:02:01 +03:00
value = & ability [ " effectRange " ] ;
if ( ! value - > isNull ( ) )
2023-05-01 00:20:01 +02:00
b - > effectRange = static_cast < BonusLimitEffect > ( parseByMapN ( bonusLimitEffect , value , " effect range " ) ) ;
2013-01-16 19:41:01 +03:00
2013-01-16 16:02:01 +03:00
value = & ability [ " duration " ] ;
if ( ! value - > isNull ( ) )
2013-01-16 19:41:01 +03:00
{
switch ( value - > getType ( ) )
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRING :
2023-05-05 11:56:53 +02:00
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 ;
}
2013-01-16 19:41:01 +03:00
break ;
default :
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error! Wrong bonus duration format. " ) ;
2013-01-16 19:41:01 +03:00
}
}
2023-02-13 10:52:11 +02:00
value = & ability [ " sourceType " ] ;
2013-01-16 16:02:01 +03:00
if ( ! value - > isNull ( ) )
2023-05-01 00:20:01 +02:00
b - > source = static_cast < BonusSource > ( parseByMap ( bonusSourceMap , value , " source type " ) ) ;
2013-01-16 16:02:01 +03:00
2023-02-15 21:19:35 +02:00
value = & ability [ " targetSourceType " ] ;
if ( ! value - > isNull ( ) )
2023-05-01 00:20:01 +02:00
b - > targetSourceType = static_cast < BonusSource > ( parseByMap ( bonusSourceMap , value , " target type " ) ) ;
2023-02-15 21:19:35 +02:00
2013-01-16 16:02:01 +03:00
value = & ability [ " limiters " ] ;
if ( ! value - > isNull ( ) )
2018-04-01 13:17:34 +02:00
b - > limiter = parseLimiter ( * value ) ;
2013-01-16 16:02:01 +03:00
value = & ability [ " propagator " ] ;
if ( ! value - > isNull ( ) )
2023-04-06 19:03:17 +02:00
{
//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 " ) ;
}
2013-01-16 16:02:01 +03:00
2017-09-11 02:36:02 +02:00
value = & ability [ " updater " ] ;
2017-09-17 05:48:01 +02:00
if ( ! value - > isNull ( ) )
2023-03-09 12:05:47 +02:00
b - > addUpdater ( parseUpdater ( * value ) ) ;
value = & ability [ " propagationUpdater " ] ;
if ( ! value - > isNull ( ) )
b - > propagationUpdater = parseUpdater ( * value ) ;
2016-10-22 10:00:55 +02:00
return true ;
2013-01-16 16:02:01 +03:00
}
2023-03-09 01:18:30 +02:00
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 . Or ( parseSelector ( andN ) ) ;
ret = ret . And ( base ) ;
}
value = & ability [ " noneOf " ] ;
if ( value - > isVector ( ) )
{
CSelector base = Selector : : all ;
for ( const auto & andN : value - > Vector ( ) )
base . And ( parseSelector ( andN ) ) ;
ret = ret . And ( base . Not ( ) ) ;
}
// Actual selector parser
value = & ability [ " type " ] ;
if ( value - > isString ( ) )
{
auto it = bonusNameMap . find ( value - > String ( ) ) ;
if ( it ! = bonusNameMap . end ( ) )
ret = ret . And ( Selector : : type ( ) ( it - > second ) ) ;
}
value = & ability [ " subtype " ] ;
if ( ! value - > isNull ( ) )
{
TBonusSubtype subtype ;
resolveIdentifier ( subtype , ability , " subtype " ) ;
ret = ret . And ( Selector : : subtype ( ) ( subtype ) ) ;
}
value = & ability [ " sourceType " ] ;
2023-05-05 18:08:36 +02:00
std : : optional < BonusSource > src = std : : nullopt ; //Fixes for GCC false maybe-uninitialized
std : : optional < si32 > id = std : : nullopt ;
2023-03-09 01:18:30 +02:00
if ( value - > isString ( ) )
{
auto it = bonusSourceMap . find ( value - > String ( ) ) ;
if ( it ! = bonusSourceMap . end ( ) )
src = it - > second ;
}
2023-05-05 18:08:36 +02:00
2023-03-09 01:18:30 +02:00
value = & ability [ " sourceID " ] ;
if ( ! value - > isNull ( ) )
{
2023-05-05 18:08:36 +02:00
id = - 1 ;
resolveIdentifier ( * id , ability , " sourceID " ) ;
2023-03-09 01:18:30 +02:00
}
2023-05-05 18:08:36 +02:00
if ( src & & id )
ret = ret . And ( Selector : : source ( * src , * id ) ) ;
else if ( src )
ret = ret . And ( Selector : : sourceTypeSel ( * src ) ) ;
2023-03-09 01:18:30 +02:00
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 ;
}
2013-01-16 16:02:01 +03:00
//returns first Key with value equal to given one
template < class Key , class Val >
2013-11-07 15:48:41 +03:00
Key reverseMapFirst ( const Val & val , const std : : map < Key , Val > & map )
2013-01-16 16:02:01 +03:00
{
2013-06-29 16:05:48 +03:00
for ( auto it : map )
2013-01-16 16:02:01 +03:00
{
if ( it . second = = val )
{
return it . first ;
}
}
assert ( 0 ) ;
return " " ;
}
2023-06-20 23:26:12 +02:00
static JsonNode getDefaultValue ( const JsonNode & schema , std : : string fieldName )
{
const JsonNode & fieldProps = schema [ " properties " ] [ fieldName ] ;
# if defined(VCMI_IOS)
if ( ! fieldProps [ " defaultIOS " ] . isNull ( ) )
return fieldProps [ " defaultIOS " ] ;
# elif defined(VCMI_ANDROID)
if ( ! fieldProps [ " defaultAndroid " ] . isNull ( ) )
return fieldProps [ " defaultAndroid " ] ;
# elif !defined(VCMI_MOBILE)
if ( ! fieldProps [ " defaultDesktop " ] . isNull ( ) )
return fieldProps [ " defaultDesktop " ] ;
# endif
return fieldProps [ " default " ] ;
}
static void eraseOptionalNodes ( JsonNode & node , const JsonNode & schema )
2013-04-02 20:06:43 +03:00
{
2023-06-20 23:26:12 +02:00
assert ( schema [ " type " ] . String ( ) = = " object " ) ;
2013-04-03 16:39:46 +03:00
2023-06-20 23:26:12 +02:00
std : : set < std : : string > foundEntries ;
2013-04-02 20:06:43 +03:00
2023-06-20 23:26:12 +02:00
for ( const auto & entry : schema [ " required " ] . Vector ( ) )
foundEntries . insert ( entry . String ( ) ) ;
2013-04-02 20:06:43 +03:00
2023-06-20 23:26:12 +02:00
vstd : : erase_if ( node . Struct ( ) , [ & ] ( const auto & node ) {
return ! vstd : : contains ( foundEntries , node . first ) ;
} ) ;
2013-04-02 20:06:43 +03:00
}
2023-06-20 23:26:12 +02:00
static void minimizeNode ( JsonNode & node , const JsonNode & schema )
2013-04-02 20:06:43 +03:00
{
2023-06-20 23:26:12 +02:00
if ( schema [ " type " ] . String ( ) ! = " object " )
return ;
for ( const auto & entry : schema [ " required " ] . Vector ( ) )
{
const std : : string & name = entry . String ( ) ;
minimizeNode ( node [ name ] , schema [ " properties " ] [ name ] ) ;
if ( vstd : : contains ( node . Struct ( ) , name ) & & node [ name ] = = getDefaultValue ( schema , name ) )
node . Struct ( ) . erase ( name ) ;
}
eraseOptionalNodes ( node , schema ) ;
2013-04-02 20:06:43 +03:00
}
2023-06-20 23:26:12 +02:00
static void maximizeNode ( JsonNode & node , const JsonNode & schema )
2013-01-16 16:02:01 +03:00
{
2013-04-03 16:39:46 +03:00
// "required" entry can only be found in object/struct
2023-06-20 23:26:12 +02:00
if ( schema [ " type " ] . String ( ) ! = " object " )
return ;
// check all required entries that have default version
for ( const auto & entry : schema [ " required " ] . Vector ( ) )
2013-04-02 20:06:43 +03:00
{
2023-06-20 23:26:12 +02:00
const std : : string & name = entry . String ( ) ;
2013-04-02 20:06:43 +03:00
2023-06-20 23:26:12 +02:00
if ( node [ name ] . isNull ( ) & & ! getDefaultValue ( schema , name ) . isNull ( ) )
node [ name ] = getDefaultValue ( schema , name ) ;
2013-04-03 16:39:46 +03:00
2023-06-20 23:26:12 +02:00
maximizeNode ( node [ name ] , schema [ " properties " ] [ name ] ) ;
2013-04-02 20:06:43 +03:00
}
2023-06-20 23:26:12 +02:00
eraseOptionalNodes ( node , schema ) ;
}
void JsonUtils : : minimize ( JsonNode & node , const std : : string & schemaName )
{
minimizeNode ( node , getSchema ( schemaName ) ) ;
2013-01-16 16:02:01 +03:00
}
2023-03-13 23:26:44 +02:00
void JsonUtils : : maximize ( JsonNode & node , const std : : string & schemaName )
2013-01-16 16:02:01 +03:00
{
2013-04-02 20:06:43 +03:00
maximizeNode ( node , getSchema ( schemaName ) ) ;
}
2023-03-13 23:26:44 +02:00
bool JsonUtils : : validate ( const JsonNode & node , const std : : string & schemaName , const std : : string & dataName )
2013-04-02 20:06:43 +03:00
{
2013-10-26 22:33:34 +03:00
std : : string log = Validation : : check ( schemaName , node ) ;
if ( ! log . empty ( ) )
{
2017-08-30 23:23:19 +02:00
logMod - > warn ( " Data in %s is invalid! " , dataName ) ;
logMod - > warn ( log ) ;
2018-02-06 08:22:28 +02:00
logMod - > trace ( " %s json: %s " , dataName , node . toJson ( true ) ) ;
2013-10-26 22:33:34 +03:00
}
return log . empty ( ) ;
2013-04-02 20:06:43 +03:00
}
2023-03-13 23:26:44 +02:00
const JsonNode & getSchemaByName ( const std : : string & name )
2013-04-02 20:06:43 +03:00
{
// cached schemas to avoid loading json data multiple times
static std : : map < std : : string , JsonNode > loadedSchemas ;
if ( vstd : : contains ( loadedSchemas , name ) )
return loadedSchemas [ name ] ;
2018-10-30 23:30:56 +02:00
std : : string filename = " config/schemas/ " + name ;
2013-04-02 20:06:43 +03:00
if ( CResourceHandler : : get ( ) - > existsResource ( ResourceID ( filename ) ) )
{
loadedSchemas [ name ] = JsonNode ( ResourceID ( filename ) ) ;
return loadedSchemas [ name ] ;
}
2017-08-30 23:23:19 +02:00
logMod - > error ( " Error: missing schema with name %s! " , name ) ;
2013-04-02 20:06:43 +03:00
assert ( 0 ) ;
return nullNode ;
}
2023-03-13 23:26:44 +02:00
const JsonNode & JsonUtils : : getSchema ( const std : : string & URI )
2013-04-02 20:06:43 +03:00
{
2013-04-12 23:49:02 +03:00
size_t posColon = URI . find ( ' : ' ) ;
size_t posHash = URI . find ( ' # ' ) ;
2018-10-30 23:30:56 +02:00
std : : string filename ;
2017-08-11 19:03:05 +02:00
if ( posColon = = std : : string : : npos )
{
2018-10-30 23:30:56 +02:00
filename = URI . substr ( 0 , posHash ) ;
2017-08-11 19:03:05 +02:00
}
2018-10-30 23:30:56 +02:00
else
2013-04-02 20:06:43 +03:00
{
2018-10-30 23:30:56 +02:00
std : : string protocolName = URI . substr ( 0 , posColon ) ;
filename = URI . substr ( posColon + 1 , posHash - posColon - 1 ) + " .json " ;
if ( protocolName ! = " vcmi " )
{
logMod - > error ( " Error: unsupported URI protocol for schema: %s " , URI ) ;
return nullNode ;
}
2013-04-02 20:06:43 +03:00
}
2013-04-12 23:49:02 +03:00
// check if json pointer if present (section after hash in string)
2017-08-11 19:03:05 +02:00
if ( posHash = = std : : string : : npos | | posHash = = URI . size ( ) - 1 )
2013-04-12 23:49:02 +03:00
return getSchemaByName ( filename ) ;
else
return getSchemaByName ( filename ) . resolvePointer ( URI . substr ( posHash + 1 ) ) ;
2013-01-16 16:02:01 +03:00
}
2022-11-30 17:38:53 +02:00
void JsonUtils : : merge ( JsonNode & dest , JsonNode & source , bool ignoreOverride , bool copyMeta )
2013-01-16 16:02:01 +03:00
{
2017-11-26 23:18:18 +02:00
if ( dest . getType ( ) = = JsonNode : : JsonType : : DATA_NULL )
2013-01-16 16:02:01 +03:00
{
std : : swap ( dest , source ) ;
return ;
}
switch ( source . getType ( ) )
{
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_NULL :
2013-04-25 17:03:35 +03:00
{
dest . clear ( ) ;
break ;
}
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_BOOL :
case JsonNode : : JsonType : : DATA_FLOAT :
case JsonNode : : JsonType : : DATA_INTEGER :
case JsonNode : : JsonType : : DATA_STRING :
case JsonNode : : JsonType : : DATA_VECTOR :
2013-01-16 16:02:01 +03:00
{
2013-12-04 13:36:39 +03:00
std : : swap ( dest , source ) ;
2013-04-25 17:03:35 +03:00
break ;
2013-01-16 16:02:01 +03:00
}
2017-11-26 23:18:18 +02:00
case JsonNode : : JsonType : : DATA_STRUCT :
2013-01-16 16:02:01 +03:00
{
2022-11-30 17:38:53 +02:00
if ( ! ignoreOverride & & vstd : : contains ( source . flags , " override " ) )
2018-03-05 05:30:10 +02:00
{
std : : swap ( dest , source ) ;
}
else
{
2022-11-30 17:38:53 +02:00
if ( copyMeta )
dest . meta = source . meta ;
2018-03-05 05:30:10 +02:00
//recursively merge all entries from struct
for ( auto & node : source . Struct ( ) )
2022-11-30 17:38:53 +02:00
merge ( dest [ node . first ] , node . second , ignoreOverride ) ;
2018-03-05 05:30:10 +02:00
}
2013-01-16 16:02:01 +03:00
}
}
}
2022-11-30 17:38:53 +02:00
void JsonUtils : : mergeCopy ( JsonNode & dest , JsonNode source , bool ignoreOverride , bool copyMeta )
2013-01-16 16:02:01 +03:00
{
// uses copy created in stack to safely merge two nodes
2022-11-30 17:38:53 +02:00
merge ( dest , source , ignoreOverride , copyMeta ) ;
2013-01-16 16:02:01 +03:00
}
2014-05-19 01:28:44 +03:00
void JsonUtils : : inherit ( JsonNode & descendant , const JsonNode & base )
{
2014-06-15 19:43:01 +03:00
JsonNode inheritedNode ( base ) ;
2022-11-30 17:38:53 +02:00
merge ( inheritedNode , descendant , true , true ) ;
2014-05-19 01:28:44 +03:00
descendant . swap ( inheritedNode ) ;
}
2017-09-13 08:58:14 +02:00
JsonNode JsonUtils : : intersect ( const std : : vector < JsonNode > & nodes , bool pruneEmpty )
{
2023-03-13 23:26:44 +02:00
if ( nodes . empty ( ) )
2017-09-13 08:58:14 +02:00
return nullNode ;
JsonNode result = nodes [ 0 ] ;
for ( int i = 1 ; i < nodes . size ( ) ; i + + )
{
if ( result . isNull ( ) )
break ;
result = JsonUtils : : intersect ( result , nodes [ i ] , pruneEmpty ) ;
}
return result ;
}
JsonNode JsonUtils : : intersect ( const JsonNode & a , const JsonNode & b , bool pruneEmpty )
{
if ( a . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT & & b . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
{
// intersect individual properties
JsonNode result ( JsonNode : : JsonType : : DATA_STRUCT ) ;
2023-03-13 23:26:44 +02:00
for ( const auto & property : a . Struct ( ) )
2017-09-13 08:58:14 +02:00
{
if ( vstd : : contains ( b . Struct ( ) , property . first ) )
{
JsonNode propertyIntersect = JsonUtils : : intersect ( property . second , b . Struct ( ) . find ( property . first ) - > second ) ;
2017-09-16 00:23:31 +02:00
if ( pruneEmpty & & ! propertyIntersect . containsBaseData ( ) )
2017-09-13 08:58:14 +02:00
continue ;
result [ property . first ] = propertyIntersect ;
}
}
return result ;
}
else
{
// not a struct - same or different, no middle ground
if ( a = = b )
return a ;
}
return nullNode ;
}
2017-09-13 10:27:01 +02:00
JsonNode JsonUtils : : difference ( const JsonNode & node , const JsonNode & base )
{
2017-09-17 04:45:23 +02:00
auto addsInfo = [ ] ( JsonNode diff ) - > bool
{
switch ( diff . getType ( ) )
{
case JsonNode : : JsonType : : DATA_NULL :
return false ;
case JsonNode : : JsonType : : DATA_STRUCT :
2023-03-13 23:26:44 +02:00
return ! diff . Struct ( ) . empty ( ) ;
2017-09-17 04:45:23 +02:00
default :
return true ;
}
} ;
2017-09-14 03:03:44 +02:00
if ( node . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT & & base . getType ( ) = = JsonNode : : JsonType : : DATA_STRUCT )
2017-09-13 10:27:01 +02:00
{
// subtract individual properties
2017-09-14 03:03:44 +02:00
JsonNode result ( JsonNode : : JsonType : : DATA_STRUCT ) ;
2023-03-13 23:26:44 +02:00
for ( const auto & property : node . Struct ( ) )
2017-09-13 10:27:01 +02:00
{
if ( vstd : : contains ( base . Struct ( ) , property . first ) )
{
2017-09-16 00:23:31 +02:00
const JsonNode propertyDifference = JsonUtils : : difference ( property . second , base . Struct ( ) . find ( property . first ) - > second ) ;
2017-09-17 04:45:23 +02:00
if ( addsInfo ( propertyDifference ) )
2017-09-16 00:23:31 +02:00
result [ property . first ] = propertyDifference ;
2017-09-13 10:27:01 +02:00
}
else
{
result [ property . first ] = property . second ;
}
}
return result ;
}
else
{
if ( node = = base )
return nullNode ;
}
return node ;
}
2023-03-13 23:26:44 +02:00
JsonNode JsonUtils : : assembleFromFiles ( const std : : vector < std : : string > & files )
2013-01-16 16:02:01 +03:00
{
2023-03-13 23:26:44 +02:00
bool isValid = false ;
2013-11-08 23:36:26 +03:00
return assembleFromFiles ( files , isValid ) ;
}
2023-03-13 23:26:44 +02:00
JsonNode JsonUtils : : assembleFromFiles ( const std : : vector < std : : string > & files , bool & isValid )
2013-11-08 23:36:26 +03:00
{
isValid = true ;
2013-01-16 16:02:01 +03:00
JsonNode result ;
2023-03-13 23:26:44 +02:00
for ( const std : : string & file : files )
2013-01-16 16:02:01 +03:00
{
2023-03-13 23:26:44 +02:00
bool isValidFile = false ;
2013-11-08 23:36:26 +03:00
JsonNode section ( ResourceID ( file , EResType : : TEXT ) , isValidFile ) ;
2013-01-16 16:02:01 +03:00
merge ( result , section ) ;
2013-11-08 23:36:26 +03:00
isValid | = isValidFile ;
2013-01-16 16:02:01 +03:00
}
return result ;
}
2013-04-26 00:50:55 +03:00
2023-03-13 23:26:44 +02:00
JsonNode JsonUtils : : assembleFromFiles ( const std : : string & filename )
2013-04-26 00:50:55 +03:00
{
JsonNode result ;
2013-07-28 17:49:50 +03:00
ResourceID resID ( filename , EResType : : TEXT ) ;
2013-04-26 00:50:55 +03:00
2013-07-28 17:49:50 +03:00
for ( auto & loader : CResourceHandler : : get ( ) - > getResourcesWithName ( resID ) )
2013-04-26 00:50:55 +03:00
{
// FIXME: some way to make this code more readable
2013-07-28 17:49:50 +03:00
auto stream = loader - > load ( resID ) ;
2013-04-26 00:50:55 +03:00
std : : unique_ptr < ui8 [ ] > textData ( new ui8 [ stream - > getSize ( ) ] ) ;
stream - > read ( textData . get ( ) , stream - > getSize ( ) ) ;
2023-03-13 23:26:44 +02:00
JsonNode section ( reinterpret_cast < char * > ( textData . get ( ) ) , stream - > getSize ( ) ) ;
2013-04-26 00:50:55 +03:00
merge ( result , section ) ;
}
return result ;
}
2017-09-12 01:56:38 +02:00
DLL_LINKAGE JsonNode JsonUtils : : boolNode ( bool value )
{
JsonNode node ;
node . Bool ( ) = value ;
return node ;
}
DLL_LINKAGE JsonNode JsonUtils : : floatNode ( double value )
{
JsonNode node ;
node . Float ( ) = value ;
return node ;
}
2023-03-13 23:26:44 +02:00
DLL_LINKAGE JsonNode JsonUtils : : stringNode ( const std : : string & value )
2017-09-12 01:56:38 +02:00
{
JsonNode node ;
node . String ( ) = value ;
return node ;
}
DLL_LINKAGE JsonNode JsonUtils : : intNode ( si64 value )
{
JsonNode node ;
node . Integer ( ) = value ;
return node ;
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END