Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
9.6 KiB

  1. // nav_merge.cpp
  2. // Save/merge for partial nav meshes
  3. // Copyright 2005 Turtle Rock Studios, Inc.
  4. #include "cbase.h"
  5. #include "fmtstr.h"
  6. #include "tier0/vprof.h"
  7. #include "utldict.h"
  8. #include "nav_mesh.h"
  9. // memdbgon must be the last include file in a .cpp file!!!
  10. #include "tier0/memdbgon.h"
  11. //--------------------------------------------------------------------------------------------------------
  12. void CNavArea::SaveToSelectedSet( KeyValues *areaKey ) const
  13. {
  14. const char *placeName = TheNavMesh->PlaceToName( GetPlace() );
  15. areaKey->SetString( "Place", (placeName)?placeName:"" );
  16. areaKey->SetInt( "Attributes", GetAttributes() );
  17. }
  18. //--------------------------------------------------------------------------------------------------------
  19. void CNavArea::RestoreFromSelectedSet( KeyValues *areaKey )
  20. {
  21. SetPlace( TheNavMesh->NameToPlace( areaKey->GetString( "Place" ) ) );
  22. SetAttributes( areaKey->GetInt( "Attributes" ) );
  23. }
  24. //--------------------------------------------------------------------------------------------------------
  25. class BuildSelectedSet
  26. {
  27. public:
  28. BuildSelectedSet( KeyValues *kv )
  29. {
  30. m_kv = kv;
  31. m_areaCount = 0;
  32. }
  33. bool operator() ( CNavArea *area )
  34. {
  35. CFmtStrN<32> name( "%d", area->GetID() );
  36. KeyValues *areaKey = m_kv->FindKey( name.Access(), true );
  37. if ( areaKey )
  38. {
  39. ++m_areaCount;
  40. WriteCorner( area, areaKey, NORTH_WEST, "NorthWest" );
  41. WriteCorner( area, areaKey, NORTH_EAST, "NorthEast" );
  42. WriteCorner( area, areaKey, SOUTH_WEST, "SouthWest" );
  43. WriteCorner( area, areaKey, SOUTH_EAST, "SouthEast" );
  44. WriteConnections( area, areaKey, NORTH, "North" );
  45. WriteConnections( area, areaKey, SOUTH, "South" );
  46. WriteConnections( area, areaKey, EAST, "East" );
  47. WriteConnections( area, areaKey, WEST, "West" );
  48. area->SaveToSelectedSet( areaKey );
  49. }
  50. return true;
  51. }
  52. int Count( void ) const
  53. {
  54. return m_areaCount;
  55. }
  56. private:
  57. void WriteCorner( CNavArea *area, KeyValues *areaKey, NavCornerType corner, const char *cornerName )
  58. {
  59. KeyValues *cornerKey = areaKey->FindKey( cornerName, true );
  60. if ( cornerKey )
  61. {
  62. Vector pos = area->GetCorner( corner );
  63. cornerKey->SetFloat( "x", pos.x );
  64. cornerKey->SetFloat( "y", pos.y );
  65. cornerKey->SetFloat( "z", pos.z );
  66. }
  67. }
  68. void WriteConnections( CNavArea *area, KeyValues *areaKey, NavDirType dir, const char *dirName )
  69. {
  70. KeyValues *dirKey = areaKey->FindKey( dirName, true );
  71. if ( dirKey )
  72. {
  73. for ( int i=0; i<area->GetAdjacentCount( dir ); ++i )
  74. {
  75. CNavArea *other = area->GetAdjacentArea( dir, i );
  76. if ( other && TheNavMesh->IsInSelectedSet( other ) )
  77. {
  78. CFmtStrN<32> name( "%d", i );
  79. dirKey->SetInt( name.Access(), other->GetID() );
  80. }
  81. }
  82. }
  83. }
  84. int m_areaCount;
  85. KeyValues *m_kv;
  86. };
  87. //--------------------------------------------------------------------------------------------------------
  88. void CNavMesh::CommandNavSaveSelected( const CCommand &args )
  89. {
  90. KeyValues *data = new KeyValues( "Selected Nav Areas" );
  91. data->SetInt( "version", 1 );
  92. BuildSelectedSet setBuilder( data );
  93. TheNavMesh->ForAllSelectedAreas( setBuilder );
  94. if ( !setBuilder.Count() )
  95. {
  96. Msg( "Not saving empty selected set to disk.\n" );
  97. data->deleteThis();
  98. return;
  99. }
  100. char fname[32];
  101. char path[MAX_PATH];
  102. if ( args.ArgC() == 2 )
  103. {
  104. V_FileBase( args[0], fname, sizeof( fname ) );
  105. }
  106. else
  107. {
  108. V_strncpy( fname, STRING( gpGlobals->mapname ), sizeof( fname ) );
  109. }
  110. int i;
  111. for ( i=0; i<1000; ++i )
  112. {
  113. V_snprintf( path, sizeof( path ), "maps/%s_selected_%4.4d.txt", fname, i );
  114. if ( !filesystem->FileExists( path ) )
  115. {
  116. break;
  117. }
  118. }
  119. if ( i == 1000 )
  120. {
  121. Msg( "Unable to find a filename to save the selected set to disk.\n" );
  122. data->deleteThis();
  123. return;
  124. }
  125. if ( !data->SaveToFile( filesystem, path ) )
  126. {
  127. Msg( "Unable to save the selected set to disk.\n" );
  128. }
  129. Msg( "Selected set saved to %s. Use 'nav_merge_mesh %s_selected_%4.4d' to merge it into another mesh.\n", path, fname, i );
  130. data->deleteThis();
  131. }
  132. //--------------------------------------------------------------------------------------------------------
  133. 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 )
  134. {
  135. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  136. return;
  137. TheNavMesh->CommandNavSaveSelected( args );
  138. }
  139. //--------------------------------------------------------------------------------------------------------
  140. Vector ReadCorner( KeyValues *areaKey, const char *cornerName )
  141. {
  142. Vector pos( vec3_origin );
  143. KeyValues *cornerKey = areaKey->FindKey( cornerName, false );
  144. if ( cornerKey )
  145. {
  146. pos.x = cornerKey->GetFloat( "x" );
  147. pos.y = cornerKey->GetFloat( "y" );
  148. pos.z = cornerKey->GetFloat( "z" );
  149. }
  150. return pos;
  151. }
  152. //--------------------------------------------------------------------------------------------------------
  153. void ReconnectMergedArea( CUtlDict< CNavArea *, int > &newAreas, KeyValues *areaKey, NavDirType dir, const char *dirName )
  154. {
  155. int index = newAreas.Find( areaKey->GetName() );
  156. if ( index == newAreas.InvalidIndex() )
  157. {
  158. Assert( false );
  159. return;
  160. }
  161. CNavArea *area = newAreas[index];
  162. KeyValues *dirKey = areaKey->FindKey( dirName, true );
  163. if ( dirKey )
  164. {
  165. KeyValues *connection = dirKey->GetFirstValue();
  166. while ( connection )
  167. {
  168. const char *otherID = connection->GetString();
  169. int otherIndex = newAreas.Find( otherID );
  170. Assert( otherIndex != newAreas.InvalidIndex() );
  171. if ( otherIndex != newAreas.InvalidIndex() )
  172. {
  173. CNavArea *other = newAreas[otherIndex];
  174. area->ConnectTo( other, dir ); // only a 1-way connection. the other area will connect back to us.
  175. }
  176. connection = connection->GetNextValue();
  177. }
  178. }
  179. }
  180. //--------------------------------------------------------------------------------------------------------
  181. void CNavMesh::CommandNavMergeMesh( const CCommand &args )
  182. {
  183. if ( args.ArgC() != 2 )
  184. {
  185. Msg( "Usage: nav_merge_mesh filename\n" );
  186. return;
  187. }
  188. char fname[64];
  189. char path[MAX_PATH];
  190. V_FileBase( args[1], fname, sizeof( fname ) );
  191. V_snprintf( path, sizeof( path ), "maps/%s.txt", fname );
  192. KeyValues *data = new KeyValues( "Nav Selected Set" );
  193. if ( !data->LoadFromFile( filesystem, path ) )
  194. {
  195. Msg( "Unable to load %s.\n", path );
  196. }
  197. else
  198. {
  199. // Loaded the data - plug it into the existing mesh!
  200. // First add the areas, and put them in the correct places. We can save off the new area ID
  201. // at the same time.
  202. CUtlDict< CNavArea *, int > newAreas;
  203. CUtlVector< CNavArea * > areaVector;
  204. KeyValues *areaKey = data->GetFirstSubKey();
  205. while ( areaKey )
  206. {
  207. Vector northWest = ReadCorner( areaKey, "NorthWest" );
  208. Vector northEast = ReadCorner( areaKey, "NorthEast" );
  209. Vector southWest = ReadCorner( areaKey, "SouthWest" );
  210. Vector southEast = ReadCorner( areaKey, "SouthEast" );
  211. CNavArea *newArea = TheNavMesh->CreateArea();
  212. if (newArea == NULL)
  213. {
  214. Warning( "nav_merge_mesh: Out of memory\n" );
  215. return;
  216. }
  217. newArea->Build( northWest, northEast, southEast, southWest );
  218. TheNavAreas.AddToTail( newArea );
  219. TheNavMesh->AddNavArea( newArea );
  220. areaVector.AddToTail( newArea );
  221. // save the new ID for connections
  222. int index = newAreas.Find( areaKey->GetName() );
  223. Assert( index == newAreas.InvalidIndex() );
  224. if ( index == newAreas.InvalidIndex() )
  225. {
  226. newAreas.Insert( areaKey->GetName(), newArea );
  227. }
  228. // Restore additional data
  229. newArea->RestoreFromSelectedSet( areaKey );
  230. areaKey = areaKey->GetNextKey();
  231. }
  232. // Go back and reconnect the new areas to each other
  233. areaKey = data->GetFirstSubKey();
  234. while ( areaKey )
  235. {
  236. ReconnectMergedArea( newAreas, areaKey, NORTH, "North" );
  237. ReconnectMergedArea( newAreas, areaKey, SOUTH, "South" );
  238. ReconnectMergedArea( newAreas, areaKey, EAST, "East" );
  239. ReconnectMergedArea( newAreas, areaKey, WEST, "West" );
  240. areaKey = areaKey->GetNextKey();
  241. }
  242. // Connect selected areas with pre-existing areas
  243. StitchAreaSet( &areaVector );
  244. }
  245. data->deleteThis();
  246. }
  247. //--------------------------------------------------------------------------------------------------------
  248. int NavMeshMergeAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
  249. {
  250. char *commandName = "nav_merge_mesh";
  251. int numMatches = 0;
  252. partial += Q_strlen( commandName ) + 1;
  253. int partialLength = Q_strlen( partial );
  254. FileFindHandle_t findHandle;
  255. char txtFilenameNoExtension[ MAX_PATH ];
  256. const char *txtFilename = filesystem->FindFirstEx( "maps/*_selected_*.txt", "MOD", &findHandle );
  257. while ( txtFilename )
  258. {
  259. Q_FileBase( txtFilename, txtFilenameNoExtension, sizeof( txtFilenameNoExtension ) );
  260. if ( !Q_strnicmp( txtFilenameNoExtension, partial, partialLength ) && V_stristr( txtFilenameNoExtension, "_selected_" ) )
  261. {
  262. // Add the place name to the autocomplete array
  263. Q_snprintf( commands[ numMatches++ ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s", commandName, txtFilenameNoExtension );
  264. // Make sure we don't try to return too many place names
  265. if ( numMatches == COMMAND_COMPLETION_MAXITEMS )
  266. return numMatches;
  267. }
  268. txtFilename = filesystem->FindNext( findHandle );
  269. }
  270. filesystem->FindClose( findHandle );
  271. return numMatches;
  272. }
  273. //--------------------------------------------------------------------------------------------------------
  274. CON_COMMAND_F_COMPLETION( nav_merge_mesh, "Merges a saved selected set into the current mesh.", FCVAR_GAMEDLL | FCVAR_CHEAT, NavMeshMergeAutocomplete )
  275. {
  276. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  277. return;
  278. TheNavMesh->CommandNavMergeMesh( args );
  279. }
  280. //--------------------------------------------------------------------------------------------------------
  281. //--------------------------------------------------------------------------------------------------------