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.

1712 lines
42 KiB

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