diff options
Diffstat (limited to 'game/server/nav_merge.cpp')
| -rw-r--r-- | game/server/nav_merge.cpp | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/game/server/nav_merge.cpp b/game/server/nav_merge.cpp new file mode 100644 index 0000000..d62764a --- /dev/null +++ b/game/server/nav_merge.cpp @@ -0,0 +1,350 @@ +// nav_merge.cpp +// Save/merge for partial nav meshes +//========= Copyright Valve Corporation, All rights reserved. ============// + +#include "cbase.h" +#include "fmtstr.h" +#include "tier0/vprof.h" +#include "utldict.h" + +#include "nav_mesh.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +//-------------------------------------------------------------------------------------------------------- +void CNavArea::SaveToSelectedSet( KeyValues *areaKey ) const +{ + const char *placeName = TheNavMesh->PlaceToName( GetPlace() ); + areaKey->SetString( "Place", (placeName)?placeName:"" ); + + areaKey->SetInt( "Attributes", GetAttributes() ); +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavArea::RestoreFromSelectedSet( KeyValues *areaKey ) +{ + SetPlace( TheNavMesh->NameToPlace( areaKey->GetString( "Place" ) ) ); + + SetAttributes( areaKey->GetInt( "Attributes" ) ); +} + + +//-------------------------------------------------------------------------------------------------------- +class BuildSelectedSet +{ +public: + BuildSelectedSet( KeyValues *kv ) + { + m_kv = kv; + m_areaCount = 0; + } + + bool operator() ( CNavArea *area ) + { + CFmtStrN<32> name( "%d", area->GetID() ); + KeyValues *areaKey = m_kv->FindKey( name.Access(), true ); + if ( areaKey ) + { + ++m_areaCount; + + WriteCorner( area, areaKey, NORTH_WEST, "NorthWest" ); + WriteCorner( area, areaKey, NORTH_EAST, "NorthEast" ); + WriteCorner( area, areaKey, SOUTH_WEST, "SouthWest" ); + WriteCorner( area, areaKey, SOUTH_EAST, "SouthEast" ); + + WriteConnections( area, areaKey, NORTH, "North" ); + WriteConnections( area, areaKey, SOUTH, "South" ); + WriteConnections( area, areaKey, EAST, "East" ); + WriteConnections( area, areaKey, WEST, "West" ); + + area->SaveToSelectedSet( areaKey ); + } + return true; + } + + int Count( void ) const + { + return m_areaCount; + } + +private: + void WriteCorner( CNavArea *area, KeyValues *areaKey, NavCornerType corner, const char *cornerName ) + { + KeyValues *cornerKey = areaKey->FindKey( cornerName, true ); + if ( cornerKey ) + { + Vector pos = area->GetCorner( corner ); + cornerKey->SetFloat( "x", pos.x ); + cornerKey->SetFloat( "y", pos.y ); + cornerKey->SetFloat( "z", pos.z ); + } + } + + void WriteConnections( CNavArea *area, KeyValues *areaKey, NavDirType dir, const char *dirName ) + { + KeyValues *dirKey = areaKey->FindKey( dirName, true ); + if ( dirKey ) + { + for ( int i=0; i<area->GetAdjacentCount( dir ); ++i ) + { + CNavArea *other = area->GetAdjacentArea( dir, i ); + if ( other && TheNavMesh->IsInSelectedSet( other ) ) + { + CFmtStrN<32> name( "%d", i ); + dirKey->SetInt( name.Access(), other->GetID() ); + } + } + } + } + + int m_areaCount; + KeyValues *m_kv; +}; + + +//-------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavSaveSelected( const CCommand &args ) +{ + KeyValues *data = new KeyValues( "Selected Nav Areas" ); + data->SetInt( "version", 1 ); + + BuildSelectedSet setBuilder( data ); + TheNavMesh->ForAllSelectedAreas( setBuilder ); + + if ( !setBuilder.Count() ) + { + Msg( "Not saving empty selected set to disk.\n" ); + data->deleteThis(); + return; + } + + char fname[32]; + char path[MAX_PATH]; + if ( args.ArgC() == 2 ) + { + V_FileBase( args[0], fname, sizeof( fname ) ); + } + else + { + V_strncpy( fname, STRING( gpGlobals->mapname ), sizeof( fname ) ); + } + + int i; + for ( i=0; i<1000; ++i ) + { + V_snprintf( path, sizeof( path ), "maps/%s_selected_%4.4d.txt", fname, i ); + if ( !filesystem->FileExists( path ) ) + { + break; + } + } + + if ( i == 1000 ) + { + Msg( "Unable to find a filename to save the selected set to disk.\n" ); + data->deleteThis(); + return; + } + + if ( !data->SaveToFile( filesystem, path ) ) + { + Msg( "Unable to save the selected set to disk.\n" ); + } + + Msg( "Selected set saved to %s. Use 'nav_merge_mesh %s_selected_%4.4d' to merge it into another mesh.\n", path, fname, i ); + data->deleteThis(); +} + + +//-------------------------------------------------------------------------------------------------------- +CON_COMMAND_F( nav_save_selected, "Writes the selected set to disk for merging into another mesh via nav_merge_mesh.", FCVAR_GAMEDLL | FCVAR_CHEAT ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + TheNavMesh->CommandNavSaveSelected( args ); +} + + +//-------------------------------------------------------------------------------------------------------- +Vector ReadCorner( KeyValues *areaKey, const char *cornerName ) +{ + Vector pos( vec3_origin ); + KeyValues *cornerKey = areaKey->FindKey( cornerName, false ); + if ( cornerKey ) + { + pos.x = cornerKey->GetFloat( "x" ); + pos.y = cornerKey->GetFloat( "y" ); + pos.z = cornerKey->GetFloat( "z" ); + } + + return pos; +} + + +//-------------------------------------------------------------------------------------------------------- +void ReconnectMergedArea( CUtlDict< CNavArea *, int > &newAreas, KeyValues *areaKey, NavDirType dir, const char *dirName ) +{ + int index = newAreas.Find( areaKey->GetName() ); + if ( index == newAreas.InvalidIndex() ) + { + Assert( false ); + return; + } + + CNavArea *area = newAreas[index]; + + KeyValues *dirKey = areaKey->FindKey( dirName, true ); + if ( dirKey ) + { + KeyValues *connection = dirKey->GetFirstValue(); + while ( connection ) + { + const char *otherID = connection->GetString(); + int otherIndex = newAreas.Find( otherID ); + Assert( otherIndex != newAreas.InvalidIndex() ); + if ( otherIndex != newAreas.InvalidIndex() ) + { + CNavArea *other = newAreas[otherIndex]; + + area->ConnectTo( other, dir ); // only a 1-way connection. the other area will connect back to us. + } + + connection = connection->GetNextValue(); + } + } +} + + +//-------------------------------------------------------------------------------------------------------- +void CNavMesh::CommandNavMergeMesh( const CCommand &args ) +{ + if ( args.ArgC() != 2 ) + { + Msg( "Usage: nav_merge_mesh filename\n" ); + return; + } + + char fname[64]; + char path[MAX_PATH]; + V_FileBase( args[1], fname, sizeof( fname ) ); + V_snprintf( path, sizeof( path ), "maps/%s.txt", fname ); + + KeyValues *data = new KeyValues( "Nav Selected Set" ); + if ( !data->LoadFromFile( filesystem, path ) ) + { + Msg( "Unable to load %s.\n", path ); + } + else + { + // Loaded the data - plug it into the existing mesh! + + // First add the areas, and put them in the correct places. We can save off the new area ID + // at the same time. + CUtlDict< CNavArea *, int > newAreas; + CUtlVector< CNavArea * > areaVector; + KeyValues *areaKey = data->GetFirstSubKey(); + while ( areaKey ) + { + Vector northWest = ReadCorner( areaKey, "NorthWest" ); + Vector northEast = ReadCorner( areaKey, "NorthEast" ); + Vector southWest = ReadCorner( areaKey, "SouthWest" ); + Vector southEast = ReadCorner( areaKey, "SouthEast" ); + + CNavArea *newArea = TheNavMesh->CreateArea(); + if (newArea == NULL) + { + Warning( "nav_merge_mesh: Out of memory\n" ); + return; + } + + newArea->Build( northWest, northEast, southEast, southWest ); + TheNavAreas.AddToTail( newArea ); + TheNavMesh->AddNavArea( newArea ); + areaVector.AddToTail( newArea ); + + // save the new ID for connections + int index = newAreas.Find( areaKey->GetName() ); + Assert( index == newAreas.InvalidIndex() ); + if ( index == newAreas.InvalidIndex() ) + { + newAreas.Insert( areaKey->GetName(), newArea ); + } + + // Restore additional data + newArea->RestoreFromSelectedSet( areaKey ); + + areaKey = areaKey->GetNextKey(); + } + + // Go back and reconnect the new areas to each other + areaKey = data->GetFirstSubKey(); + while ( areaKey ) + { + ReconnectMergedArea( newAreas, areaKey, NORTH, "North" ); + ReconnectMergedArea( newAreas, areaKey, SOUTH, "South" ); + ReconnectMergedArea( newAreas, areaKey, EAST, "East" ); + ReconnectMergedArea( newAreas, areaKey, WEST, "West" ); + + areaKey = areaKey->GetNextKey(); + } + + // Connect selected areas with pre-existing areas + StitchAreaSet( &areaVector ); + } + + data->deleteThis(); +} + + +//-------------------------------------------------------------------------------------------------------- +int NavMeshMergeAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] ) +{ + char *commandName = "nav_merge_mesh"; + int numMatches = 0; + partial += Q_strlen( commandName ) + 1; + int partialLength = Q_strlen( partial ); + + FileFindHandle_t findHandle; + char txtFilenameNoExtension[ MAX_PATH ]; + const char *txtFilename = filesystem->FindFirstEx( "maps/*_selected_*.txt", "MOD", &findHandle ); + while ( txtFilename ) + { + Q_FileBase( txtFilename, txtFilenameNoExtension, sizeof( txtFilenameNoExtension ) ); + if ( !Q_strnicmp( txtFilenameNoExtension, partial, partialLength ) && V_stristr( txtFilenameNoExtension, "_selected_" ) ) + { + // Add the place name to the autocomplete array + Q_snprintf( commands[ numMatches++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", commandName, txtFilenameNoExtension ); + + // Make sure we don't try to return too many place names + if ( numMatches == COMMAND_COMPLETION_MAXITEMS ) + return numMatches; + } + + txtFilename = filesystem->FindNext( findHandle ); + } + filesystem->FindClose( findHandle ); + + return numMatches; +} + + +//-------------------------------------------------------------------------------------------------------- +CON_COMMAND_F_COMPLETION( nav_merge_mesh, "Merges a saved selected set into the current mesh.", FCVAR_GAMEDLL | FCVAR_CHEAT, NavMeshMergeAutocomplete ) +{ + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + + TheNavMesh->CommandNavMergeMesh( args ); +} + + +//-------------------------------------------------------------------------------------------------------- + + + + +//-------------------------------------------------------------------------------------------------------- |