Team Fortress 2 Source Code as on 22/4/2020
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.

1709 lines
42 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_file.cpp
  9. // Reading and writing nav files
  10. // Author: Michael S. Booth ([email protected]), January-September 2003
  11. #include "cbase.h"
  12. #include "nav_mesh.h"
  13. #include "gamerules.h"
  14. #include "datacache/imdlcache.h"
  15. #include "tier2/tier2.h"
  16. #include "tier2/p4helpers.h"
  17. #include "tier2/fileutils.h"
  18. #ifdef TERROR
  19. #include "func_elevator.h"
  20. #endif
  21. #include "tier1/lzmaDecoder.h"
  22. #ifdef CSTRIKE_DLL
  23. #include "cs_shareddefs.h"
  24. #include "nav_pathfind.h"
  25. #include "cs_nav_area.h"
  26. #endif
  27. // NOTE: This has to be the last file included!
  28. #include "tier0/memdbgon.h"
  29. //--------------------------------------------------------------------------------------------------------------
  30. /// The current version of the nav file format
  31. /// IMPORTANT: If this version changes, the swap function in makegamedata
  32. /// must be updated to match. If not, this will break the Xbox 360.
  33. // TODO: Was changed from 15, update when latest 360 code is integrated (MSB 5/5/09)
  34. const int NavCurrentVersion = 16;
  35. //--------------------------------------------------------------------------------------------------------------
  36. //
  37. // The 'place directory' is used to save and load places from
  38. // nav files in a size-efficient manner that also allows for the
  39. // order of the place ID's to change without invalidating the
  40. // nav files.
  41. //
  42. // The place directory is stored in the nav file as a list of
  43. // place name strings. Each nav area then contains an index
  44. // into that directory, or zero if no place has been assigned to
  45. // that area.
  46. //
  47. PlaceDirectory::PlaceDirectory( void )
  48. {
  49. Reset();
  50. }
  51. void PlaceDirectory::Reset( void )
  52. {
  53. m_directory.RemoveAll();
  54. m_hasUnnamedAreas = false;
  55. }
  56. /// return true if this place is already in the directory
  57. bool PlaceDirectory::IsKnown( Place place ) const
  58. {
  59. return m_directory.HasElement( place );
  60. }
  61. /// return the directory index corresponding to this Place (0 = no entry)
  62. PlaceDirectory::IndexType PlaceDirectory::GetIndex( Place place ) const
  63. {
  64. if (place == UNDEFINED_PLACE)
  65. return 0;
  66. int i = m_directory.Find( place );
  67. if (i < 0)
  68. {
  69. AssertMsg( false, "PlaceDirectory::GetIndex failure" );
  70. return 0;
  71. }
  72. return (IndexType)(i+1);
  73. }
  74. /// add the place to the directory if not already known
  75. void PlaceDirectory::AddPlace( Place place )
  76. {
  77. if (place == UNDEFINED_PLACE)
  78. {
  79. m_hasUnnamedAreas = true;
  80. return;
  81. }
  82. Assert( place < 1000 );
  83. if (IsKnown( place ))
  84. return;
  85. m_directory.AddToTail( place );
  86. }
  87. /// given an index, return the Place
  88. Place PlaceDirectory::IndexToPlace( IndexType entry ) const
  89. {
  90. if (entry == 0)
  91. return UNDEFINED_PLACE;
  92. int i = entry-1;
  93. if (i >= m_directory.Count())
  94. {
  95. AssertMsg( false, "PlaceDirectory::IndexToPlace: Invalid entry" );
  96. return UNDEFINED_PLACE;
  97. }
  98. return m_directory[ i ];
  99. }
  100. /// store the directory
  101. void PlaceDirectory::Save( CUtlBuffer &fileBuffer )
  102. {
  103. // store number of entries in directory
  104. IndexType count = (IndexType)m_directory.Count();
  105. fileBuffer.PutUnsignedShort( count );
  106. // store entries
  107. for( int i=0; i<m_directory.Count(); ++i )
  108. {
  109. const char *placeName = TheNavMesh->PlaceToName( m_directory[i] );
  110. // store string length followed by string itself
  111. unsigned short len = (unsigned short)(strlen( placeName ) + 1);
  112. fileBuffer.PutUnsignedShort( len );
  113. fileBuffer.Put( placeName, len );
  114. }
  115. fileBuffer.PutUnsignedChar( m_hasUnnamedAreas );
  116. }
  117. /// load the directory
  118. void PlaceDirectory::Load( CUtlBuffer &fileBuffer, int version )
  119. {
  120. // read number of entries
  121. IndexType count = fileBuffer.GetUnsignedShort();
  122. m_directory.RemoveAll();
  123. // read each entry
  124. char placeName[256];
  125. unsigned short len;
  126. for( int i=0; i<count; ++i )
  127. {
  128. len = fileBuffer.GetUnsignedShort();
  129. fileBuffer.Get( placeName, MIN( sizeof( placeName ), len ) );
  130. Place place = TheNavMesh->NameToPlace( placeName );
  131. if (place == UNDEFINED_PLACE)
  132. {
  133. Warning( "Warning: NavMesh place %s is undefined?\n", placeName );
  134. }
  135. AddPlace( place );
  136. }
  137. if ( version > 11 )
  138. {
  139. m_hasUnnamedAreas = fileBuffer.GetUnsignedChar() != 0;
  140. }
  141. }
  142. PlaceDirectory placeDirectory;
  143. #if defined( _X360 )
  144. #define FORMAT_BSPFILE "maps\\%s.360.bsp"
  145. #define FORMAT_NAVFILE "maps\\%s.360.nav"
  146. #else
  147. #define FORMAT_BSPFILE "maps\\%s.bsp"
  148. #define FORMAT_NAVFILE "maps\\%s.nav"
  149. #define PATH_NAVFILE_EMBEDDED "maps\\embed.nav"
  150. #endif
  151. //--------------------------------------------------------------------------------------------------------------
  152. /**
  153. * Replace extension with "bsp"
  154. */
  155. char *GetBspFilename( const char *navFilename )
  156. {
  157. static char bspFilename[256];
  158. Q_snprintf( bspFilename, sizeof( bspFilename ), FORMAT_BSPFILE, STRING( gpGlobals->mapname ) );
  159. int len = strlen( bspFilename );
  160. if (len < 3)
  161. return NULL;
  162. bspFilename[ len-3 ] = 'b';
  163. bspFilename[ len-2 ] = 's';
  164. bspFilename[ len-1 ] = 'p';
  165. return bspFilename;
  166. }
  167. //--------------------------------------------------------------------------------------------------------------
  168. /**
  169. * Save a navigation area to the opened binary stream
  170. */
  171. void CNavArea::Save( CUtlBuffer &fileBuffer, unsigned int version ) const
  172. {
  173. // save ID
  174. fileBuffer.PutUnsignedInt( m_id );
  175. // save attribute flags
  176. fileBuffer.PutInt( m_attributeFlags );
  177. // save extent of area
  178. fileBuffer.Put( &m_nwCorner, 3*sizeof(float) );
  179. fileBuffer.Put( &m_seCorner, 3*sizeof(float) );
  180. // save heights of implicit corners
  181. fileBuffer.PutFloat( m_neZ );
  182. fileBuffer.PutFloat( m_swZ );
  183. // save connections to adjacent areas
  184. // in the enum order NORTH, EAST, SOUTH, WEST
  185. for( int d=0; d<NUM_DIRECTIONS; d++ )
  186. {
  187. // save number of connections for this direction
  188. unsigned int count = m_connect[d].Count();
  189. fileBuffer.PutUnsignedInt( count );
  190. FOR_EACH_VEC( m_connect[d], it )
  191. {
  192. NavConnect connect = m_connect[d][ it ];
  193. fileBuffer.PutUnsignedInt( connect.area->m_id );
  194. }
  195. }
  196. //
  197. // Store hiding spots for this area
  198. //
  199. unsigned char count;
  200. if (m_hidingSpots.Count() > 255)
  201. {
  202. count = 255;
  203. Warning( "Warning: NavArea #%d: Truncated hiding spot list to 255\n", m_id );
  204. }
  205. else
  206. {
  207. count = (unsigned char)m_hidingSpots.Count();
  208. }
  209. fileBuffer.PutUnsignedChar( count );
  210. // store HidingSpot objects
  211. unsigned int saveCount = 0;
  212. FOR_EACH_VEC( m_hidingSpots, hit )
  213. {
  214. HidingSpot *spot = m_hidingSpots[ hit ];
  215. spot->Save( fileBuffer, version );
  216. // overflow check
  217. if (++saveCount == count)
  218. break;
  219. }
  220. //
  221. // Save encounter spots for this area
  222. //
  223. {
  224. // save number of encounter paths for this area
  225. unsigned int count = m_spotEncounters.Count();
  226. fileBuffer.PutUnsignedInt( count );
  227. SpotEncounter *e;
  228. FOR_EACH_VEC( m_spotEncounters, it )
  229. {
  230. e = m_spotEncounters[ it ];
  231. if (e->from.area)
  232. fileBuffer.PutUnsignedInt( e->from.area->m_id );
  233. else
  234. fileBuffer.PutUnsignedInt( 0 );
  235. unsigned char dir = (unsigned char)e->fromDir;
  236. fileBuffer.PutUnsignedChar( dir );
  237. if (e->to.area)
  238. fileBuffer.PutUnsignedInt( e->to.area->m_id );
  239. else
  240. fileBuffer.PutUnsignedInt( 0 );
  241. dir = (unsigned char)e->toDir;
  242. fileBuffer.PutUnsignedChar( dir );
  243. // write list of spots along this path
  244. unsigned char spotCount;
  245. if (e->spots.Count() > 255)
  246. {
  247. spotCount = 255;
  248. Warning( "Warning: NavArea #%d: Truncated encounter spot list to 255\n", m_id );
  249. }
  250. else
  251. {
  252. spotCount = (unsigned char)e->spots.Count();
  253. }
  254. fileBuffer.PutUnsignedChar( spotCount );
  255. saveCount = 0;
  256. FOR_EACH_VEC( e->spots, sit )
  257. {
  258. SpotOrder *order = &e->spots[ sit ];
  259. // order->spot may be NULL if we've loaded a nav mesh that has been edited but not re-analyzed
  260. unsigned int id = (order->spot) ? order->spot->GetID() : 0;
  261. fileBuffer.PutUnsignedInt( id );
  262. unsigned char t = (unsigned char)(255 * order->t);
  263. fileBuffer.PutUnsignedChar( t );
  264. // overflow check
  265. if (++saveCount == spotCount)
  266. break;
  267. }
  268. }
  269. }
  270. // store place dictionary entry
  271. PlaceDirectory::IndexType entry = placeDirectory.GetIndex( GetPlace() );
  272. fileBuffer.Put( &entry, sizeof(entry) );
  273. // write out ladder info
  274. int i;
  275. for ( i=0; i<CNavLadder::NUM_LADDER_DIRECTIONS; ++i )
  276. {
  277. // save number of encounter paths for this area
  278. unsigned int count = m_ladder[i].Count();
  279. fileBuffer.PutUnsignedInt( count );
  280. NavLadderConnect ladder;
  281. FOR_EACH_VEC( m_ladder[i], it )
  282. {
  283. ladder = m_ladder[i][it];
  284. unsigned int id = ladder.ladder->GetID();
  285. fileBuffer.PutUnsignedInt( id );
  286. }
  287. }
  288. // save earliest occupy times
  289. for( i=0; i<MAX_NAV_TEAMS; ++i )
  290. {
  291. // no spot in the map should take longer than this to reach
  292. fileBuffer.Put( &m_earliestOccupyTime[i], sizeof(m_earliestOccupyTime[i]) );
  293. }
  294. // save light intensity
  295. for ( i=0; i<NUM_CORNERS; ++i )
  296. {
  297. fileBuffer.PutFloat( m_lightIntensity[i] );
  298. }
  299. // save visible area set
  300. unsigned int visibleAreaCount = m_potentiallyVisibleAreas.Count();
  301. fileBuffer.PutUnsignedInt( visibleAreaCount );
  302. for ( int vit=0; vit<m_potentiallyVisibleAreas.Count(); ++vit )
  303. {
  304. CNavArea *area = m_potentiallyVisibleAreas[ vit ].area;
  305. unsigned int id = area ? area->GetID() : 0;
  306. fileBuffer.PutUnsignedInt( id );
  307. fileBuffer.PutUnsignedChar( m_potentiallyVisibleAreas[ vit ].attributes );
  308. }
  309. // store area we inherit visibility from
  310. unsigned int id = ( m_inheritVisibilityFrom.area ) ? m_inheritVisibilityFrom.area->GetID() : 0;
  311. fileBuffer.PutUnsignedInt( id );
  312. }
  313. //--------------------------------------------------------------------------------------------------------------
  314. /**
  315. * Load a navigation area from the file
  316. */
  317. NavErrorType CNavArea::Load( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion )
  318. {
  319. // load ID
  320. m_id = fileBuffer.GetUnsignedInt();
  321. // update nextID to avoid collisions
  322. if (m_id >= m_nextID)
  323. m_nextID = m_id+1;
  324. // load attribute flags
  325. if ( version <= 8 )
  326. {
  327. m_attributeFlags = fileBuffer.GetUnsignedChar();
  328. }
  329. else if ( version < 13 )
  330. {
  331. m_attributeFlags = fileBuffer.GetUnsignedShort();
  332. }
  333. else
  334. {
  335. m_attributeFlags = fileBuffer.GetInt();
  336. }
  337. // load extent of area
  338. fileBuffer.Get( &m_nwCorner, 3*sizeof(float) );
  339. fileBuffer.Get( &m_seCorner, 3*sizeof(float) );
  340. m_center.x = (m_nwCorner.x + m_seCorner.x)/2.0f;
  341. m_center.y = (m_nwCorner.y + m_seCorner.y)/2.0f;
  342. m_center.z = (m_nwCorner.z + m_seCorner.z)/2.0f;
  343. if ( ( m_seCorner.x - m_nwCorner.x ) > 0.0f && ( m_seCorner.y - m_nwCorner.y ) > 0.0f )
  344. {
  345. m_invDxCorners = 1.0f / ( m_seCorner.x - m_nwCorner.x );
  346. m_invDyCorners = 1.0f / ( m_seCorner.y - m_nwCorner.y );
  347. }
  348. else
  349. {
  350. m_invDxCorners = m_invDyCorners = 0;
  351. DevWarning( "Degenerate Navigation Area #%d at setpos %g %g %g\n",
  352. m_id, m_center.x, m_center.y, m_center.z );
  353. }
  354. // load heights of implicit corners
  355. m_neZ = fileBuffer.GetFloat();
  356. m_swZ = fileBuffer.GetFloat();
  357. CheckWaterLevel();
  358. // load connections (IDs) to adjacent areas
  359. // in the enum order NORTH, EAST, SOUTH, WEST
  360. for( int d=0; d<NUM_DIRECTIONS; d++ )
  361. {
  362. // load number of connections for this direction
  363. unsigned int count = fileBuffer.GetUnsignedInt();
  364. Assert( fileBuffer.IsValid() );
  365. m_connect[d].EnsureCapacity( count );
  366. for( unsigned int i=0; i<count; ++i )
  367. {
  368. NavConnect connect;
  369. connect.id = fileBuffer.GetUnsignedInt();
  370. Assert( fileBuffer.IsValid() );
  371. // don't allow self-referential connections
  372. if ( connect.id != m_id )
  373. {
  374. m_connect[d].AddToTail( connect );
  375. }
  376. }
  377. }
  378. //
  379. // Load hiding spots
  380. //
  381. // load number of hiding spots
  382. unsigned char hidingSpotCount = fileBuffer.GetUnsignedChar();
  383. if (version == 1)
  384. {
  385. // load simple vector array
  386. Vector pos;
  387. for( int h=0; h<hidingSpotCount; ++h )
  388. {
  389. fileBuffer.Get( &pos, 3 * sizeof(float) );
  390. // create new hiding spot and put on master list
  391. HidingSpot *spot = TheNavMesh->CreateHidingSpot();
  392. spot->SetPosition( pos );
  393. spot->SetFlags( HidingSpot::IN_COVER );
  394. m_hidingSpots.AddToTail( spot );
  395. }
  396. }
  397. else
  398. {
  399. // load HidingSpot objects for this area
  400. for( int h=0; h<hidingSpotCount; ++h )
  401. {
  402. // create new hiding spot and put on master list
  403. HidingSpot *spot = TheNavMesh->CreateHidingSpot();
  404. spot->Load( fileBuffer, version );
  405. m_hidingSpots.AddToTail( spot );
  406. }
  407. }
  408. if ( version < 15 )
  409. {
  410. //
  411. // Eat the approach areas
  412. //
  413. int nToEat = fileBuffer.GetUnsignedChar();
  414. // load approach area info (IDs)
  415. for( int a=0; a<nToEat; ++a )
  416. {
  417. fileBuffer.GetUnsignedInt();
  418. fileBuffer.GetUnsignedInt();
  419. fileBuffer.GetUnsignedChar();
  420. fileBuffer.GetUnsignedInt();
  421. fileBuffer.GetUnsignedChar();
  422. }
  423. }
  424. //
  425. // Load encounter paths for this area
  426. //
  427. unsigned int count = fileBuffer.GetUnsignedInt();
  428. if (version < 3)
  429. {
  430. // old data, read and discard
  431. for( unsigned int e=0; e<count; ++e )
  432. {
  433. SpotEncounter encounter;
  434. encounter.from.id = fileBuffer.GetUnsignedInt();
  435. encounter.to.id = fileBuffer.GetUnsignedInt();
  436. fileBuffer.Get( &encounter.path.from.x, 3 * sizeof(float) );
  437. fileBuffer.Get( &encounter.path.to.x, 3 * sizeof(float) );
  438. // read list of spots along this path
  439. unsigned char spotCount = fileBuffer.GetUnsignedChar();
  440. for( int s=0; s<spotCount; ++s )
  441. {
  442. fileBuffer.GetFloat();
  443. fileBuffer.GetFloat();
  444. fileBuffer.GetFloat();
  445. fileBuffer.GetFloat();
  446. }
  447. }
  448. return NAV_OK;
  449. }
  450. for( unsigned int e=0; e<count; ++e )
  451. {
  452. SpotEncounter *encounter = new SpotEncounter;
  453. encounter->from.id = fileBuffer.GetUnsignedInt();
  454. unsigned char dir = fileBuffer.GetUnsignedChar();
  455. encounter->fromDir = static_cast<NavDirType>( dir );
  456. encounter->to.id = fileBuffer.GetUnsignedInt();
  457. dir = fileBuffer.GetUnsignedChar();
  458. encounter->toDir = static_cast<NavDirType>( dir );
  459. // read list of spots along this path
  460. unsigned char spotCount = fileBuffer.GetUnsignedChar();
  461. SpotOrder order;
  462. for( int s=0; s<spotCount; ++s )
  463. {
  464. order.id = fileBuffer.GetUnsignedInt();
  465. unsigned char t = fileBuffer.GetUnsignedChar();
  466. order.t = (float)t/255.0f;
  467. encounter->spots.AddToTail( order );
  468. }
  469. m_spotEncounters.AddToTail( encounter );
  470. }
  471. if (version < 5)
  472. return NAV_OK;
  473. //
  474. // Load Place data
  475. //
  476. PlaceDirectory::IndexType entry = fileBuffer.GetUnsignedShort();
  477. // convert entry to actual Place
  478. SetPlace( placeDirectory.IndexToPlace( entry ) );
  479. if ( version < 7 )
  480. return NAV_OK;
  481. // load ladder data
  482. for ( int dir=0; dir<CNavLadder::NUM_LADDER_DIRECTIONS; ++dir )
  483. {
  484. count = fileBuffer.GetUnsignedInt();
  485. for( unsigned int i=0; i<count; ++i )
  486. {
  487. NavLadderConnect connect;
  488. connect.id = fileBuffer.GetUnsignedInt();
  489. bool alreadyConnected = false;
  490. FOR_EACH_VEC( m_ladder[dir], j )
  491. {
  492. if ( m_ladder[dir][j].id == connect.id )
  493. {
  494. alreadyConnected = true;
  495. break;
  496. }
  497. }
  498. if ( !alreadyConnected )
  499. {
  500. m_ladder[dir].AddToTail( connect );
  501. }
  502. }
  503. }
  504. if ( version < 8 )
  505. return NAV_OK;
  506. // load earliest occupy times
  507. for( int i=0; i<MAX_NAV_TEAMS; ++i )
  508. {
  509. // no spot in the map should take longer than this to reach
  510. m_earliestOccupyTime[i] = fileBuffer.GetFloat();
  511. }
  512. if ( version < 11 )
  513. return NAV_OK;
  514. // load light intensity
  515. for ( int i=0; i<NUM_CORNERS; ++i )
  516. {
  517. m_lightIntensity[i] = fileBuffer.GetFloat();
  518. }
  519. if ( version < 16 )
  520. return NAV_OK;
  521. // load visibility information
  522. unsigned int visibleAreaCount = fileBuffer.GetUnsignedInt();
  523. if ( !IsX360() )
  524. {
  525. m_potentiallyVisibleAreas.EnsureCapacity( visibleAreaCount );
  526. }
  527. else
  528. {
  529. /* TODO: Re-enable when latest 360 code gets integrated (MSB 5/5/09)
  530. size_t nBytes = visibleAreaCount * sizeof( AreaBindInfo );
  531. m_potentiallyVisibleAreas.~CAreaBindInfoArray();
  532. new ( &m_potentiallyVisibleAreas ) CAreaBindInfoArray( (AreaBindInfo *)engine->AllocLevelStaticData( nBytes ), visibleAreaCount );
  533. */
  534. }
  535. for( unsigned int j=0; j<visibleAreaCount; ++j )
  536. {
  537. AreaBindInfo info;
  538. info.id = fileBuffer.GetUnsignedInt();
  539. info.attributes = fileBuffer.GetUnsignedChar();
  540. m_potentiallyVisibleAreas.AddToTail( info );
  541. }
  542. // read area from which we inherit visibility
  543. m_inheritVisibilityFrom.id = fileBuffer.GetUnsignedInt();
  544. return NAV_OK;
  545. }
  546. //--------------------------------------------------------------------------------------------------------------
  547. /**
  548. * Convert loaded IDs to pointers
  549. * Make sure all IDs are converted, even if corrupt data is encountered.
  550. */
  551. NavErrorType CNavArea::PostLoad( void )
  552. {
  553. NavErrorType error = NAV_OK;
  554. for ( int dir=0; dir<CNavLadder::NUM_LADDER_DIRECTIONS; ++dir )
  555. {
  556. FOR_EACH_VEC( m_ladder[dir], it )
  557. {
  558. NavLadderConnect& connect = m_ladder[dir][it];
  559. unsigned int id = connect.id;
  560. if ( TheNavMesh->GetLadders().Find( connect.ladder ) == TheNavMesh->GetLadders().InvalidIndex() )
  561. {
  562. connect.ladder = TheNavMesh->GetLadderByID( id );
  563. }
  564. if (id && connect.ladder == NULL)
  565. {
  566. Msg( "CNavArea::PostLoad: Corrupt navigation ladder data. Cannot connect Navigation Areas.\n" );
  567. error = NAV_CORRUPT_DATA;
  568. }
  569. }
  570. }
  571. // connect areas together
  572. for( int d=0; d<NUM_DIRECTIONS; d++ )
  573. {
  574. FOR_EACH_VEC( m_connect[d], it )
  575. {
  576. NavConnect *connect = &m_connect[ d ][ it ];
  577. // convert connect ID into an actual area
  578. unsigned int id = connect->id;
  579. connect->area = TheNavMesh->GetNavAreaByID( id );
  580. if (id && connect->area == NULL)
  581. {
  582. Msg( "CNavArea::PostLoad: Corrupt navigation data. Cannot connect Navigation Areas.\n" );
  583. error = NAV_CORRUPT_DATA;
  584. }
  585. connect->length = ( connect->area->GetCenter() - GetCenter() ).Length();
  586. }
  587. }
  588. // resolve spot encounter IDs
  589. SpotEncounter *e;
  590. FOR_EACH_VEC( m_spotEncounters, it )
  591. {
  592. e = m_spotEncounters[ it ];
  593. e->from.area = TheNavMesh->GetNavAreaByID( e->from.id );
  594. if (e->from.area == NULL)
  595. {
  596. Msg( "CNavArea::PostLoad: Corrupt navigation data. Missing \"from\" Navigation Area for Encounter Spot.\n" );
  597. error = NAV_CORRUPT_DATA;
  598. }
  599. e->to.area = TheNavMesh->GetNavAreaByID( e->to.id );
  600. if (e->to.area == NULL)
  601. {
  602. Msg( "CNavArea::PostLoad: Corrupt navigation data. Missing \"to\" Navigation Area for Encounter Spot.\n" );
  603. error = NAV_CORRUPT_DATA;
  604. }
  605. if (e->from.area && e->to.area)
  606. {
  607. // compute path
  608. float halfWidth;
  609. ComputePortal( e->to.area, e->toDir, &e->path.to, &halfWidth );
  610. ComputePortal( e->from.area, e->fromDir, &e->path.from, &halfWidth );
  611. const float eyeHeight = HalfHumanHeight;
  612. e->path.from.z = e->from.area->GetZ( e->path.from ) + eyeHeight;
  613. e->path.to.z = e->to.area->GetZ( e->path.to ) + eyeHeight;
  614. }
  615. // resolve HidingSpot IDs
  616. FOR_EACH_VEC( e->spots, sit )
  617. {
  618. SpotOrder *order = &e->spots[ sit ];
  619. order->spot = GetHidingSpotByID( order->id );
  620. if (order->spot == NULL)
  621. {
  622. Msg( "CNavArea::PostLoad: Corrupt navigation data. Missing Hiding Spot\n" );
  623. error = NAV_CORRUPT_DATA;
  624. }
  625. }
  626. }
  627. // convert visible ID's to pointers to actual areas
  628. for ( int it=0; it<m_potentiallyVisibleAreas.Count(); ++it )
  629. {
  630. AreaBindInfo &info = m_potentiallyVisibleAreas[ it ];
  631. info.area = TheNavMesh->GetNavAreaByID( info.id );
  632. if ( info.area == NULL )
  633. {
  634. Warning( "Invalid area in visible set for area #%d\n", GetID() );
  635. }
  636. }
  637. m_inheritVisibilityFrom.area = TheNavMesh->GetNavAreaByID( m_inheritVisibilityFrom.id );
  638. Assert( m_inheritVisibilityFrom.area != this );
  639. // remove any invalid areas from the list
  640. AreaBindInfo bad;
  641. bad.area = NULL;
  642. while( m_potentiallyVisibleAreas.FindAndRemove( bad ) );
  643. // func avoid/prefer attributes are controlled by func_nav_cost entities
  644. ClearAllNavCostEntities();
  645. return error;
  646. }
  647. //--------------------------------------------------------------------------------------------------------------
  648. /**
  649. * Compute travel distance along shortest path from startPos to goalPos.
  650. * Return -1 if can't reach endPos from goalPos.
  651. */
  652. template< typename CostFunctor >
  653. float NavAreaTravelDistance( const Vector &startPos, const Vector &goalPos, CostFunctor &costFunc )
  654. {
  655. CNavArea *startArea = TheNavMesh->GetNearestNavArea( startPos );
  656. if (startArea == NULL)
  657. {
  658. return -1.0f;
  659. }
  660. // compute path between areas using given cost heuristic
  661. CNavArea *goalArea = NULL;
  662. if (NavAreaBuildPath( startArea, NULL, &goalPos, costFunc, &goalArea ) == false)
  663. {
  664. return -1.0f;
  665. }
  666. // compute distance along path
  667. if (goalArea->GetParent() == NULL)
  668. {
  669. // both points are in the same area - return euclidean distance
  670. return (goalPos - startPos).Length();
  671. }
  672. else
  673. {
  674. CNavArea *area;
  675. float distance;
  676. // goalPos is assumed to be inside goalArea (or very close to it) - skip to next area
  677. area = goalArea->GetParent();
  678. distance = (goalPos - area->GetCenter()).Length();
  679. for( ; area->GetParent(); area = area->GetParent() )
  680. {
  681. distance += (area->GetCenter() - area->GetParent()->GetCenter()).Length();
  682. }
  683. // add in distance to startPos
  684. distance += (startPos - area->GetCenter()).Length();
  685. return distance;
  686. }
  687. }
  688. //--------------------------------------------------------------------------------------------------------------
  689. /**
  690. * Determine the earliest time this hiding spot can be reached by either team
  691. */
  692. void CNavArea::ComputeEarliestOccupyTimes( void )
  693. {
  694. #ifdef CSTRIKE_DLL
  695. /// @todo Derive cstrike-specific navigation classes
  696. for( int i=0; i<MAX_NAV_TEAMS; ++i )
  697. {
  698. // no spot in the map should take longer than this to reach
  699. m_earliestOccupyTime[i] = 120.0f;
  700. }
  701. if (nav_quicksave.GetBool())
  702. return;
  703. // maximum player speed in units/second
  704. const float playerSpeed = 240.0f;
  705. ShortestPathCost cost;
  706. CBaseEntity *spot;
  707. // determine the shortest time it will take a Terrorist to reach this area
  708. int team = TEAM_TERRORIST % MAX_NAV_TEAMS;
  709. for( spot = gEntList.FindEntityByClassname( NULL, "info_player_terrorist" );
  710. spot;
  711. spot = gEntList.FindEntityByClassname( spot, "info_player_terrorist" ) )
  712. {
  713. float travelDistance = NavAreaTravelDistance( spot->GetAbsOrigin(), m_center, cost );
  714. if (travelDistance < 0.0f)
  715. continue;
  716. float travelTime = travelDistance / playerSpeed;
  717. if (travelTime < m_earliestOccupyTime[ team ])
  718. {
  719. m_earliestOccupyTime[ team ] = travelTime;
  720. }
  721. }
  722. // determine the shortest time it will take a CT to reach this area
  723. team = TEAM_CT % MAX_NAV_TEAMS;
  724. for( spot = gEntList.FindEntityByClassname( NULL, "info_player_counterterrorist" );
  725. spot;
  726. spot = gEntList.FindEntityByClassname( spot, "info_player_counterterrorist" ) )
  727. {
  728. float travelDistance = NavAreaTravelDistance( spot->GetAbsOrigin(), m_center, cost );
  729. if (travelDistance < 0.0f)
  730. continue;
  731. float travelTime = travelDistance / playerSpeed;
  732. if (travelTime < m_earliestOccupyTime[ team ])
  733. {
  734. m_earliestOccupyTime[ team ] = travelTime;
  735. }
  736. }
  737. #else
  738. for( int i=0; i<MAX_NAV_TEAMS; ++i )
  739. {
  740. m_earliestOccupyTime[i] = 0.0f;
  741. }
  742. #endif
  743. }
  744. //--------------------------------------------------------------------------------------------------------------
  745. /**
  746. * Determine if this area is a "battlefront" area - where two rushing teams first meet.
  747. */
  748. void CNavMesh::ComputeBattlefrontAreas( void )
  749. {
  750. #if 0
  751. #ifdef CSTRIKE_DLL
  752. ShortestPathCost cost;
  753. CBaseEntity *tSpawn, *ctSpawn;
  754. for( tSpawn = gEntList.FindEntityByClassname( NULL, "info_player_terrorist" );
  755. tSpawn;
  756. tSpawn = gEntList.FindEntityByClassname( tSpawn, "info_player_terrorist" ) )
  757. {
  758. CNavArea *tArea = TheNavMesh->GetNavArea( tSpawn->GetAbsOrigin() );
  759. if (tArea == NULL)
  760. continue;
  761. for( ctSpawn = gEntList.FindEntityByClassname( NULL, "info_player_counterterrorist" );
  762. ctSpawn;
  763. ctSpawn = gEntList.FindEntityByClassname( ctSpawn, "info_player_counterterrorist" ) )
  764. {
  765. CNavArea *ctArea = TheNavMesh->GetNavArea( ctSpawn->GetAbsOrigin() );
  766. if (ctArea == NULL)
  767. continue;
  768. if (tArea == ctArea)
  769. {
  770. m_isBattlefront = true;
  771. return;
  772. }
  773. // build path between these two spawn points - assume if path fails, it at least got close
  774. // (ie: imagine spawn points that you jump down from - can't path to)
  775. CNavArea *goalArea = NULL;
  776. NavAreaBuildPath( tArea, ctArea, NULL, cost, &goalArea );
  777. if (goalArea == NULL)
  778. continue;
  779. /**
  780. * @todo Need to enumerate ALL paths between all pairs of spawn points to find all battlefront areas
  781. */
  782. // find the area with the earliest overlapping occupy times
  783. CNavArea *battlefront = NULL;
  784. float earliestTime = 999999.9f;
  785. const float epsilon = 1.0f;
  786. CNavArea *area;
  787. for( area = goalArea; area; area = area->GetParent() )
  788. {
  789. if (fabs(area->GetEarliestOccupyTime( TEAM_TERRORIST ) - area->GetEarliestOccupyTime( TEAM_CT )) < epsilon)
  790. {
  791. }
  792. }
  793. }
  794. }
  795. #endif
  796. #endif
  797. }
  798. //--------------------------------------------------------------------------------------------------------------
  799. /**
  800. * Return the filename for this map's "nav map" file
  801. */
  802. const char *CNavMesh::GetFilename( void ) const
  803. {
  804. // filename is local to game dir for Steam, so we need to prepend game dir for regular file save
  805. char gamePath[256];
  806. engine->GetGameDir( gamePath, 256 );
  807. // persistant return value
  808. static char filename[256];
  809. Q_snprintf( filename, sizeof( filename ), "%s\\" FORMAT_NAVFILE, gamePath, STRING( gpGlobals->mapname ) );
  810. return filename;
  811. }
  812. //--------------------------------------------------------------------------------------------------------------
  813. /*
  814. ============
  815. COM_FixSlashes
  816. Changes all '/' characters into '\' characters, in place.
  817. ============
  818. */
  819. inline void COM_FixSlashes( char *pname )
  820. {
  821. #ifdef _WIN32
  822. while ( *pname )
  823. {
  824. if ( *pname == '/' )
  825. *pname = '\\';
  826. pname++;
  827. }
  828. #else
  829. while ( *pname )
  830. {
  831. if ( *pname == '\\' )
  832. *pname = '/';
  833. pname++;
  834. }
  835. #endif
  836. }
  837. static void WarnIfMeshNeedsAnalysis( int version )
  838. {
  839. // Quick check to warn about needing to analyze: nav_strip, nav_delete, etc set
  840. // every CNavArea's m_approachCount to 0, and delete their m_spotEncounterList.
  841. // So, if no area has either, odds are good we need an analyze.
  842. if ( version >= 14 )
  843. {
  844. if ( !TheNavMesh->IsAnalyzed() )
  845. {
  846. Warning( "The nav mesh needs a full nav_analyze\n" );
  847. return;
  848. }
  849. }
  850. #ifdef CSTRIKE_DLL
  851. else
  852. {
  853. bool hasApproachAreas = false;
  854. bool hasSpotEncounters = false;
  855. FOR_EACH_VEC( TheNavAreas, it )
  856. {
  857. CCSNavArea *area = dynamic_cast< CCSNavArea * >( TheNavAreas[ it ] );
  858. if ( area )
  859. {
  860. if ( area->GetApproachInfoCount() )
  861. {
  862. hasApproachAreas = true;
  863. }
  864. if ( area->GetSpotEncounterCount() )
  865. {
  866. hasSpotEncounters = true;
  867. }
  868. }
  869. }
  870. if ( !hasApproachAreas || !hasSpotEncounters )
  871. {
  872. Warning( "The nav mesh needs a full nav_analyze\n" );
  873. }
  874. }
  875. #endif
  876. }
  877. /**
  878. * Store Navigation Mesh to a file
  879. */
  880. bool CNavMesh::Save( void ) const
  881. {
  882. WarnIfMeshNeedsAnalysis( NavCurrentVersion );
  883. const char *filename = GetFilename();
  884. if (filename == NULL)
  885. return false;
  886. //
  887. // Store the NAV file
  888. //
  889. COM_FixSlashes( const_cast<char *>(filename) );
  890. // get size of source bsp file for later (before we open the nav file for writing, in
  891. // case of failure)
  892. char *bspFilename = GetBspFilename( filename );
  893. if (bspFilename == NULL)
  894. {
  895. return false;
  896. }
  897. CUtlBuffer fileBuffer( 4096, 1024*1024 );
  898. // store "magic number" to help identify this kind of file
  899. unsigned int magic = NAV_MAGIC_NUMBER;
  900. fileBuffer.PutUnsignedInt( magic );
  901. // store version number of file
  902. // 1 = hiding spots as plain vector array
  903. // 2 = hiding spots as HidingSpot objects
  904. // 3 = Encounter spots use HidingSpot ID's instead of storing vector again
  905. // 4 = Includes size of source bsp file to verify nav data correlation
  906. // ---- Beta Release at V4 -----
  907. // 5 = Added Place info
  908. // ---- Conversion to Src ------
  909. // 6 = Added Ladder info
  910. // 7 = Areas store ladder ID's so ladders can have one-way connections
  911. // 8 = Added earliest occupy times (2 floats) to each area
  912. // 9 = Promoted CNavArea's attribute flags to a short
  913. // 10 - Added sub-version number to allow derived classes to have custom area data
  914. // 11 - Added light intensity to each area
  915. // 12 - Storing presence of unnamed areas in the PlaceDirectory
  916. // 13 - Widened NavArea attribute bits from unsigned short to int
  917. // 14 - Added a bool for if the nav needs analysis
  918. // 15 - removed approach areas
  919. // 16 - Added visibility data to the base mesh
  920. fileBuffer.PutUnsignedInt( NavCurrentVersion );
  921. // The sub-version number is maintained and owned by classes derived from CNavMesh and CNavArea
  922. // and allows them to track their custom data just as we do at this top level
  923. fileBuffer.PutUnsignedInt( GetSubVersionNumber() );
  924. // store the size of source bsp file in the nav file
  925. // so we can test if the bsp changed since the nav file was made
  926. unsigned int bspSize = filesystem->Size( bspFilename );
  927. DevMsg( "Size of bsp file '%s' is %u bytes.\n", bspFilename, bspSize );
  928. fileBuffer.PutUnsignedInt( bspSize );
  929. // Store the analysis state
  930. fileBuffer.PutUnsignedChar( m_isAnalyzed );
  931. //
  932. // Build a directory of the Places in this map
  933. //
  934. placeDirectory.Reset();
  935. FOR_EACH_VEC( TheNavAreas, nit )
  936. {
  937. CNavArea *area = TheNavAreas[ nit ];
  938. Place place = area->GetPlace();
  939. placeDirectory.AddPlace( place );
  940. }
  941. placeDirectory.Save( fileBuffer );
  942. SaveCustomDataPreArea( fileBuffer );
  943. //
  944. // Store navigation areas
  945. //
  946. {
  947. // store number of areas
  948. unsigned int count = TheNavAreas.Count();
  949. fileBuffer.PutUnsignedInt( count );
  950. // store each area
  951. FOR_EACH_VEC( TheNavAreas, it )
  952. {
  953. CNavArea *area = TheNavAreas[ it ];
  954. area->Save( fileBuffer, NavCurrentVersion );
  955. }
  956. }
  957. //
  958. // Store ladders
  959. //
  960. {
  961. // store number of ladders
  962. unsigned int count = m_ladders.Count();
  963. fileBuffer.PutUnsignedInt( count );
  964. // store each ladder
  965. for ( int i=0; i<m_ladders.Count(); ++i )
  966. {
  967. CNavLadder *ladder = m_ladders[i];
  968. ladder->Save( fileBuffer, NavCurrentVersion );
  969. }
  970. }
  971. //
  972. // Store derived class mesh info
  973. //
  974. SaveCustomData( fileBuffer );
  975. if ( p4 )
  976. {
  977. char szCorrectPath[MAX_PATH];
  978. filesystem->GetCaseCorrectFullPath( filename, szCorrectPath );
  979. CP4AutoEditAddFile a( szCorrectPath );
  980. }
  981. if ( !filesystem->WriteFile( filename, "MOD", fileBuffer ) )
  982. {
  983. Warning( "Unable to save %d bytes to %s\n", fileBuffer.Size(), filename );
  984. return false;
  985. }
  986. unsigned int navSize = filesystem->Size( filename );
  987. DevMsg( "Size of nav file '%s' is %u bytes.\n", filename, navSize );
  988. return true;
  989. }
  990. //--------------------------------------------------------------------------------------------------------------
  991. static NavErrorType CheckNavFile( const char *bspFilename )
  992. {
  993. if ( !bspFilename )
  994. return NAV_CANT_ACCESS_FILE;
  995. char baseName[256];
  996. Q_StripExtension(bspFilename,baseName,sizeof(baseName));
  997. char bspPathname[256];
  998. Q_snprintf(bspPathname,sizeof(bspPathname), FORMAT_BSPFILE, baseName);
  999. char filename[256];
  1000. Q_snprintf(filename,sizeof(filename), FORMAT_NAVFILE, baseName);
  1001. bool navIsInBsp = false;
  1002. FileHandle_t file = filesystem->Open( filename, "rb", "MOD" ); // this ignores .nav files embedded in the .bsp ...
  1003. if ( !file )
  1004. {
  1005. navIsInBsp = true;
  1006. file = filesystem->Open( filename, "rb", "GAME" ); // ... and this looks for one if it's the only one around.
  1007. }
  1008. if (!file)
  1009. {
  1010. return NAV_CANT_ACCESS_FILE;
  1011. }
  1012. // check magic number
  1013. int result;
  1014. unsigned int magic;
  1015. result = filesystem->Read( &magic, sizeof(unsigned int), file );
  1016. if (!result || magic != NAV_MAGIC_NUMBER)
  1017. {
  1018. filesystem->Close( file );
  1019. return NAV_INVALID_FILE;
  1020. }
  1021. // read file version number
  1022. unsigned int version;
  1023. result = filesystem->Read( &version, sizeof(unsigned int), file );
  1024. if (!result || version > NavCurrentVersion || version < 4)
  1025. {
  1026. filesystem->Close( file );
  1027. return NAV_BAD_FILE_VERSION;
  1028. }
  1029. // get size of source bsp file and verify that the bsp hasn't changed
  1030. unsigned int saveBspSize;
  1031. filesystem->Read( &saveBspSize, sizeof(unsigned int), file );
  1032. // verify size
  1033. unsigned int bspSize = filesystem->Size( bspPathname );
  1034. if (bspSize != saveBspSize && !navIsInBsp)
  1035. {
  1036. return NAV_FILE_OUT_OF_DATE;
  1037. }
  1038. return NAV_OK;
  1039. }
  1040. //--------------------------------------------------------------------------------------------------------------
  1041. void CommandNavCheckFileConsistency( void )
  1042. {
  1043. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  1044. return;
  1045. FileFindHandle_t findHandle;
  1046. const char *bspFilename = filesystem->FindFirstEx( "maps/*.bsp", "MOD", &findHandle );
  1047. while ( bspFilename )
  1048. {
  1049. switch ( CheckNavFile( bspFilename ) )
  1050. {
  1051. case NAV_CANT_ACCESS_FILE:
  1052. Warning( "Missing nav file for %s\n", bspFilename );
  1053. break;
  1054. case NAV_INVALID_FILE:
  1055. Warning( "Invalid nav file for %s\n", bspFilename );
  1056. break;
  1057. case NAV_BAD_FILE_VERSION:
  1058. Warning( "Old nav file for %s\n", bspFilename );
  1059. break;
  1060. case NAV_FILE_OUT_OF_DATE:
  1061. Warning( "The nav file for %s is built from an old version of the map\n", bspFilename );
  1062. break;
  1063. case NAV_OK:
  1064. Msg( "The nav file for %s is up-to-date\n", bspFilename );
  1065. break;
  1066. }
  1067. bspFilename = filesystem->FindNext( findHandle );
  1068. }
  1069. filesystem->FindClose( findHandle );
  1070. }
  1071. static ConCommand nav_check_file_consistency( "nav_check_file_consistency", CommandNavCheckFileConsistency, "Scans the maps directory and reports any missing/out-of-date navigation files.", FCVAR_GAMEDLL | FCVAR_CHEAT );
  1072. //--------------------------------------------------------------------------------------------------------------
  1073. /**
  1074. * Reads the used place names from the nav file (can be used to selectively precache before the nav is loaded)
  1075. */
  1076. const CUtlVector< Place > *CNavMesh::GetPlacesFromNavFile( bool *hasUnnamedPlaces )
  1077. {
  1078. placeDirectory.Reset();
  1079. // nav filename is derived from map filename
  1080. char filename[256];
  1081. Q_snprintf( filename, sizeof( filename ), FORMAT_NAVFILE, STRING( gpGlobals->mapname ) );
  1082. CUtlBuffer fileBuffer( 4096, 1024*1024, CUtlBuffer::READ_ONLY );
  1083. if ( GetNavDataFromFile( fileBuffer ) != NAV_OK )
  1084. {
  1085. return NULL;
  1086. }
  1087. // check magic number
  1088. unsigned int magic = fileBuffer.GetUnsignedInt();
  1089. if ( !fileBuffer.IsValid() || magic != NAV_MAGIC_NUMBER )
  1090. {
  1091. return NULL; // Corrupt nav file?
  1092. }
  1093. // read file version number
  1094. unsigned int version = fileBuffer.GetUnsignedInt();
  1095. if ( !fileBuffer.IsValid() || version > NavCurrentVersion )
  1096. {
  1097. return NULL; // Unknown nav file version
  1098. }
  1099. if ( version < 5 )
  1100. {
  1101. return NULL; // Too old to have place names
  1102. }
  1103. unsigned int subVersion = 0;
  1104. if ( version >= 10 )
  1105. {
  1106. subVersion = fileBuffer.GetUnsignedInt();
  1107. if ( !fileBuffer.IsValid() )
  1108. {
  1109. return NULL; // No sub-version
  1110. }
  1111. }
  1112. fileBuffer.GetUnsignedInt(); // skip BSP file size
  1113. if ( version >= 14 )
  1114. {
  1115. fileBuffer.GetUnsignedChar(); // skip m_isAnalyzed
  1116. }
  1117. placeDirectory.Load( fileBuffer, version );
  1118. LoadCustomDataPreArea( fileBuffer, subVersion );
  1119. if ( hasUnnamedPlaces )
  1120. {
  1121. *hasUnnamedPlaces = placeDirectory.HasUnnamedPlaces();
  1122. }
  1123. return placeDirectory.GetPlaces();
  1124. }
  1125. //--------------------------------------------------------------------------------------------------------------
  1126. /**
  1127. * Fetch raw nav data into buffer
  1128. */
  1129. NavErrorType CNavMesh::GetNavDataFromFile( CUtlBuffer &outBuffer, bool *pNavDataFromBSP )
  1130. {
  1131. // nav filename is derived from map filename
  1132. char filename[MAX_PATH] = { 0 };
  1133. Q_snprintf( filename, sizeof( filename ), FORMAT_NAVFILE, STRING( gpGlobals->mapname ) );
  1134. if ( !filesystem->ReadFile( filename, "MOD", outBuffer ) ) // this ignores .nav files embedded in the .bsp ...
  1135. {
  1136. if ( !filesystem->ReadFile( filename, "BSP", outBuffer ) ) // ... and this looks for one if it's the only one around.
  1137. {
  1138. // Finally, check for the special embed name for in-BSP nav meshes only
  1139. if ( !filesystem->ReadFile( PATH_NAVFILE_EMBEDDED, "BSP", outBuffer ) )
  1140. {
  1141. return NAV_CANT_ACCESS_FILE;
  1142. }
  1143. }
  1144. if ( pNavDataFromBSP )
  1145. {
  1146. *pNavDataFromBSP = true;
  1147. }
  1148. }
  1149. if ( IsX360() )
  1150. {
  1151. // 360 has compressed NAVs
  1152. if ( CLZMA::IsCompressed( (unsigned char *)outBuffer.Base() ) )
  1153. {
  1154. int originalSize = CLZMA::GetActualSize( (unsigned char *)outBuffer.Base() );
  1155. unsigned char *pOriginalData = new unsigned char[originalSize];
  1156. CLZMA::Uncompress( (unsigned char *)outBuffer.Base(), pOriginalData );
  1157. outBuffer.AssumeMemory( pOriginalData, originalSize, originalSize, CUtlBuffer::READ_ONLY );
  1158. }
  1159. }
  1160. return NAV_OK;
  1161. }
  1162. //--------------------------------------------------------------------------------------------------------------
  1163. /**
  1164. * Load AI navigation data from a file
  1165. */
  1166. NavErrorType CNavMesh::Load( void )
  1167. {
  1168. MDLCACHE_CRITICAL_SECTION();
  1169. // free previous navigation mesh data
  1170. Reset();
  1171. placeDirectory.Reset();
  1172. CNavVectorNoEditAllocator::Reset();
  1173. GameRules()->OnNavMeshLoad();
  1174. CNavArea::m_nextID = 1;
  1175. bool navIsInBsp = false;
  1176. CUtlBuffer fileBuffer( 4096, 1024*1024, CUtlBuffer::READ_ONLY );
  1177. NavErrorType readResult = GetNavDataFromFile( fileBuffer, &navIsInBsp );
  1178. if ( readResult != NAV_OK )
  1179. {
  1180. return readResult;
  1181. }
  1182. // check magic number
  1183. unsigned int magic = fileBuffer.GetUnsignedInt();
  1184. if ( !fileBuffer.IsValid() || magic != NAV_MAGIC_NUMBER )
  1185. {
  1186. Msg( "Invalid navigation file.\n" );
  1187. return NAV_INVALID_FILE;
  1188. }
  1189. // read file version number
  1190. unsigned int version = fileBuffer.GetUnsignedInt();
  1191. if ( !fileBuffer.IsValid() || version > NavCurrentVersion )
  1192. {
  1193. Msg( "Unknown navigation file version.\n" );
  1194. return NAV_BAD_FILE_VERSION;
  1195. }
  1196. unsigned int subVersion = 0;
  1197. if ( version >= 10 )
  1198. {
  1199. subVersion = fileBuffer.GetUnsignedInt();
  1200. if ( !fileBuffer.IsValid() )
  1201. {
  1202. Msg( "Error reading sub-version number.\n" );
  1203. return NAV_INVALID_FILE;
  1204. }
  1205. }
  1206. if ( version >= 4 )
  1207. {
  1208. // get size of source bsp file and verify that the bsp hasn't changed
  1209. unsigned int saveBspSize = fileBuffer.GetUnsignedInt();
  1210. // verify size
  1211. char bspFilename[MAX_PATH] = { 0 };
  1212. Q_snprintf( bspFilename, sizeof( bspFilename ), FORMAT_BSPFILE , STRING( gpGlobals->mapname ) );
  1213. unsigned int bspSize = filesystem->Size( bspFilename );
  1214. if ( bspSize != saveBspSize && !navIsInBsp )
  1215. {
  1216. if ( engine->IsDedicatedServer() )
  1217. {
  1218. // Warning doesn't print to the dedicated server console, so we'll use Msg instead
  1219. DevMsg( "The Navigation Mesh was built using a different version of this map.\n" );
  1220. }
  1221. else
  1222. {
  1223. DevWarning( "The Navigation Mesh was built using a different version of this map.\n" );
  1224. }
  1225. m_isOutOfDate = true;
  1226. }
  1227. }
  1228. if ( version >= 14 )
  1229. {
  1230. m_isAnalyzed = fileBuffer.GetUnsignedChar() != 0;
  1231. }
  1232. else
  1233. {
  1234. m_isAnalyzed = false;
  1235. }
  1236. // load Place directory
  1237. if ( version >= 5 )
  1238. {
  1239. placeDirectory.Load( fileBuffer, version );
  1240. }
  1241. LoadCustomDataPreArea( fileBuffer, subVersion );
  1242. // get number of areas
  1243. unsigned int count = fileBuffer.GetUnsignedInt();
  1244. unsigned int i;
  1245. if ( count == 0 )
  1246. {
  1247. return NAV_INVALID_FILE;
  1248. }
  1249. Extent extent;
  1250. extent.lo.x = 9999999999.9f;
  1251. extent.lo.y = 9999999999.9f;
  1252. extent.hi.x = -9999999999.9f;
  1253. extent.hi.y = -9999999999.9f;
  1254. // load the areas and compute total extent
  1255. TheNavMesh->PreLoadAreas( count );
  1256. Extent areaExtent;
  1257. for( i=0; i<count; ++i )
  1258. {
  1259. CNavArea *area = TheNavMesh->CreateArea();
  1260. area->Load( fileBuffer, version, subVersion );
  1261. TheNavAreas.AddToTail( area );
  1262. area->GetExtent( &areaExtent );
  1263. if (areaExtent.lo.x < extent.lo.x)
  1264. extent.lo.x = areaExtent.lo.x;
  1265. if (areaExtent.lo.y < extent.lo.y)
  1266. extent.lo.y = areaExtent.lo.y;
  1267. if (areaExtent.hi.x > extent.hi.x)
  1268. extent.hi.x = areaExtent.hi.x;
  1269. if (areaExtent.hi.y > extent.hi.y)
  1270. extent.hi.y = areaExtent.hi.y;
  1271. }
  1272. // add the areas to the grid
  1273. AllocateGrid( extent.lo.x, extent.hi.x, extent.lo.y, extent.hi.y );
  1274. FOR_EACH_VEC( TheNavAreas, it )
  1275. {
  1276. AddNavArea( TheNavAreas[ it ] );
  1277. }
  1278. //
  1279. // Set up all the ladders
  1280. //
  1281. if (version >= 6)
  1282. {
  1283. count = fileBuffer.GetUnsignedInt();
  1284. m_ladders.EnsureCapacity( count );
  1285. // load the ladders
  1286. for( i=0; i<count; ++i )
  1287. {
  1288. CNavLadder *ladder = new CNavLadder;
  1289. ladder->Load( fileBuffer, version );
  1290. m_ladders.AddToTail( ladder );
  1291. }
  1292. }
  1293. else
  1294. {
  1295. BuildLadders();
  1296. }
  1297. // mark stairways (TODO: this can be removed once all maps are re-saved with this attribute in them)
  1298. MarkStairAreas();
  1299. //
  1300. // Load derived class mesh info
  1301. //
  1302. LoadCustomData( fileBuffer, subVersion );
  1303. //
  1304. // Bind pointers, etc
  1305. //
  1306. NavErrorType loadResult = PostLoad( version );
  1307. WarnIfMeshNeedsAnalysis( version );
  1308. return loadResult;
  1309. }
  1310. struct OneWayLink_t
  1311. {
  1312. CNavArea *destArea;
  1313. CNavArea *area;
  1314. int backD;
  1315. static int Compare(const OneWayLink_t *lhs, const OneWayLink_t *rhs )
  1316. {
  1317. int result = ( lhs->destArea - rhs->destArea );
  1318. if ( result != 0 )
  1319. {
  1320. return result;
  1321. }
  1322. return ( lhs->backD - rhs->backD );
  1323. }
  1324. };
  1325. //--------------------------------------------------------------------------------------------------------------
  1326. /**
  1327. * Invoked after all areas have been loaded - for pointer binding, etc
  1328. */
  1329. NavErrorType CNavMesh::PostLoad( unsigned int version )
  1330. {
  1331. // allow areas to connect to each other, etc
  1332. FOR_EACH_VEC( TheNavAreas, pit )
  1333. {
  1334. CNavArea *area = TheNavAreas[ pit ];
  1335. area->PostLoad();
  1336. }
  1337. // allow hiding spots to compute information
  1338. FOR_EACH_VEC( TheHidingSpots, hit )
  1339. {
  1340. HidingSpot *spot = TheHidingSpots[ hit ];
  1341. spot->PostLoad();
  1342. }
  1343. if ( version < 8 )
  1344. {
  1345. // Old nav meshes need to compute earliest occupy times
  1346. FOR_EACH_VEC( TheNavAreas, nit )
  1347. {
  1348. CNavArea *area = TheNavAreas[ nit ];
  1349. area->ComputeEarliestOccupyTimes();
  1350. }
  1351. }
  1352. ComputeBattlefrontAreas();
  1353. //
  1354. // Allow each nav area to know what other areas have one-way connections to it. Need to gather
  1355. // then sort due to allocation restrictions on the 360
  1356. //
  1357. OneWayLink_t oneWayLink;
  1358. CUtlVectorFixedGrowable<OneWayLink_t, 512> oneWayLinks;
  1359. FOR_EACH_VEC( TheNavAreas, oit )
  1360. {
  1361. oneWayLink.area = TheNavAreas[ oit ];
  1362. for( int d=0; d<NUM_DIRECTIONS; d++ )
  1363. {
  1364. const NavConnectVector *connectList = oneWayLink.area->GetAdjacentAreas( (NavDirType)d );
  1365. FOR_EACH_VEC( (*connectList), it )
  1366. {
  1367. NavConnect connect = (*connectList)[ it ];
  1368. oneWayLink.destArea = connect.area;
  1369. // if the area we connect to has no connection back to us, allow that area to remember us as an incoming connection
  1370. oneWayLink.backD = OppositeDirection( (NavDirType)d );
  1371. const NavConnectVector *backConnectList = oneWayLink.destArea->GetAdjacentAreas( (NavDirType)oneWayLink.backD );
  1372. bool isOneWay = true;
  1373. FOR_EACH_VEC( (*backConnectList), bit )
  1374. {
  1375. NavConnect backConnect = (*backConnectList)[ bit ];
  1376. if (backConnect.area->GetID() == oneWayLink.area->GetID())
  1377. {
  1378. isOneWay = false;
  1379. break;
  1380. }
  1381. }
  1382. if (isOneWay)
  1383. {
  1384. oneWayLinks.AddToTail( oneWayLink );
  1385. }
  1386. }
  1387. }
  1388. }
  1389. oneWayLinks.Sort( &OneWayLink_t::Compare );
  1390. for ( int i = 0; i < oneWayLinks.Count(); i++ )
  1391. {
  1392. // add this one-way connection
  1393. oneWayLinks[i].destArea->AddIncomingConnection( oneWayLinks[i].area, (NavDirType)oneWayLinks[i].backD );
  1394. }
  1395. ValidateNavAreaConnections();
  1396. // TERROR: loading into a map directly creates entities before the mesh is loaded. Tell the preexisting
  1397. // entities now that the mesh is loaded so they can update areas.
  1398. for ( int i=0; i<m_avoidanceObstacles.Count(); ++i )
  1399. {
  1400. m_avoidanceObstacles[i]->OnNavMeshLoaded();
  1401. }
  1402. // the Navigation Mesh has been successfully loaded
  1403. m_isLoaded = true;
  1404. return NAV_OK;
  1405. }