summaryrefslogtreecommitdiff
path: root/dmserializers/importvmf.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /dmserializers/importvmf.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'dmserializers/importvmf.cpp')
-rw-r--r--dmserializers/importvmf.cpp629
1 files changed, 629 insertions, 0 deletions
diff --git a/dmserializers/importvmf.cpp b/dmserializers/importvmf.cpp
new file mode 100644
index 0000000..7f57c6d
--- /dev/null
+++ b/dmserializers/importvmf.cpp
@@ -0,0 +1,629 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "importkeyvaluebase.h"
+#include "dmserializers.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel/dmelement.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "datamodel/dmattribute.h"
+
+
+//-----------------------------------------------------------------------------
+// Serialization class for VMF files (map files)
+//-----------------------------------------------------------------------------
+class CImportVMF : public CImportKeyValueBase
+{
+public:
+ virtual const char *GetName() const { return "vmf"; }
+ virtual const char *GetDescription() const { return "Valve Map File"; }
+ virtual int GetCurrentVersion() const { return 0; } // doesn't store a version
+
+ bool Serialize( CUtlBuffer &outBuf, CDmElement *pRoot );
+ CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues );
+
+private:
+ // Reads a single entity
+ bool UnserializeEntityKey( CDmAttribute *pEntities, KeyValues *pKeyValues );
+
+ // Reads entity editor keys
+ bool UnserializeEntityEditorKey( CDmAttribute *pEditor, KeyValues *pKeyValues );
+
+ // Reads keys that we currently do nothing with
+ bool UnserializeUnusedKeys( DmElementHandle_t hOther, KeyValues *pKeyValues );
+
+ // Writes out all everything other than entities
+ bool SerializeOther( CUtlBuffer &buf, CDmAttribute *pOther, const char **ppFilter = 0 );
+
+ // Writes out all entities
+ bool SerializeEntities( CUtlBuffer &buf, CDmAttribute *pEntities );
+
+ // Writes out a single attribute recursively
+ bool SerializeAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, bool bElementArrays );
+
+ // Writes entity editor keys
+ bool SerializeEntityEditorKey( CUtlBuffer &buf, DmElementHandle_t hEditor );
+
+ // Updates the max hammer id
+ void UpdateMaxHammerId( KeyValues *pKeyValue );
+
+ // Max id read from the file
+ int m_nMaxHammerId;
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CImportVMF s_ImportVMF;
+
+void InstallVMFImporter( IDataModel *pFactory )
+{
+ pFactory->AddSerializer( &s_ImportVMF );
+}
+
+
+//-----------------------------------------------------------------------------
+// Deals with poorly-named key values for the DME system
+//-----------------------------------------------------------------------------
+static const char *s_pKeyRemapNames[][2] =
+{
+ { "id", "__id" },
+ { "name", "__name" },
+ { "type", "__type" },
+ { NULL, NULL },
+};
+
+
+//-----------------------------------------------------------------------------
+// Gets remap name for unserialization/serailzation
+//-----------------------------------------------------------------------------
+static const char *GetRemapName( const char *pName, bool bSerialization )
+{
+ for ( int i = 0; s_pKeyRemapNames[i][0]; ++i )
+ {
+ if ( !Q_stricmp( pName, s_pKeyRemapNames[i][bSerialization] ) )
+ return s_pKeyRemapNames[i][1 - bSerialization];
+ }
+ return pName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a single attribute recursively
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, bool bElementArrays )
+{
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD | FATTRIB_DONTSAVE ) )
+ return true;
+
+ const char *pFieldName = GetRemapName( pAttribute->GetName(), true );
+ if ( !Q_stricmp( pFieldName, "editorType" ) )
+ return true;
+
+ if ( !IsArrayType( pAttribute->GetType() ) )
+ {
+ if ( !bElementArrays )
+ {
+ buf.Printf( "\"%s\" ", pFieldName );
+ if ( pAttribute->GetType() != AT_STRING )
+ {
+ buf.Printf( "\"" );
+ }
+ g_pDataModel->SetSerializationDelimiter( GetCStringCharConversion() );
+ pAttribute->Serialize( buf );
+ g_pDataModel->SetSerializationDelimiter( NULL );
+ if ( pAttribute->GetType() != AT_STRING )
+ {
+ buf.Printf( "\"" );
+ }
+ buf.Printf( "\n" );
+ }
+ }
+ else
+ {
+ if ( bElementArrays )
+ {
+ Assert( pAttribute->GetType() == AT_ELEMENT_ARRAY );
+ if ( !SerializeOther( buf, pAttribute ) )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all everything other than entities
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeOther( CUtlBuffer &buf, CDmAttribute *pOther, const char **ppFilter )
+{
+ CDmrElementArray<> array( pOther );
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pElement = array[i];
+ const char *pElementName = pElement->GetName();
+ if ( ppFilter )
+ {
+ int j;
+ for ( j = 0; ppFilter[j]; ++j )
+ {
+ if ( !Q_stricmp( pElementName, ppFilter[j] ) )
+ break;
+ }
+
+ if ( !ppFilter[j] )
+ continue;
+ }
+
+ int nLen = Q_strlen( pElementName ) + 1;
+ char *pTemp = (char*)_alloca( nLen );
+ Q_strncpy( pTemp, pElementName, nLen );
+ Q_strlower( pTemp );
+ buf.Printf( "%s\n", pTemp );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+
+ // Normal attributes first
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( !SerializeAttribute( buf, pAttribute, false ) )
+ return false;
+ }
+
+ // Subkeys later
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( !SerializeAttribute( buf, pAttribute, true ) )
+ return false;
+ }
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes entity editor keys
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeEntityEditorKey( CUtlBuffer &buf, DmElementHandle_t hEditor )
+{
+ CDmElement *pEditorElement = g_pDataModel->GetElement( hEditor );
+ if ( !pEditorElement )
+ return true;
+
+ buf.Printf( "editor\n" );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+
+ {
+ CDmAttribute *pAttribute = pEditorElement->GetAttribute( "color" );
+ if ( pAttribute )
+ {
+ Color c = pAttribute->GetValue<Color>();
+ buf.Printf( "\"color\" \"%d %d %d\"\n", c.r(), c.g(), c.b() );
+ }
+ }
+ PrintIntAttribute( pEditorElement, buf, "id" ); // FIXME - id is a DmObjectId_t!!! This should never print anything!
+ PrintStringAttribute( pEditorElement, buf, "comments" );
+ PrintBoolAttribute( pEditorElement, buf, "visgroupshown" );
+ PrintBoolAttribute( pEditorElement, buf, "visgroupautoshown" );
+
+ for ( CDmAttribute *pAttribute = pEditorElement->FirstAttribute(); pAttribute != NULL; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD | FATTRIB_DONTSAVE ) )
+ continue;
+
+ const char *pKeyName = pAttribute->GetName();
+ if ( Q_stricmp( pKeyName, "color" ) && Q_stricmp( pKeyName, "id" ) &&
+ Q_stricmp( pKeyName, "comments" ) && Q_stricmp( pKeyName, "visgroupshown" ) &&
+ Q_stricmp( pKeyName, "visgroupautoshown" ) )
+ {
+ PrintStringAttribute( pEditorElement, buf, pKeyName );
+ }
+ }
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out all entities
+//-----------------------------------------------------------------------------
+bool CImportVMF::SerializeEntities( CUtlBuffer &buf, CDmAttribute *pEntities )
+{
+ // FIXME: Make this serialize in the order in which it appears in the FGD
+ // to minimize diffs
+ CDmrElementArray<> array( pEntities );
+
+ int nCount = array.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pElement = array[i];
+ buf.Printf( "entity\n" );
+ buf.Printf( "{\n" );
+ buf.PushTab();
+ buf.Printf( "\"id\" \"%s\"\n", pElement->GetName() );
+
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ // Do 'editor' at the end to preserve ordering and not make terrible diffs
+ if ( !Q_stricmp( pAttribute->GetName(), "editor" ) )
+ continue;
+
+ if ( !SerializeAttribute( buf, pAttribute, false ) )
+ return false;
+ }
+
+ for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ // Do 'editor' at the end to preserve ordering and not make terrible diffs
+ if ( !Q_stricmp( pAttribute->GetName(), "editor" ) )
+ continue;
+
+ if ( !SerializeAttribute( buf, pAttribute, true ) )
+ return false;
+ }
+
+ // Do the 'editor'
+ CDmAttribute *pEditor = pElement->GetAttribute( "editor" );
+ if ( pEditor )
+ {
+ SerializeEntityEditorKey( buf, pEditor->GetValue<DmElementHandle_t>() );
+ }
+
+ buf.PopTab();
+ buf.Printf( "}\n" );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes out a new VMF file
+//-----------------------------------------------------------------------------
+bool CImportVMF::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ // This is done in this strange way (namely, serializing other twice) to minimize diffs
+ const char *pOtherFilter1[] =
+ {
+ "versioninfo", "visgroups", "viewsettings", "world", NULL
+ };
+
+ const char *pOtherFilter2[] =
+ {
+ "cameras", "cordon", "hidden", NULL
+ };
+
+ CDmAttribute *pOther = pRoot->GetAttribute( "other" );
+ if ( pOther && pOther->GetType() == AT_ELEMENT_ARRAY )
+ {
+ if ( !SerializeOther( buf, pOther, pOtherFilter1 ) )
+ return false;
+ }
+
+ // Serialize entities
+ CDmAttribute *pEntities = pRoot->GetAttribute( "entities" );
+ if ( pEntities && pEntities->GetType() == AT_ELEMENT_ARRAY )
+ {
+ if ( !SerializeEntities( buf, pEntities ) )
+ return false;
+ }
+
+ if ( pOther && pOther->GetType() == AT_ELEMENT_ARRAY )
+ {
+ if ( !SerializeOther( buf, pOther, pOtherFilter2 ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the max hammer id
+//-----------------------------------------------------------------------------
+void CImportVMF::UpdateMaxHammerId( KeyValues *pField )
+{
+ if ( !Q_stricmp( pField->GetName(), "id" ) )
+ {
+ int nId = atoi( pField->GetString() );
+ if ( nId > m_nMaxHammerId )
+ {
+ m_nMaxHammerId = nId;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads entity editor keys
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeEntityEditorKey( CDmAttribute *pEditorAttribute, KeyValues *pKeyValues )
+{
+ CDmElement *pEditor;
+ DmElementHandle_t hEditor = pEditorAttribute->GetValue<DmElementHandle_t>();
+ if ( hEditor == DMELEMENT_HANDLE_INVALID )
+ {
+ pEditor = CreateDmElement( "DmElement", "editor", NULL );;
+ if ( !pEditor )
+ return false;
+ hEditor = pEditor->GetHandle();
+ pEditorAttribute->SetValue( hEditor );
+ }
+ else
+ {
+ pEditor = g_pDataModel->GetElement( hEditor );
+ }
+
+ int r, g, b;
+ if ( sscanf( pKeyValues->GetString( "color", "" ), "%d %d %d", &r, &g, &b ) == 3 )
+ {
+ Color c( r, g, b, 255 );
+ if ( !pEditor->SetValue( "color", c ) )
+ return false;
+ }
+ KeyValues *pIdKey = pKeyValues->FindKey( "id" );
+ if ( pIdKey )
+ {
+ UpdateMaxHammerId( pIdKey );
+ }
+ AddIntAttribute( pEditor, pKeyValues, "id" );
+ AddStringAttribute( pEditor, pKeyValues, "comments" );
+ AddBoolAttribute( pEditor, pKeyValues, "visgroupshown" );
+ AddBoolAttribute( pEditor, pKeyValues, "visgroupautoshown" );
+
+ for ( KeyValues *pUserKey = pKeyValues->GetFirstValue(); pUserKey != NULL; pUserKey = pUserKey->GetNextValue() )
+ {
+ const char *pKeyName = pUserKey->GetName();
+ if ( Q_stricmp( pKeyName, "color" ) && Q_stricmp( pKeyName, "id" ) &&
+ Q_stricmp( pKeyName, "comments" ) && Q_stricmp( pKeyName, "visgroupshown" ) &&
+ Q_stricmp( pKeyName, "visgroupautoshown" ) )
+ {
+ AddStringAttribute( pEditor, pKeyValues, pKeyName );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads a single entity
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeEntityKey( CDmAttribute *pEntities, KeyValues *pKeyValues )
+{
+ CDmElement *pEntity = CreateDmElement( "DmeVMFEntity", pKeyValues->GetString( "id", "-1" ), NULL );
+ if ( !pEntity )
+ return false;
+
+ CDmrElementArray<> array( pEntities );
+ array.AddToTail( pEntity );
+
+ // Each act busy needs to have an editortype associated with it so it displays nicely in editors
+ pEntity->SetValue( "editorType", "vmfEntity" );
+
+ const char *pClassName = pKeyValues->GetString( "classname", NULL );
+ if ( !pClassName )
+ return false;
+
+ // Read the actual fields
+ for ( KeyValues *pField = pKeyValues->GetFirstValue(); pField != NULL; pField = pField->GetNextValue() )
+ {
+ // FIXME: Knowing the FGD here would be useful for type determination.
+ // Look up the field by name based on class name
+ // In the meantime, just use the keyvalues type?
+ char pFieldName[512];
+ Q_strncpy( pFieldName, pField->GetName(), sizeof(pFieldName) );
+ Q_strlower( pFieldName );
+
+ // Don't do id: it's used as the name
+ // Not to mention it's a protected name
+ if ( !Q_stricmp( pFieldName, "id" ) )
+ {
+ UpdateMaxHammerId( pField );
+ continue;
+ }
+
+ // Type, name, and editortype are protected names
+ Assert( Q_stricmp( pFieldName, "type" ) && Q_stricmp( pFieldName, "name" ) && Q_stricmp( pFieldName, "editortype" ) );
+
+ switch( pField->GetDataType() )
+ {
+ case KeyValues::TYPE_INT:
+ if ( !AddIntAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) )
+ return false;
+ break;
+
+ case KeyValues::TYPE_FLOAT:
+ if ( !AddFloatAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) )
+ return false;
+ break;
+
+ case KeyValues::TYPE_STRING:
+ {
+ const char* pString = pField->GetString();
+ if (!pString || !pString[0])
+ return false;
+
+ // Look for vectors
+ Vector4D v;
+ if ( sscanf( pString, "%f %f %f %f", &v.x, &v.y, &v.z, &v.w ) == 4 )
+ {
+ if ( !pEntity->SetValue( pFieldName, v ) )
+ return false;
+ CDmAttribute *pAttribute = pEntity->GetAttribute( pFieldName );
+ pAttribute->AddFlag( FATTRIB_USERDEFINED );
+ }
+ else if ( sscanf( pString, "%f %f %f", &v.x, &v.y, &v.z ) == 3 )
+ {
+ if ( !pEntity->SetValue( pFieldName, v.AsVector3D() ) )
+ {
+ QAngle ang( v.x, v.y, v.z );
+ if ( !pEntity->SetValue( pFieldName, ang ) )
+ return false;
+ }
+ CDmAttribute *pAttribute = pEntity->GetAttribute( pFieldName );
+ pAttribute->AddFlag( FATTRIB_USERDEFINED );
+ }
+ else
+ {
+ if ( !AddStringAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) )
+ return false;
+ }
+ }
+ break;
+ }
+ }
+
+ // Read the subkeys
+ CDmAttribute *pEditor = pEntity->AddAttribute( "editor", AT_ELEMENT );
+ CDmrElementArray<> otherKeys( pEntity->AddAttribute( "other", AT_ELEMENT_ARRAY ) );
+ for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ bool bOk = false;
+ if ( !Q_stricmp( pSubKey->GetName(), "editor" ) )
+ {
+ bOk = UnserializeEntityEditorKey( pEditor, pSubKey );
+ }
+ else
+ {
+ // We don't currently do anything with the other keys
+ CDmElement *pOther = CreateDmElement( "DmElement", pSubKey->GetName(), NULL );
+ otherKeys.AddToTail( pOther );
+ bOk = UnserializeUnusedKeys( pOther->GetHandle(), pSubKey );
+ }
+
+ if ( !bOk )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reads keys that we currently do nothing with
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeUnusedKeys( DmElementHandle_t hOther, KeyValues *pKeyValues )
+{
+ CDmElement *pOther = g_pDataModel->GetElement( hOther );
+
+ // Read the actual fields
+ for ( KeyValues *pField = pKeyValues->GetFirstValue(); pField != NULL; pField = pField->GetNextValue() )
+ {
+ UpdateMaxHammerId( pField );
+ const char *pFieldName = GetRemapName( pField->GetName(), false );
+ pOther->SetValue( pFieldName, pField->GetString() );
+ }
+
+ // Read the subkeys
+ CDmrElementArray<> subKeys( pOther->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) );
+ for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() )
+ {
+ CDmElement *pSubElement = CreateDmElement( "DmElement", pSubKey->GetName(), NULL );
+ subKeys.AddToTail( pSubElement );
+ if ( !UnserializeUnusedKeys( pSubElement->GetHandle(), pSubKey ) )
+ return false;
+ }
+ return true;
+}
+
+
+/*
+//-----------------------------------------------------------------------------
+// Reads the cordon data
+//-----------------------------------------------------------------------------
+bool CImportVMF::UnserializeCordonKey( IDmAttributeElement *pCordon, KeyValues *pKeyValues )
+{
+ DmElementHandle_t hCordon = pCordon->GetValue().Get();
+ if ( hCordon == DMELEMENT_HANDLE_INVALID )
+ {
+ hCordon = CreateDmElement( "DmElement", "cordon", NULL );
+ if ( hCordon == DMELEMENT_HANDLE_INVALID )
+ return false;
+ pCordon->SetValue( hCordon );
+ }
+
+ AddBoolAttribute( hCordon, pKeyValues, "active" );
+
+ Vector v;
+ if ( sscanf( pKeyValues->GetString( "mins", "" ), "(%f %f %f)", &v.x, &v.y, &v.z ) == 3 )
+ {
+ if ( !DmElementAddAttribute( hCordon, "mins", v ) )
+ return false;
+ }
+ if ( sscanf( pKeyValues->GetString( "maxs", "" ), "(%f %f %f)", &v.x, &v.y, &v.z ) == 3 )
+ {
+ if ( !DmElementAddAttribute( hCordon, "maxs", v ) )
+ return false;
+ }
+ return true;
+}
+*/
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for the unserialization
+//-----------------------------------------------------------------------------
+CDmElement* CImportVMF::UnserializeFromKeyValues( KeyValues *pKeyValues )
+{
+ m_nMaxHammerId = 0;
+
+ // Create the main element
+ CDmElement *pElement = CreateDmElement( "DmElement", "VMF", NULL );
+ if ( !pElement )
+ return NULL;
+
+ // Each vmf needs to have an editortype associated with it so it displays nicely in editors
+ pElement->SetValue( "editorType", "VMF" );
+
+ // The VMF is a series of keyvalue blocks; either
+ // 'entity', 'cameras', 'cordon', 'world', 'versioninfo', or 'viewsettings'
+ CDmAttribute *pEntityArray = pElement->AddAttribute( "entities", AT_ELEMENT_ARRAY );
+
+ // All main keys are root keys
+ CDmrElementArray<> otherKeys( pElement->AddAttribute( "other", AT_ELEMENT_ARRAY ) );
+ for ( ; pKeyValues != NULL; pKeyValues = pKeyValues->GetNextKey() )
+ {
+ bool bOk = false;
+ if ( !Q_stricmp( pKeyValues->GetName(), "entity" ) )
+ {
+ bOk = UnserializeEntityKey( pEntityArray, pKeyValues );
+ }
+ else
+ {
+ // We don't currently do anything with
+ CDmElement *pOther = CreateDmElement( "DmElement", pKeyValues->GetName(), NULL );
+ otherKeys.AddToTail( pOther );
+ bOk = UnserializeUnusedKeys( pOther->GetHandle(), pKeyValues );
+ }
+
+ if ( !bOk )
+ {
+ Warning( "Error importing VMF element %s\n", pKeyValues->GetName() );
+ return NULL;
+ }
+ }
+
+ // Resolve all element references recursively
+ RecursivelyResolveElement( pElement );
+
+ // Add the max id read in from the file to the root entity
+ pElement->SetValue( "maxHammerId", m_nMaxHammerId );
+
+ return pElement;
+}