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.

505 lines
12 KiB

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