2022-06-20 17:39:50 +03:00
/*
2017-07-13 11:26:03 +03:00
* CObjectClassesHandler . 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"
2025-07-05 19:49:16 +03:00
# include "CConfigHandler.h"
2014-06-05 19:57:43 +03:00
# include "CObjectClassesHandler.h"
2014-06-05 20:26:50 +03:00
# include "../filesystem/Filesystem.h"
# include "../filesystem/CBinaryReader.h"
2024-02-11 23:09:01 +02:00
# include "../json/JsonUtils.h"
2025-02-14 16:23:37 +00:00
# include "../GameLibrary.h"
2014-06-05 20:26:50 +03:00
# include "../GameConstants.h"
2023-08-20 00:22:31 +03:00
# include "../constants/StringConstants.h"
2024-08-31 11:00:36 +00:00
# include "../IGameSettings.h"
2017-09-13 03:35:58 +03:00
# include "../CSoundBase.h"
2014-06-05 19:57:43 +03:00
2023-06-02 21:47:37 +03:00
# include "../mapObjectConstructors/CRewardableConstructor.h"
# include "../mapObjectConstructors/CommonConstructors.h"
2023-06-08 17:29:29 +03:00
# include "../mapObjectConstructors/DwellingInstanceConstructor.h"
2024-10-25 16:48:10 +00:00
# include "../mapObjectConstructors/FlaggableInstanceConstructor.h"
2023-06-07 00:17:39 +03:00
# include "../mapObjectConstructors/HillFortInstanceConstructor.h"
2025-07-21 19:38:24 +03:00
# include "../mapObjectConstructors/MarketInstanceConstructor.h"
2023-06-08 00:04:13 +03:00
# include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
2024-10-25 16:48:10 +00:00
2023-06-17 15:21:42 +03:00
# include "../mapObjects/CGCreature.h"
2024-10-25 16:48:10 +00:00
# include "../mapObjects/CGHeroInstance.h"
# include "../mapObjects/CGMarket.h"
2023-06-02 21:47:37 +03:00
# include "../mapObjects/CGPandoraBox.h"
2024-10-25 16:48:10 +00:00
# include "../mapObjects/CGTownInstance.h"
2023-06-08 00:04:13 +03:00
# include "../mapObjects/CQuest.h"
2024-10-25 16:48:10 +00:00
# include "../mapObjects/FlaggableMapObject.h"
2023-06-08 00:42:47 +03:00
# include "../mapObjects/MiscObjects.h"
2024-10-25 16:48:10 +00:00
# include "../mapObjects/ObjectTemplate.h"
2024-04-04 21:39:01 +02:00
# include "../mapObjects/ObstacleSetHandler.h"
2024-10-25 16:48:10 +00:00
2023-07-30 20:12:25 +03:00
# include "../modding/IdentifierStorage.h"
# include "../modding/CModHandler.h"
2023-11-21 18:32:07 +02:00
# include "../modding/ModScope.h"
2024-07-20 12:55:17 +00:00
# include "../texts/CGeneralTextHandler.h"
# include "../texts/CLegacyConfigParser.h"
2014-06-05 19:57:43 +03:00
2024-09-12 21:04:27 +02:00
# include <vstd/StringUtils.h>
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_BEGIN
2014-06-05 19:57:43 +03:00
CObjectClassesHandler : : CObjectClassesHandler ( )
{
2024-01-16 19:25:21 +00:00
# define SET_HANDLER_CLASS(STRING, CLASSNAME) handlerConstructors[STRING] = std::make_shared<CLASSNAME>
2017-07-16 12:58:05 +03:00
# define SET_HANDLER(STRING, TYPENAME) handlerConstructors[STRING] = std::make_shared<CDefaultObjectTypeHandler<TYPENAME>>
2014-06-05 19:57:43 +03:00
// list of all known handlers, hardcoded for now since the only way to add new objects is via C++ code
2014-06-17 14:57:47 +03:00
//Note: should be in sync with registerTypesMapObjectTypes function
2014-06-05 19:57:43 +03:00
SET_HANDLER_CLASS ( " configurable " , CRewardableConstructor ) ;
2023-06-08 17:29:29 +03:00
SET_HANDLER_CLASS ( " dwelling " , DwellingInstanceConstructor ) ;
2014-06-14 18:42:13 +03:00
SET_HANDLER_CLASS ( " hero " , CHeroInstanceConstructor ) ;
SET_HANDLER_CLASS ( " town " , CTownInstanceConstructor ) ;
2023-04-18 17:27:39 +04:00
SET_HANDLER_CLASS ( " boat " , BoatInstanceConstructor ) ;
2024-10-25 16:48:10 +00:00
SET_HANDLER_CLASS ( " flaggable " , FlaggableInstanceConstructor ) ;
2023-04-28 05:16:10 +04:00
SET_HANDLER_CLASS ( " market " , MarketInstanceConstructor ) ;
2023-06-07 00:17:39 +03:00
SET_HANDLER_CLASS ( " hillFort " , HillFortInstanceConstructor ) ;
2023-06-08 00:04:13 +03:00
SET_HANDLER_CLASS ( " shipyard " , ShipyardInstanceConstructor ) ;
2023-07-15 14:50:09 +03:00
SET_HANDLER_CLASS ( " monster " , CreatureInstanceConstructor ) ;
SET_HANDLER_CLASS ( " resource " , ResourceInstanceConstructor ) ;
2014-06-05 19:57:43 +03:00
2014-06-14 18:42:13 +03:00
SET_HANDLER_CLASS ( " static " , CObstacleConstructor ) ;
SET_HANDLER_CLASS ( " " , CObstacleConstructor ) ;
2014-06-05 19:57:43 +03:00
2014-06-27 19:08:36 +03:00
SET_HANDLER ( " randomArtifact " , CGArtifact ) ;
SET_HANDLER ( " randomHero " , CGHeroInstance ) ;
SET_HANDLER ( " randomResource " , CGResource ) ;
SET_HANDLER ( " randomTown " , CGTownInstance ) ;
SET_HANDLER ( " randomMonster " , CGCreature ) ;
SET_HANDLER ( " randomDwelling " , CGDwelling ) ;
2014-06-14 18:42:13 +03:00
SET_HANDLER ( " generic " , CGObjectInstance ) ;
2014-06-05 19:57:43 +03:00
SET_HANDLER ( " artifact " , CGArtifact ) ;
SET_HANDLER ( " borderGate " , CGBorderGate ) ;
SET_HANDLER ( " borderGuard " , CGBorderGuard ) ;
SET_HANDLER ( " denOfThieves " , CGDenOfthieves ) ;
SET_HANDLER ( " event " , CGEvent ) ;
SET_HANDLER ( " garrison " , CGGarrison ) ;
SET_HANDLER ( " heroPlaceholder " , CGHeroPlaceholder ) ;
SET_HANDLER ( " keymaster " , CGKeymasterTent ) ;
SET_HANDLER ( " magi " , CGMagi ) ;
SET_HANDLER ( " mine " , CGMine ) ;
SET_HANDLER ( " obelisk " , CGObelisk ) ;
SET_HANDLER ( " pandora " , CGPandoraBox ) ;
2014-06-17 14:57:47 +03:00
SET_HANDLER ( " prison " , CGHeroInstance ) ;
2014-06-05 19:57:43 +03:00
SET_HANDLER ( " questGuard " , CGQuestGuard ) ;
SET_HANDLER ( " seerHut " , CGSeerHut ) ;
SET_HANDLER ( " sign " , CGSignBottle ) ;
SET_HANDLER ( " siren " , CGSirens ) ;
2015-03-08 16:11:23 +03:00
SET_HANDLER ( " monolith " , CGMonolith ) ;
SET_HANDLER ( " subterraneanGate " , CGSubterraneanGate ) ;
SET_HANDLER ( " whirlpool " , CGWhirlpool ) ;
2022-06-26 10:21:05 +03:00
SET_HANDLER ( " terrain " , CGTerrainPatch ) ;
2014-06-05 19:57:43 +03:00
# undef SET_HANDLER_CLASS
# undef SET_HANDLER
}
2023-12-16 13:42:12 +02:00
CObjectClassesHandler : : ~ CObjectClassesHandler ( ) = default ;
2016-08-17 09:24:01 +03:00
2023-03-15 21:34:29 +02:00
std : : vector < JsonNode > CObjectClassesHandler : : loadLegacyData ( )
2014-06-05 19:57:43 +03:00
{
2025-02-14 16:23:37 +00:00
size_t dataSize = LIBRARY - > engineSettings ( ) - > getInteger ( EGameSettings : : TEXTS_OBJECT ) ;
2023-03-15 21:34:29 +02:00
2023-09-02 00:26:14 +03:00
CLegacyConfigParser parser ( TextPath : : builtin ( " Data/Objects.txt " ) ) ;
2023-02-12 23:39:17 +03:00
auto totalNumber = static_cast < size_t > ( parser . readNumber ( ) ) ; // first line contains number of objects to read and nothing else
2014-06-05 19:57:43 +03:00
parser . endLine ( ) ;
2022-09-29 19:07:56 +02:00
for ( size_t i = 0 ; i < totalNumber ; i + + )
2014-06-05 19:57:43 +03:00
{
2024-01-20 18:40:03 +02:00
auto tmpl = std : : make_shared < ObjectTemplate > ( ) ;
2022-09-29 19:07:56 +02:00
tmpl - > readTxt ( parser ) ;
2014-06-05 19:57:43 +03:00
parser . endLine ( ) ;
2022-09-29 19:07:56 +02:00
2023-11-05 19:13:18 +02:00
std : : pair key ( tmpl - > id , tmpl - > subid ) ;
2024-01-20 18:40:03 +02:00
legacyTemplates . insert ( std : : make_pair ( key , tmpl ) ) ;
2014-06-05 19:57:43 +03:00
}
2024-08-17 01:19:42 +02:00
mapObjectTypes . resize ( 256 ) ;
2023-01-01 20:51:56 +02:00
2014-06-05 19:57:43 +03:00
std : : vector < JsonNode > ret ( dataSize ) ; // create storage for 256 objects
assert ( dataSize = = 256 ) ;
2023-09-02 00:26:14 +03:00
CLegacyConfigParser namesParser ( TextPath : : builtin ( " Data/ObjNames.txt " ) ) ;
2014-06-05 19:57:43 +03:00
for ( size_t i = 0 ; i < 256 ; i + + )
{
2023-01-11 00:48:51 +02:00
ret [ i ] [ " name " ] . String ( ) = namesParser . readString ( ) ;
2014-06-17 14:57:47 +03:00
namesParser . endLine ( ) ;
2014-06-05 19:57:43 +03:00
}
2014-06-30 00:16:45 +03:00
2022-12-31 15:01:19 +02:00
JsonNode cregen1 ;
JsonNode cregen4 ;
2023-09-02 00:26:14 +03:00
CLegacyConfigParser cregen1Parser ( TextPath : : builtin ( " data/crgen1 " ) ) ;
2014-06-30 00:16:45 +03:00
do
2022-12-31 15:01:19 +02:00
{
JsonNode subObject ;
subObject [ " name " ] . String ( ) = cregen1Parser . readString ( ) ;
cregen1 . Vector ( ) . push_back ( subObject ) ;
}
2014-06-30 00:16:45 +03:00
while ( cregen1Parser . endLine ( ) ) ;
2023-09-02 00:26:14 +03:00
CLegacyConfigParser cregen4Parser ( TextPath : : builtin ( " data/crgen4 " ) ) ;
2014-06-30 00:16:45 +03:00
do
2022-12-31 15:01:19 +02:00
{
JsonNode subObject ;
subObject [ " name " ] . String ( ) = cregen4Parser . readString ( ) ;
cregen4 . Vector ( ) . push_back ( subObject ) ;
}
2014-06-30 00:16:45 +03:00
while ( cregen4Parser . endLine ( ) ) ;
2022-12-31 15:01:19 +02:00
ret [ Obj : : CREATURE_GENERATOR1 ] [ " subObjects " ] = cregen1 ;
ret [ Obj : : CREATURE_GENERATOR4 ] [ " subObjects " ] = cregen4 ;
2023-01-11 00:48:51 +02:00
ret [ Obj : : REFUGEE_CAMP ] [ " subObjects " ] . Vector ( ) . push_back ( ret [ Obj : : REFUGEE_CAMP ] ) ;
ret [ Obj : : WAR_MACHINE_FACTORY ] [ " subObjects " ] . Vector ( ) . push_back ( ret [ Obj : : WAR_MACHINE_FACTORY ] ) ;
2014-06-05 19:57:43 +03:00
return ret ;
}
2024-08-17 01:19:42 +02:00
void CObjectClassesHandler : : loadSubObject ( const std : : string & scope , const std : : string & identifier , const JsonNode & entry , ObjectClass * baseObject )
2014-06-05 19:57:43 +03:00
{
2024-08-17 01:19:42 +02:00
auto subObject = loadSubObjectFromJson ( scope , identifier , entry , baseObject , baseObject - > objectTypeHandlers . size ( ) ) ;
2022-12-31 15:01:19 +02:00
2024-08-17 01:19:42 +02:00
assert ( subObject ) ;
baseObject - > objectTypeHandlers . push_back ( subObject ) ;
2014-06-05 19:57:43 +03:00
2024-08-17 01:19:42 +02:00
registerObject ( scope , baseObject - > getJsonKey ( ) , subObject - > getSubTypeName ( ) , subObject - > subtype ) ;
2023-04-16 20:42:56 +03:00
for ( const auto & compatID : entry [ " compatibilityIdentifiers " ] . Vector ( ) )
2025-02-25 15:26:16 +00:00
{
if ( identifier ! = compatID . String ( ) )
registerObject ( scope , baseObject - > getJsonKey ( ) , compatID . String ( ) , subObject - > subtype ) ;
else
2025-02-27 10:14:02 +00:00
logMod - > warn ( " Mod '%s' map object '%s': compatibility identifier has same name as object itself! " , scope , identifier ) ;
2025-02-25 15:26:16 +00:00
}
2014-06-05 19:57:43 +03:00
}
2024-08-17 01:19:42 +02:00
void CObjectClassesHandler : : loadSubObject ( const std : : string & scope , const std : : string & identifier , const JsonNode & entry , ObjectClass * baseObject , size_t index )
2014-06-05 19:57:43 +03:00
{
2024-08-17 01:19:42 +02:00
auto subObject = loadSubObjectFromJson ( scope , identifier , entry , baseObject , index ) ;
2014-06-30 00:16:45 +03:00
2024-08-17 01:19:42 +02:00
assert ( subObject ) ;
if ( baseObject - > objectTypeHandlers . at ( index ) ! = nullptr )
2024-01-04 23:52:01 +02:00
throw std : : runtime_error ( " Attempt to load already loaded object: " + identifier ) ;
2024-08-17 01:19:42 +02:00
baseObject - > objectTypeHandlers . at ( index ) = subObject ;
2014-06-05 19:57:43 +03:00
2024-08-17 01:19:42 +02:00
registerObject ( scope , baseObject - > getJsonKey ( ) , subObject - > getSubTypeName ( ) , subObject - > subtype ) ;
2023-04-16 20:42:56 +03:00
for ( const auto & compatID : entry [ " compatibilityIdentifiers " ] . Vector ( ) )
2025-02-25 15:26:16 +00:00
{
if ( identifier ! = compatID . String ( ) )
registerObject ( scope , baseObject - > getJsonKey ( ) , compatID . String ( ) , subObject - > subtype ) ;
else
logMod - > warn ( " Mod '%s' map object '%s': compatibility identifier has same name as object itself! " ) ;
}
2022-12-31 15:01:19 +02:00
}
2014-06-30 00:16:45 +03:00
2024-08-17 01:19:42 +02:00
TObjectTypeHandler CObjectClassesHandler : : loadSubObjectFromJson ( const std : : string & scope , const std : : string & identifier , const JsonNode & entry , ObjectClass * baseObject , size_t index )
2022-12-31 15:01:19 +02:00
{
2023-01-19 00:54:19 +02:00
assert ( ! scope . empty ( ) ) ;
2025-07-05 19:49:16 +03:00
if ( settings [ " mods " ] [ " validation " ] . String ( ) ! = " off " )
{
size_t separator = identifier . find ( ' : ' ) ;
if ( separator ! = std : : string : : npos )
{
std : : string modName = identifier . substr ( 0 , separator ) ;
std : : string objectName = identifier . substr ( separator + 1 ) ;
logMod - > warn ( " Mod %s: Map object type with format '%s' will add new map object, not modify it! Please use '%s' form and add dependency on mod '%s' instead! " , scope , identifier , modName , identifier ) ;
}
}
2024-08-17 01:19:42 +02:00
std : : string handler = baseObject - > handlerName ;
2023-10-29 13:47:56 +02:00
if ( ! handlerConstructors . count ( handler ) )
2014-06-05 19:57:43 +03:00
{
2023-10-29 13:47:56 +02:00
logMod - > error ( " Handler with name %s was not found! " , handler ) ;
2023-10-26 15:32:13 +03:00
// workaround for potential crash - if handler does not exists, continue with generic handler that is used for objects without any custom logc
handler = " generic " ;
assert ( handlerConstructors . count ( handler ) ! = 0 ) ;
2014-06-05 19:57:43 +03:00
}
2014-06-30 17:11:25 +03:00
2023-10-26 15:32:13 +03:00
auto createdObject = handlerConstructors . at ( handler ) ( ) ;
2023-01-11 00:48:51 +02:00
2023-01-19 00:54:19 +02:00
createdObject - > modScope = scope ;
2024-08-17 01:19:42 +02:00
createdObject - > typeName = baseObject - > identifier ;
2023-01-19 00:54:19 +02:00
createdObject - > subTypeName = identifier ;
2023-01-11 00:48:51 +02:00
2024-08-17 01:19:42 +02:00
createdObject - > type = baseObject - > id ;
2023-01-19 00:54:19 +02:00
createdObject - > subtype = index ;
2022-12-31 15:01:19 +02:00
createdObject - > init ( entry ) ;
2020-10-25 01:03:32 +03:00
2024-04-12 14:53:07 +02:00
bool staticObject = createdObject - > isStaticObject ( ) ;
if ( staticObject )
2024-04-08 16:39:41 +02:00
{
2024-04-12 14:53:07 +02:00
for ( auto & templ : createdObject - > getTemplates ( ) )
{
// Register templates for new objects from mods
2025-02-14 16:23:37 +00:00
LIBRARY - > biomeHandler - > addTemplate ( scope , templ - > stringID , templ ) ;
2024-04-12 14:53:07 +02:00
}
2024-04-08 16:39:41 +02:00
}
2024-08-17 01:19:42 +02:00
auto range = legacyTemplates . equal_range ( std : : make_pair ( baseObject - > id , index ) ) ;
2022-12-31 15:01:19 +02:00
for ( auto & templ : boost : : make_iterator_range ( range . first , range . second ) )
2020-10-04 00:06:25 +03:00
{
2024-04-12 14:53:07 +02:00
if ( staticObject )
{
// Register legacy templates as "core"
// FIXME: Why does it clear stringID?
2025-02-14 16:23:37 +00:00
LIBRARY - > biomeHandler - > addTemplate ( " core " , templ . second - > stringID , templ . second ) ;
2024-04-12 14:53:07 +02:00
}
2022-12-31 15:01:19 +02:00
createdObject - > addTemplate ( templ . second ) ;
2024-04-09 17:29:33 +02:00
2021-02-20 04:57:50 +03:00
}
2022-12-31 15:01:19 +02:00
legacyTemplates . erase ( range . first , range . second ) ;
2014-06-05 19:57:43 +03:00
2024-08-17 01:19:42 +02:00
logGlobal - > debug ( " Loaded object %s(%d)::%s(%d) " , baseObject - > getJsonKey ( ) , baseObject - > id , identifier , index ) ;
2022-12-30 19:18:31 +02:00
2022-12-31 15:01:19 +02:00
return createdObject ;
2022-12-30 19:18:31 +02:00
}
2023-12-16 14:13:47 +02:00
ObjectClass : : ObjectClass ( ) = default ;
ObjectClass : : ~ ObjectClass ( ) = default ;
2023-01-11 00:48:51 +02:00
std : : string ObjectClass : : getJsonKey ( ) const
{
2023-01-19 00:54:19 +02:00
return modScope + ' : ' + identifier ;
2023-01-11 00:48:51 +02:00
}
std : : string ObjectClass : : getNameTextID ( ) const
2022-12-30 19:18:31 +02:00
{
2023-01-19 01:43:12 +02:00
return TextIdentifier ( " object " , modScope , identifier , " name " ) . get ( ) ;
2023-01-11 00:48:51 +02:00
}
std : : string ObjectClass : : getNameTranslated ( ) const
{
2025-02-14 16:23:37 +00:00
return LIBRARY - > generaltexth - > translate ( getNameTextID ( ) ) ;
2022-12-30 19:18:31 +02:00
}
2023-12-16 13:42:12 +02:00
std : : unique_ptr < ObjectClass > CObjectClassesHandler : : loadFromJson ( const std : : string & scope , const JsonNode & json , const std : : string & name , size_t index )
2014-06-05 19:57:43 +03:00
{
2024-08-17 01:19:42 +02:00
auto newObject = std : : make_unique < ObjectClass > ( ) ;
2022-12-30 19:18:31 +02:00
2024-08-17 01:19:42 +02:00
newObject - > modScope = scope ;
newObject - > identifier = name ;
newObject - > handlerName = json [ " handler " ] . String ( ) ;
newObject - > base = json [ " base " ] ;
newObject - > id = index ;
2021-04-25 15:07:06 +03:00
2025-02-14 16:23:37 +00:00
LIBRARY - > generaltexth - > registerString ( scope , newObject - > getNameTextID ( ) , json [ " name " ] ) ;
2023-01-11 00:48:51 +02:00
2024-08-17 01:19:42 +02:00
newObject - > objectTypeHandlers . resize ( json [ " lastReservedIndex " ] . Float ( ) + 1 ) ;
2017-09-13 03:35:58 +03:00
2022-12-31 15:01:19 +02:00
for ( auto subData : json [ " types " ] . Struct ( ) )
{
if ( ! subData . second [ " index " ] . isNull ( ) )
{
2024-02-13 14:34:16 +02:00
const std : : string & subMeta = subData . second [ " index " ] . getModScope ( ) ;
2023-01-11 00:48:51 +02:00
2024-01-04 23:52:01 +02:00
if ( subMeta = = " core " )
{
size_t subIndex = subData . second [ " index " ] . Integer ( ) ;
2024-08-17 01:19:42 +02:00
loadSubObject ( subData . second . getModScope ( ) , subData . first , subData . second , newObject . get ( ) , subIndex ) ;
2024-01-04 23:52:01 +02:00
}
else
{
logMod - > error ( " Object %s:%s.%s - attempt to load object with preset index! This option is reserved for built-in mod " , subMeta , name , subData . first ) ;
2024-08-17 01:19:42 +02:00
loadSubObject ( subData . second . getModScope ( ) , subData . first , subData . second , newObject . get ( ) ) ;
2024-01-04 23:52:01 +02:00
}
2022-12-31 15:01:19 +02:00
}
else
2024-08-17 01:19:42 +02:00
loadSubObject ( subData . second . getModScope ( ) , subData . first , subData . second , newObject . get ( ) ) ;
2022-12-31 15:01:19 +02:00
}
2023-11-21 18:32:07 +02:00
2024-08-17 01:19:42 +02:00
if ( newObject - > id = = MapObjectID : : MONOLITH_TWO_WAY )
generateExtraMonolithsForRMG ( newObject . get ( ) ) ;
2023-11-21 18:32:07 +02:00
2024-08-17 01:19:42 +02:00
return newObject ;
2014-06-05 19:57:43 +03:00
}
void CObjectClassesHandler : : loadObject ( std : : string scope , std : : string name , const JsonNode & data )
{
2024-08-17 01:19:42 +02:00
mapObjectTypes . push_back ( loadFromJson ( scope , data , name , mapObjectTypes . size ( ) ) ) ;
2023-12-16 13:42:12 +02:00
2025-02-14 16:23:37 +00:00
LIBRARY - > identifiersHandler - > registerObject ( scope , " object " , name , mapObjectTypes . back ( ) - > id ) ;
2014-06-05 19:57:43 +03:00
}
void CObjectClassesHandler : : loadObject ( std : : string scope , std : : string name , const JsonNode & data , size_t index )
{
2024-08-17 01:19:42 +02:00
assert ( mapObjectTypes . at ( index ) = = nullptr ) ; // ensure that this id was not loaded before
2023-12-16 13:42:12 +02:00
2024-08-17 01:19:42 +02:00
mapObjectTypes . at ( index ) = loadFromJson ( scope , data , name , index ) ;
2025-02-14 16:23:37 +00:00
LIBRARY - > identifiersHandler - > registerObject ( scope , " object " , name , mapObjectTypes . at ( index ) - > id ) ;
2014-06-05 19:57:43 +03:00
}
2023-11-02 18:45:46 +02:00
void CObjectClassesHandler : : loadSubObject ( const std : : string & identifier , JsonNode config , MapObjectID ID , MapObjectSubID subID )
2014-06-05 19:57:43 +03:00
{
2017-11-26 22:18:18 +01:00
config . setType ( JsonNode : : JsonType : : DATA_STRUCT ) ; // ensure that input is not NULL
2024-08-17 01:23:42 +02:00
2024-08-17 01:19:42 +02:00
assert ( mapObjectTypes . at ( ID . getNum ( ) ) ) ;
2022-12-31 15:01:19 +02:00
2024-08-17 01:19:42 +02:00
if ( subID . getNum ( ) > = mapObjectTypes . at ( ID . getNum ( ) ) - > objectTypeHandlers . size ( ) )
{
mapObjectTypes . at ( ID . getNum ( ) ) - > objectTypeHandlers . resize ( subID . getNum ( ) + 1 ) ;
}
2023-01-01 20:51:56 +02:00
2024-08-17 01:19:42 +02:00
JsonUtils : : inherit ( config , mapObjectTypes . at ( ID . getNum ( ) ) - > base ) ;
loadSubObject ( config . getModScope ( ) , identifier , config , mapObjectTypes . at ( ID . getNum ( ) ) . get ( ) , subID . getNum ( ) ) ;
2014-06-05 19:57:43 +03:00
}
2023-11-02 18:45:46 +02:00
void CObjectClassesHandler : : removeSubObject ( MapObjectID ID , MapObjectSubID subID )
2014-06-05 19:57:43 +03:00
{
2024-08-17 01:19:42 +02:00
assert ( mapObjectTypes . at ( ID . getNum ( ) ) ) ;
mapObjectTypes . at ( ID . getNum ( ) ) - > objectTypeHandlers . at ( subID . getNum ( ) ) = nullptr ;
2014-06-05 19:57:43 +03:00
}
2023-11-02 18:45:46 +02:00
TObjectTypeHandler CObjectClassesHandler : : getHandlerFor ( MapObjectID type , MapObjectSubID subtype ) const
2014-06-05 19:57:43 +03:00
{
2023-08-08 12:50:39 +03:00
try
{
2024-08-17 01:19:42 +02:00
if ( mapObjectTypes . at ( type . getNum ( ) ) = = nullptr )
return mapObjectTypes . front ( ) - > objectTypeHandlers . front ( ) ;
2023-12-11 17:25:19 +02:00
2023-12-21 09:58:39 +01:00
auto subID = subtype . getNum ( ) ;
2024-10-01 20:44:08 +02:00
if ( type = = Obj : : PRISON | | type = = Obj : : HERO_PLACEHOLDER | | type = = Obj : : SPELL_SCROLL )
2023-12-21 09:58:39 +01:00
subID = 0 ;
2024-08-17 01:19:42 +02:00
auto result = mapObjectTypes . at ( type . getNum ( ) ) - > objectTypeHandlers . at ( subID ) ;
2022-12-31 15:01:19 +02:00
2023-08-08 12:50:39 +03:00
if ( result ! = nullptr )
return result ;
}
catch ( std : : out_of_range & e )
{
2024-04-22 16:40:43 +03:00
// Leave catch block silently and handle error in block outside of try ... catch - all valid values should use 'return' in try block
2023-08-08 12:50:39 +03:00
}
2023-11-05 18:58:07 +02:00
std : : string errorString = " Failed to find object of type " + std : : to_string ( type . getNum ( ) ) + " :: " + std : : to_string ( subtype . getNum ( ) ) ;
2023-08-08 12:50:39 +03:00
logGlobal - > error ( errorString ) ;
2024-04-22 16:40:43 +03:00
throw std : : out_of_range ( errorString ) ;
2015-11-14 16:50:29 +03:00
}
2023-02-12 23:39:17 +03:00
TObjectTypeHandler CObjectClassesHandler : : getHandlerFor ( const std : : string & scope , const std : : string & type , const std : : string & subtype ) const
2015-11-14 16:50:29 +03:00
{
2025-02-14 16:23:37 +00:00
std : : optional < si32 > id = LIBRARY - > identifiers ( ) - > getIdentifier ( scope , " object " , type ) ;
2015-11-14 16:50:29 +03:00
if ( id )
{
2024-08-17 01:19:42 +02:00
const auto & object = mapObjectTypes . at ( id . value ( ) ) ;
2025-02-14 16:23:37 +00:00
std : : optional < si32 > subID = LIBRARY - > identifiers ( ) - > getIdentifier ( scope , object - > getJsonKey ( ) , subtype ) ;
2018-01-09 10:14:56 +03:00
2022-12-31 15:01:19 +02:00
if ( subID )
2024-08-17 01:19:42 +02:00
return object - > objectTypeHandlers . at ( subID . value ( ) ) ;
2014-06-05 19:57:43 +03:00
}
2022-12-31 15:01:19 +02:00
2022-12-03 20:52:27 +04:00
std : : string errorString = " Failed to find object of type " + type + " :: " + subtype ;
logGlobal - > error ( errorString ) ;
throw std : : runtime_error ( errorString ) ;
2014-06-05 19:57:43 +03:00
}
2018-07-22 19:12:11 +02:00
TObjectTypeHandler CObjectClassesHandler : : getHandlerFor ( CompoundMapObjectID compoundIdentifier ) const
2018-07-22 18:12:29 +02:00
{
return getHandlerFor ( compoundIdentifier . primaryID , compoundIdentifier . secondaryID ) ;
}
2024-08-21 20:16:41 +02:00
CompoundMapObjectID CObjectClassesHandler : : getCompoundIdentifier ( const std : : string & scope , const std : : string & type , const std : : string & subtype ) const
{
std : : optional < si32 > id ;
if ( scope . empty ( ) )
{
2025-02-14 16:23:37 +00:00
id = LIBRARY - > identifiers ( ) - > getIdentifier ( " object " , type ) ;
2024-08-21 20:16:41 +02:00
}
else
{
2025-02-14 16:23:37 +00:00
id = LIBRARY - > identifiers ( ) - > getIdentifier ( scope , " object " , type ) ;
2024-08-21 20:16:41 +02:00
}
if ( id )
{
2024-09-12 21:04:27 +02:00
if ( subtype . empty ( ) )
return CompoundMapObjectID ( id . value ( ) , 0 ) ;
2024-09-14 10:19:22 +02:00
const auto & object = mapObjectTypes . at ( id . value ( ) ) ;
2025-02-14 16:23:37 +00:00
std : : optional < si32 > subID = LIBRARY - > identifiers ( ) - > getIdentifier ( scope , object - > getJsonKey ( ) , subtype ) ;
2024-08-21 20:16:41 +02:00
if ( subID )
return CompoundMapObjectID ( id . value ( ) , subID . value ( ) ) ;
}
std : : string errorString = " Failed to get id for object of type " + type + " . " + subtype ;
logGlobal - > error ( errorString ) ;
throw std : : runtime_error ( errorString ) ;
}
CompoundMapObjectID CObjectClassesHandler : : getCompoundIdentifier ( const std : : string & objectName ) const
{
2024-09-12 21:04:27 +02:00
std : : string subtype = " object " ; //Default for objects with no subIds
std : : string type ;
2024-08-21 20:16:41 +02:00
2024-09-12 21:04:27 +02:00
auto scopeAndFullName = vstd : : splitStringToPair ( objectName , ' : ' ) ;
logGlobal - > debug ( " scopeAndFullName: %s, %s " , scopeAndFullName . first , scopeAndFullName . second ) ;
2024-08-21 20:16:41 +02:00
2024-09-12 21:04:27 +02:00
auto typeAndName = vstd : : splitStringToPair ( scopeAndFullName . second , ' . ' ) ;
logGlobal - > debug ( " typeAndName: %s, %s " , typeAndName . first , typeAndName . second ) ;
auto nameAndSubtype = vstd : : splitStringToPair ( typeAndName . second , ' . ' ) ;
logGlobal - > debug ( " nameAndSubtype: %s, %s " , nameAndSubtype . first , nameAndSubtype . second ) ;
if ( ! nameAndSubtype . first . empty ( ) )
2024-08-21 20:16:41 +02:00
{
2024-09-12 21:04:27 +02:00
type = nameAndSubtype . first ;
subtype = nameAndSubtype . second ;
2024-08-21 20:16:41 +02:00
}
else
{
2024-09-12 21:04:27 +02:00
type = typeAndName . second ;
2024-08-21 20:16:41 +02:00
}
2024-09-12 21:04:27 +02:00
2024-09-23 20:34:26 +02:00
return getCompoundIdentifier ( boost : : to_lower_copy ( scopeAndFullName . first ) , type , subtype ) ;
2024-08-21 20:16:41 +02:00
}
2023-11-02 18:45:46 +02:00
std : : set < MapObjectID > CObjectClassesHandler : : knownObjects ( ) const
2014-06-05 23:51:24 +03:00
{
2023-11-02 18:45:46 +02:00
std : : set < MapObjectID > ret ;
2014-06-05 23:51:24 +03:00
2024-08-17 01:19:42 +02:00
for ( auto & entry : mapObjectTypes )
2022-12-31 15:01:19 +02:00
if ( entry )
ret . insert ( entry - > id ) ;
2014-06-05 23:51:24 +03:00
return ret ;
}
2023-11-02 18:45:46 +02:00
std : : set < MapObjectSubID > CObjectClassesHandler : : knownSubObjects ( MapObjectID primaryID ) const
2014-06-05 23:51:24 +03:00
{
2023-11-02 18:45:46 +02:00
std : : set < MapObjectSubID > ret ;
2014-06-05 23:51:24 +03:00
2024-08-17 01:19:42 +02:00
if ( ! mapObjectTypes . at ( primaryID . getNum ( ) ) )
2023-03-14 21:48:39 +02:00
{
logGlobal - > error ( " Failed to find object %d " , primaryID ) ;
return ret ;
}
2024-08-17 01:19:42 +02:00
for ( const auto & entry : mapObjectTypes . at ( primaryID . getNum ( ) ) - > objectTypeHandlers )
2022-12-31 15:01:19 +02:00
if ( entry )
ret . insert ( entry - > subtype ) ;
2014-06-05 23:51:24 +03:00
return ret ;
}
2014-06-05 19:57:43 +03:00
void CObjectClassesHandler : : beforeValidate ( JsonNode & object )
{
for ( auto & entry : object [ " types " ] . Struct ( ) )
{
2023-01-10 16:24:42 +02:00
if ( object . Struct ( ) . count ( " subObjects " ) )
{
2023-02-12 23:39:17 +03:00
const auto & vector = object [ " subObjects " ] . Vector ( ) ;
2023-01-10 16:24:42 +02:00
2023-01-20 02:07:22 +02:00
if ( entry . second . Struct ( ) . count ( " index " ) )
{
size_t index = entry . second [ " index " ] . Integer ( ) ;
2023-01-10 16:24:42 +02:00
2023-01-20 02:07:22 +02:00
if ( index < vector . size ( ) )
JsonUtils : : inherit ( entry . second , vector [ index ] ) ;
}
2023-01-10 16:24:42 +02:00
}
2022-11-30 17:38:53 +02:00
JsonUtils : : inherit ( entry . second , object [ " base " ] ) ;
2014-06-05 19:57:43 +03:00
for ( auto & templ : entry . second [ " templates " ] . Struct ( ) )
2022-11-30 17:38:53 +02:00
JsonUtils : : inherit ( templ . second , entry . second [ " base " ] ) ;
2014-06-05 19:57:43 +03:00
}
2023-01-10 16:24:42 +02:00
object . Struct ( ) . erase ( " subObjects " ) ;
2014-06-05 19:57:43 +03:00
}
void CObjectClassesHandler : : afterLoadFinalization ( )
{
2024-08-17 01:19:42 +02:00
for ( auto & entry : mapObjectTypes )
2014-06-05 19:57:43 +03:00
{
2022-12-31 15:01:19 +02:00
if ( ! entry )
continue ;
2024-08-17 01:19:42 +02:00
for ( const auto & obj : entry - > objectTypeHandlers )
2014-06-05 19:57:43 +03:00
{
2023-01-01 20:51:56 +02:00
if ( ! obj )
continue ;
2022-12-31 15:01:19 +02:00
obj - > afterLoadFinalization ( ) ;
if ( obj - > getTemplates ( ) . empty ( ) )
2024-11-14 15:41:31 +00:00
logMod - > debug ( " No templates found for %s:%s " , entry - > getJsonKey ( ) , obj - > getJsonKey ( ) ) ;
2014-06-05 19:57:43 +03:00
}
}
2024-08-21 20:16:41 +02:00
for ( auto & entry : objectIdHandlers )
{
// Call function for each object id
entry . second ( entry . first ) ;
}
}
void CObjectClassesHandler : : resolveObjectCompoundId ( const std : : string & id , std : : function < void ( CompoundMapObjectID ) > callback )
{
auto compoundId = getCompoundIdentifier ( id ) ;
objectIdHandlers . push_back ( std : : make_pair ( compoundId , callback ) ) ;
2023-03-16 06:58:25 +01:00
}
2023-11-21 18:32:07 +02:00
void CObjectClassesHandler : : generateExtraMonolithsForRMG ( ObjectClass * container )
2023-03-16 06:58:25 +01:00
{
2016-01-09 09:03:40 +01:00
//duplicate existing two-way portals to make reserve for RMG
2024-08-17 01:19:42 +02:00
auto & portalVec = container - > objectTypeHandlers ;
2023-03-16 06:58:25 +01:00
//FIXME: Monoliths in this vector can be already not useful for every terrain
const size_t portalCount = portalVec . size ( ) ;
//Invalid portals will be skipped and portalVec size stays unchanged
for ( size_t i = portalCount ; portalVec . size ( ) < 100 ; + + i )
{
auto index = static_cast < si32 > ( i % portalCount ) ;
auto portal = portalVec [ index ] ;
auto templates = portal - > getTemplates ( ) ;
if ( templates . empty ( ) | | ! templates [ 0 ] - > canBePlacedAtAnyTerrain ( ) )
{
continue ; //Do not clone HoTA water-only portals or any others we can't use
}
2023-01-01 20:51:56 +02:00
2023-03-16 06:58:25 +01:00
//deep copy of noncopyable object :?
auto newPortal = std : : make_shared < CDefaultObjectTypeHandler < CGMonolith > > ( ) ;
newPortal - > rmgInfo = portal - > getRMGInfo ( ) ;
newPortal - > templates = portal - > getTemplates ( ) ;
newPortal - > sounds = portal - > getSounds ( ) ;
newPortal - > aiValue = portal - > getAiValue ( ) ;
newPortal - > battlefield = portal - > battlefield ; //getter is not initialized at this point
newPortal - > modScope = portal - > modScope ; //private
newPortal - > typeName = portal - > getTypeName ( ) ;
newPortal - > subTypeName = std : : string ( " monolith " ) + std : : to_string ( portalVec . size ( ) ) ;
newPortal - > type = portal - > getIndex ( ) ;
2023-12-11 08:28:53 +01:00
// Inconsintent original indexing: monolith1 has index 0
newPortal - > subtype = portalVec . size ( ) - 1 ; //indexes must be unique, they are returned as a set
2023-12-09 14:25:03 +01:00
newPortal - > blockVisit = portal - > blockVisit ;
newPortal - > removable = portal - > removable ;
2023-11-21 18:32:07 +02:00
2023-03-16 06:58:25 +01:00
portalVec . push_back ( newPortal ) ;
2023-11-21 18:32:07 +02:00
2023-12-09 14:25:03 +01:00
registerObject ( newPortal - > modScope , container - > getJsonKey ( ) , newPortal - > subTypeName , newPortal - > subtype ) ;
2023-03-16 06:58:25 +01:00
}
2014-06-05 19:57:43 +03:00
}
2023-11-02 18:45:46 +02:00
std : : string CObjectClassesHandler : : getObjectName ( MapObjectID type , MapObjectSubID subtype ) const
2014-06-30 00:16:45 +03:00
{
2023-02-12 23:39:17 +03:00
const auto handler = getHandlerFor ( type , subtype ) ;
2023-03-06 00:35:10 +02:00
if ( handler & & handler - > hasNameTextID ( ) )
2023-01-11 00:48:51 +02:00
return handler - > getNameTranslated ( ) ;
2023-12-11 17:25:19 +02:00
2024-08-17 01:19:42 +02:00
if ( mapObjectTypes . at ( type . getNum ( ) ) )
return mapObjectTypes . at ( type . getNum ( ) ) - > getNameTranslated ( ) ;
2023-12-11 17:25:19 +02:00
2024-08-17 01:19:42 +02:00
return mapObjectTypes . front ( ) - > getNameTranslated ( ) ;
2017-09-13 03:35:58 +03:00
}
2023-11-02 18:45:46 +02:00
SObjectSounds CObjectClassesHandler : : getObjectSounds ( MapObjectID type , MapObjectSubID subtype ) const
2017-09-13 03:35:58 +03:00
{
2023-01-11 00:48:51 +02:00
// TODO: these objects may have subID's that does not have associated handler:
// Prison: uses hero type as subID
// Hero: uses hero type as subID, but registers hero classes as subtypes
// Spell scroll: uses spell ID as subID
2023-01-10 01:30:01 +02:00
if ( type = = Obj : : PRISON | | type = = Obj : : HERO | | type = = Obj : : SPELL_SCROLL )
2023-01-01 20:51:56 +02:00
subtype = 0 ;
2024-08-17 01:19:42 +02:00
if ( mapObjectTypes . at ( type . getNum ( ) ) )
2023-12-11 17:25:19 +02:00
return getHandlerFor ( type , subtype ) - > getSounds ( ) ;
else
2024-08-17 01:19:42 +02:00
return mapObjectTypes . front ( ) - > objectTypeHandlers . front ( ) - > getSounds ( ) ;
2017-09-13 03:35:58 +03:00
}
2023-11-02 18:45:46 +02:00
std : : string CObjectClassesHandler : : getObjectHandlerName ( MapObjectID type ) const
2014-07-30 18:07:30 +02:00
{
2024-08-17 01:19:42 +02:00
if ( mapObjectTypes . at ( type . getNum ( ) ) )
return mapObjectTypes . at ( type . getNum ( ) ) - > handlerName ;
2023-12-11 17:25:19 +02:00
else
2024-08-17 01:19:42 +02:00
return mapObjectTypes . front ( ) - > handlerName ;
2014-07-30 18:07:30 +02:00
}
2023-11-02 18:45:46 +02:00
std : : string CObjectClassesHandler : : getJsonKey ( MapObjectID type ) const
2023-10-14 18:13:59 +03:00
{
2024-08-17 01:19:42 +02:00
if ( mapObjectTypes . at ( type . getNum ( ) ) ! = nullptr )
return mapObjectTypes . at ( type . getNum ( ) ) - > getJsonKey ( ) ;
2023-12-11 17:25:19 +02:00
logGlobal - > warn ( " Unknown object of type %d! " , type ) ;
2024-08-17 01:19:42 +02:00
return mapObjectTypes . front ( ) - > getJsonKey ( ) ;
2023-10-14 18:13:59 +03:00
}
2022-07-26 16:07:42 +03:00
VCMI_LIB_NAMESPACE_END