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.

585 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Used to create a path that can be followed by NPCs and trains.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "pathtrack.h"
  8. #include "entitylist.h"
  9. #include "ndebugoverlay.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. //-----------------------------------------------------------------------------
  13. // Save/load
  14. //-----------------------------------------------------------------------------
  15. BEGIN_DATADESC( CPathTrack )
  16. DEFINE_FIELD( m_pnext, FIELD_CLASSPTR ),
  17. DEFINE_FIELD( m_pprevious, FIELD_CLASSPTR ),
  18. DEFINE_FIELD( m_paltpath, FIELD_CLASSPTR ),
  19. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
  20. DEFINE_FIELD( m_length, FIELD_FLOAT ),
  21. DEFINE_KEYFIELD( m_altName, FIELD_STRING, "altpath" ),
  22. DEFINE_KEYFIELD( m_eOrientationType, FIELD_INTEGER, "orientationtype" ),
  23. // DEFINE_FIELD( m_nIterVal, FIELD_INTEGER ),
  24. DEFINE_INPUTFUNC( FIELD_VOID, "InPass", InputPass ),
  25. DEFINE_INPUTFUNC( FIELD_VOID, "InTeleport", InputTeleport ),
  26. DEFINE_INPUTFUNC( FIELD_VOID, "EnableAlternatePath", InputEnableAlternatePath ),
  27. DEFINE_INPUTFUNC( FIELD_VOID, "DisableAlternatePath", InputDisableAlternatePath ),
  28. DEFINE_INPUTFUNC( FIELD_VOID, "ToggleAlternatePath", InputToggleAlternatePath ),
  29. DEFINE_INPUTFUNC( FIELD_VOID, "EnablePath", InputEnablePath ),
  30. DEFINE_INPUTFUNC( FIELD_VOID, "DisablePath", InputDisablePath ),
  31. DEFINE_INPUTFUNC( FIELD_VOID, "TogglePath", InputTogglePath ),
  32. // Outputs
  33. DEFINE_OUTPUT(m_OnPass, "OnPass"),
  34. DEFINE_OUTPUT(m_OnTeleport, "OnTeleport"),
  35. END_DATADESC()
  36. LINK_ENTITY_TO_CLASS( path_track, CPathTrack );
  37. //-----------------------------------------------------------------------------
  38. // Finds circular paths
  39. //-----------------------------------------------------------------------------
  40. int CPathTrack::s_nCurrIterVal = 0;
  41. bool CPathTrack::s_bIsIterating = false;
  42. //-----------------------------------------------------------------------------
  43. // Constructor
  44. //-----------------------------------------------------------------------------
  45. CPathTrack::CPathTrack()
  46. {
  47. m_nIterVal = -1;
  48. m_eOrientationType = TrackOrientation_FacePath;
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Spawn!
  52. //-----------------------------------------------------------------------------
  53. void CPathTrack::Spawn( void )
  54. {
  55. SetSolid( SOLID_NONE );
  56. UTIL_SetSize(this, Vector(-8, -8, -8), Vector(8, 8, 8));
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Activate!
  60. //-----------------------------------------------------------------------------
  61. void CPathTrack::Activate( void )
  62. {
  63. BaseClass::Activate();
  64. if ( GetEntityName() != NULL_STRING ) // Link to next, and back-link
  65. {
  66. Link();
  67. }
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Connects up the previous + next pointers
  71. //-----------------------------------------------------------------------------
  72. void CPathTrack::Link( void )
  73. {
  74. CBaseEntity *pTarget;
  75. if ( m_target != NULL_STRING )
  76. {
  77. pTarget = gEntList.FindEntityByName( NULL, m_target );
  78. if ( pTarget == this)
  79. {
  80. Warning("ERROR: path_track (%s) refers to itself as a target!\n", GetDebugName());
  81. //FIXME: Why were we removing this? If it was already connected to, we weren't updating the other linked
  82. // end, causing problems with walking through bogus memory links! -- jdw
  83. //UTIL_Remove(this);
  84. //return;
  85. }
  86. else if ( pTarget )
  87. {
  88. m_pnext = dynamic_cast<CPathTrack*>( pTarget );
  89. if ( m_pnext ) // If no next pointer, this is the end of a path
  90. {
  91. m_pnext->SetPrevious( this );
  92. }
  93. }
  94. else
  95. {
  96. Warning("Dead end link: %s\n", STRING( m_target ) );
  97. }
  98. }
  99. // Find "alternate" path
  100. if ( m_altName != NULL_STRING )
  101. {
  102. pTarget = gEntList.FindEntityByName( NULL, m_altName );
  103. if ( pTarget )
  104. {
  105. m_paltpath = dynamic_cast<CPathTrack*>( pTarget );
  106. m_paltpath->SetPrevious( this );
  107. }
  108. }
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Circular path checking
  112. //-----------------------------------------------------------------------------
  113. void CPathTrack::BeginIteration()
  114. {
  115. Assert( !s_bIsIterating );
  116. ++s_nCurrIterVal;
  117. s_bIsIterating = true;
  118. }
  119. void CPathTrack::EndIteration()
  120. {
  121. Assert( s_bIsIterating );
  122. s_bIsIterating = false;
  123. }
  124. void CPathTrack::Visit()
  125. {
  126. m_nIterVal = s_nCurrIterVal;
  127. }
  128. bool CPathTrack::HasBeenVisited() const
  129. {
  130. return ( m_nIterVal == s_nCurrIterVal );
  131. }
  132. //-----------------------------------------------------------------------------
  133. // Do we have an alternate path?
  134. //-----------------------------------------------------------------------------
  135. bool CPathTrack::HasAlternathPath() const
  136. {
  137. return ( m_paltpath != NULL );
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose: Toggles the track to or from its alternate path
  141. //-----------------------------------------------------------------------------
  142. void CPathTrack::ToggleAlternatePath( void )
  143. {
  144. // Use toggles between two paths
  145. if ( m_paltpath != NULL )
  146. {
  147. if ( FBitSet( m_spawnflags, SF_PATH_ALTERNATE ) == false )
  148. {
  149. EnableAlternatePath();
  150. }
  151. else
  152. {
  153. DisableAlternatePath();
  154. }
  155. }
  156. }
  157. //-----------------------------------------------------------------------------
  158. // Purpose:
  159. //-----------------------------------------------------------------------------
  160. void CPathTrack::EnableAlternatePath( void )
  161. {
  162. if ( m_paltpath != NULL )
  163. {
  164. SETBITS( m_spawnflags, SF_PATH_ALTERNATE );
  165. }
  166. }
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. //-----------------------------------------------------------------------------
  170. void CPathTrack::DisableAlternatePath( void )
  171. {
  172. if ( m_paltpath != NULL )
  173. {
  174. CLEARBITS( m_spawnflags, SF_PATH_ALTERNATE );
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose:
  179. // Input : &inputdata -
  180. //-----------------------------------------------------------------------------
  181. void CPathTrack::InputEnableAlternatePath( inputdata_t &inputdata )
  182. {
  183. EnableAlternatePath();
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. // Input : &inputdata -
  188. //-----------------------------------------------------------------------------
  189. void CPathTrack::InputDisableAlternatePath( inputdata_t &inputdata )
  190. {
  191. DisableAlternatePath();
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose:
  195. // Input : &inputdata -
  196. //-----------------------------------------------------------------------------
  197. void CPathTrack::InputToggleAlternatePath( inputdata_t &inputdata )
  198. {
  199. ToggleAlternatePath();
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose: Toggles the track to or from its alternate path
  203. //-----------------------------------------------------------------------------
  204. void CPathTrack::TogglePath( void )
  205. {
  206. // Use toggles between two paths
  207. if ( FBitSet( m_spawnflags, SF_PATH_DISABLED ) )
  208. {
  209. EnablePath();
  210. }
  211. else
  212. {
  213. DisablePath();
  214. }
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Purpose:
  218. //-----------------------------------------------------------------------------
  219. void CPathTrack::EnablePath( void )
  220. {
  221. CLEARBITS( m_spawnflags, SF_PATH_DISABLED );
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose:
  225. //-----------------------------------------------------------------------------
  226. void CPathTrack::DisablePath( void )
  227. {
  228. SETBITS( m_spawnflags, SF_PATH_DISABLED );
  229. }
  230. //-----------------------------------------------------------------------------
  231. // Purpose:
  232. // Input : &inputdata -
  233. //-----------------------------------------------------------------------------
  234. void CPathTrack::InputEnablePath( inputdata_t &inputdata )
  235. {
  236. EnablePath();
  237. }
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. // Input : &inputdata -
  241. //-----------------------------------------------------------------------------
  242. void CPathTrack::InputDisablePath( inputdata_t &inputdata )
  243. {
  244. DisablePath();
  245. }
  246. //-----------------------------------------------------------------------------
  247. // Purpose:
  248. // Input : &inputdata -
  249. //-----------------------------------------------------------------------------
  250. void CPathTrack::InputTogglePath( inputdata_t &inputdata )
  251. {
  252. TogglePath();
  253. }
  254. void CPathTrack::DrawDebugGeometryOverlays()
  255. {
  256. // ----------------------------------------------
  257. // Draw line to next target is bbox is selected
  258. // ----------------------------------------------
  259. if (m_debugOverlays & (OVERLAY_BBOX_BIT|OVERLAY_ABSBOX_BIT))
  260. {
  261. if (m_pnext)
  262. {
  263. NDebugOverlay::Line(GetAbsOrigin(),m_pnext->GetAbsOrigin(),255,100,100,true,0.0);
  264. }
  265. }
  266. BaseClass::DrawDebugGeometryOverlays();
  267. }
  268. CPathTrack *CPathTrack::ValidPath( CPathTrack *ppath, int testFlag )
  269. {
  270. if ( !ppath )
  271. return NULL;
  272. if ( testFlag && FBitSet( ppath->m_spawnflags, SF_PATH_DISABLED ) )
  273. return NULL;
  274. return ppath;
  275. }
  276. void CPathTrack::Project( CPathTrack *pstart, CPathTrack *pend, Vector &origin, float dist )
  277. {
  278. if ( pstart && pend )
  279. {
  280. Vector dir = (pend->GetLocalOrigin() - pstart->GetLocalOrigin());
  281. VectorNormalize( dir );
  282. origin = pend->GetLocalOrigin() + dir * dist;
  283. }
  284. }
  285. CPathTrack *CPathTrack::GetNext( void )
  286. {
  287. if ( m_paltpath && FBitSet( m_spawnflags, SF_PATH_ALTERNATE ) && !FBitSet( m_spawnflags, SF_PATH_ALTREVERSE ) )
  288. {
  289. Assert( !m_paltpath.IsValid() || m_paltpath.Get() != NULL );
  290. return m_paltpath;
  291. }
  292. // The paths shouldn't normally be getting deleted so assert that if it was set, it's valid.
  293. Assert( !m_pnext.IsValid() || m_pnext.Get() != NULL );
  294. return m_pnext;
  295. }
  296. CPathTrack *CPathTrack::GetPrevious( void )
  297. {
  298. if ( m_paltpath && FBitSet( m_spawnflags, SF_PATH_ALTERNATE ) && FBitSet( m_spawnflags, SF_PATH_ALTREVERSE ) )
  299. {
  300. Assert( !m_paltpath.IsValid() || m_paltpath.Get() != NULL );
  301. return m_paltpath;
  302. }
  303. Assert( !m_pprevious.IsValid() || m_pprevious.Get() != NULL );
  304. return m_pprevious;
  305. }
  306. void CPathTrack::SetPrevious( CPathTrack *pprev )
  307. {
  308. // Only set previous if this isn't my alternate path
  309. if ( pprev && !FStrEq( STRING(pprev->GetEntityName()), STRING(m_altName) ) )
  310. m_pprevious = pprev;
  311. }
  312. CPathTrack *CPathTrack::GetNextInDir( bool bForward )
  313. {
  314. if ( bForward )
  315. return GetNext();
  316. return GetPrevious();
  317. }
  318. //-----------------------------------------------------------------------------
  319. // Purpose: Assumes this is ALWAYS enabled
  320. // Input : origin - position along path to look ahead from
  321. // dist - distance to look ahead, negative values look backward
  322. // move -
  323. // Output : Returns the track that we will be PAST in 'dist' units.
  324. //-----------------------------------------------------------------------------
  325. CPathTrack *CPathTrack::LookAhead( Vector &origin, float dist, int move, CPathTrack **pNextNext )
  326. {
  327. CPathTrack *pcurrent = this;
  328. float originalDist = dist;
  329. Vector currentPos = origin;
  330. bool bForward = true;
  331. if ( dist < 0 )
  332. {
  333. // Travelling backwards along the path.
  334. dist = -dist;
  335. bForward = false;
  336. }
  337. // Move along the path, until we've gone 'dist' units or run out of path.
  338. while ( dist > 0 )
  339. {
  340. // If there is no next path track, or it's disabled, we're done.
  341. if ( !ValidPath( pcurrent->GetNextInDir( bForward ), move ) )
  342. {
  343. if ( !move )
  344. {
  345. Project( pcurrent->GetNextInDir( !bForward ), pcurrent, origin, dist );
  346. }
  347. return NULL;
  348. }
  349. // The next path track is valid. How far are we from it?
  350. Vector dir = pcurrent->GetNextInDir( bForward )->GetLocalOrigin() - currentPos;
  351. float length = dir.Length();
  352. // If we are at the next node and there isn't one beyond it, return the next node.
  353. if ( !length && !ValidPath( pcurrent->GetNextInDir( bForward )->GetNextInDir( bForward ), move ) )
  354. {
  355. if ( pNextNext )
  356. {
  357. *pNextNext = NULL;
  358. }
  359. if ( dist == originalDist )
  360. {
  361. // Didn't move at all, must be in a dead end.
  362. return NULL;
  363. }
  364. return pcurrent->GetNextInDir( bForward );
  365. }
  366. // If we don't hit the next path track within the distance remaining, we're done.
  367. if ( length > dist )
  368. {
  369. origin = currentPos + ( dir * ( dist / length ) );
  370. if ( pNextNext )
  371. {
  372. *pNextNext = pcurrent->GetNextInDir( bForward );
  373. }
  374. return pcurrent;
  375. }
  376. // We hit the next path track, advance to it.
  377. dist -= length;
  378. currentPos = pcurrent->GetNextInDir( bForward )->GetLocalOrigin();
  379. pcurrent = pcurrent->GetNextInDir( bForward );
  380. origin = currentPos;
  381. }
  382. // We consumed all of the distance, and exactly landed on a path track.
  383. if ( pNextNext )
  384. {
  385. *pNextNext = pcurrent->GetNextInDir( bForward );
  386. }
  387. return pcurrent;
  388. }
  389. // Assumes this is ALWAYS enabled
  390. CPathTrack *CPathTrack::Nearest( const Vector &origin )
  391. {
  392. int deadCount;
  393. float minDist, dist;
  394. Vector delta;
  395. CPathTrack *ppath, *pnearest;
  396. delta = origin - GetLocalOrigin();
  397. delta.z = 0;
  398. minDist = delta.Length();
  399. pnearest = this;
  400. ppath = GetNext();
  401. // Hey, I could use the old 2 racing pointers solution to this, but I'm lazy :)
  402. deadCount = 0;
  403. while ( ppath && ppath != this )
  404. {
  405. deadCount++;
  406. if ( deadCount > 9999 )
  407. {
  408. Warning( "Bad sequence of path_tracks from %s\n", GetDebugName() );
  409. Assert(0);
  410. return NULL;
  411. }
  412. delta = origin - ppath->GetLocalOrigin();
  413. delta.z = 0;
  414. dist = delta.Length();
  415. if ( dist < minDist )
  416. {
  417. minDist = dist;
  418. pnearest = ppath;
  419. }
  420. ppath = ppath->GetNext();
  421. }
  422. return pnearest;
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Returns how the path follower should orient itself when moving
  426. // through this path track.
  427. //-----------------------------------------------------------------------------
  428. TrackOrientationType_t CPathTrack::GetOrientationType()
  429. {
  430. return m_eOrientationType;
  431. }
  432. //-----------------------------------------------------------------------------
  433. // Purpose:
  434. //-----------------------------------------------------------------------------
  435. QAngle CPathTrack::GetOrientation( bool bForwardDir )
  436. {
  437. TrackOrientationType_t eOrient = GetOrientationType();
  438. if ( eOrient == TrackOrientation_FacePathAngles )
  439. {
  440. return GetLocalAngles();
  441. }
  442. CPathTrack *pPrev = this;
  443. CPathTrack *pNext = GetNextInDir( bForwardDir );
  444. if ( !pNext )
  445. { pPrev = GetNextInDir( !bForwardDir );
  446. pNext = this;
  447. }
  448. Vector vecDir = pNext->GetLocalOrigin() - pPrev->GetLocalOrigin();
  449. QAngle angDir;
  450. VectorAngles( vecDir, angDir );
  451. return angDir;
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose:
  455. // Input : *pent -
  456. // Output : CPathTrack
  457. //-----------------------------------------------------------------------------
  458. CPathTrack *CPathTrack::Instance( edict_t *pent )
  459. {
  460. CBaseEntity *pEntity = CBaseEntity::Instance( pent );
  461. if ( FClassnameIs( pEntity, "path_track" ) )
  462. return (CPathTrack *)pEntity;
  463. return NULL;
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose:
  467. //-----------------------------------------------------------------------------
  468. void CPathTrack::InputPass( inputdata_t &inputdata )
  469. {
  470. m_OnPass.FireOutput( inputdata.pActivator, this );
  471. #ifdef TF_DLL
  472. IGameEvent * event = gameeventmanager->CreateEvent( "path_track_passed" );
  473. if ( event )
  474. {
  475. event->SetInt( "index", entindex() );
  476. gameeventmanager->FireEvent( event, true );
  477. }
  478. #endif
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Purpose:
  482. //-----------------------------------------------------------------------------
  483. void CPathTrack::InputTeleport( inputdata_t &inputdata )
  484. {
  485. m_OnTeleport.FireOutput( inputdata.pActivator, this );
  486. }