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.

3378 lines
84 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Spawn, think, and touch functions for trains, etc.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "ai_basenpc.h"
  8. #include "trains.h"
  9. #include "ndebugoverlay.h"
  10. #include "entitylist.h"
  11. #include "engine/IEngineSound.h"
  12. #include "soundenvelope.h"
  13. #include "physics_npc_solver.h"
  14. #include "vphysics/friction.h"
  15. #include "hierarchy.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. static void PlatSpawnInsideTrigger(edict_t *pevPlatform);
  19. #define SF_PLAT_TOGGLE 0x0001
  20. class CBasePlatTrain : public CBaseToggle
  21. {
  22. DECLARE_CLASS( CBasePlatTrain, CBaseToggle );
  23. public:
  24. ~CBasePlatTrain();
  25. bool KeyValue( const char *szKeyName, const char *szValue );
  26. void Precache( void );
  27. // This is done to fix spawn flag collisions between this class and a derived class
  28. virtual bool IsTogglePlat( void ) { return (m_spawnflags & SF_PLAT_TOGGLE) ? true : false; }
  29. DECLARE_DATADESC();
  30. void PlayMovingSound();
  31. void StopMovingSound();
  32. string_t m_NoiseMoving; // sound a plat makes while moving
  33. string_t m_NoiseArrived;
  34. CSoundPatch *m_pMovementSound;
  35. #ifdef HL1_DLL
  36. int m_MoveSound;
  37. int m_StopSound;
  38. #endif
  39. float m_volume; // Sound volume
  40. float m_flTWidth;
  41. float m_flTLength;
  42. };
  43. BEGIN_DATADESC( CBasePlatTrain )
  44. DEFINE_KEYFIELD( m_NoiseMoving, FIELD_SOUNDNAME, "noise1" ),
  45. DEFINE_KEYFIELD( m_NoiseArrived, FIELD_SOUNDNAME, "noise2" ),
  46. #ifdef HL1_DLL
  47. DEFINE_KEYFIELD( m_MoveSound, FIELD_INTEGER, "movesnd" ),
  48. DEFINE_KEYFIELD( m_StopSound, FIELD_INTEGER, "stopsnd" ),
  49. #endif
  50. DEFINE_SOUNDPATCH( m_pMovementSound ),
  51. DEFINE_KEYFIELD( m_volume, FIELD_FLOAT, "volume" ),
  52. DEFINE_FIELD( m_flTWidth, FIELD_FLOAT ),
  53. DEFINE_FIELD( m_flTLength, FIELD_FLOAT ),
  54. DEFINE_KEYFIELD( m_flLip, FIELD_FLOAT, "lip" ),
  55. DEFINE_KEYFIELD( m_flWait, FIELD_FLOAT, "wait" ),
  56. DEFINE_KEYFIELD( m_flHeight, FIELD_FLOAT, "height" ),
  57. END_DATADESC()
  58. bool CBasePlatTrain::KeyValue( const char *szKeyName, const char *szValue )
  59. {
  60. if (FStrEq(szKeyName, "rotation"))
  61. {
  62. m_vecFinalAngle.x = atof(szValue);
  63. }
  64. else
  65. {
  66. return BaseClass::KeyValue( szKeyName, szValue );
  67. }
  68. return true;
  69. }
  70. CBasePlatTrain::~CBasePlatTrain()
  71. {
  72. StopMovingSound();
  73. }
  74. void CBasePlatTrain::PlayMovingSound()
  75. {
  76. StopMovingSound();
  77. if(m_NoiseMoving != NULL_STRING )
  78. {
  79. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  80. CPASAttenuationFilter filter( this );
  81. m_pMovementSound = controller.SoundCreate( filter, entindex(), CHAN_STATIC, STRING(m_NoiseMoving), ATTN_NORM );
  82. controller.Play( m_pMovementSound, m_volume, PITCH_NORM );
  83. }
  84. }
  85. void CBasePlatTrain::StopMovingSound()
  86. {
  87. if ( m_pMovementSound )
  88. {
  89. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  90. controller.SoundDestroy( m_pMovementSound );
  91. m_pMovementSound = NULL;
  92. }
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose:
  96. //-----------------------------------------------------------------------------
  97. void CBasePlatTrain::Precache( void )
  98. {
  99. //Fill in a default value if necessary
  100. UTIL_ValidateSoundName( m_NoiseMoving, "Plat.DefaultMoving" );
  101. UTIL_ValidateSoundName( m_NoiseArrived, "Plat.DefaultArrive" );
  102. #ifdef HL1_DLL
  103. // set the plat's "in-motion" sound
  104. switch (m_MoveSound)
  105. {
  106. default:
  107. case 0:
  108. m_NoiseMoving = MAKE_STRING( "Plat.DefaultMoving" );
  109. break;
  110. case 1:
  111. m_NoiseMoving = MAKE_STRING("Plat.BigElev1");
  112. break;
  113. case 2:
  114. m_NoiseMoving = MAKE_STRING("Plat.BigElev2");
  115. break;
  116. case 3:
  117. m_NoiseMoving = MAKE_STRING("Plat.TechElev1");
  118. break;
  119. case 4:
  120. m_NoiseMoving = MAKE_STRING("Plat.TechElev2");
  121. break;
  122. case 5:
  123. m_NoiseMoving = MAKE_STRING("Plat.TechElev3");
  124. break;
  125. case 6:
  126. m_NoiseMoving = MAKE_STRING("Plat.FreightElev1");
  127. break;
  128. case 7:
  129. m_NoiseMoving = MAKE_STRING("Plat.FreightElev2");
  130. break;
  131. case 8:
  132. m_NoiseMoving = MAKE_STRING("Plat.HeavyElev");
  133. break;
  134. case 9:
  135. m_NoiseMoving = MAKE_STRING("Plat.RackElev");
  136. break;
  137. case 10:
  138. m_NoiseMoving = MAKE_STRING("Plat.RailElev");
  139. break;
  140. case 11:
  141. m_NoiseMoving = MAKE_STRING("Plat.SqueakElev");
  142. break;
  143. case 12:
  144. m_NoiseMoving = MAKE_STRING("Plat.OddElev1");
  145. break;
  146. case 13:
  147. m_NoiseMoving = MAKE_STRING("Plat.OddElev2");
  148. break;
  149. }
  150. // set the plat's 'reached destination' stop sound
  151. switch (m_StopSound)
  152. {
  153. default:
  154. case 0:
  155. m_NoiseArrived = MAKE_STRING( "Plat.DefaultArrive" );
  156. break;
  157. case 1:
  158. m_NoiseArrived = MAKE_STRING("Plat.BigElevStop1");
  159. break;
  160. case 2:
  161. m_NoiseArrived = MAKE_STRING("Plat.BigElevStop2");
  162. break;
  163. case 3:
  164. m_NoiseArrived = MAKE_STRING("Plat.FreightElevStop");
  165. break;
  166. case 4:
  167. m_NoiseArrived = MAKE_STRING("Plat.HeavyElevStop");
  168. break;
  169. case 5:
  170. m_NoiseArrived = MAKE_STRING("Plat.RackStop");
  171. break;
  172. case 6:
  173. m_NoiseArrived = MAKE_STRING("Plat.RailStop");
  174. break;
  175. case 7:
  176. m_NoiseArrived = MAKE_STRING("Plat.SqueakStop");
  177. break;
  178. case 8:
  179. m_NoiseArrived = MAKE_STRING("Plat.QuickStop");
  180. break;
  181. }
  182. #endif // HL1_DLL
  183. //Precache them all
  184. PrecacheScriptSound( (char *) STRING(m_NoiseMoving) );
  185. PrecacheScriptSound( (char *) STRING(m_NoiseArrived) );
  186. }
  187. class CFuncPlat : public CBasePlatTrain
  188. {
  189. DECLARE_CLASS( CFuncPlat, CBasePlatTrain );
  190. public:
  191. void Spawn( void );
  192. void Precache( void );
  193. bool CreateVPhysics();
  194. void Setup( void );
  195. virtual void Blocked( CBaseEntity *pOther );
  196. void PlatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  197. void CallGoDown( void ) { GoDown(); }
  198. void CallHitTop( void ) { HitTop(); }
  199. void CallHitBottom( void ) { HitBottom(); }
  200. virtual void GoUp( void );
  201. virtual void GoDown( void );
  202. virtual void HitTop( void );
  203. virtual void HitBottom( void );
  204. void InputToggle(inputdata_t &data);
  205. void InputGoUp(inputdata_t &data);
  206. void InputGoDown(inputdata_t &data);
  207. DECLARE_DATADESC();
  208. private:
  209. string_t m_sNoise;
  210. };
  211. BEGIN_DATADESC( CFuncPlat )
  212. DEFINE_FIELD( m_sNoise, FIELD_STRING ),
  213. // Function Pointers
  214. DEFINE_FUNCTION( PlatUse ),
  215. DEFINE_FUNCTION( CallGoDown ),
  216. DEFINE_FUNCTION( CallHitTop ),
  217. DEFINE_FUNCTION( CallHitBottom ),
  218. // Inputs
  219. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  220. DEFINE_INPUTFUNC( FIELD_VOID, "GoUp", InputGoUp ),
  221. DEFINE_INPUTFUNC( FIELD_VOID, "GoDown", InputGoDown ),
  222. END_DATADESC()
  223. LINK_ENTITY_TO_CLASS( func_plat, CFuncPlat );
  224. //==================================================
  225. // CPlatTrigger
  226. //==================================================
  227. class CPlatTrigger : public CBaseEntity
  228. {
  229. DECLARE_CLASS( CPlatTrigger, CBaseEntity );
  230. public:
  231. virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_DONT_SAVE; }
  232. void SpawnInsideTrigger( CFuncPlat *pPlatform );
  233. void Touch( CBaseEntity *pOther );
  234. CFuncPlat *m_pPlatform;
  235. };
  236. void CFuncPlat::Setup( void )
  237. {
  238. if (m_flTLength == 0)
  239. {
  240. m_flTLength = 80;
  241. }
  242. if (m_flTWidth == 0)
  243. {
  244. m_flTWidth = 10;
  245. }
  246. SetLocalAngles( vec3_angle );
  247. SetSolid( SOLID_BSP );
  248. SetMoveType( MOVETYPE_PUSH );
  249. // Set size and link into world
  250. SetModel( STRING( GetModelName() ) );
  251. m_vecPosition1 = GetLocalOrigin(); //Top
  252. m_vecPosition2 = GetLocalOrigin(); //Bottom
  253. if ( m_flHeight != 0 )
  254. {
  255. m_vecPosition2.z = GetLocalOrigin().z - m_flHeight;
  256. }
  257. else
  258. {
  259. // NOTE: This works because the angles were set to vec3_angle above
  260. m_vecPosition2.z = GetLocalOrigin().z - CollisionProp()->OBBSize().z + 8;
  261. }
  262. if (m_flSpeed == 0)
  263. {
  264. m_flSpeed = 150;
  265. }
  266. if ( m_volume == 0.0f )
  267. {
  268. m_volume = 0.85f;
  269. }
  270. }
  271. void CFuncPlat::Precache( )
  272. {
  273. BaseClass::Precache();
  274. if ( IsTogglePlat() == false )
  275. {
  276. // Create the "start moving" trigger
  277. PlatSpawnInsideTrigger( edict() );
  278. }
  279. }
  280. void CFuncPlat::Spawn( )
  281. {
  282. Setup();
  283. Precache();
  284. // If this platform is the target of some button, it starts at the TOP position,
  285. // and is brought down by that button. Otherwise, it starts at BOTTOM.
  286. if ( GetEntityName() != NULL_STRING )
  287. {
  288. UTIL_SetOrigin( this, m_vecPosition1);
  289. m_toggle_state = TS_AT_TOP;
  290. SetUse( &CFuncPlat::PlatUse );
  291. }
  292. else
  293. {
  294. UTIL_SetOrigin( this, m_vecPosition2);
  295. m_toggle_state = TS_AT_BOTTOM;
  296. }
  297. CreateVPhysics();
  298. }
  299. bool CFuncPlat::CreateVPhysics()
  300. {
  301. VPhysicsInitShadow( false, false );
  302. return true;
  303. }
  304. static void PlatSpawnInsideTrigger(edict_t* pevPlatform)
  305. {
  306. // old code: //GetClassPtr( (CPlatTrigger *)NULL)->SpawnInsideTrigger( GetClassPtr( (CFuncPlat *)pevPlatform ) );
  307. CPlatTrigger *plattrig = CREATE_UNSAVED_ENTITY( CPlatTrigger, "plat_trigger" );
  308. plattrig->SpawnInsideTrigger( (CFuncPlat *)GetContainingEntity( pevPlatform ) );
  309. }
  310. //
  311. // Create a trigger entity for a platform.
  312. //
  313. void CPlatTrigger::SpawnInsideTrigger( CFuncPlat *pPlatform )
  314. {
  315. m_pPlatform = pPlatform;
  316. // Create trigger entity, "point" it at the owning platform, give it a touch method
  317. SetSolid( SOLID_BSP );
  318. AddSolidFlags( FSOLID_TRIGGER );
  319. SetMoveType( MOVETYPE_NONE );
  320. SetLocalOrigin( pPlatform->GetLocalOrigin() );
  321. // Establish the trigger field's size
  322. CCollisionProperty *pCollision = m_pPlatform->CollisionProp();
  323. Vector vecTMin = pCollision->OBBMins() + Vector ( 25 , 25 , 0 );
  324. Vector vecTMax = pCollision->OBBMaxs() + Vector ( 25 , 25 , 8 );
  325. vecTMin.z = vecTMax.z - ( m_pPlatform->m_vecPosition1.z - m_pPlatform->m_vecPosition2.z + 8 );
  326. if ( pCollision->OBBSize().x <= 50 )
  327. {
  328. vecTMin.x = (pCollision->OBBMins().x + pCollision->OBBMaxs().x) / 2;
  329. vecTMax.x = vecTMin.x + 1;
  330. }
  331. if ( pCollision->OBBSize().y <= 50 )
  332. {
  333. vecTMin.y = (pCollision->OBBMins().y + pCollision->OBBMaxs().y) / 2;
  334. vecTMax.y = vecTMin.y + 1;
  335. }
  336. UTIL_SetSize ( this, vecTMin, vecTMax );
  337. }
  338. //
  339. // When the platform's trigger field is touched, the platform ???
  340. //
  341. void CPlatTrigger::Touch( CBaseEntity *pOther )
  342. {
  343. // Ignore touches by non-players
  344. if ( !pOther->IsPlayer() )
  345. return;
  346. // Ignore touches by corpses
  347. if (!pOther->IsAlive())
  348. return;
  349. // Make linked platform go up/down.
  350. if (m_pPlatform->m_toggle_state == TS_AT_BOTTOM)
  351. m_pPlatform->GoUp();
  352. else if (m_pPlatform->m_toggle_state == TS_AT_TOP)
  353. m_pPlatform->SetMoveDoneTime( 1 );// delay going down
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose: Used when a platform is the target of a button.
  357. // Start bringing platform down.
  358. // Input : pActivator -
  359. // pCaller -
  360. // useType -
  361. // value -
  362. //-----------------------------------------------------------------------------
  363. void CFuncPlat::InputToggle(inputdata_t &data)
  364. {
  365. if ( IsTogglePlat() )
  366. {
  367. if (m_toggle_state == TS_AT_TOP)
  368. GoDown();
  369. else if ( m_toggle_state == TS_AT_BOTTOM )
  370. GoUp();
  371. }
  372. else
  373. {
  374. SetUse( NULL );
  375. if (m_toggle_state == TS_AT_TOP)
  376. GoDown();
  377. }
  378. }
  379. void CFuncPlat::InputGoUp(inputdata_t &data)
  380. {
  381. if ( m_toggle_state == TS_AT_BOTTOM )
  382. GoUp();
  383. }
  384. void CFuncPlat::InputGoDown(inputdata_t &data)
  385. {
  386. if ( m_toggle_state == TS_AT_TOP )
  387. GoDown();
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: Used when a platform is the target of a button.
  391. // Start bringing platform down.
  392. // Input : pActivator -
  393. // pCaller -
  394. // useType -
  395. // value -
  396. //-----------------------------------------------------------------------------
  397. void CFuncPlat::PlatUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  398. {
  399. if ( IsTogglePlat() )
  400. {
  401. // Top is off, bottom is on
  402. bool on = (m_toggle_state == TS_AT_BOTTOM) ? true : false;
  403. if ( !ShouldToggle( useType, on ) )
  404. return;
  405. if (m_toggle_state == TS_AT_TOP)
  406. GoDown();
  407. else if ( m_toggle_state == TS_AT_BOTTOM )
  408. GoUp();
  409. }
  410. else
  411. {
  412. SetUse( NULL );
  413. if (m_toggle_state == TS_AT_TOP)
  414. GoDown();
  415. }
  416. }
  417. //
  418. // Platform is at top, now starts moving down.
  419. //
  420. void CFuncPlat::GoDown( void )
  421. {
  422. PlayMovingSound();
  423. ASSERT(m_toggle_state == TS_AT_TOP || m_toggle_state == TS_GOING_UP);
  424. m_toggle_state = TS_GOING_DOWN;
  425. SetMoveDone(&CFuncPlat::CallHitBottom);
  426. LinearMove(m_vecPosition2, m_flSpeed);
  427. }
  428. //
  429. // Platform has hit bottom. Stops and waits forever.
  430. //
  431. void CFuncPlat::HitBottom( void )
  432. {
  433. StopMovingSound();
  434. if ( m_NoiseArrived != NULL_STRING )
  435. {
  436. CPASAttenuationFilter filter( this );
  437. EmitSound_t ep;
  438. ep.m_nChannel = CHAN_WEAPON;
  439. ep.m_pSoundName = STRING(m_NoiseArrived);
  440. ep.m_flVolume = m_volume;
  441. ep.m_SoundLevel = SNDLVL_NORM;
  442. EmitSound( filter, entindex(), ep );
  443. }
  444. ASSERT(m_toggle_state == TS_GOING_DOWN);
  445. m_toggle_state = TS_AT_BOTTOM;
  446. }
  447. //
  448. // Platform is at bottom, now starts moving up
  449. //
  450. void CFuncPlat::GoUp( void )
  451. {
  452. PlayMovingSound();
  453. ASSERT(m_toggle_state == TS_AT_BOTTOM || m_toggle_state == TS_GOING_DOWN);
  454. m_toggle_state = TS_GOING_UP;
  455. SetMoveDone(&CFuncPlat::CallHitTop);
  456. LinearMove(m_vecPosition1, m_flSpeed);
  457. }
  458. //
  459. // Platform has hit top. Pauses, then starts back down again.
  460. //
  461. void CFuncPlat::HitTop( void )
  462. {
  463. StopMovingSound();
  464. if ( m_NoiseArrived != NULL_STRING )
  465. {
  466. CPASAttenuationFilter filter( this );
  467. EmitSound_t ep;
  468. ep.m_nChannel = CHAN_WEAPON;
  469. ep.m_pSoundName = STRING(m_NoiseArrived);
  470. ep.m_flVolume = m_volume;
  471. ep.m_SoundLevel = SNDLVL_NORM;
  472. EmitSound( filter, entindex(), ep );
  473. }
  474. ASSERT(m_toggle_state == TS_GOING_UP);
  475. m_toggle_state = TS_AT_TOP;
  476. if ( !IsTogglePlat() )
  477. {
  478. // After a delay, the platform will automatically start going down again.
  479. SetMoveDone( &CFuncPlat::CallGoDown );
  480. SetMoveDoneTime( 3 );
  481. }
  482. }
  483. //-----------------------------------------------------------------------------
  484. // Purpose: Called when we are blocked.
  485. //-----------------------------------------------------------------------------
  486. void CFuncPlat::Blocked( CBaseEntity *pOther )
  487. {
  488. DevMsg( 2, "%s Blocked by %s\n", GetClassname(), pOther->GetClassname() );
  489. // Hurt the blocker a little
  490. pOther->TakeDamage( CTakeDamageInfo( this, this, 1, DMG_CRUSH ) );
  491. if (m_sNoise != NULL_STRING)
  492. {
  493. StopSound(entindex(), CHAN_STATIC, (char*)STRING(m_sNoise));
  494. }
  495. // Send the platform back where it came from
  496. ASSERT(m_toggle_state == TS_GOING_UP || m_toggle_state == TS_GOING_DOWN);
  497. if (m_toggle_state == TS_GOING_UP)
  498. {
  499. GoDown();
  500. }
  501. else if (m_toggle_state == TS_GOING_DOWN)
  502. {
  503. GoUp ();
  504. }
  505. }
  506. class CFuncPlatRot : public CFuncPlat
  507. {
  508. DECLARE_CLASS( CFuncPlatRot, CFuncPlat );
  509. public:
  510. void Spawn( void );
  511. void SetupRotation( void );
  512. virtual void GoUp( void );
  513. virtual void GoDown( void );
  514. virtual void HitTop( void );
  515. virtual void HitBottom( void );
  516. void RotMove( QAngle &destAngle, float time );
  517. DECLARE_DATADESC();
  518. QAngle m_end, m_start;
  519. };
  520. LINK_ENTITY_TO_CLASS( func_platrot, CFuncPlatRot );
  521. BEGIN_DATADESC( CFuncPlatRot )
  522. DEFINE_FIELD( m_end, FIELD_VECTOR ),
  523. DEFINE_FIELD( m_start, FIELD_VECTOR ),
  524. END_DATADESC()
  525. void CFuncPlatRot::SetupRotation( void )
  526. {
  527. if ( m_vecFinalAngle.x != 0 ) // This plat rotates too!
  528. {
  529. CBaseToggle::AxisDir();
  530. m_start = GetLocalAngles();
  531. m_end = GetLocalAngles() + m_vecMoveAng * m_vecFinalAngle.x;
  532. }
  533. else
  534. {
  535. m_start = vec3_angle;
  536. m_end = vec3_angle;
  537. }
  538. if ( GetEntityName() != NULL_STRING ) // Start at top
  539. {
  540. SetLocalAngles( m_end );
  541. }
  542. }
  543. void CFuncPlatRot::Spawn( void )
  544. {
  545. BaseClass::Spawn();
  546. SetupRotation();
  547. }
  548. void CFuncPlatRot::GoDown( void )
  549. {
  550. BaseClass::GoDown();
  551. RotMove( m_start, GetMoveDoneTime() );
  552. }
  553. //
  554. // Platform has hit bottom. Stops and waits forever.
  555. //
  556. void CFuncPlatRot::HitBottom( void )
  557. {
  558. BaseClass::HitBottom();
  559. SetLocalAngularVelocity( vec3_angle );
  560. SetLocalAngles( m_start );
  561. }
  562. //
  563. // Platform is at bottom, now starts moving up
  564. //
  565. void CFuncPlatRot::GoUp( void )
  566. {
  567. BaseClass::GoUp();
  568. RotMove( m_end, GetMoveDoneTime() );
  569. }
  570. //
  571. // Platform has hit top. Pauses, then starts back down again.
  572. //
  573. void CFuncPlatRot::HitTop( void )
  574. {
  575. BaseClass::HitTop();
  576. SetLocalAngularVelocity( vec3_angle );
  577. SetLocalAngles( m_end );
  578. }
  579. void CFuncPlatRot::RotMove( QAngle &destAngle, float time )
  580. {
  581. // set destdelta to the vector needed to move
  582. QAngle vecDestDelta = destAngle - GetLocalAngles();
  583. // Travel time is so short, we're practically there already; so make it so.
  584. if ( time >= 0.1)
  585. SetLocalAngularVelocity( vecDestDelta * (1.0 / time) );
  586. else
  587. {
  588. SetLocalAngularVelocity( vecDestDelta );
  589. SetMoveDoneTime( 1 );
  590. }
  591. }
  592. class CFuncTrain : public CBasePlatTrain
  593. {
  594. DECLARE_CLASS( CFuncTrain, CBasePlatTrain );
  595. public:
  596. void Spawn( void );
  597. void Precache( void );
  598. void Activate( void );
  599. void OnRestore( void );
  600. void SetupTarget( void );
  601. void Blocked( CBaseEntity *pOther );
  602. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  603. void Wait( void );
  604. void Next( void );
  605. //Inputs
  606. void InputToggle(inputdata_t &data);
  607. void InputStart(inputdata_t &data);
  608. void InputStop(inputdata_t &data);
  609. void Start( void );
  610. void Stop( void );
  611. DECLARE_DATADESC();
  612. public:
  613. EHANDLE m_hCurrentTarget;
  614. bool m_activated;
  615. EHANDLE m_hEnemy;
  616. float m_flBlockDamage; // Damage to inflict when blocked.
  617. float m_flNextBlockTime;
  618. string_t m_iszLastTarget;
  619. };
  620. LINK_ENTITY_TO_CLASS( func_train, CFuncTrain );
  621. BEGIN_DATADESC( CFuncTrain )
  622. DEFINE_FIELD( m_hCurrentTarget, FIELD_EHANDLE ),
  623. DEFINE_FIELD( m_activated, FIELD_BOOLEAN ),
  624. DEFINE_FIELD( m_hEnemy, FIELD_EHANDLE ),
  625. DEFINE_FIELD( m_iszLastTarget, FIELD_STRING ),
  626. DEFINE_FIELD( m_flNextBlockTime, FIELD_TIME ),
  627. DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "dmg" ),
  628. // Function Pointers
  629. DEFINE_FUNCTION( Wait ),
  630. DEFINE_FUNCTION( Next ),
  631. // Inputs
  632. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  633. DEFINE_INPUTFUNC( FIELD_VOID, "Start", InputStart ),
  634. DEFINE_INPUTFUNC( FIELD_VOID, "Stop", InputStop ),
  635. END_DATADESC()
  636. //-----------------------------------------------------------------------------
  637. // Purpose: Handles a train being blocked by an entity.
  638. // Input : pOther - What was hit.
  639. //-----------------------------------------------------------------------------
  640. void CFuncTrain::Blocked( CBaseEntity *pOther )
  641. {
  642. if ( gpGlobals->curtime < m_flNextBlockTime )
  643. return;
  644. m_flNextBlockTime = gpGlobals->curtime + 0.5;
  645. //Inflict damage
  646. pOther->TakeDamage( CTakeDamageInfo( this, this, m_flBlockDamage, DMG_CRUSH ) );
  647. }
  648. void CFuncTrain::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  649. {
  650. //If we've been waiting to be retriggered, move to the next destination
  651. if ( m_spawnflags & SF_TRAIN_WAIT_RETRIGGER )
  652. {
  653. // Move toward my target
  654. m_spawnflags &= ~SF_TRAIN_WAIT_RETRIGGER;
  655. Next();
  656. }
  657. else
  658. {
  659. m_spawnflags |= SF_TRAIN_WAIT_RETRIGGER;
  660. // Pop back to last target if it's available
  661. if ( m_hEnemy )
  662. {
  663. m_target = m_hEnemy->GetEntityName();
  664. }
  665. SetNextThink( TICK_NEVER_THINK );
  666. SetLocalVelocity( vec3_origin );
  667. if ( m_NoiseArrived != NULL_STRING )
  668. {
  669. CPASAttenuationFilter filter( this );
  670. EmitSound_t ep;
  671. ep.m_nChannel = CHAN_VOICE;
  672. ep.m_pSoundName = STRING(m_NoiseArrived);
  673. ep.m_flVolume = m_volume;
  674. ep.m_SoundLevel = SNDLVL_NORM;
  675. EmitSound( filter, entindex(), ep );
  676. }
  677. }
  678. }
  679. void CFuncTrain::Wait( void )
  680. {
  681. //If we're moving passed a path track, then trip its output
  682. variant_t emptyVariant;
  683. m_hCurrentTarget->AcceptInput( "InPass", this, this, emptyVariant, 0 );
  684. // need pointer to LAST target.
  685. if ( m_hCurrentTarget->HasSpawnFlags( SF_TRAIN_WAIT_RETRIGGER ) || HasSpawnFlags( SF_TRAIN_WAIT_RETRIGGER ) )
  686. {
  687. AddSpawnFlags( SF_TRAIN_WAIT_RETRIGGER );
  688. // Clear the sound channel.
  689. StopMovingSound();
  690. if ( m_NoiseArrived != NULL_STRING )
  691. {
  692. CPASAttenuationFilter filter( this );
  693. EmitSound_t ep;
  694. ep.m_nChannel = CHAN_VOICE;
  695. ep.m_pSoundName = STRING(m_NoiseArrived);
  696. ep.m_flVolume = m_volume;
  697. ep.m_SoundLevel = SNDLVL_NORM;
  698. EmitSound( filter, entindex(), ep );
  699. }
  700. SetMoveDoneTime( -1 );
  701. return;
  702. }
  703. //NOTENOTE: -1 wait will wait forever
  704. if ( m_flWait != 0 )
  705. {
  706. SetMoveDoneTime( m_flWait );
  707. StopMovingSound();
  708. if ( m_NoiseArrived != NULL_STRING )
  709. {
  710. CPASAttenuationFilter filter( this );
  711. EmitSound_t ep;
  712. ep.m_nChannel = CHAN_VOICE;
  713. ep.m_pSoundName = STRING(m_NoiseArrived);
  714. ep.m_flVolume = m_volume;
  715. ep.m_SoundLevel = SNDLVL_NORM;
  716. EmitSound( filter, entindex(), ep );
  717. }
  718. SetMoveDone( &CFuncTrain::Next );
  719. }
  720. else
  721. {
  722. // Do it right now
  723. Next();
  724. }
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Purpose: Advances the train to the next path corner on the path.
  728. //-----------------------------------------------------------------------------
  729. void CFuncTrain::Next( void )
  730. {
  731. //Find our next target
  732. CBaseEntity *pTarg = GetNextTarget();
  733. //If none, we're done
  734. if ( pTarg == NULL )
  735. {
  736. //Stop the moving sound
  737. StopMovingSound();
  738. // Play stop sound
  739. if ( m_NoiseArrived != NULL_STRING )
  740. {
  741. CPASAttenuationFilter filter( this );
  742. EmitSound_t ep;
  743. ep.m_nChannel = CHAN_VOICE;
  744. ep.m_pSoundName = STRING(m_NoiseArrived);
  745. ep.m_flVolume = m_volume;
  746. ep.m_SoundLevel = SNDLVL_NORM;
  747. EmitSound( filter, entindex(), ep );
  748. }
  749. return;
  750. }
  751. // Save last target in case we need to find it again
  752. m_iszLastTarget = m_target;
  753. m_target = pTarg->m_target;
  754. m_flWait = pTarg->GetDelay();
  755. // If our target has a speed, take it
  756. if ( m_hCurrentTarget && m_hCurrentTarget->m_flSpeed != 0 )
  757. {
  758. m_flSpeed = m_hCurrentTarget->m_flSpeed;
  759. DevMsg( 2, "Train %s speed to %4.2f\n", GetDebugName(), m_flSpeed );
  760. }
  761. // Keep track of this since path corners change our target for us
  762. m_hCurrentTarget = pTarg;
  763. m_hEnemy = pTarg;
  764. //Check for teleport
  765. if ( m_hCurrentTarget->HasSpawnFlags( SF_CORNER_TELEPORT ) )
  766. {
  767. IncrementInterpolationFrame();
  768. // This is supposed to place the center of the func_train at the target's origin.
  769. // FIXME: This is totally busted! It's using the wrong space for the computation...
  770. UTIL_SetOrigin( this, pTarg->GetLocalOrigin() - CollisionProp()->OBBCenter() );
  771. // Get on with doing the next path corner.
  772. Wait();
  773. }
  774. else
  775. {
  776. // Normal linear move
  777. PlayMovingSound();
  778. SetMoveDone( &CFuncTrain::Wait );
  779. // This is supposed to place the center of the func_train at the target's origin.
  780. // FIXME: This is totally busted! It's using the wrong space for the computation...
  781. LinearMove ( pTarg->GetLocalOrigin() - CollisionProp()->OBBCenter(), m_flSpeed );
  782. }
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose: Called after all the entities spawn.
  786. //-----------------------------------------------------------------------------
  787. void CFuncTrain::Activate( void )
  788. {
  789. BaseClass::Activate();
  790. // Not yet active, so teleport to first target
  791. if ( m_activated == false )
  792. {
  793. SetupTarget();
  794. m_activated = true;
  795. if ( m_hCurrentTarget.Get() == NULL )
  796. return;
  797. // This is supposed to place the center of the func_train at the target's origin.
  798. // FIXME: This is totally busted! It's using the wrong space for the computation...
  799. UTIL_SetOrigin( this, m_hCurrentTarget->GetLocalOrigin() - CollisionProp()->OBBCenter() );
  800. if ( GetSolid() == SOLID_BSP )
  801. {
  802. VPhysicsInitShadow( false, false );
  803. }
  804. // Start immediately if not triggered
  805. if ( !GetEntityName() )
  806. {
  807. SetMoveDoneTime( 0.1 );
  808. SetMoveDone( &CFuncTrain::Next );
  809. }
  810. else
  811. {
  812. m_spawnflags |= SF_TRAIN_WAIT_RETRIGGER;
  813. }
  814. }
  815. }
  816. //-----------------------------------------------------------------------------
  817. // Purpose:
  818. //-----------------------------------------------------------------------------
  819. void CFuncTrain::SetupTarget( void )
  820. {
  821. // Find our target whenever we don't have one (level transition)
  822. if ( !m_hCurrentTarget )
  823. {
  824. CBaseEntity *pTarg = gEntList.FindEntityByName( NULL, m_target );
  825. if ( pTarg == NULL )
  826. {
  827. Msg( "Can't find target of train %s\n", STRING(m_target) );
  828. return;
  829. }
  830. // Keep track of this since path corners change our target for us
  831. m_target = pTarg->m_target;
  832. m_hCurrentTarget = pTarg;
  833. }
  834. }
  835. //-----------------------------------------------------------------------------
  836. // Purpose:
  837. //-----------------------------------------------------------------------------
  838. void CFuncTrain::Spawn( void )
  839. {
  840. Precache();
  841. if ( m_flSpeed == 0 )
  842. {
  843. m_flSpeed = 100;
  844. }
  845. if ( !m_target )
  846. {
  847. Warning("FuncTrain '%s' has no target.\n", GetDebugName());
  848. }
  849. if ( m_flBlockDamage == 0 )
  850. {
  851. m_flBlockDamage = 2;
  852. }
  853. SetMoveType( MOVETYPE_PUSH );
  854. SetSolid( SOLID_BSP );
  855. SetModel( STRING( GetModelName() ) );
  856. if ( m_spawnflags & SF_TRACKTRAIN_PASSABLE )
  857. {
  858. AddSolidFlags( FSOLID_NOT_SOLID );
  859. }
  860. m_activated = false;
  861. if ( m_volume == 0.0f )
  862. {
  863. m_volume = 0.85f;
  864. }
  865. }
  866. void CFuncTrain::Precache( void )
  867. {
  868. BaseClass::Precache();
  869. }
  870. void CFuncTrain::OnRestore( void )
  871. {
  872. BaseClass::OnRestore();
  873. // Are we moving?
  874. if ( IsMoving() )
  875. {
  876. // Continue moving to the same target
  877. m_target = m_iszLastTarget;
  878. }
  879. SetupTarget();
  880. }
  881. void CFuncTrain::InputToggle( inputdata_t &data )
  882. {
  883. //If we've been waiting to be retriggered, move to the next destination
  884. if( HasSpawnFlags( SF_TRAIN_WAIT_RETRIGGER ) )
  885. {
  886. Start();
  887. }
  888. else
  889. {
  890. Stop();
  891. }
  892. }
  893. void CFuncTrain::InputStart( inputdata_t &data )
  894. {
  895. Start();
  896. }
  897. void CFuncTrain::InputStop( inputdata_t &data )
  898. {
  899. Stop();
  900. }
  901. void CFuncTrain::Start( void )
  902. {
  903. //start moving
  904. if( HasSpawnFlags( SF_TRAIN_WAIT_RETRIGGER ) )
  905. {
  906. // Move toward my target
  907. RemoveSpawnFlags( SF_TRAIN_WAIT_RETRIGGER );
  908. Next();
  909. }
  910. }
  911. void CFuncTrain::Stop( void )
  912. {
  913. //stop moving
  914. if( !HasSpawnFlags( SF_TRAIN_WAIT_RETRIGGER ) )
  915. {
  916. AddSpawnFlags( SF_TRAIN_WAIT_RETRIGGER );
  917. // Pop back to last target if it's available
  918. if ( m_hEnemy )
  919. {
  920. m_target = m_hEnemy->GetEntityName();
  921. }
  922. SetNextThink( TICK_NEVER_THINK );
  923. SetAbsVelocity( vec3_origin );
  924. if ( m_NoiseArrived != NULL_STRING )
  925. {
  926. CPASAttenuationFilter filter( this );
  927. EmitSound_t ep;
  928. ep.m_nChannel = CHAN_VOICE;
  929. ep.m_pSoundName = STRING(m_NoiseArrived);
  930. ep.m_flVolume = m_volume;
  931. ep.m_SoundLevel = SNDLVL_NORM;
  932. EmitSound( filter, entindex(), ep );
  933. }
  934. //Do not teleport to our final move destination
  935. SetMoveDone( NULL );
  936. SetMoveDoneTime( -1 );
  937. }
  938. }
  939. BEGIN_DATADESC( CFuncTrackTrain )
  940. DEFINE_KEYFIELD( m_length, FIELD_FLOAT, "wheels" ),
  941. DEFINE_KEYFIELD( m_height, FIELD_FLOAT, "height" ),
  942. DEFINE_KEYFIELD( m_maxSpeed, FIELD_FLOAT, "startspeed" ),
  943. DEFINE_KEYFIELD( m_flBank, FIELD_FLOAT, "bank" ),
  944. DEFINE_KEYFIELD( m_flBlockDamage, FIELD_FLOAT, "dmg" ),
  945. DEFINE_KEYFIELD( m_iszSoundMove, FIELD_SOUNDNAME, "MoveSound" ),
  946. DEFINE_KEYFIELD( m_iszSoundMovePing, FIELD_SOUNDNAME, "MovePingSound" ),
  947. DEFINE_KEYFIELD( m_iszSoundStart, FIELD_SOUNDNAME, "StartSound" ),
  948. DEFINE_KEYFIELD( m_iszSoundStop, FIELD_SOUNDNAME, "StopSound" ),
  949. DEFINE_KEYFIELD( m_nMoveSoundMinPitch, FIELD_INTEGER, "MoveSoundMinPitch" ),
  950. DEFINE_KEYFIELD( m_nMoveSoundMaxPitch, FIELD_INTEGER, "MoveSoundMaxPitch" ),
  951. DEFINE_KEYFIELD( m_flMoveSoundMinTime, FIELD_FLOAT, "MoveSoundMinTime" ),
  952. DEFINE_KEYFIELD( m_flMoveSoundMaxTime, FIELD_FLOAT, "MoveSoundMaxTime" ),
  953. DEFINE_FIELD( m_flNextMoveSoundTime, FIELD_TIME ),
  954. DEFINE_KEYFIELD( m_eVelocityType, FIELD_INTEGER, "velocitytype" ),
  955. DEFINE_KEYFIELD( m_eOrientationType, FIELD_INTEGER, "orientationtype" ),
  956. DEFINE_FIELD( m_ppath, FIELD_CLASSPTR ),
  957. DEFINE_FIELD( m_dir, FIELD_FLOAT ),
  958. DEFINE_FIELD( m_controlMins, FIELD_VECTOR ),
  959. DEFINE_FIELD( m_controlMaxs, FIELD_VECTOR ),
  960. DEFINE_FIELD( m_flVolume, FIELD_FLOAT ),
  961. DEFINE_FIELD( m_oldSpeed, FIELD_FLOAT ),
  962. //DEFINE_FIELD( m_lastBlockPos, FIELD_POSITION_VECTOR ), // temp values for blocking, don't save
  963. //DEFINE_FIELD( m_lastBlockTick, FIELD_INTEGER ),
  964. DEFINE_FIELD( m_bSoundPlaying, FIELD_BOOLEAN ),
  965. DEFINE_KEYFIELD( m_bManualSpeedChanges, FIELD_BOOLEAN, "ManualSpeedChanges" ),
  966. DEFINE_KEYFIELD( m_flAccelSpeed, FIELD_FLOAT, "ManualAccelSpeed" ),
  967. DEFINE_KEYFIELD( m_flDecelSpeed, FIELD_FLOAT, "ManualDecelSpeed" ),
  968. #ifdef HL1_DLL
  969. DEFINE_FIELD( m_bOnTrackChange, FIELD_BOOLEAN ),
  970. #endif
  971. // Inputs
  972. DEFINE_INPUTFUNC( FIELD_VOID, "Stop", InputStop ),
  973. DEFINE_INPUTFUNC( FIELD_VOID, "StartForward", InputStartForward ),
  974. DEFINE_INPUTFUNC( FIELD_VOID, "StartBackward", InputStartBackward ),
  975. DEFINE_INPUTFUNC( FIELD_VOID, "Toggle", InputToggle ),
  976. DEFINE_INPUTFUNC( FIELD_VOID, "Resume", InputResume ),
  977. DEFINE_INPUTFUNC( FIELD_VOID, "Reverse", InputReverse ),
  978. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeed", InputSetSpeed ),
  979. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeedDir", InputSetSpeedDir ),
  980. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeedReal", InputSetSpeedReal ),
  981. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeedDirAccel", InputSetSpeedDirAccel ),
  982. DEFINE_INPUTFUNC( FIELD_STRING, "TeleportToPathTrack", InputTeleportToPathTrack ),
  983. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetSpeedForwardModifier", InputSetSpeedForwardModifier ),
  984. // Outputs
  985. DEFINE_OUTPUT( m_OnStart, "OnStart" ),
  986. DEFINE_OUTPUT( m_OnNext, "OnNextPoint" ),
  987. // Function Pointers
  988. DEFINE_FUNCTION( Next ),
  989. DEFINE_FUNCTION( Find ),
  990. DEFINE_FUNCTION( NearestPath ),
  991. DEFINE_FUNCTION( DeadEnd ),
  992. END_DATADESC()
  993. LINK_ENTITY_TO_CLASS( func_tracktrain, CFuncTrackTrain );
  994. //-----------------------------------------------------------------------------
  995. // Datatable
  996. //-----------------------------------------------------------------------------
  997. IMPLEMENT_SERVERCLASS_ST( CFuncTrackTrain, DT_FuncTrackTrain )
  998. END_SEND_TABLE()
  999. //-----------------------------------------------------------------------------
  1000. // Constructor
  1001. //-----------------------------------------------------------------------------
  1002. CFuncTrackTrain::CFuncTrackTrain()
  1003. {
  1004. #ifdef _DEBUG
  1005. m_controlMins.Init();
  1006. m_controlMaxs.Init();
  1007. #endif
  1008. // These defaults match old func_tracktrains. Changing these defaults would
  1009. // require a vmf_tweak of older content to keep it from breaking.
  1010. m_eOrientationType = TrainOrientation_AtPathTracks;
  1011. m_eVelocityType = TrainVelocity_Instantaneous;
  1012. m_lastBlockPos.Init();
  1013. m_lastBlockTick = gpGlobals->tickcount;
  1014. m_flSpeedForwardModifier = 1.0f;
  1015. m_flUnmodifiedDesiredSpeed = 0.0f;
  1016. m_bDamageChild = false;
  1017. }
  1018. //-----------------------------------------------------------------------------
  1019. // Purpose:
  1020. //-----------------------------------------------------------------------------
  1021. int CFuncTrackTrain::DrawDebugTextOverlays( void )
  1022. {
  1023. int nOffset = BaseClass::DrawDebugTextOverlays();
  1024. if (m_debugOverlays & OVERLAY_TEXT_BIT)
  1025. {
  1026. char tempstr[512];
  1027. Q_snprintf( tempstr,sizeof(tempstr), "angles: %g %g %g", (double)GetLocalAngles()[PITCH], (double)GetLocalAngles()[YAW], (double)GetLocalAngles()[ROLL] );
  1028. EntityText( nOffset, tempstr, 0 );
  1029. nOffset++;
  1030. float flCurSpeed = GetLocalVelocity().Length();
  1031. Q_snprintf( tempstr,sizeof(tempstr), "current speed (goal): %g (%g)", (double)flCurSpeed, (double)m_flSpeed );
  1032. EntityText( nOffset, tempstr, 0 );
  1033. nOffset++;
  1034. Q_snprintf( tempstr,sizeof(tempstr), "max speed: %g", (double)m_maxSpeed );
  1035. EntityText( nOffset, tempstr, 0 );
  1036. nOffset++;
  1037. }
  1038. return nOffset;
  1039. }
  1040. void CFuncTrackTrain::DrawDebugGeometryOverlays()
  1041. {
  1042. BaseClass::DrawDebugGeometryOverlays();
  1043. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  1044. {
  1045. NDebugOverlay::Box( GetAbsOrigin(), -Vector(4,4,4),Vector(4,4,4), 255, 0, 255, 0, 0);
  1046. Vector out;
  1047. VectorTransform( Vector(m_length,0,0), EntityToWorldTransform(), out );
  1048. NDebugOverlay::Box( out, -Vector(4,4,4),Vector(4,4,4), 255, 0, 255, 0, 0);
  1049. }
  1050. }
  1051. //-----------------------------------------------------------------------------
  1052. // Purpose:
  1053. //-----------------------------------------------------------------------------
  1054. bool CFuncTrackTrain::KeyValue( const char *szKeyName, const char *szValue )
  1055. {
  1056. if (FStrEq(szKeyName, "volume"))
  1057. {
  1058. m_flVolume = (float) (atoi(szValue));
  1059. m_flVolume *= 0.1f;
  1060. }
  1061. else
  1062. {
  1063. return BaseClass::KeyValue( szKeyName, szValue );
  1064. }
  1065. return true;
  1066. }
  1067. //-----------------------------------------------------------------------------
  1068. // Purpose: Input handler that stops the train.
  1069. //-----------------------------------------------------------------------------
  1070. void CFuncTrackTrain::InputStop( inputdata_t &inputdata )
  1071. {
  1072. Stop();
  1073. }
  1074. //------------------------------------------------------------------------------
  1075. // Purpose: Input handler that starts the train moving.
  1076. //------------------------------------------------------------------------------
  1077. void CFuncTrackTrain::InputResume( inputdata_t &inputdata )
  1078. {
  1079. m_flSpeed = m_oldSpeed;
  1080. Start();
  1081. }
  1082. //------------------------------------------------------------------------------
  1083. // Purpose: Input handler that reverses the trains current direction of motion.
  1084. //------------------------------------------------------------------------------
  1085. void CFuncTrackTrain::InputReverse( inputdata_t &inputdata )
  1086. {
  1087. SetDirForward( !IsDirForward() );
  1088. SetSpeed( m_flSpeed );
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Purpose: Returns whether we are travelling forward along our path.
  1092. //-----------------------------------------------------------------------------
  1093. bool CFuncTrackTrain::IsDirForward()
  1094. {
  1095. return ( m_dir == 1 );
  1096. }
  1097. //-----------------------------------------------------------------------------
  1098. // Purpose: Sets whether we go forward or backward along our path.
  1099. //-----------------------------------------------------------------------------
  1100. void CFuncTrackTrain::SetDirForward( bool bForward )
  1101. {
  1102. if ( bForward && ( m_dir != 1 ) )
  1103. {
  1104. // Reverse direction.
  1105. if ( m_ppath && m_ppath->GetPrevious() )
  1106. {
  1107. m_ppath = m_ppath->GetPrevious();
  1108. }
  1109. m_dir = 1;
  1110. }
  1111. else if ( !bForward && ( m_dir != -1 ) )
  1112. {
  1113. // Reverse direction.
  1114. if ( m_ppath && m_ppath->GetNext() )
  1115. {
  1116. m_ppath = m_ppath->GetNext();
  1117. }
  1118. m_dir = -1;
  1119. }
  1120. }
  1121. //------------------------------------------------------------------------------
  1122. // Purpose: Input handler that starts the train moving.
  1123. //------------------------------------------------------------------------------
  1124. void CFuncTrackTrain::InputStartForward( inputdata_t &inputdata )
  1125. {
  1126. SetDirForward( true );
  1127. SetSpeed( m_maxSpeed );
  1128. }
  1129. //------------------------------------------------------------------------------
  1130. // Purpose: Input handler that starts the train moving.
  1131. //------------------------------------------------------------------------------
  1132. void CFuncTrackTrain::InputStartBackward( inputdata_t &inputdata )
  1133. {
  1134. SetDirForward( false );
  1135. SetSpeed( m_maxSpeed );
  1136. }
  1137. //------------------------------------------------------------------------------
  1138. // Purpose: Starts the train moving.
  1139. //------------------------------------------------------------------------------
  1140. void CFuncTrackTrain::Start( void )
  1141. {
  1142. m_OnStart.FireOutput(this,this);
  1143. Next();
  1144. }
  1145. //-----------------------------------------------------------------------------
  1146. // Purpose: Toggles the train between moving and not moving.
  1147. //-----------------------------------------------------------------------------
  1148. void CFuncTrackTrain::InputToggle( inputdata_t &inputdata )
  1149. {
  1150. if ( m_flSpeed == 0 )
  1151. {
  1152. SetSpeed( m_maxSpeed );
  1153. }
  1154. else
  1155. {
  1156. SetSpeed( 0 );
  1157. }
  1158. }
  1159. //-----------------------------------------------------------------------------
  1160. // Purpose: Handles player use so players can control the speed of the train.
  1161. //-----------------------------------------------------------------------------
  1162. void CFuncTrackTrain::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  1163. {
  1164. // player +USE
  1165. if ( useType == USE_SET )
  1166. {
  1167. float delta = value;
  1168. delta = ((int)(m_flSpeed * 4) / (int)m_maxSpeed)*0.25 + 0.25 * delta;
  1169. if ( delta > 1 )
  1170. delta = 1;
  1171. else if ( delta < -0.25 )
  1172. delta = -0.25;
  1173. if ( m_spawnflags & SF_TRACKTRAIN_FORWARDONLY )
  1174. {
  1175. if ( delta < 0 )
  1176. delta = 0;
  1177. }
  1178. SetDirForward( delta >= 0 );
  1179. delta = fabs(delta);
  1180. SetSpeed( m_maxSpeed * delta );
  1181. }
  1182. }
  1183. //-----------------------------------------------------------------------------
  1184. // Purpose: Input handler that sets the speed of the train.
  1185. // Input : Float speed from 0 to max speed, in units per second.
  1186. //-----------------------------------------------------------------------------
  1187. void CFuncTrackTrain::InputSetSpeedReal( inputdata_t &inputdata )
  1188. {
  1189. SetSpeed( clamp( inputdata.value.Float(), 0.f, m_maxSpeed ) );
  1190. }
  1191. //-----------------------------------------------------------------------------
  1192. // Purpose: Input handler that sets the speed of the train.
  1193. // Input : Float speed scale from 0 to 1.
  1194. //-----------------------------------------------------------------------------
  1195. void CFuncTrackTrain::InputSetSpeed( inputdata_t &inputdata )
  1196. {
  1197. float flScale = clamp( inputdata.value.Float(), 0.f, 1.f );
  1198. SetSpeed( m_maxSpeed * flScale );
  1199. }
  1200. //-----------------------------------------------------------------------------
  1201. // Purpose: Input handler that sets the speed of the train and the direction
  1202. // based on the sign of the speed.
  1203. // Input : Float speed scale from -1 to 1. Negatives values indicate a reversed
  1204. // direction.
  1205. //-----------------------------------------------------------------------------
  1206. void CFuncTrackTrain::InputSetSpeedDir( inputdata_t &inputdata )
  1207. {
  1208. float newSpeed = inputdata.value.Float();
  1209. SetDirForward( newSpeed >= 0 );
  1210. newSpeed = fabs(newSpeed);
  1211. float flScale = clamp( newSpeed, 0.f, 1.f );
  1212. SetSpeed( m_maxSpeed * flScale );
  1213. }
  1214. //-----------------------------------------------------------------------------
  1215. // Purpose: Input handler that sets the speed of the train and the direction
  1216. // based on the sign of the speed, and accels/decels to that speed
  1217. // Input : Float speed scale from -1 to 1. Negatives values indicate a reversed
  1218. // direction.
  1219. //-----------------------------------------------------------------------------
  1220. void CFuncTrackTrain::InputSetSpeedDirAccel( inputdata_t &inputdata )
  1221. {
  1222. SetSpeedDirAccel( inputdata.value.Float() );
  1223. }
  1224. //-----------------------------------------------------------------------------
  1225. // Purpose:
  1226. //-----------------------------------------------------------------------------
  1227. void CFuncTrackTrain::SetSpeedDirAccel( float flNewSpeed )
  1228. {
  1229. float newSpeed = flNewSpeed;
  1230. SetDirForward( newSpeed >= 0 );
  1231. newSpeed = fabs( newSpeed );
  1232. float flScale = clamp( newSpeed, 0.f, 1.f );
  1233. SetSpeed( m_maxSpeed * flScale, true );
  1234. }
  1235. //-----------------------------------------------------------------------------
  1236. // Purpose:
  1237. //-----------------------------------------------------------------------------
  1238. void CFuncTrackTrain::InputSetSpeedForwardModifier( inputdata_t &inputdata )
  1239. {
  1240. SetSpeedForwardModifier( inputdata.value.Float() ) ;
  1241. }
  1242. //-----------------------------------------------------------------------------
  1243. // Purpose:
  1244. //-----------------------------------------------------------------------------
  1245. void CFuncTrackTrain::SetSpeedForwardModifier( float flModifier )
  1246. {
  1247. float flSpeedForwardModifier = flModifier;
  1248. flSpeedForwardModifier = fabs( flSpeedForwardModifier );
  1249. m_flSpeedForwardModifier = clamp( flSpeedForwardModifier, 0.f, 1.f );
  1250. SetSpeed( m_flUnmodifiedDesiredSpeed, true );
  1251. }
  1252. //-----------------------------------------------------------------------------
  1253. // Purpose:
  1254. //-----------------------------------------------------------------------------
  1255. void CFuncTrackTrain::InputTeleportToPathTrack( inputdata_t &inputdata )
  1256. {
  1257. const char *pszName = inputdata.value.String();
  1258. CPathTrack *pTrack = dynamic_cast<CPathTrack*>( gEntList.FindEntityByName( NULL, pszName ) );
  1259. if ( pTrack )
  1260. {
  1261. TeleportToPathTrack( pTrack );
  1262. m_ppath = pTrack;
  1263. }
  1264. }
  1265. //-----------------------------------------------------------------------------
  1266. // Purpose: Sets the speed of the train to the given value in units per second.
  1267. //-----------------------------------------------------------------------------
  1268. void CFuncTrackTrain::SetSpeed( float flSpeed, bool bAccel /*= false */ )
  1269. {
  1270. m_bAccelToSpeed = bAccel;
  1271. m_flUnmodifiedDesiredSpeed = flSpeed;
  1272. float flOldSpeed = m_flSpeed;
  1273. // are we using a speed forward modifier?
  1274. if ( m_flSpeedForwardModifier < 1.0 && m_dir > 0 )
  1275. {
  1276. flSpeed = flSpeed * m_flSpeedForwardModifier;
  1277. }
  1278. if ( m_bAccelToSpeed )
  1279. {
  1280. m_flDesiredSpeed = fabs( flSpeed ) * m_dir;
  1281. m_flSpeedChangeTime = gpGlobals->curtime;
  1282. if ( m_flSpeed == 0 && abs(m_flDesiredSpeed) > 0 )
  1283. {
  1284. m_flSpeed = 0.1; // little push to get us going
  1285. }
  1286. Start();
  1287. return;
  1288. }
  1289. m_flSpeed = fabs( flSpeed ) * m_dir;
  1290. if ( m_flSpeed != flOldSpeed)
  1291. {
  1292. // Changing speed.
  1293. if ( m_flSpeed != 0 )
  1294. {
  1295. if ( flOldSpeed == 0 )
  1296. {
  1297. // Starting to move.
  1298. Start();
  1299. }
  1300. else
  1301. {
  1302. // Continuing to move.
  1303. Next();
  1304. }
  1305. }
  1306. else
  1307. {
  1308. // Stopping.
  1309. Stop();
  1310. }
  1311. }
  1312. DevMsg( 2, "TRAIN(%s), speed to %.2f\n", GetDebugName(), m_flSpeed );
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Purpose: Stops the train.
  1316. //-----------------------------------------------------------------------------
  1317. void CFuncTrackTrain::Stop( void )
  1318. {
  1319. SetLocalVelocity( vec3_origin );
  1320. SetLocalAngularVelocity( vec3_angle );
  1321. m_oldSpeed = m_flSpeed;
  1322. m_flSpeed = 0;
  1323. SoundStop();
  1324. SetThink(NULL);
  1325. }
  1326. static CBaseEntity *FindPhysicsBlockerForHierarchy( CBaseEntity *pParentEntity )
  1327. {
  1328. CUtlVector<CBaseEntity *> list;
  1329. GetAllInHierarchy( pParentEntity, list );
  1330. CBaseEntity *pPhysicsBlocker = NULL;
  1331. float maxForce = 0;
  1332. for ( int i = 0; i < list.Count(); i++ )
  1333. {
  1334. IPhysicsObject *pPhysics = list[i]->VPhysicsGetObject();
  1335. if ( pPhysics )
  1336. {
  1337. IPhysicsFrictionSnapshot *pSnapshot = pPhysics->CreateFrictionSnapshot();
  1338. while ( pSnapshot->IsValid() )
  1339. {
  1340. IPhysicsObject *pOther = pSnapshot->GetObject(1);
  1341. CBaseEntity *pOtherEntity = static_cast<CBaseEntity *>(pOther->GetGameData());
  1342. if ( pOtherEntity && pOtherEntity->GetMoveType() == MOVETYPE_VPHYSICS )
  1343. {
  1344. Vector normal;
  1345. pSnapshot->GetSurfaceNormal(normal);
  1346. float dot = DotProduct( pParentEntity->GetAbsVelocity(), pSnapshot->GetNormalForce() * normal );
  1347. if ( !pPhysicsBlocker || dot > maxForce )
  1348. {
  1349. pPhysicsBlocker = pOtherEntity;
  1350. maxForce = dot;
  1351. }
  1352. }
  1353. pSnapshot->NextFrictionData();
  1354. }
  1355. pPhysics->DestroyFrictionSnapshot( pSnapshot );
  1356. }
  1357. }
  1358. return pPhysicsBlocker;
  1359. }
  1360. //-----------------------------------------------------------------------------
  1361. // Purpose: Called when we are blocked by another entity.
  1362. // Input : pOther -
  1363. //-----------------------------------------------------------------------------
  1364. void CFuncTrackTrain::Blocked( CBaseEntity *pOther )
  1365. {
  1366. // Blocker is on-ground on the train
  1367. if ( ( pOther->GetFlags() & FL_ONGROUND ) && pOther->GetGroundEntity() == this )
  1368. {
  1369. DevMsg( 1, "TRAIN(%s): Blocked by %s\n", GetDebugName(), pOther->GetClassname() );
  1370. float deltaSpeed = fabs(m_flSpeed);
  1371. if ( deltaSpeed > 50 )
  1372. deltaSpeed = 50;
  1373. Vector vecNewVelocity;
  1374. pOther->GetVelocity( &vecNewVelocity );
  1375. if ( !vecNewVelocity.z )
  1376. {
  1377. pOther->ApplyAbsVelocityImpulse( Vector(0,0,deltaSpeed) );
  1378. }
  1379. return;
  1380. }
  1381. else
  1382. {
  1383. Vector vecNewVelocity;
  1384. vecNewVelocity = pOther->GetAbsOrigin() - GetAbsOrigin();
  1385. VectorNormalize(vecNewVelocity);
  1386. vecNewVelocity *= m_flBlockDamage;
  1387. pOther->SetAbsVelocity( vecNewVelocity );
  1388. }
  1389. if ( HasSpawnFlags(SF_TRACKTRAIN_UNBLOCKABLE_BY_PLAYER) )
  1390. {
  1391. CBaseEntity *pPhysicsBlocker = FindPhysicsBlockerForHierarchy(this);
  1392. if ( pPhysicsBlocker )
  1393. {
  1394. // This code keeps track of how long this train has been blocked
  1395. // The heuristic here is to keep instantaneous blocks from invoking the somewhat
  1396. // heavy-handed solver (which will disable collisions until we're clear) in cases
  1397. // where physics can solve it easily enough.
  1398. int ticksBlocked = gpGlobals->tickcount - m_lastBlockTick;
  1399. float dist = 0.0f;
  1400. // wait at least 10 ticks and make sure the train isn't actually moving before really blocking
  1401. const int MIN_BLOCKED_TICKS = 10;
  1402. if ( ticksBlocked > MIN_BLOCKED_TICKS )
  1403. {
  1404. dist = (GetAbsOrigin() - m_lastBlockPos).Length();
  1405. // must have moved at least 10% of normal velocity over the blocking interval, or we're being blocked
  1406. float minLength = GetAbsVelocity().Length() * TICK_INTERVAL * MIN_BLOCKED_TICKS * 0.10f;
  1407. if ( dist < minLength )
  1408. {
  1409. // been stuck for more than one tick without moving much?
  1410. // yes, disable collisions with the physics object most likely to be blocking us
  1411. EntityPhysics_CreateSolver( this, pPhysicsBlocker, true, 4.0f );
  1412. }
  1413. }
  1414. // first time blocking or moved too far since last block, reset
  1415. if ( dist > 1.0f || m_lastBlockTick < 0 )
  1416. {
  1417. m_lastBlockPos = GetAbsOrigin();
  1418. m_lastBlockTick = gpGlobals->tickcount;
  1419. }
  1420. }
  1421. // unblockable shouldn't damage the player in this case
  1422. if ( pOther->IsPlayer() )
  1423. return;
  1424. }
  1425. DevWarning( 2, "TRAIN(%s): Blocked by %s (dmg:%.2f)\n", GetDebugName(), pOther->GetClassname(), m_flBlockDamage );
  1426. if ( m_flBlockDamage <= 0 )
  1427. return;
  1428. // we can't hurt this thing, so we're not concerned with it
  1429. pOther->TakeDamage( CTakeDamageInfo( this, this, m_flBlockDamage, DMG_CRUSH ) );
  1430. }
  1431. extern void FixupAngles( QAngle &v );
  1432. #define TRAIN_MAXSPEED 1000 // approx max speed for sound pitch calculation
  1433. //-----------------------------------------------------------------------------
  1434. // Purpose:
  1435. //-----------------------------------------------------------------------------
  1436. void CFuncTrackTrain::SoundStop( void )
  1437. {
  1438. // if sound playing, stop it
  1439. if ( m_bSoundPlaying )
  1440. {
  1441. if ( m_iszSoundMove != NULL_STRING )
  1442. {
  1443. StopSound( entindex(), CHAN_STATIC, STRING( m_iszSoundMove ) );
  1444. }
  1445. if ( m_iszSoundStop != NULL_STRING )
  1446. {
  1447. CPASAttenuationFilter filter( this );
  1448. EmitSound_t ep;
  1449. ep.m_nChannel = CHAN_ITEM;
  1450. ep.m_pSoundName = STRING(m_iszSoundStop);
  1451. ep.m_flVolume = m_flVolume;
  1452. ep.m_SoundLevel = SNDLVL_NORM;
  1453. EmitSound( filter, entindex(), ep );
  1454. }
  1455. }
  1456. m_bSoundPlaying = false;
  1457. }
  1458. //-----------------------------------------------------------------------------
  1459. // Purpose: Update pitch based on speed, start sound if not playing.
  1460. // NOTE: when train goes through transition, m_bSoundPlaying should become
  1461. // false, which will cause the looped sound to restart.
  1462. //-----------------------------------------------------------------------------
  1463. void CFuncTrackTrain::SoundUpdate( void )
  1464. {
  1465. if ( ( !m_iszSoundMove ) && ( !m_iszSoundStart ) && ( !m_iszSoundMovePing ))
  1466. {
  1467. return;
  1468. }
  1469. // In multiplayer, only update the sound once a second
  1470. if ( g_pGameRules->IsMultiplayer() && m_bSoundPlaying )
  1471. {
  1472. if ( m_flNextMPSoundTime > gpGlobals->curtime )
  1473. return;
  1474. m_flNextMPSoundTime = gpGlobals->curtime + 1.0;
  1475. }
  1476. float flSpeedRatio = 0;
  1477. if ( HasSpawnFlags( SF_TRACKTRAIN_USE_MAXSPEED_FOR_PITCH ) )
  1478. {
  1479. flSpeedRatio = clamp( fabs( m_flSpeed ) / m_maxSpeed, 0.f, 1.f );
  1480. }
  1481. else
  1482. {
  1483. flSpeedRatio = clamp( fabs( m_flSpeed ) / TRAIN_MAXSPEED, 0.f, 1.f );
  1484. }
  1485. float flpitch = RemapVal( flSpeedRatio, 0, 1, m_nMoveSoundMinPitch, m_nMoveSoundMaxPitch );
  1486. CPASAttenuationFilter filter( this );
  1487. CPASAttenuationFilter filterReliable( this );
  1488. filterReliable.MakeReliable();
  1489. Vector vecWorldSpaceCenter = WorldSpaceCenter();
  1490. if (!m_bSoundPlaying)
  1491. {
  1492. if ( m_iszSoundStart != NULL_STRING )
  1493. {
  1494. EmitSound_t ep;
  1495. ep.m_nChannel = CHAN_ITEM;
  1496. ep.m_pSoundName = STRING(m_iszSoundStart);
  1497. ep.m_flVolume = m_flVolume;
  1498. ep.m_SoundLevel = SNDLVL_NORM;
  1499. ep.m_pOrigin = &vecWorldSpaceCenter;
  1500. EmitSound( filter, entindex(), ep );
  1501. }
  1502. if ( m_iszSoundMove != NULL_STRING )
  1503. {
  1504. EmitSound_t ep;
  1505. ep.m_nChannel = CHAN_STATIC;
  1506. ep.m_pSoundName = STRING(m_iszSoundMove);
  1507. ep.m_flVolume = m_flVolume;
  1508. ep.m_SoundLevel = SNDLVL_NORM;
  1509. ep.m_nPitch = (int)flpitch;
  1510. ep.m_pOrigin = &vecWorldSpaceCenter;
  1511. EmitSound( filterReliable, entindex(), ep );
  1512. }
  1513. // We've just started moving. Delay the next move ping sound.
  1514. m_flNextMoveSoundTime = gpGlobals->curtime + RemapVal( flSpeedRatio, 0, 1, m_flMoveSoundMaxTime, m_flMoveSoundMinTime );
  1515. m_bSoundPlaying = true;
  1516. }
  1517. else
  1518. {
  1519. if ( m_iszSoundMove != NULL_STRING )
  1520. {
  1521. // update pitch
  1522. EmitSound_t ep;
  1523. ep.m_nChannel = CHAN_STATIC;
  1524. ep.m_pSoundName = STRING(m_iszSoundMove);
  1525. ep.m_flVolume = m_flVolume;
  1526. ep.m_SoundLevel = SNDLVL_NORM;
  1527. ep.m_nPitch = (int)flpitch;
  1528. ep.m_nFlags = SND_CHANGE_PITCH;
  1529. ep.m_pOrigin = &vecWorldSpaceCenter;
  1530. // In multiplayer, don't make this reliable
  1531. if ( g_pGameRules->IsMultiplayer() )
  1532. {
  1533. EmitSound( filter, entindex(), ep );
  1534. }
  1535. else
  1536. {
  1537. EmitSound( filterReliable, entindex(), ep );
  1538. }
  1539. }
  1540. if ( ( m_iszSoundMovePing != NULL_STRING ) && ( gpGlobals->curtime > m_flNextMoveSoundTime ) )
  1541. {
  1542. EmitSound(STRING(m_iszSoundMovePing));
  1543. m_flNextMoveSoundTime = gpGlobals->curtime + RemapVal( flSpeedRatio, 0, 1, m_flMoveSoundMaxTime, m_flMoveSoundMinTime );
  1544. }
  1545. }
  1546. }
  1547. //-----------------------------------------------------------------------------
  1548. // Purpose:
  1549. // Input : *pNode -
  1550. //-----------------------------------------------------------------------------
  1551. void CFuncTrackTrain::ArriveAtNode( CPathTrack *pNode )
  1552. {
  1553. // BUGBUG: This is wrong. We need to fire all targets between the one we've passed and the one
  1554. // we've switched to.
  1555. FirePassInputs( pNode, pNode->GetNext(), true );
  1556. //
  1557. // Disable train controls if this path track says to do so.
  1558. //
  1559. if ( pNode->HasSpawnFlags( SF_PATH_DISABLE_TRAIN ) )
  1560. {
  1561. m_spawnflags |= SF_TRACKTRAIN_NOCONTROL;
  1562. }
  1563. //
  1564. // Don't override the train speed if it's under user control.
  1565. //
  1566. if ( m_spawnflags & SF_TRACKTRAIN_NOCONTROL )
  1567. {
  1568. //
  1569. // Don't copy speed from path track if it is 0 (uninitialized).
  1570. //
  1571. if ( pNode->m_flSpeed != 0 )
  1572. {
  1573. SetSpeed( pNode->m_flSpeed );
  1574. DevMsg( 2, "TrackTrain %s arrived at %s, speed to %4.2f\n", GetDebugName(), pNode->GetDebugName(), pNode->m_flSpeed );
  1575. }
  1576. }
  1577. }
  1578. //-----------------------------------------------------------------------------
  1579. // Purpose: Controls how the train accelerates as it moves along the path.
  1580. //-----------------------------------------------------------------------------
  1581. TrainVelocityType_t CFuncTrackTrain::GetTrainVelocityType()
  1582. {
  1583. return m_eVelocityType;
  1584. }
  1585. //-----------------------------------------------------------------------------
  1586. // Purpose:
  1587. // Input : *pnext -
  1588. //-----------------------------------------------------------------------------
  1589. void CFuncTrackTrain::UpdateTrainVelocity( CPathTrack *pPrev, CPathTrack *pNext, const Vector &nextPos, float flInterval )
  1590. {
  1591. switch ( GetTrainVelocityType() )
  1592. {
  1593. case TrainVelocity_Instantaneous:
  1594. {
  1595. Vector velDesired = nextPos - GetLocalOrigin();
  1596. VectorNormalize( velDesired );
  1597. velDesired *= fabs( m_flSpeed );
  1598. SetLocalVelocity( velDesired );
  1599. break;
  1600. }
  1601. case TrainVelocity_LinearBlend:
  1602. case TrainVelocity_EaseInEaseOut:
  1603. {
  1604. if ( m_bAccelToSpeed )
  1605. {
  1606. float flPrevSpeed = m_flSpeed;
  1607. float flNextSpeed = m_flDesiredSpeed;
  1608. if ( flPrevSpeed != flNextSpeed )
  1609. {
  1610. float flSpeedChangeTime = ( abs(flNextSpeed) > abs(flPrevSpeed) ) ? m_flAccelSpeed : m_flDecelSpeed;
  1611. m_flSpeed = UTIL_Approach( m_flDesiredSpeed, m_flSpeed, flSpeedChangeTime * gpGlobals->frametime );
  1612. }
  1613. }
  1614. else if ( pPrev && pNext )
  1615. {
  1616. // Get the speed to blend from.
  1617. float flPrevSpeed = m_flSpeed;
  1618. if ( pPrev->m_flSpeed != 0 )
  1619. {
  1620. flPrevSpeed = pPrev->m_flSpeed;
  1621. }
  1622. // Get the speed to blend to.
  1623. float flNextSpeed = flPrevSpeed;
  1624. if ( pNext->m_flSpeed != 0 )
  1625. {
  1626. flNextSpeed = pNext->m_flSpeed;
  1627. }
  1628. // If they're different, do the blend.
  1629. if ( flPrevSpeed != flNextSpeed )
  1630. {
  1631. Vector vecSegment = pNext->GetLocalOrigin() - pPrev->GetLocalOrigin();
  1632. float flSegmentLen = vecSegment.Length();
  1633. if ( flSegmentLen )
  1634. {
  1635. Vector vecCurOffset = GetLocalOrigin() - pPrev->GetLocalOrigin();
  1636. float p = vecCurOffset.Length() / flSegmentLen;
  1637. if ( GetTrainVelocityType() == TrainVelocity_EaseInEaseOut )
  1638. {
  1639. p = SimpleSplineRemapVal( p, 0.0f, 1.0f, 0.0f, 1.0f );
  1640. }
  1641. m_flSpeed = m_dir * ( flPrevSpeed * ( 1 - p ) + flNextSpeed * p );
  1642. }
  1643. }
  1644. else
  1645. {
  1646. m_flSpeed = m_dir * flPrevSpeed;
  1647. }
  1648. }
  1649. Vector velDesired = nextPos - GetLocalOrigin();
  1650. VectorNormalize( velDesired );
  1651. velDesired *= fabs( m_flSpeed );
  1652. SetLocalVelocity( velDesired );
  1653. break;
  1654. }
  1655. }
  1656. }
  1657. //-----------------------------------------------------------------------------
  1658. // Purpose: Controls how the train blends angles as it moves along the path.
  1659. //-----------------------------------------------------------------------------
  1660. TrainOrientationType_t CFuncTrackTrain::GetTrainOrientationType()
  1661. {
  1662. #ifdef HL1_DLL
  1663. return TrainOrientation_AtPathTracks;
  1664. #else
  1665. return m_eOrientationType;
  1666. #endif
  1667. }
  1668. //-----------------------------------------------------------------------------
  1669. // Purpose:
  1670. // Input : pnext -
  1671. //-----------------------------------------------------------------------------
  1672. void CFuncTrackTrain::UpdateTrainOrientation( CPathTrack *pPrev, CPathTrack *pNext, const Vector &nextPos, float flInterval )
  1673. {
  1674. // FIXME: old way of doing fixed orienation trains, remove!
  1675. if ( HasSpawnFlags( SF_TRACKTRAIN_FIXED_ORIENTATION ) )
  1676. return;
  1677. // Trains *can* work in local space, but only if all elements of the track share
  1678. // the same move parent as the train.
  1679. Assert( !pPrev || (pPrev->GetMoveParent() == GetMoveParent()) );
  1680. switch ( GetTrainOrientationType() )
  1681. {
  1682. case TrainOrientation_Fixed:
  1683. {
  1684. // Fixed orientation. Do nothing.
  1685. break;
  1686. }
  1687. case TrainOrientation_AtPathTracks:
  1688. {
  1689. UpdateOrientationAtPathTracks( pPrev, pNext, nextPos, flInterval );
  1690. break;
  1691. }
  1692. case TrainOrientation_EaseInEaseOut:
  1693. case TrainOrientation_LinearBlend:
  1694. {
  1695. UpdateOrientationBlend( GetTrainOrientationType(), pPrev, pNext, nextPos, flInterval );
  1696. break;
  1697. }
  1698. }
  1699. }
  1700. //-----------------------------------------------------------------------------
  1701. // Purpose: Adjusts our angles as we hit each path track. This is for support of
  1702. // trains with wheels that round corners a la HL1 trains.
  1703. // FIXME: move into path_track, have the angles come back from LookAhead
  1704. //-----------------------------------------------------------------------------
  1705. void CFuncTrackTrain::UpdateOrientationAtPathTracks( CPathTrack *pPrev, CPathTrack *pNext, const Vector &nextPos, float flInterval )
  1706. {
  1707. if ( !m_ppath )
  1708. return;
  1709. Vector nextFront = GetLocalOrigin();
  1710. CPathTrack *pNextNode = NULL;
  1711. nextFront.z -= m_height;
  1712. if ( m_length > 0 )
  1713. {
  1714. m_ppath->LookAhead( nextFront, IsDirForward() ? m_length : -m_length, 0, &pNextNode );
  1715. }
  1716. else
  1717. {
  1718. m_ppath->LookAhead( nextFront, IsDirForward() ? 100 : -100, 0, &pNextNode );
  1719. }
  1720. nextFront.z += m_height;
  1721. Vector vecFaceDir = nextFront - GetLocalOrigin();
  1722. if ( !IsDirForward() )
  1723. {
  1724. vecFaceDir *= -1;
  1725. }
  1726. QAngle angles;
  1727. VectorAngles( vecFaceDir, angles );
  1728. // !!! All of this crap has to be done to make the angles not wrap around, revisit this.
  1729. FixupAngles( angles );
  1730. // Wrapped with this bool so we don't affect old trains
  1731. if ( m_bManualSpeedChanges )
  1732. {
  1733. if ( pNextNode && pNextNode->GetOrientationType() == TrackOrientation_FacePathAngles )
  1734. {
  1735. angles = pNextNode->GetOrientation( IsDirForward() );
  1736. }
  1737. }
  1738. QAngle curAngles = GetLocalAngles();
  1739. FixupAngles( curAngles );
  1740. if ( !pPrev || (vecFaceDir.x == 0 && vecFaceDir.y == 0) )
  1741. angles = curAngles;
  1742. DoUpdateOrientation( curAngles, angles, flInterval );
  1743. }
  1744. //-----------------------------------------------------------------------------
  1745. // Purpose: Blends our angles using one of two orientation blending types.
  1746. // ASSUMES that eOrientationType is either LinearBlend or EaseInEaseOut.
  1747. // FIXME: move into path_track, have the angles come back from LookAhead
  1748. //-----------------------------------------------------------------------------
  1749. void CFuncTrackTrain::UpdateOrientationBlend( TrainOrientationType_t eOrientationType, CPathTrack *pPrev, CPathTrack *pNext, const Vector &nextPos, float flInterval )
  1750. {
  1751. // Get the angles to blend from.
  1752. QAngle angPrev = pPrev->GetOrientation( IsDirForward() );
  1753. FixupAngles( angPrev );
  1754. // Get the angles to blend to.
  1755. QAngle angNext;
  1756. if ( pNext )
  1757. {
  1758. angNext = pNext->GetOrientation( IsDirForward() );
  1759. FixupAngles( angNext );
  1760. }
  1761. else
  1762. {
  1763. // At a dead end, just use the last path track's angles.
  1764. angNext = angPrev;
  1765. }
  1766. if ( m_spawnflags & SF_TRACKTRAIN_NOPITCH )
  1767. {
  1768. angNext[PITCH] = angPrev[PITCH];
  1769. }
  1770. // Calculate our parametric distance along the path segment from 0 to 1.
  1771. float p = 0;
  1772. if ( pPrev && ( angPrev != angNext ) )
  1773. {
  1774. Vector vecSegment = pNext->GetLocalOrigin() - pPrev->GetLocalOrigin();
  1775. float flSegmentLen = vecSegment.Length();
  1776. if ( flSegmentLen )
  1777. {
  1778. Vector vecCurOffset = GetLocalOrigin() - pPrev->GetLocalOrigin();
  1779. p = vecCurOffset.Length() / flSegmentLen;
  1780. }
  1781. }
  1782. if ( eOrientationType == TrainOrientation_EaseInEaseOut )
  1783. {
  1784. p = SimpleSplineRemapVal( p, 0.0f, 1.0f, 0.0f, 1.0f );
  1785. }
  1786. //Msg( "UpdateOrientationFacePathAngles: %s->%s, p=%f, ", pPrev->GetDebugName(), pNext->GetDebugName(), p );
  1787. Quaternion qtPrev;
  1788. Quaternion qtNext;
  1789. AngleQuaternion( angPrev, qtPrev );
  1790. AngleQuaternion( angNext, qtNext );
  1791. QAngle angNew = angNext;
  1792. float flAngleDiff = QuaternionAngleDiff( qtPrev, qtNext );
  1793. if ( flAngleDiff )
  1794. {
  1795. Quaternion qtNew;
  1796. QuaternionSlerp( qtPrev, qtNext, p, qtNew );
  1797. QuaternionAngles( qtNew, angNew );
  1798. }
  1799. if ( m_spawnflags & SF_TRACKTRAIN_NOPITCH )
  1800. {
  1801. angNew[PITCH] = angPrev[PITCH];
  1802. }
  1803. DoUpdateOrientation( GetLocalAngles(), angNew, flInterval );
  1804. }
  1805. //-----------------------------------------------------------------------------
  1806. // Purpose: Sets our angular velocity to approach the target angles over the given interval.
  1807. //-----------------------------------------------------------------------------
  1808. void CFuncTrackTrain::DoUpdateOrientation( const QAngle &curAngles, const QAngle &angles, float flInterval )
  1809. {
  1810. float vy, vx;
  1811. if ( !(m_spawnflags & SF_TRACKTRAIN_NOPITCH) )
  1812. {
  1813. vx = UTIL_AngleDistance( angles.x, curAngles.x );
  1814. }
  1815. else
  1816. {
  1817. vx = 0;
  1818. }
  1819. vy = UTIL_AngleDistance( angles.y, curAngles.y );
  1820. // HACKHACK: Clamp really small angular deltas to avoid rotating movement on things
  1821. // that are close enough
  1822. if ( fabs(vx) < 0.1 )
  1823. {
  1824. vx = 0;
  1825. }
  1826. if ( fabs(vy) < 0.1 )
  1827. {
  1828. vy = 0;
  1829. }
  1830. if ( flInterval == 0 )
  1831. {
  1832. // Avoid dividing by zero
  1833. flInterval = 0.1;
  1834. }
  1835. QAngle vecAngVel( vx / flInterval, vy / flInterval, GetLocalAngularVelocity().z );
  1836. if ( m_flBank != 0 )
  1837. {
  1838. if ( vecAngVel.y < -5 )
  1839. {
  1840. vecAngVel.z = UTIL_AngleDistance( UTIL_ApproachAngle( -m_flBank, curAngles.z, m_flBank*2 ), curAngles.z);
  1841. }
  1842. else if ( vecAngVel.y > 5 )
  1843. {
  1844. vecAngVel.z = UTIL_AngleDistance( UTIL_ApproachAngle( m_flBank, curAngles.z, m_flBank*2 ), curAngles.z);
  1845. }
  1846. else
  1847. {
  1848. vecAngVel.z = UTIL_AngleDistance( UTIL_ApproachAngle( 0, curAngles.z, m_flBank*4 ), curAngles.z) * 4;
  1849. }
  1850. }
  1851. SetLocalAngularVelocity( vecAngVel );
  1852. }
  1853. //-----------------------------------------------------------------------------
  1854. // Purpose:
  1855. // Input : pTeleport -
  1856. //-----------------------------------------------------------------------------
  1857. void CFuncTrackTrain::TeleportToPathTrack( CPathTrack *pTeleport )
  1858. {
  1859. QAngle angCur = GetLocalAngles();
  1860. Vector nextPos = pTeleport->GetLocalOrigin();
  1861. Vector look = nextPos;
  1862. pTeleport->LookAhead( look, m_length, 0 );
  1863. QAngle nextAngles;
  1864. if ( HasSpawnFlags( SF_TRACKTRAIN_FIXED_ORIENTATION ) || ( look == nextPos ) )
  1865. {
  1866. nextAngles = GetLocalAngles();
  1867. }
  1868. else
  1869. {
  1870. nextAngles = pTeleport->GetOrientation( IsDirForward() );
  1871. if ( HasSpawnFlags( SF_TRACKTRAIN_NOPITCH ) )
  1872. {
  1873. nextAngles[PITCH] = angCur[PITCH];
  1874. }
  1875. }
  1876. Teleport( &pTeleport->GetLocalOrigin(), &nextAngles, NULL );
  1877. SetLocalAngularVelocity( vec3_angle );
  1878. variant_t emptyVariant;
  1879. pTeleport->AcceptInput( "InTeleport", this, this, emptyVariant, 0 );
  1880. }
  1881. //-----------------------------------------------------------------------------
  1882. // Purpose: Advances the train to the next path corner on the path.
  1883. //-----------------------------------------------------------------------------
  1884. void CFuncTrackTrain::Next( void )
  1885. {
  1886. if ( !m_flSpeed )
  1887. {
  1888. DevMsg( 2, "TRAIN(%s): Speed is 0\n", GetDebugName() );
  1889. SoundStop();
  1890. return;
  1891. }
  1892. if ( !m_ppath )
  1893. {
  1894. DevMsg( 2, "TRAIN(%s): Lost path\n", GetDebugName() );
  1895. SoundStop();
  1896. m_flSpeed = 0;
  1897. return;
  1898. }
  1899. SoundUpdate();
  1900. //
  1901. // Based on our current position and speed, look ahead along our path and see
  1902. // where we should be in 0.1 seconds.
  1903. //
  1904. Vector nextPos = GetLocalOrigin();
  1905. float flSpeed = m_flSpeed;
  1906. nextPos.z -= m_height;
  1907. CPathTrack *pNextNext = NULL;
  1908. CPathTrack *pNext = m_ppath->LookAhead( nextPos, flSpeed * 0.1, 1, &pNextNext );
  1909. //Assert( pNext != NULL );
  1910. // If we're moving towards a dead end, but our desired speed goes in the opposite direction
  1911. // this fixes us from stalling
  1912. if ( m_bManualSpeedChanges && ( ( flSpeed < 0 ) != ( m_flDesiredSpeed < 0 ) ) )
  1913. {
  1914. if ( !pNext )
  1915. pNext = m_ppath;
  1916. }
  1917. if (m_debugOverlays & OVERLAY_BBOX_BIT)
  1918. {
  1919. if ( pNext != NULL )
  1920. {
  1921. NDebugOverlay::Line( GetAbsOrigin(), pNext->GetAbsOrigin(), 255, 0, 0, true, 0.1 );
  1922. NDebugOverlay::Line( pNext->GetAbsOrigin(), pNext->GetAbsOrigin() + Vector( 0,0,32), 255, 0, 0, true, 0.1 );
  1923. NDebugOverlay::Box( pNext->GetAbsOrigin(), Vector( -8, -8, -8 ), Vector( 8, 8, 8 ), 255, 0, 0, 0, 0.1 );
  1924. }
  1925. if ( pNextNext != NULL )
  1926. {
  1927. NDebugOverlay::Line( GetAbsOrigin(), pNextNext->GetAbsOrigin(), 0, 255, 0, true, 0.1 );
  1928. NDebugOverlay::Line( pNextNext->GetAbsOrigin(), pNextNext->GetAbsOrigin() + Vector( 0,0,32), 0, 255, 0, true, 0.1 );
  1929. NDebugOverlay::Box( pNextNext->GetAbsOrigin(), Vector( -8, -8, -8 ), Vector( 8, 8, 8 ), 0, 255, 0, 0, 0.1 );
  1930. }
  1931. }
  1932. nextPos.z += m_height;
  1933. // Trains *can* work in local space, but only if all elements of the track share
  1934. // the same move parent as the train.
  1935. Assert( !pNext || (pNext->GetMoveParent() == GetMoveParent()) );
  1936. if ( pNext )
  1937. {
  1938. UpdateTrainVelocity( pNext, pNextNext, nextPos, gpGlobals->frametime );
  1939. UpdateTrainOrientation( pNext, pNextNext, nextPos, gpGlobals->frametime );
  1940. if ( pNext != m_ppath )
  1941. {
  1942. //
  1943. // We have reached a new path track. Fire its OnPass output.
  1944. //
  1945. m_ppath = pNext;
  1946. ArriveAtNode( pNext );
  1947. #ifdef HL1_DLL
  1948. m_bOnTrackChange = false;
  1949. #endif
  1950. //
  1951. // See if we should teleport to the next path track.
  1952. //
  1953. CPathTrack *pTeleport = pNext->GetNext();
  1954. if ( ( pTeleport != NULL ) && pTeleport->HasSpawnFlags( SF_PATH_TELEPORT ) )
  1955. {
  1956. TeleportToPathTrack( pTeleport );
  1957. }
  1958. }
  1959. m_OnNext.FireOutput( pNext, this );
  1960. SetThink( &CFuncTrackTrain::Next );
  1961. SetMoveDoneTime( 0.5 );
  1962. SetNextThink( gpGlobals->curtime );
  1963. SetMoveDone( NULL );
  1964. }
  1965. else
  1966. {
  1967. //
  1968. // We've reached the end of the path, stop.
  1969. //
  1970. SoundStop();
  1971. SetLocalVelocity(nextPos - GetLocalOrigin());
  1972. SetLocalAngularVelocity( vec3_angle );
  1973. float distance = GetLocalVelocity().Length();
  1974. m_oldSpeed = m_flSpeed;
  1975. m_flSpeed = 0;
  1976. // Move to the dead end
  1977. // Are we there yet?
  1978. if ( distance > 0 )
  1979. {
  1980. // no, how long to get there?
  1981. float flTime = distance / fabs( m_oldSpeed );
  1982. SetLocalVelocity( GetLocalVelocity() * (m_oldSpeed / distance) );
  1983. SetMoveDone( &CFuncTrackTrain::DeadEnd );
  1984. SetNextThink( TICK_NEVER_THINK );
  1985. SetMoveDoneTime( flTime );
  1986. }
  1987. else
  1988. {
  1989. DeadEnd();
  1990. }
  1991. }
  1992. }
  1993. void CFuncTrackTrain::FirePassInputs( CPathTrack *pStart, CPathTrack *pEnd, bool forward )
  1994. {
  1995. CPathTrack *pCurrent = pStart;
  1996. // swap if going backward
  1997. if ( !forward )
  1998. {
  1999. pCurrent = pEnd;
  2000. pEnd = pStart;
  2001. }
  2002. variant_t emptyVariant;
  2003. while ( pCurrent && pCurrent != pEnd )
  2004. {
  2005. //Msg("Fired pass on %s\n", STRING(pCurrent->GetEntityName()) );
  2006. pCurrent->AcceptInput( "InPass", this, this, emptyVariant, 0 );
  2007. pCurrent = forward ? pCurrent->GetNext() : pCurrent->GetPrevious();
  2008. }
  2009. }
  2010. void CFuncTrackTrain::DeadEnd( void )
  2011. {
  2012. // Fire the dead-end target if there is one
  2013. CPathTrack *pTrack, *pNext;
  2014. pTrack = m_ppath;
  2015. DevMsg( 2, "TRAIN(%s): Dead end ", GetDebugName() );
  2016. // Find the dead end path node
  2017. // HACKHACK -- This is bugly, but the train can actually stop moving at a different node depending on it's speed
  2018. // so we have to traverse the list to it's end.
  2019. if ( pTrack )
  2020. {
  2021. if ( m_oldSpeed < 0 )
  2022. {
  2023. do
  2024. {
  2025. pNext = pTrack->ValidPath( pTrack->GetPrevious(), true );
  2026. if ( pNext )
  2027. pTrack = pNext;
  2028. } while ( pNext );
  2029. }
  2030. else
  2031. {
  2032. do
  2033. {
  2034. pNext = pTrack->ValidPath( pTrack->GetNext(), true );
  2035. if ( pNext )
  2036. pTrack = pNext;
  2037. } while ( pNext );
  2038. }
  2039. }
  2040. SetLocalVelocity( vec3_origin );
  2041. SetLocalAngularVelocity( vec3_angle );
  2042. if ( pTrack )
  2043. {
  2044. DevMsg( 2, "at %s\n", pTrack->GetDebugName() );
  2045. variant_t emptyVariant;
  2046. pTrack->AcceptInput( "InPass", this, this, emptyVariant, 0 );
  2047. }
  2048. else
  2049. {
  2050. DevMsg( 2, "\n" );
  2051. }
  2052. }
  2053. void CFuncTrackTrain::SetControls( CBaseEntity *pControls )
  2054. {
  2055. Vector offset = pControls->GetLocalOrigin();
  2056. m_controlMins = pControls->WorldAlignMins() + offset;
  2057. m_controlMaxs = pControls->WorldAlignMaxs() + offset;
  2058. }
  2059. //-----------------------------------------------------------------------------
  2060. // Purpose: Returns true if the entity's origin is within the controls region.
  2061. //-----------------------------------------------------------------------------
  2062. bool CFuncTrackTrain::OnControls( CBaseEntity *pTest )
  2063. {
  2064. Vector offset = pTest->GetLocalOrigin() - GetLocalOrigin();
  2065. if ( m_spawnflags & SF_TRACKTRAIN_NOCONTROL )
  2066. return false;
  2067. // Transform offset into local coordinates
  2068. VMatrix tmp = SetupMatrixAngles( GetLocalAngles() );
  2069. // rotate into local space
  2070. Vector local = tmp.VMul3x3Transpose( offset );
  2071. /*
  2072. NDebugOverlay::Box( GetLocalOrigin(), m_controlMins, m_controlMaxs,
  2073. 255, 0, 0, 100, 5.0 );
  2074. NDebugOverlay::Box( GetLocalOrigin() + local, Vector(-5,-5,-5), Vector(5,5,5),
  2075. 0, 0, 255, 100, 5.0 );
  2076. */
  2077. if ( local.x >= m_controlMins.x && local.y >= m_controlMins.y && local.z >= m_controlMins.z &&
  2078. local.x <= m_controlMaxs.x && local.y <= m_controlMaxs.y && local.z <= m_controlMaxs.z )
  2079. return true;
  2080. return false;
  2081. }
  2082. void CFuncTrackTrain::Find( void )
  2083. {
  2084. m_ppath = (CPathTrack *)gEntList.FindEntityByName( NULL, m_target );
  2085. if ( !m_ppath )
  2086. return;
  2087. if ( !FClassnameIs( m_ppath, "path_track" )
  2088. #ifndef PORTAL //env_portal_path_track is a child of path_track and would like to get found
  2089. && !FClassnameIs( m_ppath, "env_portal_path_track" )
  2090. #endif //#ifndef PORTAL
  2091. )
  2092. {
  2093. Warning( "func_track_train must be on a path of path_track\n" );
  2094. Assert(0);
  2095. m_ppath = NULL;
  2096. return;
  2097. }
  2098. Vector nextPos = m_ppath->GetLocalOrigin();
  2099. Vector look = nextPos;
  2100. m_ppath->LookAhead( look, m_length, 0 );
  2101. nextPos.z += m_height;
  2102. look.z += m_height;
  2103. QAngle nextAngles;
  2104. if ( HasSpawnFlags( SF_TRACKTRAIN_FIXED_ORIENTATION ) )
  2105. {
  2106. nextAngles = GetLocalAngles();
  2107. }
  2108. else
  2109. {
  2110. VectorAngles( look - nextPos, nextAngles );
  2111. if ( HasSpawnFlags( SF_TRACKTRAIN_NOPITCH ) )
  2112. {
  2113. nextAngles.x = 0;
  2114. }
  2115. }
  2116. Teleport( &nextPos, &nextAngles, NULL );
  2117. ArriveAtNode( m_ppath );
  2118. if ( m_flSpeed != 0 )
  2119. {
  2120. SetNextThink( gpGlobals->curtime + 0.1f );
  2121. SetThink( &CFuncTrackTrain::Next );
  2122. SoundUpdate();
  2123. }
  2124. }
  2125. void CFuncTrackTrain::NearestPath( void )
  2126. {
  2127. CBaseEntity *pTrack = NULL;
  2128. CBaseEntity *pNearest = NULL;
  2129. float dist, closest;
  2130. closest = 1024;
  2131. for ( CEntitySphereQuery sphere( GetAbsOrigin(), 1024 ); ( pTrack = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  2132. {
  2133. // filter out non-tracks
  2134. if ( !(pTrack->GetFlags() & (FL_CLIENT|FL_NPC)) && FClassnameIs( pTrack, "path_track" ) )
  2135. {
  2136. dist = (GetAbsOrigin() - pTrack->GetAbsOrigin()).Length();
  2137. if ( dist < closest )
  2138. {
  2139. closest = dist;
  2140. pNearest = pTrack;
  2141. }
  2142. }
  2143. }
  2144. if ( !pNearest )
  2145. {
  2146. Msg( "Can't find a nearby track !!!\n" );
  2147. SetThink(NULL);
  2148. return;
  2149. }
  2150. DevMsg( 2, "TRAIN: %s, Nearest track is %s\n", GetDebugName(), pNearest->GetDebugName() );
  2151. // If I'm closer to the next path_track on this path, then it's my real path
  2152. pTrack = ((CPathTrack *)pNearest)->GetNext();
  2153. if ( pTrack )
  2154. {
  2155. if ( (GetLocalOrigin() - pTrack->GetLocalOrigin()).Length() < (GetLocalOrigin() - pNearest->GetLocalOrigin()).Length() )
  2156. pNearest = pTrack;
  2157. }
  2158. m_ppath = (CPathTrack *)pNearest;
  2159. if ( m_flSpeed != 0 )
  2160. {
  2161. SetMoveDoneTime( 0.1 );
  2162. SetMoveDone( &CFuncTrackTrain::Next );
  2163. }
  2164. }
  2165. void CFuncTrackTrain::OnRestore( void )
  2166. {
  2167. BaseClass::OnRestore();
  2168. if ( !m_ppath
  2169. #ifdef HL1_DLL
  2170. && !m_bOnTrackChange
  2171. #endif
  2172. )
  2173. {
  2174. NearestPath();
  2175. SetThink( NULL );
  2176. }
  2177. }
  2178. CFuncTrackTrain *CFuncTrackTrain::Instance( edict_t *pent )
  2179. {
  2180. CBaseEntity *pEntity = CBaseEntity::Instance( pent );
  2181. if ( FClassnameIs( pEntity, "func_tracktrain" ) )
  2182. return (CFuncTrackTrain *)pEntity;
  2183. return NULL;
  2184. }
  2185. //-----------------------------------------------------------------------------
  2186. // Purpose:
  2187. //-----------------------------------------------------------------------------
  2188. void CFuncTrackTrain::Spawn( void )
  2189. {
  2190. if ( m_maxSpeed == 0 )
  2191. {
  2192. if ( m_flSpeed == 0 )
  2193. {
  2194. m_maxSpeed = 100;
  2195. }
  2196. else
  2197. {
  2198. m_maxSpeed = m_flSpeed;
  2199. }
  2200. }
  2201. if ( m_nMoveSoundMinPitch == 0 )
  2202. {
  2203. m_nMoveSoundMinPitch = 60;
  2204. }
  2205. if ( m_nMoveSoundMaxPitch == 0 )
  2206. {
  2207. m_nMoveSoundMaxPitch = 200;
  2208. }
  2209. SetLocalVelocity(vec3_origin);
  2210. SetLocalAngularVelocity( vec3_angle );
  2211. m_dir = 1;
  2212. if ( !m_target )
  2213. {
  2214. Msg("FuncTrackTrain '%s' has no target.\n", GetDebugName());
  2215. }
  2216. SetModel( STRING( GetModelName() ) );
  2217. SetMoveType( MOVETYPE_PUSH );
  2218. #ifdef HL1_DLL
  2219. // BUGBUG: For now, just force this for testing. Remove if we want to tag all of the trains in the levels
  2220. SetSolid( SOLID_BSP );
  2221. #else
  2222. SetSolid( HasSpawnFlags( SF_TRACKTRAIN_HL1TRAIN ) ? SOLID_BSP : SOLID_VPHYSICS );
  2223. //SetSolid( SOLID_VPHYSICS );
  2224. #endif
  2225. if ( HasSpawnFlags( SF_TRACKTRAIN_UNBLOCKABLE_BY_PLAYER ) )
  2226. {
  2227. AddFlag( FL_UNBLOCKABLE_BY_PLAYER );
  2228. }
  2229. if ( m_spawnflags & SF_TRACKTRAIN_PASSABLE )
  2230. {
  2231. AddSolidFlags( FSOLID_NOT_SOLID );
  2232. }
  2233. m_controlMins = CollisionProp()->OBBMins();
  2234. m_controlMaxs = CollisionProp()->OBBMaxs();
  2235. m_controlMaxs.z += 72;
  2236. // start trains on the next frame, to make sure their targets have had
  2237. // a chance to spawn/activate
  2238. SetThink( &CFuncTrackTrain::Find );
  2239. SetNextThink( gpGlobals->curtime );
  2240. Precache();
  2241. CreateVPhysics();
  2242. }
  2243. bool CFuncTrackTrain::CreateVPhysics( void )
  2244. {
  2245. VPhysicsInitShadow( false, false );
  2246. return true;
  2247. }
  2248. //-----------------------------------------------------------------------------
  2249. // Purpose: Precaches the train sounds.
  2250. //-----------------------------------------------------------------------------
  2251. void CFuncTrackTrain::Precache( void )
  2252. {
  2253. if (m_flVolume == 0.0)
  2254. {
  2255. m_flVolume = 1.0;
  2256. }
  2257. if ( m_iszSoundMove != NULL_STRING )
  2258. {
  2259. PrecacheScriptSound( STRING( m_iszSoundMove ) );
  2260. }
  2261. if ( m_iszSoundMovePing != NULL_STRING )
  2262. {
  2263. PrecacheScriptSound( STRING( m_iszSoundMovePing ) );
  2264. }
  2265. if ( m_iszSoundStart != NULL_STRING )
  2266. {
  2267. PrecacheScriptSound( STRING( m_iszSoundStart ) );
  2268. }
  2269. if ( m_iszSoundStop != NULL_STRING )
  2270. {
  2271. PrecacheScriptSound( STRING( m_iszSoundStop ) );
  2272. }
  2273. }
  2274. //-----------------------------------------------------------------------------
  2275. // Purpose:
  2276. //-----------------------------------------------------------------------------
  2277. void CFuncTrackTrain::UpdateOnRemove()
  2278. {
  2279. SoundStop();
  2280. BaseClass::UpdateOnRemove();
  2281. }
  2282. void CFuncTrackTrain::MoveDone()
  2283. {
  2284. m_lastBlockPos.Init();
  2285. m_lastBlockTick = -1;
  2286. BaseClass::MoveDone();
  2287. }
  2288. int CFuncTrackTrain::OnTakeDamage( const CTakeDamageInfo &info )
  2289. {
  2290. if ( m_bDamageChild )
  2291. {
  2292. if ( FirstMoveChild() )
  2293. {
  2294. FirstMoveChild()->TakeDamage( info );
  2295. }
  2296. return 0;
  2297. }
  2298. else
  2299. {
  2300. return BaseClass::OnTakeDamage( info );
  2301. }
  2302. }
  2303. //-----------------------------------------------------------------------------
  2304. // Purpose: Defines the volume of space that the player must stand in to
  2305. // control the train
  2306. //-----------------------------------------------------------------------------
  2307. class CFuncTrainControls : public CBaseEntity
  2308. {
  2309. DECLARE_CLASS( CFuncTrainControls, CBaseEntity );
  2310. public:
  2311. void Spawn( void );
  2312. void Find( void );
  2313. DECLARE_DATADESC();
  2314. };
  2315. BEGIN_DATADESC( CFuncTrainControls )
  2316. // Function Pointers
  2317. DEFINE_FUNCTION( Find ),
  2318. END_DATADESC()
  2319. LINK_ENTITY_TO_CLASS( func_traincontrols, CFuncTrainControls );
  2320. void CFuncTrainControls::Find( void )
  2321. {
  2322. CBaseEntity *pTarget = NULL;
  2323. do
  2324. {
  2325. pTarget = gEntList.FindEntityByName( pTarget, m_target );
  2326. } while ( pTarget && !FClassnameIs(pTarget, "func_tracktrain") );
  2327. if ( !pTarget )
  2328. {
  2329. Msg( "No train %s\n", STRING(m_target) );
  2330. return;
  2331. }
  2332. CFuncTrackTrain *ptrain = (CFuncTrackTrain*) pTarget;
  2333. ptrain->SetControls( this );
  2334. SetThink( NULL );
  2335. }
  2336. void CFuncTrainControls::Spawn( void )
  2337. {
  2338. SetSolid( SOLID_NONE );
  2339. SetMoveType( MOVETYPE_NONE );
  2340. SetModel( STRING( GetModelName() ) );
  2341. AddEffects( EF_NODRAW );
  2342. Assert( GetParent() && "func_traincontrols needs parent to properly align to train" );
  2343. SetThink( &CFuncTrainControls::Find );
  2344. SetNextThink( gpGlobals->curtime );
  2345. }
  2346. #define SF_TRACK_ACTIVATETRAIN 0x00000001
  2347. #define SF_TRACK_RELINK 0x00000002
  2348. #define SF_TRACK_ROTMOVE 0x00000004
  2349. #define SF_TRACK_STARTBOTTOM 0x00000008
  2350. #define SF_TRACK_DONT_MOVE 0x00000010
  2351. typedef enum { TRAIN_SAFE, TRAIN_BLOCKING, TRAIN_FOLLOWING } TRAIN_CODE;
  2352. //-----------------------------------------------------------------------------
  2353. // This entity is a rotating/moving platform that will carry a train to a new track.
  2354. // It must be larger in X-Y planar area than the train, since it must contain the
  2355. // train within these dimensions in order to operate when the train is near it.
  2356. //-----------------------------------------------------------------------------
  2357. class CFuncTrackChange : public CFuncPlatRot
  2358. {
  2359. DECLARE_CLASS( CFuncTrackChange, CFuncPlatRot );
  2360. public:
  2361. void Spawn( void );
  2362. void Precache( void );
  2363. // virtual void Blocked( void );
  2364. virtual void GoUp( void );
  2365. virtual void GoDown( void );
  2366. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  2367. void Find( void );
  2368. TRAIN_CODE EvaluateTrain( CPathTrack *pcurrent );
  2369. void UpdateTrain( QAngle &dest );
  2370. virtual void HitBottom( void );
  2371. virtual void HitTop( void );
  2372. void Touch( CBaseEntity *pOther );
  2373. virtual void UpdateAutoTargets( int toggleState );
  2374. virtual bool IsTogglePlat( void ) { return true; }
  2375. void DisableUse( void ) { m_use = 0; }
  2376. void EnableUse( void ) { m_use = 1; }
  2377. int UseEnabled( void ) { return m_use; }
  2378. DECLARE_DATADESC();
  2379. CPathTrack *m_trackTop;
  2380. CPathTrack *m_trackBottom;
  2381. CFuncTrackTrain *m_train;
  2382. string_t m_trackTopName;
  2383. string_t m_trackBottomName;
  2384. string_t m_trainName;
  2385. TRAIN_CODE m_code;
  2386. int m_targetState;
  2387. int m_use;
  2388. };
  2389. LINK_ENTITY_TO_CLASS( func_trackchange, CFuncTrackChange );
  2390. BEGIN_DATADESC( CFuncTrackChange )
  2391. DEFINE_GLOBAL_FIELD( m_trackTop, FIELD_CLASSPTR ),
  2392. DEFINE_GLOBAL_FIELD( m_trackBottom, FIELD_CLASSPTR ),
  2393. DEFINE_GLOBAL_FIELD( m_train, FIELD_CLASSPTR ),
  2394. DEFINE_GLOBAL_KEYFIELD( m_trackTopName, FIELD_STRING, "toptrack" ),
  2395. DEFINE_GLOBAL_KEYFIELD( m_trackBottomName, FIELD_STRING, "bottomtrack" ),
  2396. DEFINE_GLOBAL_KEYFIELD( m_trainName, FIELD_STRING, "train" ),
  2397. DEFINE_FIELD( m_code, FIELD_INTEGER ),
  2398. DEFINE_FIELD( m_targetState, FIELD_INTEGER ),
  2399. DEFINE_FIELD( m_use, FIELD_INTEGER ),
  2400. // Function Pointers
  2401. DEFINE_FUNCTION( Find ),
  2402. END_DATADESC()
  2403. void CFuncTrackChange::Spawn( void )
  2404. {
  2405. Setup();
  2406. if ( FBitSet( m_spawnflags, SF_TRACK_DONT_MOVE ) )
  2407. m_vecPosition2.z = GetLocalOrigin().z;
  2408. SetupRotation();
  2409. if ( FBitSet( m_spawnflags, SF_TRACK_STARTBOTTOM ) )
  2410. {
  2411. UTIL_SetOrigin( this, m_vecPosition2);
  2412. m_toggle_state = TS_AT_BOTTOM;
  2413. SetLocalAngles( m_start );
  2414. m_targetState = TS_AT_TOP;
  2415. }
  2416. else
  2417. {
  2418. UTIL_SetOrigin( this, m_vecPosition1);
  2419. m_toggle_state = TS_AT_TOP;
  2420. SetLocalAngles( m_end );
  2421. m_targetState = TS_AT_BOTTOM;
  2422. }
  2423. EnableUse();
  2424. SetThink( &CFuncTrackChange::Find );
  2425. SetNextThink( gpGlobals->curtime + 2 );
  2426. Precache();
  2427. }
  2428. void CFuncTrackChange::Precache( void )
  2429. {
  2430. BaseClass::Precache();
  2431. PrecacheScriptSound( "FuncTrackChange.Blocking" );
  2432. }
  2433. // UNDONE: Filter touches before re-evaluating the train.
  2434. void CFuncTrackChange::Touch( CBaseEntity *pOther )
  2435. {
  2436. }
  2437. void CFuncTrackChange::Find( void )
  2438. {
  2439. // Find track entities
  2440. CBaseEntity *target;
  2441. target = gEntList.FindEntityByName( NULL, m_trackTopName );
  2442. if ( target )
  2443. {
  2444. m_trackTop = (CPathTrack*) target;
  2445. target = gEntList.FindEntityByName( NULL, m_trackBottomName );
  2446. if ( target )
  2447. {
  2448. m_trackBottom = (CPathTrack*) target;
  2449. target = gEntList.FindEntityByName( NULL, m_trainName );
  2450. if ( target )
  2451. {
  2452. m_train = (CFuncTrackTrain *)gEntList.FindEntityByName( NULL, m_trainName );
  2453. if ( !m_train )
  2454. {
  2455. Warning( "Can't find train for track change! %s\n", STRING(m_trainName) );
  2456. Assert(0);
  2457. return;
  2458. }
  2459. Vector center = WorldSpaceCenter();
  2460. m_trackBottom = m_trackBottom->Nearest( center );
  2461. m_trackTop = m_trackTop->Nearest( center );
  2462. UpdateAutoTargets( m_toggle_state );
  2463. SetThink( NULL );
  2464. return;
  2465. }
  2466. else
  2467. {
  2468. Warning( "Can't find train for track change! %s\n", STRING(m_trainName) );
  2469. Assert(0);
  2470. target = gEntList.FindEntityByName( NULL, m_trainName );
  2471. }
  2472. }
  2473. else
  2474. {
  2475. Warning( "Can't find bottom track for track change! %s\n", STRING(m_trackBottomName) );
  2476. Assert(0);
  2477. }
  2478. }
  2479. else
  2480. {
  2481. Warning( "Can't find top track for track change! %s\n", STRING(m_trackTopName) );
  2482. Assert(0);
  2483. }
  2484. }
  2485. TRAIN_CODE CFuncTrackChange::EvaluateTrain( CPathTrack *pcurrent )
  2486. {
  2487. // Go ahead and work, we don't have anything to switch, so just be an elevator
  2488. if ( !pcurrent || !m_train )
  2489. return TRAIN_SAFE;
  2490. if ( m_train->m_ppath == pcurrent || (pcurrent->m_pprevious && m_train->m_ppath == pcurrent->m_pprevious) ||
  2491. (pcurrent->m_pnext && m_train->m_ppath == pcurrent->m_pnext) )
  2492. {
  2493. if ( m_train->m_flSpeed != 0 )
  2494. return TRAIN_BLOCKING;
  2495. Vector dist = GetLocalOrigin() - m_train->GetLocalOrigin();
  2496. float length = dist.Length2D();
  2497. if ( length < m_train->m_length ) // Empirically determined close distance
  2498. return TRAIN_FOLLOWING;
  2499. else if ( length > (150 + m_train->m_length) )
  2500. return TRAIN_SAFE;
  2501. return TRAIN_BLOCKING;
  2502. }
  2503. return TRAIN_SAFE;
  2504. }
  2505. void CFuncTrackChange::UpdateTrain( QAngle &dest )
  2506. {
  2507. float time = GetMoveDoneTime();
  2508. m_train->SetAbsVelocity( GetAbsVelocity() );
  2509. m_train->SetLocalAngularVelocity( GetLocalAngularVelocity() );
  2510. m_train->SetMoveDoneTime( time );
  2511. // Attempt at getting the train to rotate properly around the origin of the trackchange
  2512. if ( time <= 0 )
  2513. return;
  2514. Vector offset = m_train->GetLocalOrigin() - GetLocalOrigin();
  2515. QAngle delta = dest - GetLocalAngles();
  2516. // Transform offset into local coordinates
  2517. Vector forward, right, up;
  2518. AngleVectorsTranspose( delta, &forward, &right, &up );
  2519. Vector local;
  2520. local.x = DotProduct( offset, forward );
  2521. local.y = DotProduct( offset, right );
  2522. local.z = DotProduct( offset, up );
  2523. local = local - offset;
  2524. m_train->SetAbsVelocity( GetAbsVelocity() + (local * (1.0/time)) );
  2525. }
  2526. void CFuncTrackChange::GoDown( void )
  2527. {
  2528. if ( m_code == TRAIN_BLOCKING )
  2529. return;
  2530. // HitBottom may get called during CFuncPlat::GoDown(), so set up for that
  2531. // before you call GoDown()
  2532. UpdateAutoTargets( TS_GOING_DOWN );
  2533. // If ROTMOVE, move & rotate
  2534. if ( FBitSet( m_spawnflags, SF_TRACK_DONT_MOVE ) )
  2535. {
  2536. SetMoveDone( &CFuncTrackChange::CallHitBottom );
  2537. m_toggle_state = TS_GOING_DOWN;
  2538. AngularMove( m_start, m_flSpeed );
  2539. }
  2540. else
  2541. {
  2542. BaseClass::GoDown();
  2543. SetMoveDone( &CFuncTrackChange::CallHitBottom );
  2544. RotMove( m_start, GetMoveDoneTime() );
  2545. }
  2546. // Otherwise, rotate first, move second
  2547. // If the train is moving with the platform, update it
  2548. if ( m_code == TRAIN_FOLLOWING )
  2549. {
  2550. UpdateTrain( m_start );
  2551. m_train->m_ppath = NULL;
  2552. #ifdef HL1_DLL
  2553. m_train->m_bOnTrackChange = true;
  2554. #endif
  2555. }
  2556. }
  2557. //
  2558. // Platform is at bottom, now starts moving up
  2559. //
  2560. void CFuncTrackChange::GoUp( void )
  2561. {
  2562. if ( m_code == TRAIN_BLOCKING )
  2563. return;
  2564. // HitTop may get called during CFuncPlat::GoUp(), so set up for that
  2565. // before you call GoUp();
  2566. UpdateAutoTargets( TS_GOING_UP );
  2567. if ( FBitSet( m_spawnflags, SF_TRACK_DONT_MOVE ) )
  2568. {
  2569. m_toggle_state = TS_GOING_UP;
  2570. SetMoveDone( &CFuncTrackChange::CallHitTop );
  2571. AngularMove( m_end, m_flSpeed );
  2572. }
  2573. else
  2574. {
  2575. // If ROTMOVE, move & rotate
  2576. BaseClass::GoUp();
  2577. SetMoveDone( &CFuncTrackChange::CallHitTop );
  2578. RotMove( m_end, GetMoveDoneTime() );
  2579. }
  2580. // Otherwise, move first, rotate second
  2581. // If the train is moving with the platform, update it
  2582. if ( m_code == TRAIN_FOLLOWING )
  2583. {
  2584. UpdateTrain( m_end );
  2585. m_train->m_ppath = NULL;
  2586. }
  2587. }
  2588. //-----------------------------------------------------------------------------
  2589. // Purpose: Normal track change
  2590. // Input : toggleState -
  2591. //-----------------------------------------------------------------------------
  2592. void CFuncTrackChange::UpdateAutoTargets( int toggleState )
  2593. {
  2594. if ( !m_trackTop || !m_trackBottom )
  2595. return;
  2596. if ( toggleState == TS_AT_TOP )
  2597. {
  2598. m_trackTop->RemoveSpawnFlags( SF_PATH_DISABLED );
  2599. }
  2600. else
  2601. {
  2602. m_trackTop->AddSpawnFlags( SF_PATH_DISABLED );
  2603. }
  2604. if ( toggleState == TS_AT_BOTTOM )
  2605. {
  2606. m_trackBottom->RemoveSpawnFlags( SF_PATH_DISABLED );
  2607. }
  2608. else
  2609. {
  2610. m_trackBottom->AddSpawnFlags( SF_PATH_DISABLED );
  2611. }
  2612. }
  2613. void CFuncTrackChange::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  2614. {
  2615. if ( m_toggle_state != TS_AT_TOP && m_toggle_state != TS_AT_BOTTOM )
  2616. return;
  2617. // If train is in "safe" area, but not on the elevator, play alarm sound
  2618. if ( m_toggle_state == TS_AT_TOP )
  2619. m_code = EvaluateTrain( m_trackTop );
  2620. else if ( m_toggle_state == TS_AT_BOTTOM )
  2621. m_code = EvaluateTrain( m_trackBottom );
  2622. else
  2623. m_code = TRAIN_BLOCKING;
  2624. if ( m_code == TRAIN_BLOCKING )
  2625. {
  2626. // Play alarm and return
  2627. EmitSound( "FuncTrackChange.Blocking" );
  2628. return;
  2629. }
  2630. // Otherwise, it's safe to move
  2631. // If at top, go down
  2632. // at bottom, go up
  2633. DisableUse();
  2634. if (m_toggle_state == TS_AT_TOP)
  2635. GoDown();
  2636. else
  2637. GoUp();
  2638. }
  2639. //
  2640. // Platform has hit bottom. Stops and waits forever.
  2641. //
  2642. void CFuncTrackChange::HitBottom( void )
  2643. {
  2644. BaseClass::HitBottom();
  2645. if ( m_code == TRAIN_FOLLOWING )
  2646. {
  2647. // UpdateTrain();
  2648. m_train->SetTrack( m_trackBottom );
  2649. }
  2650. SetMoveDone( NULL );
  2651. SetMoveDoneTime( -1 );
  2652. UpdateAutoTargets( m_toggle_state );
  2653. EnableUse();
  2654. }
  2655. //
  2656. // Platform has hit bottom. Stops and waits forever.
  2657. //
  2658. void CFuncTrackChange::HitTop( void )
  2659. {
  2660. BaseClass::HitTop();
  2661. if ( m_code == TRAIN_FOLLOWING )
  2662. {
  2663. // UpdateTrain();
  2664. m_train->SetTrack( m_trackTop );
  2665. }
  2666. // Don't let the plat go back down
  2667. SetMoveDone( NULL );
  2668. SetMoveDoneTime( -1 );
  2669. UpdateAutoTargets( m_toggle_state );
  2670. EnableUse();
  2671. }
  2672. class CFuncTrackAuto : public CFuncTrackChange
  2673. {
  2674. DECLARE_CLASS( CFuncTrackAuto, CFuncTrackChange );
  2675. public:
  2676. void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  2677. virtual void UpdateAutoTargets( int toggleState );
  2678. void TriggerTrackChange( inputdata_t &inputdata );
  2679. DECLARE_DATADESC();
  2680. };
  2681. BEGIN_DATADESC( CFuncTrackAuto )
  2682. DEFINE_INPUTFUNC( FIELD_VOID, "Trigger", TriggerTrackChange ),
  2683. END_DATADESC()
  2684. LINK_ENTITY_TO_CLASS( func_trackautochange, CFuncTrackAuto );
  2685. // Auto track change
  2686. void CFuncTrackAuto::UpdateAutoTargets( int toggleState )
  2687. {
  2688. CPathTrack *pTarget, *pNextTarget;
  2689. if ( !m_trackTop || !m_trackBottom )
  2690. return;
  2691. if ( m_targetState == TS_AT_TOP )
  2692. {
  2693. pTarget = m_trackTop->GetNext();
  2694. pNextTarget = m_trackBottom->GetNext();
  2695. }
  2696. else
  2697. {
  2698. pTarget = m_trackBottom->GetNext();
  2699. pNextTarget = m_trackTop->GetNext();
  2700. }
  2701. if ( pTarget )
  2702. {
  2703. pTarget->RemoveSpawnFlags( SF_PATH_DISABLED );
  2704. if ( m_code == TRAIN_FOLLOWING && m_train && m_train->m_flSpeed == 0 )
  2705. {
  2706. m_train->SetSpeed( pTarget->m_flSpeed );
  2707. m_train->Use( this, this, USE_SET, 0 );
  2708. }
  2709. }
  2710. if ( pNextTarget )
  2711. {
  2712. pNextTarget->AddSpawnFlags( SF_PATH_DISABLED );
  2713. }
  2714. }
  2715. void CFuncTrackAuto::TriggerTrackChange ( inputdata_t &inputdata )
  2716. {
  2717. CPathTrack *pTarget;
  2718. if ( !UseEnabled() )
  2719. return;
  2720. if ( m_toggle_state == TS_AT_TOP )
  2721. pTarget = m_trackTop;
  2722. else if ( m_toggle_state == TS_AT_BOTTOM )
  2723. pTarget = m_trackBottom;
  2724. else
  2725. pTarget = NULL;
  2726. if ( inputdata.pActivator && FClassnameIs( inputdata.pActivator, "func_tracktrain" ) )
  2727. {
  2728. m_code = EvaluateTrain( pTarget );
  2729. // Safe to fire?
  2730. if ( m_code == TRAIN_FOLLOWING && m_toggle_state != m_targetState )
  2731. {
  2732. DisableUse();
  2733. if (m_toggle_state == TS_AT_TOP)
  2734. GoDown();
  2735. else
  2736. GoUp();
  2737. }
  2738. }
  2739. else
  2740. {
  2741. if ( pTarget )
  2742. pTarget = pTarget->GetNext();
  2743. if ( pTarget && m_train->m_ppath != pTarget && ShouldToggle( USE_TOGGLE, m_targetState ) )
  2744. {
  2745. if ( m_targetState == TS_AT_TOP )
  2746. m_targetState = TS_AT_BOTTOM;
  2747. else
  2748. m_targetState = TS_AT_TOP;
  2749. }
  2750. UpdateAutoTargets( m_targetState );
  2751. }
  2752. }
  2753. void CFuncTrackAuto::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  2754. {
  2755. CPathTrack *pTarget;
  2756. if ( !UseEnabled() )
  2757. return;
  2758. if ( m_toggle_state == TS_AT_TOP )
  2759. pTarget = m_trackTop;
  2760. else if ( m_toggle_state == TS_AT_BOTTOM )
  2761. pTarget = m_trackBottom;
  2762. else
  2763. pTarget = NULL;
  2764. if ( FClassnameIs( pActivator, "func_tracktrain" ) )
  2765. {
  2766. m_code = EvaluateTrain( pTarget );
  2767. // Safe to fire?
  2768. if ( m_code == TRAIN_FOLLOWING && m_toggle_state != m_targetState )
  2769. {
  2770. DisableUse();
  2771. if (m_toggle_state == TS_AT_TOP)
  2772. GoDown();
  2773. else
  2774. GoUp();
  2775. }
  2776. }
  2777. else
  2778. {
  2779. if ( pTarget )
  2780. pTarget = pTarget->GetNext();
  2781. if ( pTarget && m_train->m_ppath != pTarget && ShouldToggle( useType, m_targetState ) )
  2782. {
  2783. if ( m_targetState == TS_AT_TOP )
  2784. m_targetState = TS_AT_BOTTOM;
  2785. else
  2786. m_targetState = TS_AT_TOP;
  2787. }
  2788. UpdateAutoTargets( m_targetState );
  2789. }
  2790. }