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.

473 lines
11 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // nav_area.cpp
  9. // AI Navigation areas
  10. // Author: Michael S. Booth ([email protected]), January 2003
  11. #include "cbase.h"
  12. #include "cs_nav_mesh.h"
  13. #include "cs_nav_area.h"
  14. #include "nav_pathfind.h"
  15. #include "nav_colors.h"
  16. #include "fmtstr.h"
  17. #include "props_shared.h"
  18. #include "func_breakablesurf.h"
  19. #include "Color.h"
  20. #include "collisionutils.h"
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include <tier0/memdbgon.h>
  23. #ifdef _WIN32
  24. #pragma warning (disable:4701) // disable warning that variable *may* not be initialized
  25. #endif
  26. //--------------------------------------------------------------------------------------------------------------
  27. /**
  28. * Constructor used during normal runtime.
  29. */
  30. CCSNavArea::CCSNavArea( void )
  31. {
  32. m_approachCount = 0;
  33. }
  34. //--------------------------------------------------------------------------------------------------------------
  35. /**
  36. * Destructor
  37. */
  38. CCSNavArea::~CCSNavArea()
  39. {
  40. }
  41. void CCSNavArea::OnServerActivate( void )
  42. {
  43. CNavArea::OnServerActivate();
  44. }
  45. void CCSNavArea::OnRoundRestart( void )
  46. {
  47. CNavArea::OnRoundRestart();
  48. }
  49. void CCSNavArea::Save( CUtlBuffer &fileBuffer, unsigned int version ) const
  50. {
  51. CNavArea::Save( fileBuffer, version );
  52. //
  53. // Save the approach areas for this area
  54. //
  55. // save number of approach areas
  56. fileBuffer.PutUnsignedChar(m_approachCount);
  57. // save approach area info
  58. for( int a=0; a<m_approachCount; ++a )
  59. {
  60. if (m_approach[a].here.area)
  61. fileBuffer.PutUnsignedInt(m_approach[a].here.area->GetID());
  62. else
  63. fileBuffer.PutUnsignedInt(0);
  64. if (m_approach[a].prev.area)
  65. fileBuffer.PutUnsignedInt(m_approach[a].prev.area->GetID());
  66. else
  67. fileBuffer.PutUnsignedInt(0);
  68. fileBuffer.PutUnsignedChar(m_approach[a].prevToHereHow);
  69. if (m_approach[a].next.area)
  70. fileBuffer.PutUnsignedInt(m_approach[a].next.area->GetID());
  71. else
  72. fileBuffer.PutUnsignedInt(0);
  73. fileBuffer.PutUnsignedChar(m_approach[a].hereToNextHow);
  74. }
  75. }
  76. NavErrorType CCSNavArea::Load( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion )
  77. {
  78. if ( version < 15 )
  79. return LoadLegacy(fileBuffer, version, subVersion);
  80. // load base class data
  81. NavErrorType error = CNavArea::Load( fileBuffer, version, subVersion );
  82. switch ( subVersion )
  83. {
  84. case 1:
  85. //
  86. // Load number of approach areas
  87. //
  88. m_approachCount = fileBuffer.GetUnsignedChar();
  89. // load approach area info (IDs)
  90. for( int a = 0; a < m_approachCount; ++a )
  91. {
  92. m_approach[a].here.id = fileBuffer.GetUnsignedInt();
  93. m_approach[a].prev.id = fileBuffer.GetUnsignedInt();
  94. m_approach[a].prevToHereHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
  95. m_approach[a].next.id = fileBuffer.GetUnsignedInt();
  96. m_approach[a].hereToNextHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
  97. }
  98. if ( !fileBuffer.IsValid() )
  99. error = NAV_INVALID_FILE;
  100. // fall through
  101. case 0:
  102. // legacy version
  103. break;
  104. default:
  105. Warning( "Unknown NavArea sub-version number\n" );
  106. error = NAV_INVALID_FILE;
  107. }
  108. return error;
  109. }
  110. NavErrorType CCSNavArea::PostLoad( void )
  111. {
  112. NavErrorType error = CNavArea::PostLoad();
  113. // resolve approach area IDs
  114. for ( int a = 0; a < m_approachCount; ++a )
  115. {
  116. m_approach[a].here.area = TheNavMesh->GetNavAreaByID( m_approach[a].here.id );
  117. if (m_approach[a].here.id && m_approach[a].here.area == NULL)
  118. {
  119. Msg( "CNavArea::PostLoad: Corrupt navigation data. Missing Approach Area (here).\n" );
  120. error = NAV_CORRUPT_DATA;
  121. }
  122. m_approach[a].prev.area = TheNavMesh->GetNavAreaByID( m_approach[a].prev.id );
  123. if (m_approach[a].prev.id && m_approach[a].prev.area == NULL)
  124. {
  125. Msg( "CNavArea::PostLoad: Corrupt navigation data. Missing Approach Area (prev).\n" );
  126. error = NAV_CORRUPT_DATA;
  127. }
  128. m_approach[a].next.area = TheNavMesh->GetNavAreaByID( m_approach[a].next.id );
  129. if (m_approach[a].next.id && m_approach[a].next.area == NULL)
  130. {
  131. Msg( "CNavArea::PostLoad: Corrupt navigation data. Missing Approach Area (next).\n" );
  132. error = NAV_CORRUPT_DATA;
  133. }
  134. }
  135. return error;
  136. }
  137. void CCSNavArea::Draw( void ) const
  138. {
  139. CNavArea::Draw();
  140. }
  141. void CCSNavArea::CustomAnalysis( bool isIncremental /*= false */ )
  142. {
  143. ComputeApproachAreas();
  144. }
  145. //--------------------------------------------------------------------------------------------------------------
  146. /**
  147. * Load legacy navigation area from the file
  148. */
  149. NavErrorType CCSNavArea::LoadLegacy( CUtlBuffer &fileBuffer, unsigned int version, unsigned int subVersion )
  150. {
  151. // load ID
  152. m_id = fileBuffer.GetUnsignedInt();
  153. // update nextID to avoid collisions
  154. if (m_id >= m_nextID)
  155. m_nextID = m_id+1;
  156. // load attribute flags
  157. if ( version <= 8 )
  158. {
  159. m_attributeFlags = fileBuffer.GetUnsignedChar();
  160. }
  161. else if ( version < 13 )
  162. {
  163. m_attributeFlags = fileBuffer.GetUnsignedShort();
  164. }
  165. else
  166. {
  167. m_attributeFlags = fileBuffer.GetInt();
  168. }
  169. // load extent of area
  170. fileBuffer.Get( &m_nwCorner, 3*sizeof(float) );
  171. fileBuffer.Get( &m_seCorner, 3*sizeof(float) );
  172. m_center.x = (m_nwCorner.x + m_seCorner.x)/2.0f;
  173. m_center.y = (m_nwCorner.y + m_seCorner.y)/2.0f;
  174. m_center.z = (m_nwCorner.z + m_seCorner.z)/2.0f;
  175. if ( ( m_seCorner.x - m_nwCorner.x ) > 0.0f && ( m_seCorner.y - m_nwCorner.y ) > 0.0f )
  176. {
  177. m_invDxCorners = 1.0f / ( m_seCorner.x - m_nwCorner.x );
  178. m_invDyCorners = 1.0f / ( m_seCorner.y - m_nwCorner.y );
  179. }
  180. else
  181. {
  182. m_invDxCorners = m_invDyCorners = 0;
  183. DevWarning( "Degenerate Navigation Area #%d at setpos %g %g %g\n",
  184. m_id, m_center.x, m_center.y, m_center.z );
  185. }
  186. // load heights of implicit corners
  187. m_neZ = fileBuffer.GetFloat();
  188. m_swZ = fileBuffer.GetFloat();
  189. CheckWaterLevel();
  190. // load connections (IDs) to adjacent areas
  191. // in the enum order NORTH, EAST, SOUTH, WEST
  192. for( int d=0; d<NUM_DIRECTIONS; d++ )
  193. {
  194. // load number of connections for this direction
  195. unsigned int count = fileBuffer.GetUnsignedInt();
  196. Assert( fileBuffer.IsValid() );
  197. m_connect[d].EnsureCapacity( count );
  198. for( unsigned int i=0; i<count; ++i )
  199. {
  200. NavConnect connect;
  201. connect.id = fileBuffer.GetUnsignedInt();
  202. Assert( fileBuffer.IsValid() );
  203. // don't allow self-referential connections
  204. if ( connect.id != m_id )
  205. {
  206. m_connect[d].AddToTail( connect );
  207. }
  208. }
  209. }
  210. //
  211. // Load hiding spots
  212. //
  213. // load number of hiding spots
  214. unsigned char hidingSpotCount = fileBuffer.GetUnsignedChar();
  215. if (version == 1)
  216. {
  217. // load simple vector array
  218. Vector pos;
  219. for( int h=0; h<hidingSpotCount; ++h )
  220. {
  221. fileBuffer.Get( &pos, 3 * sizeof(float) );
  222. // create new hiding spot and put on master list
  223. HidingSpot *spot = TheNavMesh->CreateHidingSpot();
  224. spot->SetPosition( pos );
  225. spot->SetFlags( HidingSpot::IN_COVER );
  226. m_hidingSpots.AddToTail( spot );
  227. }
  228. }
  229. else
  230. {
  231. // load HidingSpot objects for this area
  232. for( int h=0; h<hidingSpotCount; ++h )
  233. {
  234. // create new hiding spot and put on master list
  235. HidingSpot *spot = TheNavMesh->CreateHidingSpot();
  236. spot->Load( fileBuffer, version );
  237. m_hidingSpots.AddToTail( spot );
  238. }
  239. }
  240. if ( version < 15 )
  241. {
  242. //
  243. // Load number of approach areas
  244. //
  245. m_approachCount = fileBuffer.GetUnsignedChar();
  246. // load approach area info (IDs)
  247. for( int a = 0; a < m_approachCount; ++a )
  248. {
  249. m_approach[a].here.id = fileBuffer.GetUnsignedInt();
  250. m_approach[a].prev.id = fileBuffer.GetUnsignedInt();
  251. m_approach[a].prevToHereHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
  252. m_approach[a].next.id = fileBuffer.GetUnsignedInt();
  253. m_approach[a].hereToNextHow = (NavTraverseType)fileBuffer.GetUnsignedChar();
  254. }
  255. }
  256. //
  257. // Load encounter paths for this area
  258. //
  259. unsigned int count = fileBuffer.GetUnsignedInt();
  260. if (version < 3)
  261. {
  262. // old data, read and discard
  263. for( unsigned int e=0; e<count; ++e )
  264. {
  265. SpotEncounter encounter;
  266. encounter.from.id = fileBuffer.GetUnsignedInt();
  267. encounter.to.id = fileBuffer.GetUnsignedInt();
  268. fileBuffer.Get( &encounter.path.from.x, 3 * sizeof(float) );
  269. fileBuffer.Get( &encounter.path.to.x, 3 * sizeof(float) );
  270. // read list of spots along this path
  271. unsigned char spotCount = fileBuffer.GetUnsignedChar();
  272. for( int s=0; s<spotCount; ++s )
  273. {
  274. fileBuffer.GetFloat();
  275. fileBuffer.GetFloat();
  276. fileBuffer.GetFloat();
  277. fileBuffer.GetFloat();
  278. }
  279. }
  280. return NAV_OK;
  281. }
  282. for( unsigned int e=0; e<count; ++e )
  283. {
  284. SpotEncounter *encounter = new SpotEncounter;
  285. encounter->from.id = fileBuffer.GetUnsignedInt();
  286. unsigned char dir = fileBuffer.GetUnsignedChar();
  287. encounter->fromDir = static_cast<NavDirType>( dir );
  288. encounter->to.id = fileBuffer.GetUnsignedInt();
  289. dir = fileBuffer.GetUnsignedChar();
  290. encounter->toDir = static_cast<NavDirType>( dir );
  291. // read list of spots along this path
  292. unsigned char spotCount = fileBuffer.GetUnsignedChar();
  293. SpotOrder order;
  294. for( int s=0; s<spotCount; ++s )
  295. {
  296. order.id = fileBuffer.GetUnsignedInt();
  297. unsigned char t = fileBuffer.GetUnsignedChar();
  298. order.t = (float)t/255.0f;
  299. encounter->spots.AddToTail( order );
  300. }
  301. m_spotEncounters.AddToTail( encounter );
  302. }
  303. if (version < 5)
  304. return NAV_OK;
  305. //
  306. // Load Place data
  307. //
  308. PlaceDirectory::IndexType entry = fileBuffer.GetUnsignedShort();
  309. // convert entry to actual Place
  310. SetPlace( placeDirectory.IndexToPlace( entry ) );
  311. if ( version < 7 )
  312. return NAV_OK;
  313. // load ladder data
  314. for ( int dir=0; dir<CNavLadder::NUM_LADDER_DIRECTIONS; ++dir )
  315. {
  316. count = fileBuffer.GetUnsignedInt();
  317. for( unsigned int i=0; i<count; ++i )
  318. {
  319. NavLadderConnect connect;
  320. connect.id = fileBuffer.GetUnsignedInt();
  321. bool alreadyConnected = false;
  322. FOR_EACH_VEC( m_ladder[dir], j )
  323. {
  324. if ( m_ladder[dir][j].id == connect.id )
  325. {
  326. alreadyConnected = true;
  327. break;
  328. }
  329. }
  330. if ( !alreadyConnected )
  331. {
  332. m_ladder[dir].AddToTail( connect );
  333. }
  334. }
  335. }
  336. if ( version < 8 )
  337. return NAV_OK;
  338. // load earliest occupy times
  339. for( int i=0; i<MAX_NAV_TEAMS; ++i )
  340. {
  341. // no spot in the map should take longer than this to reach
  342. m_earliestOccupyTime[i] = fileBuffer.GetFloat();
  343. }
  344. if ( version < 11 )
  345. return NAV_OK;
  346. // load light intensity
  347. for ( int i=0; i<NUM_CORNERS; ++i )
  348. {
  349. m_lightIntensity[i] = fileBuffer.GetFloat();
  350. }
  351. if ( version < 16 )
  352. return NAV_OK;
  353. // load visibility information
  354. unsigned int visibleAreaCount = fileBuffer.GetUnsignedInt();
  355. if ( !IsX360() )
  356. {
  357. m_potentiallyVisibleAreas.EnsureCapacity( visibleAreaCount );
  358. }
  359. else
  360. {
  361. /* TODO: Re-enable when latest 360 code gets integrated (MSB 5/5/09)
  362. size_t nBytes = visibleAreaCount * sizeof( AreaBindInfo );
  363. m_potentiallyVisibleAreas.~CAreaBindInfoArray();
  364. new ( &m_potentiallyVisibleAreas ) CAreaBindInfoArray( (AreaBindInfo *)engine->AllocLevelStaticData( nBytes ), visibleAreaCount );
  365. */
  366. }
  367. for( unsigned int j=0; j<visibleAreaCount; ++j )
  368. {
  369. AreaBindInfo info;
  370. info.id = fileBuffer.GetUnsignedInt();
  371. info.attributes = fileBuffer.GetUnsignedChar();
  372. m_potentiallyVisibleAreas.AddToTail( info );
  373. }
  374. // read area from which we inherit visibility
  375. m_inheritVisibilityFrom.id = fileBuffer.GetUnsignedInt();
  376. return NAV_OK;
  377. }