home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / SplineMover.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  77KB  |  3,290 lines

  1. #include "../idlib/precompiled.h"
  2. #pragma hdrstop
  3.  
  4. #include "Game_local.h"
  5. #include "spawner.h"
  6.  
  7. const idEventDef EV_OnAcceleration( "<onAcceleration>" );
  8. const idEventDef EV_OnDeceleration( "<onDeceleration>" );
  9. const idEventDef EV_OnCruising( "<onCruising>" );
  10.  
  11. const idEventDef EV_OnStartMoving( "<onStartMoving>" );
  12. const idEventDef EV_OnStopMoving( "<onStopMoving>" );
  13.  
  14. //=======================================================
  15. //
  16. //    rvPhysics_Spline
  17. //
  18. //=======================================================
  19. CLASS_DECLARATION( idPhysics_Base, rvPhysics_Spline )
  20.     EVENT( EV_PostRestore,            rvPhysics_Spline::Event_PostRestore )
  21. END_CLASS
  22.  
  23. void splinePState_t::ApplyAccelerationDelta( float timeStepSec ) {
  24.     speed = SignZero(idealSpeed) * Min( idMath::Fabs(idealSpeed), idMath::Fabs(speed) + acceleration * timeStepSec );
  25. }
  26.  
  27. void splinePState_t::ApplyDecelerationDelta( float timeStepSec ) {
  28.     speed = SignZero(speed) * Max( idMath::Fabs(idealSpeed), idMath::Fabs(speed) - deceleration * timeStepSec );
  29. }
  30.  
  31. void splinePState_t::UpdateDist( float timeStepSec ) {
  32.     dist += speed * timeStepSec;
  33. }
  34.  
  35. bool splinePState_t::ShouldAccelerate() const {
  36.     if( Sign(idealSpeed) == Sign(speed) ) {
  37.         return idMath::Fabs(speed) < idMath::Fabs(idealSpeed);
  38.     } else if( !Sign(speed) ) {
  39.         return true;
  40.     }
  41.     
  42.     return false;
  43. }
  44.  
  45. bool splinePState_t::ShouldDecelerate() const {
  46.     if( Sign(speed) == Sign(idealSpeed) ) {
  47.         return idMath::Fabs(speed) > idMath::Fabs(idealSpeed);
  48.     } else if( !Sign(idealSpeed) ) {
  49.         return true;
  50.     }
  51.  
  52.     return false;
  53. }
  54.  
  55. void splinePState_t::Clear() {
  56.     origin.Zero();
  57.     localOrigin.Zero();
  58.     axis.Identity();
  59.     localAxis.Identity();
  60.     speed = 0.0f;
  61.     idealSpeed = 0.0f;
  62.     dist = 0.0f;
  63.     acceleration = 0.0f;
  64.     deceleration = 0.0f;
  65. }
  66.  
  67. void splinePState_t::WriteToSnapshot( idBitMsgDelta &msg ) const {
  68.     msg.WriteDeltaVec3( vec3_zero, origin );
  69.     msg.WriteDeltaVec3( vec3_zero, localOrigin );
  70.     msg.WriteDeltaMat3( mat3_identity, axis );
  71.     msg.WriteDeltaMat3( mat3_identity, localAxis );
  72.     msg.WriteDeltaFloat( 0.0f, speed );
  73.     msg.WriteDeltaFloat( 0.0f, idealSpeed );
  74.     msg.WriteDeltaFloat( 0.0f, dist );
  75.     msg.WriteDeltaFloat( 0.0f, acceleration );
  76.     msg.WriteDeltaFloat( 0.0f, deceleration );
  77. }
  78.  
  79. void splinePState_t::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  80.     origin = msg.ReadDeltaVec3( vec3_zero );
  81.     localOrigin = msg.ReadDeltaVec3( vec3_zero );
  82.     axis = msg.ReadDeltaMat3( mat3_identity );
  83.     localAxis = msg.ReadDeltaMat3( mat3_identity );
  84.     speed = msg.ReadDeltaFloat( 0.0f );
  85.     idealSpeed = msg.ReadDeltaFloat( 0.0f );
  86.     dist = msg.ReadDeltaFloat( 0.0f );
  87.     acceleration = msg.ReadDeltaFloat( 0.0f );
  88.     deceleration = msg.ReadDeltaFloat( 0.0f );
  89. }
  90.  
  91. void splinePState_t::Save( idSaveGame *savefile ) const {
  92.     savefile->WriteVec3( origin );
  93.     savefile->WriteVec3( localOrigin );
  94.     savefile->WriteMat3( axis );
  95.     savefile->WriteMat3( localAxis );
  96.     savefile->WriteFloat( speed );
  97.     savefile->WriteFloat( idealSpeed );
  98.     savefile->WriteFloat( dist );
  99.     savefile->WriteFloat( acceleration );
  100.     savefile->WriteFloat( deceleration );
  101. }
  102.  
  103. void splinePState_t::Restore( idRestoreGame *savefile ) {
  104.     savefile->ReadVec3( origin );
  105.     savefile->ReadVec3( localOrigin );
  106.     savefile->ReadMat3( axis );
  107.     savefile->ReadMat3( localAxis );
  108.     savefile->ReadFloat( speed );
  109.     savefile->ReadFloat( idealSpeed );
  110.     savefile->ReadFloat( dist );
  111.     savefile->ReadFloat( acceleration );
  112.     savefile->ReadFloat( deceleration );
  113. }
  114.  
  115. splinePState_t&    splinePState_t::Assign( const splinePState_t* state ) {
  116.     SIMDProcessor->Memcpy( this, state, sizeof(splinePState_t) );
  117.     return *this;
  118. }
  119.  
  120. splinePState_t&    splinePState_t::operator=( const splinePState_t& state ) {
  121.     return Assign( &state );
  122. }
  123.  
  124. splinePState_t&    splinePState_t::operator=( const splinePState_t* state ) {
  125.     return Assign( state );
  126. }
  127.  
  128. /*
  129. ================
  130. rvPhysics_Spline::rvPhysics_Spline
  131. ================
  132. */
  133. rvPhysics_Spline::rvPhysics_Spline( void ) {
  134.     accelDecelStateThread.SetName( "AccelDecel" );
  135.     accelDecelStateThread.SetOwner( this );
  136.     accelDecelStateThread.SetState( "Cruising" );
  137.  
  138.     clipModel = NULL;
  139.  
  140.     spline = NULL;
  141.     SetSplineEntity( NULL );
  142.  
  143.     memset( &pushResults, 0, sizeof(trace_t) );
  144.     pushResults.fraction = 1.0f;
  145.  
  146.     current.Clear();
  147.     SaveState();
  148. }
  149.  
  150. /*
  151. ================
  152. rvPhysics_Spline::~rvPhysics_Spline
  153. ================
  154. */
  155. rvPhysics_Spline::~rvPhysics_Spline( void ) {
  156.     SAFE_DELETE_PTR( clipModel );
  157.  
  158.     SAFE_DELETE_PTR( spline );
  159. }
  160.  
  161. /*
  162. ================
  163. rvPhysics_Spline::Save
  164. ================
  165. */
  166. void rvPhysics_Spline::Save( idSaveGame *savefile ) const {
  167.  
  168.     current.Save( savefile );
  169.     saved.Save( savefile );
  170.  
  171.     savefile->WriteFloat( splineLength );
  172.     // This spline was retored as NULL, so there's no reason to save it.
  173.     //savefile->WriteInt(spline != NULL ? spline->GetTime( 0 ) : -1 );    // cnicholson: Added unsaved var
  174.     splineEntity.Save( savefile );
  175.  
  176.     savefile->WriteTrace( pushResults );
  177.  
  178.     savefile->WriteClipModel( clipModel );
  179.  
  180.     accelDecelStateThread.Save( savefile );
  181. }
  182.  
  183. /*
  184. ================
  185. rvPhysics_Spline::Restore
  186. ================
  187. */
  188. void rvPhysics_Spline::Event_PostRestore( void ) {
  189.  
  190.     if( splineEntity.IsValid() ) {
  191.         spline = splineEntity->GetSpline();
  192.     }
  193. }
  194.  
  195. void rvPhysics_Spline::Restore( idRestoreGame *savefile ) {
  196.  
  197.     current.Restore( savefile );
  198.     saved.Restore( savefile );
  199.  
  200.     savefile->ReadFloat( splineLength );
  201.     SAFE_DELETE_PTR( spline );
  202.     splineEntity.Restore( savefile );
  203.  
  204.     savefile->ReadTrace( pushResults );
  205.     
  206.     savefile->ReadClipModel( clipModel );
  207.  
  208.     accelDecelStateThread.Restore( savefile, this );
  209. }
  210.  
  211. /*
  212. ================
  213. rvPhysics_Spline::SetClipModel
  214. ================
  215. */
  216. void rvPhysics_Spline::SetClipModel( idClipModel *model, const float density, int id, bool freeOld ) {
  217.  
  218.     assert( self );
  219.     assert( model );                    // we need a clip model
  220.  
  221.     if ( clipModel && clipModel != model && freeOld ) {
  222.         delete clipModel;
  223.     }
  224.     
  225.     clipModel = model;
  226.  
  227.     LinkClip();
  228. }
  229.  
  230. /*
  231. ================
  232. rvPhysics_Spline::GetClipModel
  233. ================
  234. */
  235. idClipModel *rvPhysics_Spline::GetClipModel( int id ) const {
  236.     return clipModel;
  237. }
  238.  
  239. /*
  240. ================
  241. rvPhysics_Spline::SetContents
  242. ================
  243. */
  244. void rvPhysics_Spline::SetContents( int contents, int id ) {
  245.     clipModel->SetContents( contents );
  246. }
  247.  
  248. /*
  249. ================
  250. rvPhysics_Spline::GetContents
  251. ================
  252. */
  253. int rvPhysics_Spline::GetContents( int id ) const {
  254.     return clipModel->GetContents();
  255. }
  256.  
  257. /*
  258. ================
  259. rvPhysics_Spline::GetBounds
  260. ================
  261. */
  262. const idBounds &rvPhysics_Spline::GetBounds( int id ) const {
  263.     return clipModel->GetBounds();
  264. }
  265.  
  266. /*
  267. ================
  268. rvPhysics_Spline::GetAbsBounds
  269. ================
  270. */
  271. const idBounds &rvPhysics_Spline::GetAbsBounds( int id ) const {
  272.     return clipModel->GetAbsBounds();
  273. }
  274.  
  275. /*
  276. ================
  277. rvPhysics_Spline::SetSpline
  278. ================
  279. */
  280. void rvPhysics_Spline::SetSpline( idCurve_Spline<idVec3>* spline ) {
  281.     SAFE_DELETE_PTR( this->spline );
  282.  
  283.     //Keep any left over dist from last spline to minimize hitches
  284.     if( GetSpeed() >= 0.0f ) {
  285.         current.dist = Max( 0.0f, current.dist - splineLength );
  286.     }
  287.  
  288.     if( !spline ) {
  289.         splineLength = 0.0f;
  290.         return;
  291.     }
  292.  
  293.     this->spline = spline;
  294.  
  295.     splineLength = spline->GetLengthForTime( spline->GetTime(spline->GetNumValues() - 1) );
  296.     if( GetSpeed() < 0.0f ) {
  297.         current.dist = splineLength - current.dist;
  298.     }
  299.  
  300.     Activate();
  301. }
  302.  
  303. /*
  304. ================
  305. rvPhysics_Spline::SetSplineEntity
  306. ================
  307. */
  308. void rvPhysics_Spline::SetSplineEntity( idSplinePath* spline ) {
  309.     splineEntity = spline;
  310.     SetSpline( (spline) ? spline->GetSpline() : NULL );
  311. }
  312.  
  313. /*
  314. ================
  315. rvPhysics_Spline::ComputeDecelFromSpline
  316. ================
  317. */
  318. float rvPhysics_Spline::ComputeDecelFromSpline() const {
  319.     // FIXME: merge this in better.  It seems very special case
  320.     float numerator = GetSpeed() * GetSpeed();
  321.     float denomonator = 2.0f * ((GetSpeed() >= 0.0f) ? (splineLength - current.dist) : current.dist);
  322.  
  323.     assert( denomonator > VECTOR_EPSILON );
  324.  
  325.     return numerator / denomonator;
  326. }
  327.  
  328. /*
  329. ================
  330. rvPhysics_Spline::SetLinearAcceleration
  331. ================
  332. */
  333. void rvPhysics_Spline::SetLinearAcceleration( const float accel ) {
  334.     current.acceleration = accel;
  335. }
  336.  
  337. /*
  338. ================
  339. rvPhysics_Spline::SetLinearDeceleration
  340. ================
  341. */
  342. void rvPhysics_Spline::SetLinearDeceleration( const float decel ) {
  343.     current.deceleration = decel;
  344. }
  345.  
  346. /*
  347. ================
  348. rvPhysics_Spline::SetSpeed
  349. ================
  350. */
  351. void rvPhysics_Spline::SetSpeed( float speed ) {
  352.     if( IsAtRest() || StoppedMoving() ) {
  353.         current.dist = (speed < 0.0f) ? splineLength - current.dist : current.dist;
  354.     }
  355.  
  356.     current.idealSpeed = speed;
  357.     Activate();
  358. }
  359.  
  360. /*
  361. ================
  362. rvPhysics_Spline::GetSpeed
  363. ================
  364. */
  365. float rvPhysics_Spline::GetSpeed() const {
  366.     return current.speed;
  367. }
  368.  
  369. /*
  370. ================
  371. rvPhysics_Spline::Evaluate
  372. ================
  373. */
  374. bool rvPhysics_Spline::Evaluate( int timeStepMSec, int endTimeMSec ) {
  375.     idVec3 masterOrigin;
  376.     idMat3 masterAxis;
  377.  
  378.     splinePState_t previous = current;
  379.  
  380.     if( HasValidSpline() ) {
  381.         if( StoppedMoving() ) {
  382.             Rest();
  383.             return false;
  384.         }
  385.  
  386.         accelDecelStateThread.Execute();
  387.  
  388.         // FIXME: clean this up
  389.         if( IsAtBeginningOfSpline() || IsAtEndOfSpline() ) {
  390.             current = previous;
  391.             Rest();
  392.             self->ProcessEvent( &EV_DoneMoving );
  393.             
  394.             if( gameLocal.program.GetReturnedBool() ) {
  395.                 current.speed = 0.0f;
  396.                 return false;
  397.             } else {
  398.                 return true;
  399.             }
  400.         }
  401.     
  402.         float currentTime = splineEntity->GetSampledTime ( current.dist );
  403.         if (  currentTime ==  -1.0f ) {
  404.             currentTime = spline->GetTimeForLength( Min(current.dist, splineLength), 0.01f );
  405.         }
  406.  
  407.         current.axis = spline->GetCurrentFirstDerivative(currentTime).ToAngles().Normalize360().ToMat3();
  408.         current.origin = spline->GetCurrentValue( currentTime );
  409.         current.localOrigin = current.origin;
  410.         current.localAxis = current.axis;
  411.     } else if( self->IsBound() ) {    
  412.         self->GetMasterPosition( masterOrigin, masterAxis );
  413.         current.axis = current.localAxis * masterAxis;
  414.         current.origin = masterOrigin + current.localOrigin * masterAxis;
  415.     } else {
  416.         Rest();
  417.         return false;
  418.     }
  419.  
  420.     gameLocal.push.ClipPush( pushResults, self, 0, previous.origin, previous.axis, current.origin, current.axis );
  421.     if( pushResults.fraction < 1.0f ) {
  422.         current = previous;
  423.         LinkClip();
  424.         current.speed = 0.0f;
  425.         return false;
  426.     }
  427.  
  428.     LinkClip();
  429.  
  430.     if( StoppedMoving() && !self->IsBound() ) {
  431.         Rest();
  432.         self->ProcessEvent( &EV_DoneMoving );
  433.         return !gameLocal.program.GetReturnedBool();
  434.     }
  435.  
  436.     return true;
  437. }
  438.  
  439. /*
  440. ================
  441. rvPhysics_Spline::Activate
  442. ================
  443. */
  444. void rvPhysics_Spline::Activate( void ) {
  445.     assert( self );
  446.     self->BecomeActive( TH_PHYSICS );
  447. }
  448.  
  449. /*
  450. ================
  451. rvPhysics_Spline::Rest
  452. ================
  453. */
  454. void rvPhysics_Spline::Rest( void ) {
  455.     assert( self );
  456.     self->BecomeInactive( TH_PHYSICS );
  457. }
  458.  
  459. /*
  460. ================
  461. rvPhysics_Spline::IsAtRest
  462. ================
  463. */
  464. bool rvPhysics_Spline::IsAtRest( void ) const {
  465.     assert( self );
  466.     return !self->IsActive( TH_PHYSICS );
  467. }
  468.  
  469. /*
  470. ================
  471. rvPhysics_Spline::IsAtEndOfSpline
  472. ================
  473. */
  474. bool rvPhysics_Spline::IsAtEndOfSpline( void ) const {
  475.     return current.dist >= splineLength;
  476. }
  477.  
  478. /*
  479. ================
  480. rvPhysics_Spline::IsAtBeginningOfSpline
  481. ================
  482. */
  483. bool rvPhysics_Spline::IsAtBeginningOfSpline( void ) const {
  484.     return current.dist <= 0.0f;
  485. }
  486.  
  487. /*
  488. ================
  489. rvPhysics_Spline::IsPushable
  490. ================
  491. */
  492. bool rvPhysics_Spline::IsPushable( void ) const {
  493.     return !HasValidSpline() && idPhysics_Base::IsPushable();
  494. }
  495.  
  496. /*
  497. ================
  498. rvPhysics_Spline::StartingToMove
  499. ================
  500. */
  501. bool rvPhysics_Spline::StartingToMove( void ) const {
  502.     float firstDeltaSpeed = current.acceleration * MS2SEC(gameLocal.GetMSec());
  503.     return idMath::Fabs(current.idealSpeed) > VECTOR_EPSILON && idMath::Fabs(current.speed) <= firstDeltaSpeed;
  504. }
  505.  
  506. /*
  507. ================
  508. rvPhysics_Spline::StoppedMoving
  509. ================
  510. */
  511. bool rvPhysics_Spline::StoppedMoving( void ) const {
  512.     return idMath::Fabs(current.idealSpeed) < VECTOR_EPSILON && idMath::Fabs(current.speed) < VECTOR_EPSILON;
  513. }
  514.  
  515. /*
  516. ================
  517. rvPhysics_Spline::HasValidSpline
  518. ================
  519. */
  520. bool rvPhysics_Spline::HasValidSpline() const {
  521.     return spline && splineLength > VECTOR_EPSILON;
  522. }
  523.  
  524. /*
  525. ================
  526. rvPhysics_Spline::SaveState
  527. ================
  528. */
  529. void rvPhysics_Spline::SaveState( void ) {
  530.     saved = current;
  531. }
  532.  
  533. /*
  534. ================
  535. rvPhysics_Spline::RestoreState
  536. ================
  537. */
  538. void rvPhysics_Spline::RestoreState( void ) {
  539.     current = saved;
  540.  
  541.     LinkClip();
  542. }
  543.  
  544. /*
  545. ================
  546. idPhysics::SetOrigin
  547. ================
  548. */
  549. void rvPhysics_Spline::SetOrigin( const idVec3 &newOrigin, int id ) {
  550.     idVec3 masterOrigin;
  551.     idMat3 masterAxis;
  552.  
  553.     current.localOrigin = newOrigin;
  554.     if( self->IsBound() ) {
  555.         self->GetMasterPosition( masterOrigin, masterAxis );
  556.         current.origin = masterOrigin + current.localOrigin * masterAxis;
  557.     }
  558.     else {
  559.         current.origin = current.localOrigin;
  560.     }
  561.  
  562.     LinkClip();
  563.     Activate();
  564. }
  565.  
  566. /*
  567. ================
  568. idPhysics::SetAxis
  569. ================
  570. */
  571. void rvPhysics_Spline::SetAxis( const idMat3 &newAxis, int id ) {
  572.     idVec3 masterOrigin;
  573.     idMat3 masterAxis;
  574.  
  575.     current.localAxis = newAxis;
  576.     if ( self->IsBound() ) {
  577.         self->GetMasterPosition( masterOrigin, masterAxis );
  578.         current.axis = newAxis * masterAxis;
  579.     }
  580.     else {
  581.         current.axis = newAxis;
  582.     }
  583.  
  584.     LinkClip();
  585.     Activate();
  586. }
  587.  
  588. /*
  589. ================
  590. rvPhysics_Spline::Translate
  591. ================
  592. */
  593. void rvPhysics_Spline::Translate( const idVec3 &translation, int id ) {
  594.     SetOrigin( GetLocalOrigin() + translation );
  595. }
  596.  
  597. /*
  598. ================
  599. rvPhysics_Spline::Rotate
  600. ================
  601. */
  602. void rvPhysics_Spline::Rotate( const idRotation &rotation, int id ) {
  603.     SetAxis( GetLocalAxis() * rotation.ToMat3() );
  604.     SetOrigin( GetLocalOrigin() * rotation );
  605. }
  606.  
  607. /*
  608. ================
  609. rvPhysics_Spline::GetOrigin
  610. ================
  611. */
  612. const idVec3 &rvPhysics_Spline::GetOrigin( int id ) const {
  613.     return current.origin;
  614. }
  615.  
  616. /*
  617. ================
  618. rvPhysics_Spline::GetAxis
  619. ================
  620. */
  621. const idMat3 &rvPhysics_Spline::GetAxis( int id ) const {
  622.     return current.axis;
  623. }
  624.  
  625. /*
  626. ================
  627. rvPhysics_Spline::GetOrigin
  628. ================
  629. */
  630. idVec3 &rvPhysics_Spline::GetOrigin( int id ) {
  631.     return current.origin;
  632. }
  633.  
  634. /*
  635. ================
  636. rvPhysics_Spline::GetAxis
  637. ================
  638. */
  639. idMat3 &rvPhysics_Spline::GetAxis( int id ) {
  640.     return current.axis;
  641. }
  642.  
  643. /*
  644. ================
  645. rvPhysics_Spline::GetLocalOrigin
  646. ================
  647. */
  648. const idVec3 &rvPhysics_Spline::GetLocalOrigin( int id ) const {
  649.     return current.localOrigin;
  650. }
  651.  
  652. /*
  653. ================
  654. rvPhysics_Spline::GetLocalAxis
  655. ================
  656. */
  657. const idMat3 &rvPhysics_Spline::GetLocalAxis( int id ) const {
  658.     return current.localAxis;
  659. }
  660.  
  661. /*
  662. ================
  663. rvPhysics_Spline::GetLocalOrigin
  664. ================
  665. */
  666. idVec3 &rvPhysics_Spline::GetLocalOrigin( int id ) {
  667.     return current.localOrigin;
  668. }
  669.  
  670. /*
  671. ================
  672. rvPhysics_Spline::GetLocalAxis
  673. ================
  674. */
  675. idMat3 &rvPhysics_Spline::GetLocalAxis( int id ) {
  676.     return current.localAxis;
  677. }
  678.  
  679. /*
  680. ================
  681. rvPhysics_Spline::SetMaster
  682. ================
  683. */
  684. void rvPhysics_Spline::SetMaster( idEntity *master, const bool orientated ) {
  685.     idVec3 masterOrigin;
  686.     idMat3 masterAxis;
  687.  
  688.     if( master ) {
  689.         if( self->IsBound() ) {
  690.             // transform from world space to master space
  691.             self->GetMasterPosition( masterOrigin, masterAxis );
  692.             current.localOrigin = ( GetOrigin() - masterOrigin ) * masterAxis.Transpose();
  693.             current.localAxis = GetAxis() * masterAxis.Transpose();
  694.         }
  695.     }
  696. }
  697.  
  698. /*
  699. ================
  700. rvPhysics_Spline::ClipTranslation
  701. ================
  702. */
  703. void rvPhysics_Spline::ClipTranslation( trace_t &results, const idVec3 &translation, const idClipModel *model ) const {
  704.     if ( model ) {
  705.         gameLocal.TranslationModel( self, results, GetOrigin(), GetOrigin() + translation,
  706.                                             clipModel, GetAxis(), clipMask,
  707.                                             model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
  708.     }
  709.     else {
  710.         gameLocal.Translation( self, results, GetOrigin(), GetOrigin() + translation,
  711.                                             clipModel, GetAxis(), clipMask, self );
  712.     }
  713. }
  714.  
  715. /*
  716. ================
  717. rvPhysics_Spline::ClipRotation
  718. ================
  719. */
  720. void rvPhysics_Spline::ClipRotation( trace_t &results, const idRotation &rotation, const idClipModel *model ) const {
  721.     if ( model ) {
  722.         gameLocal.RotationModel( self, results, GetOrigin(), rotation,
  723.                                             clipModel, GetAxis(), clipMask,
  724.                                             model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
  725.     }
  726.     else {
  727.         gameLocal.Rotation( self, results, GetOrigin(), rotation,
  728.                                             clipModel, GetAxis(), clipMask, self );
  729.     }
  730. }
  731.  
  732. /*
  733. ================
  734. rvPhysics_Spline::ClipContents
  735. ================
  736. */
  737. int rvPhysics_Spline::ClipContents( const idClipModel *model ) const {
  738.     if ( model ) {
  739. // RAVEN BEGIN
  740. // ddynerman: multiple clip worlds
  741.         return gameLocal.ContentsModel( self, GetOrigin(), clipModel, GetAxis(), -1,
  742.                                     model->GetCollisionModel(), model->GetOrigin(), model->GetAxis() );
  743.     }
  744.     else {
  745.         return gameLocal.Contents( self, GetOrigin(), clipModel, GetAxis(), -1, NULL );
  746. // RAVEN END
  747.     }
  748. }
  749.  
  750. /*
  751. ================
  752. rvPhysics_Spline::DisableClip
  753. ================
  754. */
  755. void rvPhysics_Spline::DisableClip( void ) {
  756.     if( clipModel ) {
  757.         clipModel->Disable();
  758.     }
  759. }
  760.  
  761. /*
  762. ================
  763. rvPhysics_Spline::EnableClip
  764. ================
  765. */
  766. void rvPhysics_Spline::EnableClip( void ) {
  767.     if( clipModel ) {
  768.         clipModel->Enable();
  769.     }
  770. }
  771.  
  772. /*
  773. ================
  774. rvPhysics_Spline::UnlinkClip
  775. ================
  776. */
  777. void rvPhysics_Spline::UnlinkClip( void ) {
  778.     if( clipModel ) {
  779.         clipModel->Unlink();
  780.     }
  781. }
  782.  
  783. /*
  784. ================
  785. rvPhysics_Spline::LinkClip
  786. ================
  787. */
  788. void rvPhysics_Spline::LinkClip( void ) {
  789.     if( clipModel ) {
  790. // RAVEN BEGIN
  791. // ddynerman: multiple clip worlds
  792.         clipModel->Link( self, clipModel->GetId(), GetOrigin(), GetAxis() );
  793. // RAVEN END
  794.     }
  795. }
  796.  
  797. /*
  798. ================
  799. rvPhysics_Spline::GetBlockingInfo
  800. ================
  801. */
  802. const trace_t* rvPhysics_Spline::GetBlockingInfo( void ) const {
  803.     return (pushResults.fraction < 1.0f) ? &pushResults : NULL;
  804. }
  805.  
  806. /*
  807. ================
  808. rvPhysics_Spline::GetBlockingEntity
  809. ================
  810. */
  811. idEntity* rvPhysics_Spline::GetBlockingEntity( void ) const {
  812.     return (pushResults.fraction < 1.0f) ? gameLocal.entities[ pushResults.c.entityNum ] : NULL;
  813. }
  814.  
  815. /*
  816. ================
  817. rvPhysics_Spline::WriteToSnapshot
  818. ================
  819. */
  820. void rvPhysics_Spline::WriteToSnapshot( idBitMsgDelta &msg ) const {
  821.     current.WriteToSnapshot( msg );
  822. }
  823.  
  824. /*
  825. ================
  826. rvPhysics_Spline::ReadFromSnapshot
  827. ================
  828. */
  829. void rvPhysics_Spline::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  830.     current.ReadFromSnapshot( msg );
  831.  
  832.     LinkClip();
  833. }
  834.  
  835. CLASS_STATES_DECLARATION( rvPhysics_Spline )
  836.     STATE( "Accelerating",        rvPhysics_Spline::State_Accelerating )
  837.     STATE( "Decelerating",        rvPhysics_Spline::State_Decelerating )
  838.     STATE( "Cruising",            rvPhysics_Spline::State_Cruising )
  839. END_CLASS_STATES
  840.  
  841. /*
  842. ================
  843. rvPhysics_Spline::State_Accelerating
  844. ================
  845. */
  846. stateResult_t rvPhysics_Spline::State_Accelerating( const stateParms_t& parms ) {
  847.     stateResult_t returnResult = SRESULT_WAIT;
  848.  
  849.     if( !current.ShouldAccelerate() ) {
  850.         accelDecelStateThread.SetState( current.ShouldDecelerate() ? "Decelerating" : "Cruising" );
  851.         return SRESULT_DONE;
  852.     }
  853.  
  854.     if( !parms.stage ) {
  855.         if( StartingToMove() ) {
  856.             self->ProcessEvent( &EV_OnStartMoving );
  857.         }
  858.         self->ProcessEvent( &EV_OnAcceleration );
  859.  
  860.         returnResult = SRESULT_STAGE( parms.stage + 1 );
  861.     }
  862.  
  863.     float timeStepSec = MS2SEC( gameLocal.GetMSec() );
  864.     current.ApplyAccelerationDelta( timeStepSec );
  865.     current.UpdateDist( timeStepSec );
  866.  
  867.     return returnResult;
  868. }
  869.  
  870. /*
  871. ================
  872. rvPhysics_Spline::State_Decelerating
  873. ================
  874. */
  875. stateResult_t rvPhysics_Spline::State_Decelerating( const stateParms_t& parms ) {
  876.     if( !current.ShouldDecelerate() ) {
  877.         accelDecelStateThread.SetState( current.ShouldAccelerate() ? "Accelerating" : "Cruising" );
  878.         return SRESULT_DONE;
  879.     }
  880.  
  881.     float timeStepSec = MS2SEC( gameLocal.GetMSec() );
  882.     current.ApplyDecelerationDelta( timeStepSec );
  883.     current.UpdateDist( timeStepSec );
  884.  
  885.     if( !parms.stage ) {
  886.         self->ProcessEvent( &EV_OnDeceleration );
  887.         return SRESULT_STAGE( parms.stage + 1 );
  888.     }
  889.  
  890.     if( StoppedMoving() ) {
  891.         self->ProcessEvent( &EV_OnStopMoving );
  892.     }
  893.  
  894.     return SRESULT_WAIT;
  895. }
  896.  
  897. /*
  898. ================
  899. rvPhysics_Spline::State_Cruising
  900. ================
  901. */
  902. stateResult_t rvPhysics_Spline::State_Cruising( const stateParms_t& parms ) {
  903.     if( current.ShouldAccelerate() ) {
  904.         accelDecelStateThread.SetState( "Accelerating" );
  905.         return SRESULT_DONE;
  906.     } else if( current.ShouldDecelerate() ) {
  907.         accelDecelStateThread.SetState( "Decelerating" );
  908.         return SRESULT_DONE;
  909.     }
  910.  
  911.     current.UpdateDist( MS2SEC(gameLocal.GetMSec()) );
  912.  
  913.     if( !parms.stage ) {
  914.         self->ProcessEvent( &EV_OnCruising );
  915.         return SRESULT_STAGE( parms.stage + 1 );
  916.     }
  917.  
  918.     return SRESULT_WAIT;
  919. }
  920.  
  921.  
  922.  
  923. const idEventDef EV_SetSpline( "setSpline", "E" );
  924.  
  925. const idEventDef EV_SetAccel( "setAccel", "f" );
  926. const idEventDef EV_SetDecel( "setDecel", "f" );
  927.  
  928. const idEventDef EV_SetSpeed( "setSpeed", "f" );
  929. const idEventDef EV_GetSpeed( "getSpeed", "", 'f' );
  930.  
  931. const idEventDef EV_TramCar_SetIdealSpeed( "setIdealSpeed", "f" );
  932. const idEventDef EV_TramCar_GetIdealSpeed( "getIdealSpeed", "", 'f' );
  933.  
  934. const idEventDef EV_TramCar_ApplySpeedScale( "applySpeedScale", "f" );
  935.  
  936. const idEventDef EV_GetCurrentTrackInfo( "getCurrentTrackInfo", "", 's' );
  937. const idEventDef EV_GetTrackInfo( "getTrackInfo", "e", 's' );
  938.  
  939. const idEventDef EV_DoneMoving( "<doneMoving>", "", 'd' );
  940. const idEventDef EV_StartSoundPeriodic( "<startSoundPeriodic>", "sddd" );
  941.  
  942. idLinkList<rvSplineMover> rvSplineMover::splineMovers;
  943.  
  944. //=======================================================
  945. //
  946. //    rvSplineMover
  947. //
  948. //=======================================================
  949. CLASS_DECLARATION( idAnimatedEntity, rvSplineMover )
  950.     EVENT( EV_PostSpawn,                rvSplineMover::Event_PostSpawn )
  951.     EVENT( EV_Activate,                    rvSplineMover::Event_Activate )
  952.     EVENT( EV_SetSpline,                rvSplineMover::Event_SetSpline )
  953.     EVENT( EV_SetAccel,                    rvSplineMover::Event_SetAcceleration )
  954.     EVENT( EV_SetDecel,                    rvSplineMover::Event_SetDeceleration )
  955.     EVENT( EV_SetSpeed,                    rvSplineMover::Event_SetSpeed )
  956.     EVENT( EV_GetSpeed,                    rvSplineMover::Event_GetSpeed )
  957.     EVENT( EV_Thread_SetCallback,        rvSplineMover::Event_SetCallBack )
  958.     EVENT( EV_DoneMoving,                rvSplineMover::Event_DoneMoving )
  959.     EVENT( EV_GetSplineEntity,            rvSplineMover::Event_GetSpline )
  960.     EVENT( EV_GetCurrentTrackInfo,        rvSplineMover::Event_GetCurrentTrackInfo )
  961.     EVENT( EV_GetTrackInfo,                rvSplineMover::Event_GetTrackInfo )
  962.     EVENT( EV_TramCar_SetIdealSpeed,    rvSplineMover::Event_SetIdealSpeed )
  963.     EVENT( EV_TramCar_GetIdealSpeed,    rvSplineMover::Event_GetIdealSpeed )
  964.     EVENT( EV_TramCar_ApplySpeedScale,    rvSplineMover::Event_ApplySpeedScale )
  965.     EVENT( EV_OnAcceleration,            rvSplineMover::Event_OnAcceleration )
  966.     EVENT( EV_OnDeceleration,            rvSplineMover::Event_OnDeceleration )
  967.     EVENT( EV_OnCruising,                rvSplineMover::Event_OnCruising )
  968.     EVENT( EV_OnStartMoving,            rvSplineMover::Event_OnStartMoving )
  969.     EVENT( EV_OnStopMoving,                rvSplineMover::Event_OnStopMoving )
  970.     EVENT( EV_StartSoundPeriodic,        rvSplineMover::Event_StartSoundPeriodic )
  971.     EVENT( EV_PartBlocked,                rvSplineMover::Event_PartBlocked )
  972. END_CLASS
  973.  
  974. /*
  975. ================
  976. rvSplineMover::Spawn
  977. ================
  978. */
  979. void rvSplineMover::Spawn() {
  980.     waitThreadId = -1;
  981.  
  982.     physicsObj.SetSelf( this );
  983. // RAVEN BEGIN
  984. // mwhitlock: Dynamic memory consolidation
  985.     RV_PUSH_HEAP_MEM(this);
  986. // RAVEN END
  987.     physicsObj.SetClipModel( new idClipModel(GetPhysics()->GetClipModel()), 1.0f );
  988. // RAVEN BEGIN
  989. // mwhitlock: Dynamic memory consolidation
  990.     RV_POP_HEAP();
  991. // RAVEN END
  992.     physicsObj.SetContents( spawnArgs.GetBool("solid", "1") ? CONTENTS_SOLID : 0 );
  993.     physicsObj.SetClipMask( spawnArgs.GetBool("solidClip") ? CONTENTS_SOLID : 0 );
  994.     physicsObj.SetLinearVelocity( GetPhysics()->GetLinearVelocity() );
  995.     physicsObj.SetLinearAcceleration( spawnArgs.GetFloat("accel", "50") );
  996.     physicsObj.SetLinearDeceleration( spawnArgs.GetFloat("decel", "50") );
  997.     physicsObj.SetAxis( GetPhysics()->GetAxis() );
  998.     physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
  999.  
  1000.     SetPhysics( &physicsObj );
  1001.  
  1002.     AddSelfToGlobalList();
  1003.  
  1004.     // This is needed so we get sorted correctly
  1005.     BecomeInactive( TH_PHYSICS );
  1006.     BecomeActive( TH_PHYSICS );
  1007.  
  1008.     PlayAnim( ANIMCHANNEL_ALL, "idle", 0 );
  1009.  
  1010.     PostEventMS( &EV_PostSpawn, 0 );
  1011. }
  1012.  
  1013. /*
  1014. ================
  1015. rvSplineMover::~rvSplineMover
  1016. ================
  1017. */
  1018. rvSplineMover::~rvSplineMover() {
  1019.     RemoveSelfFromGlobalList();
  1020.     SetPhysics( NULL );
  1021. }
  1022.  
  1023. /*
  1024. ================
  1025. rvSplineMover::SetSpeed
  1026. ================
  1027. */
  1028. void rvSplineMover::SetSpeed( float newSpeed ) {
  1029.     physicsObj.SetSpeed( newSpeed );
  1030. }
  1031.  
  1032. /*
  1033. ================
  1034. rvSplineMover::GetSpeed
  1035. ================
  1036. */
  1037. float rvSplineMover::GetSpeed() const {
  1038.     return physicsObj.GetSpeed();
  1039. }
  1040.  
  1041. /*
  1042. ================
  1043. rvSplineMover::SetIdealSpeed
  1044. ================
  1045. */
  1046. void rvSplineMover::SetIdealSpeed( float newIdealSpeed ) {
  1047.     idealSpeed = newIdealSpeed;
  1048.     SetSpeed( newIdealSpeed );
  1049. }
  1050.  
  1051. /*
  1052. ================
  1053. rvSplineMover::GetIdealSpeed
  1054. ================
  1055. */
  1056. float rvSplineMover::GetIdealSpeed() const {
  1057.     return idealSpeed;
  1058. }
  1059.  
  1060. /*
  1061. ================
  1062. rvSplineMover::SetSpline
  1063. ================
  1064. */
  1065. void rvSplineMover::SetSpline( idSplinePath* spline ) {
  1066.     physicsObj.SetSplineEntity( spline );
  1067.     CheckSplineForOverrides( physicsObj.GetSpline(), &spline->spawnArgs );
  1068. }
  1069.  
  1070. /*
  1071. ================
  1072. rvSplineMover::GetSpline
  1073. ================
  1074. */
  1075. const idSplinePath* rvSplineMover::GetSpline() const {
  1076.     return physicsObj.GetSplineEntity();
  1077. }
  1078.  
  1079. /*
  1080. ================
  1081. rvSplineMover::GetSpline
  1082. ================
  1083. */
  1084. idSplinePath* rvSplineMover::GetSpline() {
  1085.     return physicsObj.GetSplineEntity();
  1086. }
  1087.  
  1088. /*
  1089. ================
  1090. rvSplineMover::SetAcceleration
  1091. ================
  1092. */
  1093. void rvSplineMover::SetAcceleration( float accel ) {
  1094.     physicsObj.SetLinearAcceleration( accel );
  1095. }
  1096.  
  1097. /*
  1098. ================
  1099. rvSplineMover::SetDeceleration
  1100. ================
  1101. */
  1102. void rvSplineMover::SetDeceleration( float decel ) {
  1103.     physicsObj.SetLinearDeceleration( decel );
  1104. }
  1105.  
  1106. /*
  1107. ================
  1108. rvSplineMover::CheckSplineForOverrides
  1109. ================
  1110. */
  1111. void rvSplineMover::CheckSplineForOverrides( const idCurve_Spline<idVec3>* spline, const idDict* args ) {
  1112.     if( !spline || !args ) {
  1113.         return;
  1114.     }
  1115.  
  1116.     int endSpline = args->GetInt( "end_spline" );
  1117.     if( endSpline && Sign(endSpline) == Sign(GetSpeed()) ) {
  1118.         physicsObj.SetLinearDeceleration( physicsObj.ComputeDecelFromSpline() );
  1119.         SetIdealSpeed( 0.0f );
  1120.     }
  1121. }
  1122.  
  1123. /*
  1124. ================
  1125. rvSplineMover::RestoreFromOverrides
  1126. ================
  1127. */
  1128. void rvSplineMover::RestoreFromOverrides( const idDict* args ) {
  1129.     if( !args ) {
  1130.         return;
  1131.     }
  1132.  
  1133.     physicsObj.SetLinearDeceleration( args->GetFloat("decel") );
  1134. }
  1135.  
  1136. /*
  1137. ================
  1138. rvSplineMover::PlayAnim
  1139. ================
  1140. */
  1141. int rvSplineMover::PlayAnim( int channel, const char* animName, int blendFrames ) {
  1142.     int animIndex = GetAnimator()->GetAnim( animName );
  1143.     if( !animIndex ) {
  1144.         return 0;
  1145.     }
  1146.  
  1147.     GetAnimator()->PlayAnim( channel, animIndex, gameLocal.GetTime(), FRAME2MS(blendFrames) );
  1148.     return GetAnimator()->CurrentAnim( channel )->Length();
  1149. }
  1150.  
  1151. /*
  1152. ================
  1153. rvSplineMover::CycleAnim
  1154. ================
  1155. */
  1156. void rvSplineMover::CycleAnim( int channel, const char* animName, int blendFrames ) {
  1157.     int animIndex = GetAnimator()->GetAnim( animName );
  1158.     if( !animIndex ) {
  1159.         return;
  1160.     }
  1161.  
  1162.     GetAnimator()->CycleAnim( channel, animIndex, gameLocal.GetTime(), FRAME2MS(blendFrames) );
  1163. }
  1164.  
  1165. /*
  1166. ================
  1167. rvSplineMover::ClearChannel
  1168. ================
  1169. */
  1170. void rvSplineMover::ClearChannel( int channel, int clearFrame ) {
  1171.     GetAnimator()->Clear( channel, gameLocal.GetTime(), FRAME2MS(clearFrame) );
  1172. }
  1173.  
  1174. /*
  1175. ================
  1176. rvSplineMover::PreBind
  1177. ================
  1178. */
  1179. void rvSplineMover::PreBind() {
  1180.     idAnimatedEntity::PreBind();
  1181.  
  1182.     SetSpline( NULL );
  1183. }
  1184.  
  1185. /*
  1186. ================
  1187. rvSplineMover::Save
  1188. ================
  1189. */
  1190. void rvSplineMover::Save( idSaveGame *savefile ) const {
  1191.     savefile->WriteStaticObject( physicsObj );
  1192.     savefile->WriteFloat( idealSpeed );
  1193.     savefile->WriteInt( waitThreadId );
  1194. }
  1195.  
  1196. /*
  1197. ================
  1198. rvSplineMover::Restore
  1199. ================
  1200. */
  1201. void rvSplineMover::Restore( idRestoreGame *savefile ) {
  1202.     savefile->ReadStaticObject( physicsObj );
  1203.     RestorePhysics( &physicsObj );
  1204.  
  1205.     savefile->ReadFloat( idealSpeed );
  1206.     savefile->ReadInt( waitThreadId );
  1207.  
  1208.     AddSelfToGlobalList();
  1209. }
  1210.  
  1211. /*
  1212. ================
  1213. rvSplineMover::WriteToSnapshot
  1214. ================
  1215. */
  1216. void rvSplineMover::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1217.     physicsObj.WriteToSnapshot( msg );
  1218. }
  1219.  
  1220. /*
  1221. ================
  1222. rvSplineMover::ReadFromSnapshot
  1223. ================
  1224. */
  1225. void rvSplineMover::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1226.     physicsObj.ReadFromSnapshot( msg );
  1227. }
  1228.  
  1229. /*
  1230. ================
  1231. rvSplineMover::AddSelfToGlobalList
  1232. ================
  1233. */
  1234. void rvSplineMover::AddSelfToGlobalList() {
  1235.     splineMoverNode.SetOwner( this );
  1236.  
  1237.     if( !InGlobalList() ) {
  1238.         splineMoverNode.AddToEnd( splineMovers );
  1239.     }
  1240. }
  1241.  
  1242. /*
  1243. ================
  1244. rvSplineMover::RemoveSelfFromGlobalList
  1245. ================
  1246. */
  1247. void rvSplineMover::RemoveSelfFromGlobalList() {
  1248.     splineMoverNode.Remove();
  1249. }
  1250.  
  1251. /*
  1252. ================
  1253. rvSplineMover::InGlobalList
  1254. ================
  1255. */
  1256. bool rvSplineMover::InGlobalList() const {
  1257.     return splineMoverNode.InList();
  1258. }
  1259.  
  1260. /*
  1261. ================
  1262. rvSplineMover::WhosVisible
  1263. ================
  1264. */
  1265. bool rvSplineMover::WhosVisible( const idFrustum& frustum, idList<rvSplineMover*>& list ) const {
  1266.     list.Clear();
  1267.  
  1268.     if( !frustum.IsValid() ) {
  1269.         return false;
  1270.     }
  1271.  
  1272.     for( rvSplineMover* node = splineMovers.Next(); node; node = node->splineMoverNode.Next() ) {
  1273.         if( node == this ) {
  1274.             continue;
  1275.         }
  1276.  
  1277.         if( frustum.IntersectsBounds(node->GetPhysics()->GetAbsBounds()) ) {
  1278.             list.AddUnique( node );
  1279.         }
  1280.     }
  1281.  
  1282.     return list.Num() > 0;
  1283. }
  1284.  
  1285. /*
  1286. ================
  1287. rvSplineMover::GetTrackInfo
  1288. ================
  1289. */
  1290. idStr rvSplineMover::GetTrackInfo( const idSplinePath* track ) const {
  1291.     if( !track ) {
  1292.         return idStr( "" );
  1293.     }
  1294.  
  1295.     idStr info( track->GetName() );
  1296.     return info.Mid( info.Last('_') - 1, 1 );
  1297. }
  1298.  
  1299. /*
  1300. ================
  1301. rvSplineMover::ConvertToMover
  1302. ================
  1303. */
  1304. rvSplineMover* rvSplineMover::ConvertToMover( idEntity* mover ) const {
  1305.     return mover && mover->IsType(rvSplineMover::Type) ? static_cast<rvSplineMover*>(mover) : NULL;
  1306. }
  1307.  
  1308. /*
  1309. ================
  1310. rvSplineMover::ConvertToSplinePath
  1311. ================
  1312. */
  1313. idSplinePath* rvSplineMover::ConvertToSplinePath( idEntity* spline ) const {
  1314.     return (spline && spline->IsType(idSplinePath::GetClassType())) ? static_cast<idSplinePath*>(spline) : NULL;
  1315. }
  1316.  
  1317. /*
  1318. ================
  1319. rvSplineMover::PreDoneMoving
  1320. ================
  1321. */
  1322. void rvSplineMover::PreDoneMoving() {
  1323.     if( waitThreadId >= 0 ) {
  1324.         idThread::ObjectMoveDone( waitThreadId, this );
  1325.         waitThreadId = -1;
  1326.     }
  1327.  
  1328.     RestoreFromOverrides( &spawnArgs );
  1329. }
  1330.  
  1331. /*
  1332. ================
  1333. rvSplineMover::PostDoneMoving
  1334. ================
  1335. */
  1336. void rvSplineMover::PostDoneMoving() {
  1337.     CallScriptEvents( physicsObj.GetSplineEntity(), "call_doneMoving", this );
  1338. }
  1339.  
  1340. /*
  1341. ==============
  1342. rvSplineMover::CallScriptEvents
  1343. ==============
  1344. */
  1345. // FIXME: very similier code is in the spawner...if possible try and make one function for both to call
  1346. void rvSplineMover::CallScriptEvents( const idSplinePath* spline, const char* prefixKey, idEntity* parm ) {
  1347.     if( !spline || !prefixKey || !prefixKey[0] ) {
  1348.         return;
  1349.     }
  1350.  
  1351.     rvScriptFuncUtility func;
  1352.     for( const idKeyValue* kv = spline->spawnArgs.MatchPrefix(prefixKey); kv; kv = spline->spawnArgs.MatchPrefix(prefixKey, kv) ) {
  1353.         if( !kv->GetValue().Length() ) {
  1354.             continue;
  1355.         }
  1356.  
  1357.         if( func.Init(kv->GetValue()) <= SFU_ERROR ) {
  1358.             continue;
  1359.         }
  1360.  
  1361.         func.InsertEntity( spline, 0 );
  1362.         func.InsertEntity( parm, 1 );
  1363.         func.CallFunc( &spawnArgs );
  1364.     }
  1365. }
  1366.  
  1367. /*
  1368. ================
  1369. rvSplineMover::Event_PostSpawn
  1370. ================
  1371. */
  1372. void rvSplineMover::Event_PostSpawn() {
  1373.     idEntityPtr<idEntity> target;
  1374.     for( int ix = targets.Num() - 1; ix >= 0; --ix ) {
  1375.         target = targets[ix];
  1376.  
  1377.         if( target.IsValid() && target->IsType(idSplinePath::GetClassType()) ) {
  1378.             SetSpline( static_cast<idSplinePath*>(target.GetEntity()) );
  1379.             break;
  1380.         }
  1381.     }
  1382.  
  1383.     SetIdealSpeed( spawnArgs.GetBool("waitForTrigger") ? 0.0f : spawnArgs.GetFloat("speed", "50") );
  1384. }
  1385.  
  1386. /*
  1387. ===============
  1388. rvSplineMover::Event_PartBlocked
  1389. ===============
  1390. */
  1391. void rvSplineMover::Event_PartBlocked( idEntity *blockingEntity ) {
  1392.     assert( blockingEntity );
  1393.  
  1394.     float damage = spawnArgs.GetFloat( "damage" );
  1395.     if( damage > 0.0f ) {
  1396.         blockingEntity->Damage( this, this, vec3_origin, "damage_moverCrush", damage, INVALID_JOINT );
  1397.     }
  1398.     if( g_debugMover.GetBool() ) {
  1399.         gameLocal.Printf( "%d: '%s' blocked by '%s'\n", gameLocal.GetTime(), GetName(), blockingEntity->GetName() );
  1400.     }
  1401. }
  1402.  
  1403. /*
  1404. ================
  1405. rvSplineMover::Event_SetSpline
  1406. ================
  1407. */
  1408. void rvSplineMover::Event_SetSpline( idEntity* spline ) {
  1409.     SetSpline( ConvertToSplinePath(spline) );
  1410. }
  1411.  
  1412. /*
  1413. ================
  1414. rvSplineMover::Event_GetSpline
  1415. ================
  1416. */
  1417. void rvSplineMover::Event_GetSpline() {
  1418.     idThread::ReturnEntity( GetSpline() );
  1419. }
  1420.  
  1421. /*
  1422. ================
  1423. rvSplineMover::Event_SetAcceleration
  1424. ================
  1425. */
  1426. void rvSplineMover::Event_SetAcceleration( float accel ) {
  1427.     SetAcceleration( accel );
  1428. }
  1429.  
  1430. /*
  1431. ================
  1432. rvSplineMover::Event_SetDeceleration
  1433. ================
  1434. */
  1435. void rvSplineMover::Event_SetDeceleration( float decel ) {
  1436.     SetDeceleration( decel );
  1437. }
  1438.  
  1439. /*
  1440. ================
  1441. rvSplineMover::Event_SetSpeed
  1442. ================
  1443. */
  1444. void rvSplineMover::Event_SetSpeed( float speed ) {
  1445.     SetIdealSpeed( speed );
  1446.     SetSpeed( speed );
  1447. }
  1448.  
  1449. /*
  1450. ================
  1451. rvSplineMover::Event_GetSpeed
  1452. ================
  1453. */
  1454. void rvSplineMover::Event_GetSpeed() {
  1455.     idThread::ReturnFloat( GetSpeed() );
  1456. }
  1457.  
  1458. /*
  1459. ================
  1460. rvSplineMover::Event_SetIdealSpeed
  1461. ================
  1462. */
  1463. void rvSplineMover::Event_SetIdealSpeed( float speed ) {
  1464.     SetIdealSpeed( speed );
  1465. }
  1466.  
  1467. /*
  1468. ================
  1469. rvSplineMover::Event_GetIdealSpeed
  1470. ================
  1471. */
  1472. void rvSplineMover::Event_GetIdealSpeed() {
  1473.     idThread::ReturnFloat( GetIdealSpeed() );
  1474. }
  1475.  
  1476. /*
  1477. ================
  1478. rvSplineMover::Event_ApplySpeedScale
  1479. ================
  1480. */
  1481. void rvSplineMover::Event_ApplySpeedScale( float scale ) {
  1482.     SetIdealSpeed( spawnArgs.GetFloat("speed", "50") * scale );
  1483. }
  1484.  
  1485. /*
  1486. ================
  1487. rvSplineMover::Event_SetCallBack
  1488. ================
  1489. */
  1490. void rvSplineMover::Event_SetCallBack() {
  1491.     if( waitThreadId >= 0 ) {
  1492.         idThread::ReturnInt( false );
  1493.     }
  1494.  
  1495.     waitThreadId = idThread::CurrentThreadNum();
  1496.     idThread::ReturnInt( true );
  1497. }
  1498.  
  1499. /*
  1500. ================
  1501. rvSplineMover::Event_DoneMoving
  1502. ================
  1503. */
  1504. void rvSplineMover::Event_DoneMoving() {
  1505.     PreDoneMoving();
  1506.     PostDoneMoving();
  1507.  
  1508.     idThread::ReturnInt( !physicsObj.HasValidSpline() );
  1509. }
  1510.  
  1511. /*
  1512. ================
  1513. rvSplineMover::Event_GetCurrentTrackInfo
  1514. ================
  1515. */
  1516. void rvSplineMover::Event_GetCurrentTrackInfo() {
  1517.     Event_GetTrackInfo( physicsObj.GetSplineEntity() );
  1518. }
  1519.  
  1520. /*
  1521. ================
  1522. rvSplineMover::Event_GetTrackInfo
  1523. ================
  1524. */
  1525. void rvSplineMover::Event_GetTrackInfo( idEntity* track ) {
  1526.     idThread::ReturnString( GetTrackInfo(ConvertToSplinePath(track)) );
  1527. }
  1528.  
  1529. /*
  1530. ================
  1531. rvSplineMover::Event_Activate
  1532. ================
  1533. */
  1534. void rvSplineMover::Event_Activate( idEntity* activator ) {
  1535.     // This is for my special case in tram1b
  1536.  
  1537.     //if( physicsObj.StoppedMoving() ) {
  1538.     //    SetIdealSpeed( spawnArgs.GetFloat("speed", "50") );
  1539.     //}
  1540. }
  1541.  
  1542. /*
  1543. ================
  1544. rvSplineMover::Event_OnAcceleration
  1545. ================
  1546. */
  1547. void rvSplineMover::Event_OnAcceleration() {
  1548.     StartSound( "snd_accel", SND_CHANNEL_ANY, 0, false, NULL );
  1549. }
  1550.  
  1551. /*
  1552. ================
  1553. rvSplineMover::Event_OnDeceleration
  1554. ================
  1555. */
  1556. void rvSplineMover::Event_OnDeceleration() {
  1557.     StartSound( "snd_decel", SND_CHANNEL_ANY, 0, false, NULL );
  1558. }
  1559.  
  1560. /*
  1561. ================
  1562. rvSplineMover::Event_OnCruising
  1563. ================
  1564. */
  1565. void rvSplineMover::Event_OnCruising() {
  1566.     idVec2 range( spawnArgs.GetVec2("noisePeriodRange") * idMath::M_SEC2MS );
  1567.     if( !EventIsPosted(&EV_StartSoundPeriodic) && range.Length() > VECTOR_EPSILON ) {
  1568.         ProcessEvent( &EV_StartSoundPeriodic, "snd_noise", (int)SND_CHANNEL_ANY, (int)range[0], (int)range[1] );
  1569.     }
  1570. }
  1571.  
  1572. /*
  1573. ================
  1574. rvSplineMover::Event_OnStopMoving
  1575. ================
  1576. */
  1577. void rvSplineMover::Event_OnStopMoving() {
  1578.     StopSound( SND_CHANNEL_ANY, false );
  1579.     CancelEvents( &EV_StartSoundPeriodic );
  1580. }
  1581.  
  1582. /*
  1583. ================
  1584. rvSplineMover::Event_OnStartMoving
  1585. ================
  1586. */
  1587. void rvSplineMover::Event_OnStartMoving() {
  1588. }
  1589.  
  1590. /*
  1591. ================
  1592. rvSplineMover::Event_StartSoundPeriodic
  1593. ================
  1594. */
  1595. void rvSplineMover::Event_StartSoundPeriodic( const char* sndKey, const s_channelType channel, int minDelay, int maxDelay ) {
  1596.     CancelEvents( &EV_StartSoundPeriodic );
  1597.  
  1598.     if( physicsObj.StoppedMoving() ) {
  1599.         return;
  1600.     }
  1601.  
  1602.     int length;
  1603.     StartSound( sndKey, channel, 0, false, &length );
  1604.  
  1605.     PostEventMS( &EV_StartSoundPeriodic, Max(rvRandom::irand(minDelay, maxDelay), length), sndKey, (int)channel, minDelay, maxDelay );
  1606. }
  1607.  
  1608.  
  1609. const idEventDef EV_TramCar_RadiusDamage( "<tramCar_radiusDamage>", "vs" );
  1610. const idEventDef EV_TramCar_SetIdealTrack( "setIdealTrack", "s" );
  1611. const idEventDef EV_TramCar_DriverSpeak( "driverSpeak", "s", 'e' );
  1612. const idEventDef EV_TramCar_GetDriver( "getDriver", "", 'E' );
  1613.  
  1614. const idEventDef EV_TramCar_OpenDoors( "openDoors" );
  1615. const idEventDef EV_TramCar_CloseDoors( "closeDoors" );
  1616.  
  1617. //=======================================================
  1618. //
  1619. //    rvTramCar
  1620. //
  1621. //=======================================================
  1622. CLASS_DECLARATION( rvSplineMover, rvTramCar )
  1623.     EVENT( EV_TramCar_DriverSpeak,        rvTramCar::Event_DriverSpeak )
  1624.     EVENT( EV_TramCar_GetDriver,        rvTramCar::Event_GetDriver )
  1625.     EVENT( EV_Activate,                    rvTramCar::Event_Activate )
  1626.     EVENT( EV_TramCar_RadiusDamage,        rvTramCar::Event_RadiusDamage )
  1627.     EVENT( EV_TramCar_SetIdealTrack,    rvTramCar::Event_SetIdealTrack )
  1628.     EVENT( EV_OnStartMoving,            rvTramCar::Event_OnStartMoving )
  1629.     EVENT( EV_OnStopMoving,                rvTramCar::Event_OnStopMoving )
  1630.     EVENT( EV_TramCar_OpenDoors,        rvTramCar::Event_OpenDoors )
  1631.     EVENT( EV_TramCar_CloseDoors,        rvTramCar::Event_CloseDoors )
  1632.     EVENT( AI_SetHealth,                rvTramCar::Event_SetHealth )
  1633. END_CLASS
  1634.  
  1635. /*
  1636. ================
  1637. rvTramCar::Spawn
  1638. ================
  1639. */
  1640. void rvTramCar::Spawn() {
  1641.     RegisterStateThread( idealTrackStateThread, "IdealTrack" );
  1642.     RegisterStateThread( speedSoundEffectsStateThread, "SpeedSoundEffects" );
  1643.  
  1644.     numTracksOnMap = gameLocal.world->spawnArgs.GetInt( "numTramCarTracks", "1" );
  1645.  
  1646.     idStr track;
  1647.     if ( numTracksOnMap == 1 ) {
  1648.         track += ConvertToTrackLetter( numTracksOnMap - 1 );
  1649.     } else {
  1650.         track = spawnArgs.RandomPrefix( "idealTrack", gameLocal.random, "0" );
  1651.     }
  1652.     Event_SetIdealTrack( track.c_str() );
  1653.     idealTrackStateThread.SetState( GetIdealTrack() < 0 ? "RandomTrack" : "AssignedTrack" );
  1654.  
  1655.     SpawnDriver( "def_driver" );
  1656.     SpawnWeapons( "def_weapon" );
  1657.     SpawnOccupants( "def_occupant" );
  1658.     SpawnDoors();
  1659.  
  1660.     float dNear = 0.0f, dFar = 0.0f, dLeft = 0.0f, dUp = 0.0f;
  1661.     const char* frustumKey = spawnArgs.GetString( "collisionFov" );
  1662.     if( frustumKey[0] ) {
  1663.         sscanf( frustumKey, "%f %f %f %f", &dNear, &dFar, &dLeft, &dUp );
  1664.         collisionFov.SetSize( dNear, dFar, dLeft, dUp );
  1665.     }
  1666.  
  1667.     fl.takedamage = health > 0;
  1668.  
  1669.     BecomeActive( TH_THINK );
  1670.  
  1671.     SetSpline( NULL );
  1672. }
  1673.  
  1674. /*
  1675. ================
  1676. rvTramCar::SpawnDriver
  1677. ================
  1678. */
  1679. rvTramCar::~rvTramCar() {
  1680.     SAFE_REMOVE( driver );
  1681.     occupants.RemoveContents( true );
  1682.     weapons.RemoveContents( true );
  1683.  
  1684.     SAFE_REMOVE( leftDoor );
  1685.     SAFE_REMOVE( rightDoor );
  1686. }
  1687.  
  1688. /*
  1689. ================
  1690. rvTramCar::SpawnDriver
  1691. ================
  1692. */
  1693. void rvTramCar::SpawnDriver( const char* driverKey ) {
  1694.     driver = (idAI*)SpawnPart( spawnArgs.GetString(driverKey), "def_occupant" );
  1695. }
  1696.  
  1697. /*
  1698. ================
  1699. rvTramCar::SpawnWeapons
  1700. ================
  1701. */
  1702. void rvTramCar::SpawnWeapons( const char* partKey ) {
  1703.     idEntityPtr<rvVehicle>    weapon;
  1704.  
  1705.     for( const idKeyValue* kv = spawnArgs.MatchPrefix(partKey); kv; kv = spawnArgs.MatchPrefix(partKey, kv) ) {
  1706.         if( !kv->GetValue().Length() ) {
  1707.             continue;
  1708.         }
  1709.  
  1710.         weapon = (rvVehicle*)SpawnPart( kv->GetValue().c_str(), partKey );
  1711.         weapon->IdleAnim( ANIMCHANNEL_LEGS, "idle", 0 );
  1712.         //if( weapon->GetAnimator()->GetAnim("toFire") && weapon->GetAnimator()->GetAnim("toIdle") ) {
  1713.             weapons.AddUnique( weapon );
  1714.         //}
  1715.     }
  1716. }
  1717.  
  1718. /*
  1719. ================
  1720. rvTramCar::SpawnOccupants
  1721. ================
  1722. */
  1723. void rvTramCar::SpawnOccupants( const char* partKey ) {
  1724.     idEntityPtr<idAI> occupant;
  1725.  
  1726.     for( const idKeyValue* kv = spawnArgs.MatchPrefix(partKey); kv; kv = spawnArgs.MatchPrefix(partKey, kv) ) {
  1727.         if( !kv->GetValue().Length() ) {
  1728.             continue;
  1729.         }
  1730.         
  1731.         occupant = (idAI*)SpawnPart( kv->GetValue().c_str(), partKey );
  1732.         occupants.AddUnique( occupant );
  1733.     }
  1734. }
  1735.  
  1736. /*
  1737. ================
  1738. rvTramCar::SpawnPart
  1739. ================
  1740. */
  1741. idEntity* rvTramCar::SpawnPart( const char* partDefName, const char* subPartDefName ) {
  1742.     idEntity*        part = NULL;
  1743.     idDict            entityDef;
  1744.     const idDict*    info = gameLocal.FindEntityDefDict( partDefName, false );
  1745.     if( !info ) {
  1746.         return NULL;
  1747.     }
  1748.  
  1749.     const idDict*    def = gameLocal.FindEntityDefDict( info->GetString(subPartDefName), false );
  1750.     if( !def ) {
  1751.         return NULL;
  1752.     }
  1753.  
  1754.     entityDef.Copy( *def );
  1755.  
  1756.     entityDef.SetBool( "trigger", spawnArgs.GetBool("waitForTrigger") );
  1757.     entityDef.SetVector( "origin", GetPhysics()->GetOrigin() + info->GetVector("spawn_offset") * GetPhysics()->GetAxis() );
  1758.     entityDef.SetFloat( "angle", info->GetInt("spawn_facing_offset") + spawnArgs.GetFloat("angle") );
  1759.     entityDef.Set( "bind", GetName() );
  1760.     gameLocal.SpawnEntityDef( entityDef, &part ); 
  1761.  
  1762.     return part;
  1763. }
  1764.  
  1765. /*
  1766. ================
  1767. rvTramCar::SpawnDoors
  1768. ================
  1769. */
  1770. void rvTramCar::SpawnDoors() {
  1771.     leftDoor = SpawnDoor( "clipModel_doorLeft" );
  1772.     rightDoor = SpawnDoor( "clipModel_doorRight" );
  1773. }
  1774.  
  1775. /*
  1776. ================
  1777. rvTramCar::SpawnDoors
  1778. ================
  1779. */
  1780. idMover* rvTramCar::SpawnDoor( const char* key ) {
  1781.     idDict                args;
  1782.     const idDict*        dict = NULL;
  1783.     idMover*            outer = NULL;
  1784.     idMover*            inner = NULL;
  1785.  
  1786.     dict = gameLocal.FindEntityDefDict( spawnArgs.GetString(key), false );
  1787.     if( !dict ) {
  1788.         return NULL;
  1789.     }
  1790.  
  1791.     args.Set( "open_rotation", dict->GetString("open_rotation") );
  1792.     args.Set( "close_rotation", dict->GetString("close_rotation") );
  1793.  
  1794.     args.Set( "open_dir", dict->GetString("open_dir") );
  1795.     args.Set( "close_dir", dict->GetString("close_dir") );
  1796.  
  1797.     args.Set( "distExt", dict->GetString("distExt") );
  1798.  
  1799.     args.SetVector( "origin", GetPhysics()->GetOrigin() + dict->GetVector("offset") * GetPhysics()->GetAxis() );
  1800.     args.SetMatrix( "rotation", GetPhysics()->GetAxis() );
  1801.     args.Set( "clipModel", dict->GetString("clipModel") );
  1802.     args.Set( "bind", GetName() );
  1803.     outer = gameLocal.SpawnSafeEntityDef<idMover>( "func_mover", &args );
  1804.     assert( outer );
  1805.  
  1806.     args.Set( "clipModel", dict->GetString("clipModelExt") );
  1807.     args.Set( "bind", outer->GetName() );
  1808.     inner = gameLocal.SpawnSafeEntityDef<idMover>( "func_mover", &args );
  1809.  
  1810.     return inner;
  1811. }
  1812.  
  1813. /*
  1814. ================
  1815. rvTramCar::Think
  1816. ================
  1817. */
  1818. void rvTramCar::Think() {
  1819.     RunPhysics();
  1820.  
  1821.     MidThink();
  1822.  
  1823.     Present();
  1824. }
  1825.  
  1826. /*
  1827. ================
  1828. rvTramCar::MidThink
  1829. ================
  1830. */
  1831. void rvTramCar::MidThink() {
  1832.     if( !physicsObj.StoppedMoving() ) {
  1833.         LookAround();
  1834.         TouchTriggers();
  1835.         speedSoundEffectsStateThread.Execute();
  1836.     }
  1837. }
  1838.  
  1839. /*
  1840. ================
  1841. rvTramCar::RegisterStateThread
  1842. ================
  1843. */
  1844. void rvTramCar::RegisterStateThread( rvStateThread& stateThread, const char* name ) {
  1845.     stateThread.SetName( name );
  1846.     stateThread.SetOwner( this );
  1847. }
  1848.  
  1849. /*
  1850. ================
  1851. rvTramCar::SetIdealTrack
  1852. ================
  1853. */
  1854. void rvTramCar::SetIdealTrack( int track ) {
  1855.     idealTrack = track;
  1856. }
  1857.  
  1858. /*
  1859. ================
  1860. rvTramCar::GetIdealTrack
  1861. ================
  1862. */
  1863. int rvTramCar::GetIdealTrack() const {
  1864.     return idealTrack;
  1865. }
  1866.  
  1867. /*
  1868. ================
  1869. rvTramCar::AddDamageEffect
  1870. ================
  1871. */
  1872. void rvTramCar::AddDamageEffect( const trace_t &collision, const idVec3 &velocity, const char *damageDefName, idEntity* inflictor ) {
  1873.     rvSplineMover::AddDamageEffect( collision, velocity, damageDefName, inflictor );
  1874.  
  1875.     if( GetDamageScale() >= 0.5f && rvRandom::flrand() <= spawnArgs.GetFloat("damageEffectFreq", "0.25") ) {
  1876.         idVec3 center = GetPhysics()->GetAbsBounds().GetCenter();
  1877.         idVec3 v = (center - collision.endpos).ToNormal();
  1878.         float dot = v * collision.c.normal;
  1879.         PlayEffect( (dot > 0.0f) ? "fx_damage_internal" : "fx_damage_external", collision.endpos, collision.c.normal.ToMat3(2) );
  1880.     }
  1881. }
  1882.  
  1883. /*
  1884. ================
  1885. rvTramCar::Damage
  1886. ================
  1887. */
  1888. void rvTramCar::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
  1889.     if( attacker && GetPhysics()->GetAbsBounds().Contains(attacker->GetPhysics()->GetAbsBounds()) ) {
  1890.         return;
  1891.     }
  1892.  
  1893.     if( attacker && g_debugVehicle.GetInteger() == 3 ) {
  1894.         gameLocal.Printf( "Was damaged by %s.  Current damage scale: %f\n", attacker->GetName(), GetDamageScale() );
  1895.     }
  1896.  
  1897.     rvSplineMover::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
  1898. }
  1899.  
  1900. /*
  1901. ================
  1902. rvTramCar::Killed
  1903. ================
  1904. */
  1905. void rvTramCar::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  1906.     idVec3 center = GetPhysics()->GetAbsBounds().GetCenter();
  1907.  
  1908.     fl.takedamage = false;
  1909.  
  1910.     StopSound( SND_CHANNEL_ANY, false );
  1911.  
  1912.     //Calling gameLocal's version because we don't want to get bound and hidden
  1913.     gameLocal.PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_explode" ), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
  1914.  
  1915.     PostEventMS( &EV_TramCar_RadiusDamage, 0, center, spawnArgs.GetString("def_damage_explode") );
  1916.  
  1917.     //TODO: think about giving rigid body physics and have it fall from track
  1918.     Hide();
  1919.     PostEventMS( &EV_Remove, 0 );
  1920. }
  1921.  
  1922. /*
  1923. ================
  1924. rvTramCar::GetDamageScale
  1925. ================
  1926. */
  1927. float rvTramCar::GetDamageScale() const {
  1928.     return 1.0f - GetHealthScale();
  1929. }
  1930.  
  1931. /*
  1932. ================
  1933. rvTramCar::GetDamageScale
  1934. ================
  1935. */
  1936. float rvTramCar::GetHealthScale() const {
  1937.     float spawnHealth = spawnArgs.GetFloat( "health" );
  1938.     return ( idMath::Fabs(spawnHealth) <= VECTOR_EPSILON ) ? 1.0f : (health / spawnHealth);
  1939. }
  1940.  
  1941. /*
  1942. ================
  1943. rvTramCar::Save
  1944. ================
  1945. */
  1946. void rvTramCar::Save( idSaveGame *savefile ) const {
  1947.     savefile->WriteInt( idealTrack );
  1948. //    savefile->WriteString( idealTrackTag.c_str() ); // cnicholson: FIXME: This has a comment of HACK in the .h file... Not sure how to write a idStr yet
  1949.  
  1950.     savefile->WriteFrustum( collisionFov );
  1951.  
  1952.     idealTrackStateThread.Save( savefile );
  1953.     speedSoundEffectsStateThread.Save( savefile );
  1954.  
  1955.     driver.Save( savefile );
  1956.  
  1957.     savefile->WriteInt( occupants.Num() );
  1958.     for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
  1959.         occupants[ix].Save( savefile );
  1960.     }
  1961.  
  1962.     savefile->WriteInt( weapons.Num() );
  1963.     for( int ix = weapons.Num() - 1; ix >= 0; --ix ) {
  1964.         weapons[ix].Save( savefile );
  1965.     }
  1966.  
  1967.     savefile->WriteInt( numTracksOnMap );
  1968.  
  1969.     leftDoor.Save ( savefile );
  1970.     rightDoor.Save ( savefile );
  1971. }
  1972.  
  1973. /*
  1974. ================
  1975. rvTramCar::Restore
  1976. ================
  1977. */
  1978. void rvTramCar::Restore( idRestoreGame *savefile ) {
  1979.     savefile->ReadInt( idealTrack );
  1980.  
  1981.     savefile->ReadFrustum( collisionFov );
  1982.  
  1983.     idealTrackStateThread.Restore( savefile, this );
  1984.     speedSoundEffectsStateThread.Restore( savefile, this );
  1985.  
  1986.     driver.Restore( savefile );
  1987.  
  1988.     int num = 0;
  1989.     savefile->ReadInt( num );
  1990.     occupants.SetNum( num );
  1991.     for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
  1992.         occupants[ix].Restore( savefile );
  1993.     }
  1994.  
  1995.     savefile->ReadInt( num );
  1996.     weapons.SetNum( num );
  1997.     for( int ix = weapons.Num() - 1; ix >= 0; --ix ) {
  1998.         weapons[ix].Restore( savefile );
  1999.     }
  2000.  
  2001.     savefile->ReadInt( numTracksOnMap );
  2002.  
  2003.     leftDoor.Restore ( savefile );
  2004.     rightDoor.Restore ( savefile );
  2005. }
  2006.  
  2007. /*
  2008. ================
  2009. rvTramCar::WriteToSnapshot
  2010. ================
  2011. */
  2012. void rvTramCar::WriteToSnapshot( idBitMsgDelta &msg ) const {
  2013. }
  2014.  
  2015. /*
  2016. ================
  2017. rvTramCar::ReadFromSnapshot
  2018. ================
  2019. */
  2020. void rvTramCar::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  2021. }
  2022.  
  2023. /*
  2024. ================
  2025. rvTramCar::HeadTowardsIdealTrack
  2026. ================
  2027. */
  2028. void rvTramCar::HeadTowardsIdealTrack() {
  2029.     idSplinePath*    ideal = FindSplineToIdealTrack( GetSpline() );
  2030.     if( !ideal ) {
  2031.         ideal = GetRandomSpline(GetSpline());
  2032.     }
  2033.  
  2034.     SetSpline( ideal );
  2035. }
  2036.  
  2037. /*
  2038. ================
  2039. rvTramCar::FindSplineToTrack
  2040. ================
  2041. */
  2042. enum {
  2043.     LOOK_LEFT = -1,
  2044.     LOOK_RIGHT = 1
  2045. };
  2046. idSplinePath* rvTramCar::FindSplineToTrack( idSplinePath* spline, const idStr& track ) const {
  2047.     idSplinePath*    target = NULL;
  2048.     idEntity*        ent = NULL;
  2049.     idStr            trackInfo;
  2050.     idList<rvSplineMover*> list;
  2051.  
  2052.     if( !spline ) {
  2053.         return NULL;
  2054.     }
  2055.  
  2056.     for( int ix = SortSplineTargets(spline) - 1; ix >= 0; --ix ) {
  2057.         ent = GetSplineTarget( spline, ix );
  2058.         target = static_cast<idSplinePath*>( ent );
  2059.         assert( target->IsActive() );
  2060.  
  2061.         trackInfo = GetTrackInfo( target );
  2062.         if( -1 >= trackInfo.Find(track) ) {
  2063.             continue;
  2064.         }
  2065.  
  2066.         // HACK: I hate switch statements
  2067.         switch( ConvertToTrackNumber(trackInfo) - GetCurrentTrack() ) {
  2068.             case LOOK_LEFT: {
  2069.                 if( !LookLeft(list) ) {
  2070.                     return target; 
  2071.                 }
  2072.                 break;
  2073.             }
  2074.  
  2075.             case LOOK_RIGHT: {
  2076.                 if( !LookRight(list) ) {
  2077.                     return target; 
  2078.                 }
  2079.                 break;
  2080.             }
  2081.  
  2082.             default: {
  2083.                 return target;
  2084.             }
  2085.         }
  2086.         // HACK
  2087.     }
  2088.  
  2089.     return NULL;
  2090. }
  2091.  
  2092. /*
  2093. ================
  2094. rvTramCar::SortSplineTargets
  2095. ================
  2096. */
  2097. int    rvTramCar::SortSplineTargets( idSplinePath* spline ) const {
  2098.     assert( spline );
  2099.     return (SignZero(GetSpeed()) >= 0) ? spline->SortTargets() : spline->SortBackwardsTargets();
  2100. }
  2101.  
  2102. /*
  2103. ================
  2104. rvTramCar::GetSplineTarget
  2105. ================
  2106. */
  2107. idEntity* rvTramCar::GetSplineTarget( idSplinePath* spline, int index ) const {
  2108.     assert( spline );
  2109.     return (SignZero(GetSpeed()) >= 0) ? spline->targets[index].GetEntity() : spline->backwardPathTargets[index].GetEntity();
  2110. }
  2111.  
  2112. /*
  2113. ================
  2114. rvTramCar::FindSplineToIdealTrack
  2115. ================
  2116. */
  2117. idSplinePath* rvTramCar::FindSplineToIdealTrack( idSplinePath* spline ) const {
  2118.     // HACK
  2119.     int                trackDelta = idMath::ClampInt( -1, 1, GetIdealTrack() - GetCurrentTrack() );
  2120.     idStr            trackLetter( ConvertToTrackLetter(GetCurrentTrack() + trackDelta) );
  2121.     idSplinePath*    s = NULL;
  2122.  
  2123.     if( idealTrackTag.Length() && !trackDelta ) {// On ideal track
  2124.         s = FindSplineToTrack( spline, idealTrackTag + trackLetter );
  2125.     }
  2126.     if( !s ) {
  2127.         s = FindSplineToTrack( spline, trackLetter );
  2128.     }
  2129.     return s;
  2130. }
  2131.  
  2132. /*
  2133. ================
  2134. rvTramCar::GetRandomSpline
  2135. ================
  2136. */
  2137. idSplinePath* rvTramCar::GetRandomSpline( idSplinePath* spline ) const {
  2138.     if( !spline ) {
  2139.         return NULL;
  2140.     }
  2141.  
  2142.     int numActiveTargets = SortSplineTargets( spline );
  2143.     if( !numActiveTargets ) {
  2144.         return NULL;
  2145.     }
  2146.  
  2147.     idEntity* target = GetSplineTarget( spline, rvRandom::irand(0, numActiveTargets - 1) );
  2148.     return (target && target->IsType(idSplinePath::GetClassType())) ? static_cast<idSplinePath*>(target) : NULL;
  2149. }
  2150.  
  2151. /*
  2152. ================
  2153. rvTramCar::GetCurrentTrack
  2154. ================
  2155. */
  2156. int rvTramCar::GetCurrentTrack() const {
  2157.     return ConvertToTrackNumber( GetTrackInfo(GetSpline()) );
  2158. }
  2159.  
  2160. /*
  2161. ================
  2162. rvTramCar::UpdateChannel
  2163. ================
  2164. */
  2165. void rvTramCar::UpdateChannel( const s_channelType channel, const soundShaderParms_t& parms ) {
  2166.     idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
  2167.     if( emitter ) {
  2168.         emitter->ModifySound( channel, &parms );
  2169.     }
  2170. }
  2171.  
  2172. /*
  2173. ================
  2174. rvTramCar::AttenuateTrackChannel
  2175. ================
  2176. */
  2177. void rvTramCar::AttenuateTrackChannel( float attenuation ) {
  2178.     soundShaderParms_t parms = refSound.parms;
  2179.  
  2180.     parms.frequencyShift = attenuation;//idMath::MidPointLerp( 0.0f, 1.0f, 1.1f, attenuation );
  2181.  
  2182.     UpdateChannel( SND_CHANNEL_BODY, parms );
  2183. }
  2184.  
  2185. /*
  2186. ================
  2187. rvTramCar::AttenuateTramCarChannel
  2188. ================
  2189. */
  2190. void rvTramCar::AttenuateTramCarChannel( float attenuation ) {
  2191.     soundShaderParms_t parms = refSound.parms;
  2192.  
  2193.     parms.volume = (attenuation + 1.0f) * 0.5f;
  2194.     parms.frequencyShift = Max( 0.4f, attenuation );
  2195.  
  2196.     UpdateChannel( SND_CHANNEL_BODY2, parms );
  2197. }
  2198.  
  2199. /*
  2200. ================
  2201. rvTramCar::LookForward
  2202. ================
  2203. */
  2204. bool rvTramCar::LookForward( idList<rvSplineMover*>& list ) const {
  2205.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2206.     collisionFov.SetAxis( GetPhysics()->GetAxis() );
  2207.  
  2208.     Look( collisionFov, list );
  2209.  
  2210.     for( int ix = list.Num() - 1; ix >= 0; --ix ) {
  2211.         if( !OnSameTrackAs(list[ix]) ) {
  2212.             list.Remove( list[ix] );
  2213.         }
  2214.     }
  2215.  
  2216.     return list.Num() > 0;
  2217. }
  2218.  
  2219. /*
  2220. ================
  2221. rvTramCar::LookLeft
  2222. ================
  2223. */
  2224. bool rvTramCar::LookLeft( idList<rvSplineMover*>& list ) const {
  2225.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2226.     collisionFov.SetAxis( idAngles(0.0f, 60.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
  2227.  
  2228.     return Look( collisionFov, list );
  2229. }
  2230.  
  2231. /*
  2232. ================
  2233. rvTramCar::LookRight
  2234. ================
  2235. */
  2236. bool rvTramCar::LookRight( idList<rvSplineMover*>& list ) const {
  2237.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2238.     collisionFov.SetAxis( idAngles(0.0f, -60.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
  2239.  
  2240.     return Look( collisionFov, list );
  2241. }
  2242.  
  2243. /*
  2244. ================
  2245. rvTramCar::LookLeftForTrackChange
  2246. ================
  2247. */
  2248. bool rvTramCar::LookLeftForTrackChange( idList<rvSplineMover*>& list ) const {
  2249.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2250.     collisionFov.SetAxis( idAngles(0.0f, 45.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
  2251.  
  2252.     return Look( collisionFov, list );
  2253. }
  2254.  
  2255. /*
  2256. ================
  2257. rvTramCar::LookRightForTrackChange
  2258. ================
  2259. */
  2260. bool rvTramCar::LookRightForTrackChange( idList<rvSplineMover*>& list ) const {
  2261.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2262.     collisionFov.SetAxis( idAngles(0.0f, -45.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
  2263.  
  2264.     return Look( collisionFov, list );
  2265. }
  2266.  
  2267. /*
  2268. ================
  2269. rvTramCar::Look
  2270. ================
  2271. */
  2272. int rvSortByDist( const void* left, const void* right ) {
  2273.     rvSplineMover* leftMover = *(rvSplineMover**)left;
  2274.     rvSplineMover* rightMover = *(rvSplineMover**)right;
  2275.  
  2276.     return rightMover->spawnArgs.GetFloat("distAway") - leftMover->spawnArgs.GetFloat("distAway");
  2277. }
  2278. bool rvTramCar::Look( const idFrustum& fov, idList<rvSplineMover*>& list ) const {
  2279.     bool result = WhosVisible( fov, list );
  2280.  
  2281.     if( g_debugVehicle.GetInteger() == 3 ) {
  2282.         gameRenderWorld->DebugFrustum( colorRed, fov );
  2283.     }
  2284.  
  2285.     for( int ix = list.Num() - 1; ix >= 0; --ix ) {
  2286.         list[ix]->spawnArgs.SetFloat( "distAway", (list[ix]->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).Length() );
  2287.     }
  2288.  
  2289.     qsort( list.Ptr(), list.Num(), list.TypeSize(), rvSortByDist );
  2290.  
  2291.     // Do we need to get rid of these keyvalues?
  2292.  
  2293.     return result;
  2294. }
  2295.  
  2296. /*
  2297. ================
  2298. rvTramCar::OnSameTrackAs
  2299. ================
  2300. */
  2301. bool rvTramCar::OnSameTrackAs( const rvSplineMover* tram ) const {
  2302.     return tram && !GetTrackInfo(GetSpline()).Icmp( GetTrackInfo(tram->GetSpline()) );
  2303. }
  2304.  
  2305. /*
  2306. ================
  2307. rvTramCar::SameIdealTrackAs
  2308. ================
  2309. */
  2310. bool rvTramCar::SameIdealTrackAs( const rvSplineMover* tram ) const {
  2311.     if( !tram ) {
  2312.         return false;
  2313.     }
  2314.     return GetIdealTrack() == static_cast<const rvTramCar*>(tram)->GetIdealTrack();
  2315. }
  2316.  
  2317. /*
  2318. ================
  2319. rvTramCar::LookAround
  2320. ================
  2321. */
  2322. void rvTramCar::LookAround() {
  2323.     idList<rvSplineMover*> moverList;
  2324.  
  2325.     if( !AdjustSpeed(moverList) ) {
  2326.         SetSpeed( GetIdealSpeed() );
  2327.     }
  2328. }
  2329.  
  2330. /*
  2331. ================
  2332. rvTramCar::AdjustSpeed
  2333. ================
  2334. */
  2335. bool rvTramCar::AdjustSpeed( idList<rvSplineMover*>& moverList ) {
  2336.     if( LookForward(moverList) ) {
  2337.         if( g_debugVehicle.GetInteger() ) {
  2338.             gameRenderWorld->DebugLine( colorRed, GetPhysics()->GetOrigin(), moverList[0]->GetPhysics()->GetOrigin() );
  2339.         }
  2340.  
  2341.         //Slow down and try to get onto other track if allowed
  2342.         SetSpeed( moverList[0]->GetSpeed() * rvRandom::flrand(0.5f, 0.75f) );
  2343.  
  2344.         // Is this safe if we are on a specified track
  2345.         SetIdealTrack( (GetIdealTrack() + rvRandom::irand(1, 2)) % numTracksOnMap );
  2346.         return true;
  2347.     }
  2348.  
  2349.     return false;
  2350. }
  2351.  
  2352. /*
  2353. ================
  2354. rvTramCar::DriverSpeak
  2355. ================
  2356. */
  2357. idEntity* rvTramCar::DriverSpeak( const char* speechDecl, bool random ) {
  2358.     if( !driver.IsValid() || driver->IsSpeaking() ) {
  2359.         return NULL;
  2360.     }
  2361.  
  2362.     driver->Speak( speechDecl, random );
  2363.     return driver;
  2364. }
  2365.  
  2366. /*
  2367. ================
  2368. rvTramCar::OccupantSpeak
  2369. ================
  2370. */
  2371. idEntity* rvTramCar::OccupantSpeak( const char *speechDecl, bool random ) {
  2372.     idEntityPtr<idAI> occupant;
  2373.  
  2374.     if( !occupants.Num() ) {
  2375.         return NULL;
  2376.     }
  2377.  
  2378.     occupant = occupants[ rvRandom::irand(0, occupants.Num() - 1) ];
  2379.     if( !occupant.IsValid() || occupant->IsSpeaking() ) {
  2380.         return NULL;
  2381.     }
  2382.  
  2383.     occupant->Speak( speechDecl, random );
  2384.     return occupant;
  2385. }
  2386.  
  2387. /*
  2388. ================
  2389. rvTramCar::PostDoneMoving
  2390. ================
  2391. */
  2392. void rvTramCar::PostDoneMoving() {
  2393.     rvSplineMover::PostDoneMoving();
  2394.  
  2395.     HeadTowardsIdealTrack();
  2396. }
  2397.  
  2398. /*
  2399. ================
  2400. rvTramCar::DeployRamp
  2401. ================
  2402. */
  2403. void rvTramCar::DeployRamp() {
  2404.     OperateRamp( "open" );
  2405. }
  2406.  
  2407. /*
  2408. ================
  2409. rvTramCar::RetractRamp
  2410. ================
  2411. */
  2412. void rvTramCar::RetractRamp() {
  2413.     OperateRamp( "close" );
  2414. }
  2415.  
  2416. /*
  2417. ================
  2418. rvTramCar::OperateRamp
  2419. ================
  2420. */
  2421. void rvTramCar::OperateRamp( const char* operation ) {
  2422.     if( !operation || !operation[0] ) {
  2423.         return;
  2424.     }
  2425.         
  2426.     PlayAnim( ANIMCHANNEL_ALL, operation, 0 );
  2427.  
  2428.     OperateRamp( operation, leftDoor );
  2429.     OperateRamp( operation, rightDoor );
  2430. }
  2431.  
  2432. /*
  2433. ================
  2434. rvTramCar::OperateRamp
  2435. ================
  2436. */
  2437. void rvTramCar::OperateRamp( const char* operation, idMover* door ) {
  2438.     if( !operation || !operation[0] ) {
  2439.         return;
  2440.     }
  2441.  
  2442.     idAngles    ang;
  2443.     idVec3        vec;
  2444.     if( !door ) {
  2445.         return;
  2446.     }
  2447.  
  2448.     if( !door->IsBound() ) {
  2449.         return;
  2450.     }
  2451.  
  2452.     ang = door->spawnArgs.GetAngles( va("%s%s", operation, "_rotation") );
  2453.     vec.Set( ang[0], ang[1], ang[2] );
  2454.     door->GetBindMaster()->ProcessEvent( &EV_RotateOnce, vec );
  2455.  
  2456.     vec = door->spawnArgs.GetVector(va("%s%s", operation, "_dir")).ToNormal() * door->spawnArgs.GetFloat("distExt");
  2457.     door->PostEventSec( &EV_MoveAlongVector, 0.5f, vec );
  2458. }
  2459.  
  2460. /*
  2461. ================
  2462. rvTramCar::Event_OnStopMoving
  2463. ================
  2464. */
  2465. void rvTramCar::Event_OnStopMoving() {
  2466.     rvSplineMover::Event_OnStopMoving();
  2467.  
  2468.     speedSoundEffectsStateThread.SetState( "IdleSpeed" );
  2469. }
  2470.  
  2471. /*
  2472. ================
  2473. rvTramCar::Event_OnStartMoving
  2474. ================
  2475. */
  2476. void rvTramCar::Event_OnStartMoving() {
  2477.     rvSplineMover::Event_OnStartMoving();
  2478.  
  2479.     speedSoundEffectsStateThread.SetState( "NormalSpeed" );
  2480. }
  2481.  
  2482. /*
  2483. ================
  2484. rvTramCar::Event_DriverSpeak
  2485. ================
  2486. */
  2487. void rvTramCar::Event_DriverSpeak( const char* voKey ) {
  2488.     idThread::ReturnEntity( DriverSpeak(voKey) );
  2489. }
  2490.  
  2491. /*
  2492. ================
  2493. rvTramCar::Event_GetDriver
  2494. ================
  2495. */
  2496. void rvTramCar::Event_GetDriver() {
  2497.     idThread::ReturnEntity( driver );
  2498. }
  2499.  
  2500. /*
  2501. ================
  2502. rvTramCar::Event_Activate
  2503. ================
  2504. */
  2505. void rvTramCar::Event_Activate( idEntity* activator ) {
  2506.     rvSplineMover::Event_Activate( activator );
  2507.  
  2508.     // If being activated by a spawner we need to attach to it
  2509.     if( activator->IsType(rvSpawner::GetClassType()) ) {
  2510.         static_cast<rvSpawner*>(activator)->Attach( this );
  2511.     } else {
  2512.         if( driver.IsValid() ) {
  2513.             driver->ProcessEvent( &EV_Activate, activator );
  2514.         }
  2515.  
  2516.         occupants.RemoveNull();
  2517.         for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
  2518.             occupants[ix]->ProcessEvent( &EV_Activate, activator );
  2519.         }
  2520.     }
  2521. }
  2522.  
  2523. /*
  2524. ================
  2525. rvTramCar::Event_RadiusDamage
  2526. ================
  2527. */
  2528. void rvTramCar::Event_RadiusDamage( const idVec3& origin, const char* damageDefName ) {
  2529.     gameLocal.RadiusDamage( origin, this, this, this, this, damageDefName );
  2530. }
  2531.  
  2532. /*
  2533. ================
  2534. rvTramCar::Event_SetIdealTrack
  2535. ================
  2536. */
  2537. void rvTramCar::Event_SetIdealTrack( const char* track ) {
  2538.     idStr ideal = track;
  2539.  
  2540.     if( ideal.Length() > 1 ) {
  2541.         idealTrackTag = ideal.Left( ideal.Length() - 1 );
  2542.     }
  2543.  
  2544.     idealTrackStateThread.SetState( "AssignedTrack" );
  2545.     SetIdealTrack( ConvertToTrackNumber(ideal.Right(1)) );
  2546. }
  2547.  
  2548. /*
  2549. ================
  2550. rvTramCar::Event_OpenDoors
  2551. ================
  2552. */
  2553. void rvTramCar::Event_OpenDoors() {
  2554.     DeployRamp();
  2555. }
  2556.  
  2557. /*
  2558. ================
  2559. rvTramCar::Event_CloseDoors
  2560. ================
  2561. */
  2562. void rvTramCar::Event_CloseDoors() {
  2563.     RetractRamp();
  2564. }
  2565.  
  2566. /*
  2567. ================
  2568. rvTramCar::Event_SetHealth
  2569. ================
  2570. */
  2571. void rvTramCar::Event_SetHealth    ( float health ) {
  2572.     this->health = health;
  2573. }
  2574.  
  2575. CLASS_STATES_DECLARATION( rvTramCar )
  2576.     STATE( "IdleSpeed",            rvTramCar::State_Idle )
  2577.     STATE( "NormalSpeed",        rvTramCar::State_NormalSpeed )
  2578.     STATE( "ExcessiveSpeed",    rvTramCar::State_ExcessiveSpeed )
  2579.     STATE( "RandomTrack",        rvTramCar::State_RandomTrack )
  2580.     STATE( "AssignedTrack",        rvTramCar::State_AssignedTrack )
  2581. END_CLASS_STATES
  2582.  
  2583. /*
  2584. ================
  2585. rvTramCar::State_Idle
  2586. ================
  2587. */
  2588. stateResult_t rvTramCar::State_Idle( const stateParms_t& parms ) {
  2589.     if( !parms.stage ) {
  2590.         StartSound( "snd_speed_idle_track", SND_CHANNEL_BODY, 0, false, NULL );
  2591.         StartSound( "snd_speed_idle_tram", SND_CHANNEL_BODY2, 0, false, NULL );
  2592.         return SRESULT_STAGE( parms.stage + 1 );
  2593.     }
  2594.     return SRESULT_WAIT; 
  2595. }
  2596.  
  2597. /*
  2598. ================
  2599. rvTramCar::State_NormalSpeed
  2600. ================
  2601. */
  2602. stateResult_t rvTramCar::State_NormalSpeed( const stateParms_t& parms ) {
  2603.     if( !parms.stage ) {
  2604.         StartSound( "snd_speed_normal_track", SND_CHANNEL_BODY, 0, false, NULL );
  2605.         StartSound( "snd_speed_normal_tram", SND_CHANNEL_BODY2, 0, false, NULL );
  2606.         return SRESULT_STAGE( parms.stage + 1 );
  2607.     }
  2608.  
  2609.     float speedScale = 0.8f + 0.2f * ( GetSpeed() / GetNormalSpeed() );
  2610.  
  2611.     if( speedScale >= 1.0f ) {
  2612.         speedSoundEffectsStateThread.SetState( "ExcessiveSpeed" );
  2613.         return SRESULT_DONE;
  2614.     }
  2615.  
  2616.     AttenuateTrackChannel( speedScale );
  2617.     AttenuateTramCarChannel( speedScale );
  2618.     return SRESULT_WAIT;
  2619. }
  2620.  
  2621. /*
  2622. ================
  2623. rvTramCar::State_ExcessiveSpeed
  2624. ================
  2625. */
  2626. stateResult_t rvTramCar::State_ExcessiveSpeed( const stateParms_t& parms ) {
  2627.     if( !parms.stage ) {
  2628.         StartSound( "snd_speed_excessive_track", SND_CHANNEL_BODY, 0, false, NULL );
  2629.         StartSound( "snd_speed_excessive_tram", SND_CHANNEL_BODY2, 0, false, NULL );
  2630.         return SRESULT_STAGE( parms.stage + 1 );
  2631.     }
  2632.  
  2633.     float speedScale = GetSpeed() / GetNormalSpeed();
  2634.  
  2635.     if( speedScale < 1.0f ) {
  2636.         speedSoundEffectsStateThread.SetState( "NormalSpeed" );
  2637.         return SRESULT_DONE;    
  2638.     }
  2639.  
  2640.     AttenuateTrackChannel( speedScale );
  2641.     AttenuateTramCarChannel( speedScale );
  2642.     return SRESULT_WAIT;
  2643. }
  2644.  
  2645. /*
  2646. ================
  2647. rvTramCar::State_RandomTrack
  2648. ================
  2649. */
  2650. stateResult_t rvTramCar::State_RandomTrack( const stateParms_t& parms ) {
  2651.     SetIdealTrack( rvRandom::irand(0, numTracksOnMap - 1) );
  2652.     return SRESULT_WAIT;
  2653. }
  2654.  
  2655. /*
  2656. ================
  2657. rvTramCar::State_AssignedTrack
  2658. ================
  2659. */
  2660. stateResult_t rvTramCar::State_AssignedTrack( const stateParms_t& parms ) {
  2661.     return SRESULT_WAIT;
  2662. }
  2663.  
  2664. //=======================================================
  2665. //
  2666. //    rvTramCar_Marine
  2667. //
  2668. //=======================================================
  2669. const idEventDef EV_TramCar_UseMountedGun( "useMountedGun", "e" );
  2670. const idEventDef EV_TramCar_SetPlayerDamageEnt( "setPlayerDamageEnt", "f" );
  2671.  
  2672. CLASS_DECLARATION( rvTramCar, rvTramCar_Marine )
  2673.     EVENT( EV_TramCar_UseMountedGun,    rvTramCar_Marine::Event_UseMountedGun )
  2674.     EVENT( EV_TramCar_SetPlayerDamageEnt,    rvTramCar_Marine::Event_SetPlayerDamageEntity )
  2675. END_CLASS
  2676.  
  2677. /*
  2678. ================
  2679. rvTramCar_Marine::Spawn
  2680. ================
  2681. */
  2682. void rvTramCar_Marine::Spawn() {
  2683.     RegisterStateThread( playerOccupationStateThread, "PlayerOccupation" );
  2684.     playerOccupationStateThread.SetState( "NotOccupied" );
  2685.  
  2686.     RegisterStateThread( playerUsingMountedGunStateThread, "MountedGunInUse" );
  2687.     playerUsingMountedGunStateThread.SetState( "NotUsingMountedGun" );
  2688.  
  2689.     maxHealth    = spawnArgs.GetInt( "health", "0" );
  2690.     lastHeal    = 0;
  2691.     healDelay    = spawnArgs.GetInt( "heal_delay", "15" );
  2692.     healAmount    = spawnArgs.GetInt( "heal_amount", "2" );
  2693. }
  2694.  
  2695. /*
  2696. ================
  2697. rvTramCar_Marine::MidThink
  2698. ================
  2699. */
  2700. void rvTramCar_Marine::MidThink() {
  2701.     rvTramCar::MidThink();
  2702.  
  2703.     if ( healDelay > 0 && lastHeal + healDelay < gameLocal.time && health < maxHealth ) {
  2704.         health += healAmount;
  2705.  
  2706.         if ( health > maxHealth ) {
  2707.             health = maxHealth;
  2708.         }
  2709.     }
  2710.  
  2711.     playerOccupationStateThread.Execute();
  2712. }
  2713.  
  2714. /*
  2715. ================
  2716. rvTramCar_Marine::ActivateTramHud
  2717. ================
  2718. */
  2719. void rvTramCar_Marine::ActivateTramHud( idPlayer* player ) {
  2720.     if( !player ) {
  2721.         return;
  2722.     }
  2723.  
  2724.     idUserInterface* hud = player->GetHud();
  2725.     if( !hud ) {
  2726.         return;
  2727.     }
  2728.  
  2729.     hud->HandleNamedEvent( "enterTram" );
  2730. }
  2731.  
  2732. /*
  2733. ================
  2734. rvTramCar_Marine::DeactivateTramHud
  2735. ================
  2736. */
  2737. void rvTramCar_Marine::DeactivateTramHud( idPlayer* player ) {
  2738.     if( !player ) {
  2739.         return;
  2740.     }
  2741.  
  2742.     idUserInterface* hud = player->GetHud();
  2743.     if( !hud ) {
  2744.         return;
  2745.     }
  2746.  
  2747.     hud->HandleNamedEvent( "leaveTram" );
  2748. }
  2749.  
  2750. /*
  2751. ================
  2752. rvTramCar_Marine::UpdateTramHud
  2753. ================
  2754. */
  2755. void rvTramCar_Marine::UpdateTramHud( idPlayer* player ) {
  2756.     if( !player ) {
  2757.         return;
  2758.     }
  2759.  
  2760.     idUserInterface* hud = player->GetHud();
  2761.     if( !hud ) {
  2762.         return;
  2763.     }
  2764.  
  2765.     hud->SetStateFloat( "tram_healthpct", GetHealthScale() );
  2766. }
  2767.  
  2768. /*
  2769. ============
  2770. rvTramCar_Marine::Damage
  2771. ============
  2772. */
  2773. void rvTramCar_Marine::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir, const char *damageDefName, const float damageScale, const int location ) {
  2774.     rvTramCar::Damage( inflictor, attacker, dir, damageDefName, damageScale, location );
  2775.  
  2776.     if( attacker == gameLocal.GetLocalPlayer() ) {
  2777.         return;
  2778.     }
  2779.  
  2780.     if( fl.takedamage && GetDamageScale() < 0.35f ) {
  2781.         return;
  2782.     }
  2783.  
  2784.     if( rvRandom::flrand() > spawnArgs.GetFloat("damageWarningFreq", "0.2") ) {
  2785.         return;
  2786.     }
  2787.  
  2788.     DriverSpeak( "lipsync_damageWarning", true );
  2789. }
  2790.  
  2791. /*
  2792. ================
  2793. rvTramCar_Marine::Killed
  2794. ================
  2795. */
  2796. void rvTramCar_Marine::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  2797.     playerOccupationStateThread.Execute();
  2798.  
  2799.     idPlayer* player = gameLocal.GetLocalPlayer();
  2800.     if( player && EntityIsInside(player) ) {
  2801.         player->SetDamageEntity( NULL );// This should fix the damage from tram car propogation issue
  2802.         player->ExitVehicle( true );// This should fix the issue that was causing the player to get removed
  2803.     }
  2804.  
  2805.     rvTramCar::Killed( inflictor, attacker, damage, dir, location );
  2806. }
  2807.  
  2808. /*
  2809. ================
  2810. rvTramCar_Marine::Save
  2811. ================
  2812. */
  2813. void rvTramCar_Marine::Save( idSaveGame *savefile ) const {
  2814.     savefile->WriteInt( visibleEnemies.Num() );
  2815.     for( int ix = visibleEnemies.Num() - 1; ix >= 0; --ix ) {
  2816.         visibleEnemies[ix].Save( savefile );
  2817.     }
  2818.  
  2819.     playerOccupationStateThread.Save( savefile );
  2820.     playerUsingMountedGunStateThread.Save( savefile );
  2821.  
  2822.     // no need to save this since it's set in the restore
  2823.     //savefile->WriteInt( maxHealth );    // cnicholson: added unsaved var
  2824.     savefile->WriteInt( lastHeal );
  2825.     savefile->WriteInt( healDelay );
  2826.     savefile->WriteInt( healAmount );
  2827. }
  2828.  
  2829. /*
  2830. ================
  2831. rvTramCar_Marine::Restore
  2832. ================
  2833. */
  2834. void rvTramCar_Marine::Restore( idRestoreGame *savefile ) {
  2835.     int num = 0;
  2836.     savefile->ReadInt( num );
  2837.     visibleEnemies.SetNum( num );
  2838.     for( int ix = visibleEnemies.Num() - 1; ix >= 0; --ix ) {
  2839.         visibleEnemies[ix].Restore( savefile );
  2840.     }
  2841.  
  2842.     playerOccupationStateThread.Restore( savefile, this );
  2843.     playerUsingMountedGunStateThread.Restore( savefile, this );
  2844.  
  2845.     savefile->ReadInt( lastHeal );
  2846.     savefile->ReadInt( healDelay );
  2847.     savefile->ReadInt( healAmount );
  2848.  
  2849.     maxHealth = spawnArgs.GetInt( "health", "0" );
  2850. }
  2851.  
  2852. /*
  2853. ================
  2854. rvTramCar_Marine::LookAround
  2855. ================
  2856. */
  2857. void rvTramCar_Marine::LookAround() {
  2858.      idList<rvSplineMover*> moverList;
  2859.  
  2860.     rvTramCar::LookAround();
  2861.  
  2862.     if( !driver.IsValid() || driver->IsSpeaking() ) {
  2863.         return;
  2864.     }
  2865.  
  2866.     if( LookOverLeftShoulder(moverList)) {
  2867.         DriverSpeak( "lipsync_warningLeft", true );
  2868.         driver->ScriptedAnim( "point_left", 0, false, true );
  2869.     } else if( LookOverRightShoulder(moverList)) {
  2870.         DriverSpeak( "lipsync_warningRight", true );
  2871.         driver->ScriptedAnim( "point_right", 0, false, true );
  2872.     }
  2873. }
  2874.  
  2875.  
  2876. /*
  2877. ================
  2878. rvTramCar_Marine::LookOverLeftShoulder
  2879. ================
  2880. */
  2881. bool rvTramCar_Marine::LookOverLeftShoulder( idList<rvSplineMover*>& list ) {
  2882.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2883.     collisionFov.SetAxis( idAngles(0.0, -120.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
  2884.  
  2885.     bool newOneFound = false;
  2886.     Look( collisionFov, list );
  2887.  
  2888.     // now determine if we've spotted a new enemy
  2889.     for( int ix = list.Num() - 1; ix >= 0; --ix ) {
  2890.         if ( !visibleEnemies.Find( list[ix] )) {
  2891.             newOneFound = true;
  2892.             visibleEnemies.AddUnique( list[ix] );
  2893.         }
  2894.     }
  2895.  
  2896.     return newOneFound;
  2897. }
  2898.  
  2899. /*
  2900. ================
  2901. rvTramCar_Marine::LookOverRightShoulder
  2902. ================
  2903. */
  2904. bool rvTramCar_Marine::LookOverRightShoulder( idList<rvSplineMover*>& list ) {
  2905.     collisionFov.SetOrigin( GetPhysics()->GetOrigin() );
  2906.     collisionFov.SetAxis( idAngles(0.0, 120.0f, 0.0f).ToMat3() * GetPhysics()->GetAxis() );
  2907.  
  2908.     bool newOneFound = false;
  2909.     Look( collisionFov, list );
  2910.  
  2911.     // now determine if we've spotted a new enemy
  2912.     for( int ix = list.Num() - 1; ix >= 0; --ix ) {
  2913.         if ( !visibleEnemies.Find( list[ix] )) {
  2914.             newOneFound = true;
  2915.             visibleEnemies.AddUnique( list[ix] );
  2916.         }
  2917.     }
  2918.  
  2919.     return newOneFound;
  2920. }
  2921.  
  2922. /*
  2923. ================
  2924. rvTramCar_Marine::EntityIsInside
  2925. ================
  2926. */
  2927. bool rvTramCar_Marine::EntityIsInside( const idEntity* entity ) const {
  2928.     assert( entity );
  2929.     return GetPhysics()->GetAbsBounds().ContainsPoint( entity->GetPhysics()->GetAbsBounds().GetCenter() );
  2930. }
  2931.  
  2932. /*
  2933. ================
  2934. rvTramCar_Marine::DeployRamp
  2935. ================
  2936. */
  2937. void rvTramCar_Marine::DeployRamp() {
  2938.     rvTramCar::DeployRamp();
  2939.  
  2940.     if( weapons.Num() ) {
  2941.         weapons[0]->Unlock();
  2942.         weapons[0]->EjectAllDrivers( true );
  2943.     }
  2944. }
  2945.  
  2946. /*
  2947. ================
  2948. rvTramCar_Marine::RetractRamp
  2949. ================
  2950. */
  2951. void rvTramCar_Marine::RetractRamp() {
  2952.     rvTramCar::RetractRamp();
  2953.  
  2954.     UseMountedGun( gameLocal.GetLocalPlayer() );
  2955. }
  2956.  
  2957. /*
  2958. ================
  2959. rvTramCar_Marine::UseMountedGun
  2960. ================
  2961. */
  2962. void rvTramCar_Marine::UseMountedGun( idPlayer* player ) {
  2963.     if( playerOccupationStateThread.CurrentStateIs("Occupied") && EntityIsInside(player) && weapons.Num() ) {
  2964.         player->EnterVehicle( weapons[0] );
  2965.         weapons[0]->Lock();
  2966.     }
  2967. }
  2968.  
  2969. /*
  2970. ================
  2971. rvTramCar_Marine::Event_UseMountedGun
  2972. ================
  2973. */
  2974. void rvTramCar_Marine::Event_UseMountedGun( idEntity* ent ) {
  2975.     if( !ent || !ent->IsType(idPlayer::GetClassType()) ) {
  2976.         return;
  2977.     }
  2978.  
  2979.     UseMountedGun( static_cast<idPlayer*>(ent) );
  2980. }
  2981.  
  2982. /*
  2983. ================
  2984. rvTramCar_Marine::Event_SetPlayerDamageEntity
  2985. ================
  2986. */
  2987. void rvTramCar_Marine::Event_SetPlayerDamageEntity(float f)    {
  2988.     
  2989.     gameLocal.GetLocalPlayer()->SetDamageEntity( (f? this : NULL) );
  2990. }
  2991.  
  2992. CLASS_STATES_DECLARATION( rvTramCar_Marine )
  2993.     STATE( "Occupied",            rvTramCar_Marine::State_Occupied )
  2994.     STATE( "NotOccupied",        rvTramCar_Marine::State_NotOccupied )
  2995.     STATE( "UsingMountedGun",    rvTramCar_Marine::State_UsingMountedGun )
  2996.     STATE( "NotUsingMountedGun",rvTramCar_Marine::State_NotUsingMountedGun )
  2997. END_CLASS_STATES
  2998.  
  2999.  
  3000. /*
  3001. ================
  3002. rvTramCar_Marine::State_Occupied
  3003. ================
  3004. */
  3005. stateResult_t rvTramCar_Marine::State_Occupied( const stateParms_t& parms ) {
  3006.     idPlayer* player = gameLocal.GetLocalPlayer();
  3007.  
  3008.     if( !parms.stage ) {
  3009.         //player->ProcessEvent( &EV_Player_DisableWeapon );
  3010.         ActivateTramHud( player );
  3011.         return SRESULT_STAGE( parms.stage + 1 );
  3012.     }
  3013.  
  3014.     if( !PlayerIsInside() ) {
  3015.         playerOccupationStateThread.SetState( "NotOccupied" );    
  3016.         //gameLocal.GetLocalPlayer()->SetDamageEntity( NULL );
  3017.         fl.takedamage = false;
  3018.         return SRESULT_DONE_WAIT;
  3019.     }
  3020.  
  3021.     playerUsingMountedGunStateThread.Execute();
  3022.  
  3023.     UpdateTramHud( player );
  3024.  
  3025.     return SRESULT_WAIT;
  3026. }
  3027.  
  3028.  
  3029.  
  3030. /*
  3031. ================
  3032. rvTramCar_Marine::State_NotOccupied
  3033. ================
  3034. */
  3035. stateResult_t rvTramCar_Marine::State_NotOccupied( const stateParms_t& parms ) {
  3036.     if( !parms.stage ) {
  3037.         //player->ProcessEvent( &EV_Player_EnableWeapon );
  3038.         DeactivateTramHud( gameLocal.GetLocalPlayer() );
  3039.         return SRESULT_STAGE( parms.stage + 1 );
  3040.     }
  3041.  
  3042.     if( PlayerIsInside() ) {
  3043.         playerOccupationStateThread.SetState( "Occupied" );
  3044.         //gameLocal.GetLocalPlayer()->SetDamageEntity( this );
  3045.         fl.takedamage = true;
  3046.         return SRESULT_DONE_WAIT;
  3047.     }
  3048.  
  3049.     return SRESULT_WAIT;
  3050. }
  3051.  
  3052. /*
  3053. ================
  3054. rvTramCar_Marine::State_UsingMountedGun
  3055. ================
  3056. */
  3057. stateResult_t rvTramCar_Marine::State_UsingMountedGun( const stateParms_t& parms ) {
  3058.     if( !parms.stage ) {
  3059.         ActivateTramHud( gameLocal.GetLocalPlayer() );
  3060.         return SRESULT_STAGE( parms.stage + 1 );
  3061.     }
  3062.  
  3063.     idPlayer* player = gameLocal.GetLocalPlayer();
  3064.     if( player && !player->IsInVehicle() ) {
  3065.         playerUsingMountedGunStateThread.SetState( "NotUsingMountedGun" );
  3066.     }
  3067.  
  3068.     return SRESULT_WAIT;
  3069. }
  3070.  
  3071. /*
  3072. ================
  3073. rvTramCar_Marine::State_NotUsingMountedGun
  3074. ================
  3075. */
  3076. stateResult_t rvTramCar_Marine::State_NotUsingMountedGun( const stateParms_t& parms ) {
  3077.     idPlayer* player = gameLocal.GetLocalPlayer();
  3078.     if( player && player->IsInVehicle() ) {
  3079.         playerUsingMountedGunStateThread.SetState( "UsingMountedGun" );
  3080.     }
  3081.  
  3082.     return SRESULT_WAIT;
  3083. }
  3084.  
  3085. //=======================================================
  3086. //
  3087. //    rvTramCar_Strogg
  3088. //
  3089. //=======================================================
  3090. CLASS_DECLARATION( rvTramCar, rvTramCar_Strogg )
  3091.     EVENT( EV_PostSpawn,        rvTramCar_Strogg::Event_PostSpawn )
  3092. END_CLASS
  3093.  
  3094. /*
  3095. ================
  3096. rvTramCar_Strogg::Spawn
  3097. ================
  3098. */
  3099. void rvTramCar_Strogg::Spawn() {
  3100.     SetTarget( NULL );
  3101.  
  3102.     RegisterStateThread( targetSearchStateThread, "targetSearch" );
  3103.     targetSearchStateThread.SetState( "LookingForTarget" );
  3104. }
  3105.  
  3106. /*
  3107. ================
  3108. rvTramCar_Strogg::SetTarget
  3109. ================
  3110. */
  3111. void rvTramCar_Strogg::SetTarget( idEntity* newTarget ) {
  3112.     target = ConvertToMover( newTarget );
  3113. }
  3114.  
  3115. /*
  3116. ================
  3117. rvTramCar_Strogg::GetTarget
  3118. ================
  3119. */
  3120. const rvSplineMover* rvTramCar_Strogg::GetTarget() const {
  3121.     return target.GetEntity();
  3122. }
  3123.  
  3124. /*
  3125. ================
  3126. rvTramCar_Strogg::Save
  3127. ================
  3128. */
  3129. void rvTramCar_Strogg::Save( idSaveGame *savefile ) const {
  3130.     target.Save( savefile );
  3131.     targetSearchStateThread.Save( savefile );
  3132. }
  3133.  
  3134. /*
  3135. ================
  3136. rvTramCar_Strogg::Restore
  3137. ================
  3138. */
  3139. void rvTramCar_Strogg::Restore( idRestoreGame *savefile ) {
  3140.     target.Restore( savefile );
  3141.     targetSearchStateThread.Restore( savefile, this );
  3142. }
  3143.  
  3144. /*
  3145. ================
  3146. rvTramCar_Strogg::WriteToSnapshot
  3147. ================
  3148. */
  3149. void rvTramCar_Strogg::WriteToSnapshot( idBitMsgDelta &msg ) const {
  3150. }
  3151.  
  3152. /*
  3153. ================
  3154. rvTramCar_Strogg::ReadFromSnapshot
  3155. ================
  3156. */
  3157. void rvTramCar_Strogg::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  3158. }
  3159.  
  3160. /*
  3161. ================
  3162. rvTramCar_Strogg::Event_PostSpawn
  3163. ================
  3164. */
  3165. void rvTramCar_Strogg::Event_PostSpawn() {
  3166.     idEntity* enemy = gameLocal.FindEntity( spawnArgs.GetString("enemy") );
  3167.  
  3168.     SetTarget( enemy );
  3169.  
  3170.     occupants.RemoveNull();
  3171.     for( int ix = occupants.Num() - 1; ix >= 0; --ix ) {
  3172.         occupants[ix]->SetEnemy( enemy );
  3173.     }
  3174.  
  3175.     rvTramCar::Event_PostSpawn();
  3176. }
  3177.  
  3178. /*
  3179. ================
  3180. rvTramCar_Strogg::TargetIsToLeft
  3181. ================
  3182. */
  3183. bool rvTramCar_Strogg::TargetIsToLeft() {
  3184.     idList<rvSplineMover*> list;
  3185.  
  3186.     if( LookLeft(list) ) {
  3187.         for( int ix = list.Num() - 1; ix >= 0; --ix ) {
  3188.             if( list[ix] == target ) {
  3189.                 return true;
  3190.             }
  3191.         }
  3192.     }
  3193.     
  3194.     return false;
  3195. }
  3196.  
  3197. /*
  3198. ================
  3199. rvTramCar_Strogg::TargetIsToRight
  3200. ================
  3201. */
  3202. bool rvTramCar_Strogg::TargetIsToRight() {
  3203.     idList<rvSplineMover*> list;
  3204.  
  3205.     if( LookRight(list) ) {
  3206.         for( int ix = list.Num() - 1; ix >= 0; --ix ) {
  3207.             if( list[ix] == target ) {
  3208.                 return true;
  3209.             }
  3210.         }
  3211.     }
  3212.     
  3213.     return false;
  3214. }
  3215.  
  3216. /*
  3217. ================
  3218. rvTramCar_Strogg::LookAround
  3219. ================
  3220. */
  3221. void rvTramCar_Strogg::LookAround() {
  3222.     targetSearchStateThread.Execute();
  3223. }
  3224.  
  3225. CLASS_STATES_DECLARATION( rvTramCar_Strogg )
  3226.     STATE( "LookingForTarget",        rvTramCar_Strogg::State_LookingForTarget )
  3227.     STATE( "TargetInSight",            rvTramCar_Strogg::State_TargetInSight )
  3228. END_CLASS_STATES
  3229.  
  3230. /*
  3231. ================
  3232. rvTramCar_Strogg::State_LookingForTarget
  3233. ================
  3234. */
  3235. stateResult_t rvTramCar_Strogg::State_LookingForTarget( const stateParms_t& parms ) {
  3236.     static const int MSEC_DELAY = 100;
  3237.     idList<rvSplineMover*> list;
  3238.  
  3239.     if( !parms.stage ) {// Go back to random if we are supposed to be random
  3240.         //idealTrackStateThread.SetState( "RandomTrack" );
  3241.         return SRESULT_STAGE( parms.stage + 1 );
  3242.     }
  3243.  
  3244.     if( AdjustSpeed(list) ) {
  3245.         return SRESULT_DELAY( MSEC_DELAY );
  3246.     }
  3247.  
  3248.     // Could optimise based on what track
  3249.     if( TargetIsToLeft() || TargetIsToRight() ) {
  3250.         targetSearchStateThread.SetState( "TargetInSight" );
  3251.         return SRESULT_DONE;
  3252.     }
  3253.  
  3254.     SetSpeed( GetIdealSpeed() );
  3255.     return SRESULT_DELAY( MSEC_DELAY );
  3256. }
  3257.  
  3258. /*
  3259. ================
  3260. rvTramCar_Strogg::State_TargetInSight
  3261. ================
  3262. */
  3263. stateResult_t rvTramCar_Strogg::State_TargetInSight( const stateParms_t& parms ) {
  3264.     static const int MSEC_DELAY = 3000;
  3265.  
  3266.     if( !target.IsValid() || (!TargetIsToLeft() && !TargetIsToRight()) ) {
  3267.         targetSearchStateThread.SetState( "LookingForTarget" );
  3268.         return SRESULT_DONE;
  3269.     }
  3270.  
  3271.     if( !parms.stage ) {
  3272.         SetIdealTrack( GetCurrentTrack() );
  3273.         SetSpeed( target->GetSpeed() * 0.5f );
  3274.         idealTrackStateThread.SetState( "AssignedTrack" );
  3275.         StartSound( "snd_horn", SND_CHANNEL_ANY, 0, false, NULL );
  3276.         return SRESULT_STAGE( parms.stage + 1 );
  3277.     }
  3278.  
  3279.     idList<rvSplineMover*> list;
  3280.     if( AdjustSpeed(list) ) {
  3281.         return SRESULT_DELAY( MSEC_DELAY );
  3282.     }
  3283.  
  3284.     float dot = GetPhysics()->GetAxis()[0] * (target->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).ToNormal();
  3285.     float deltaSpeed = target->GetIdealSpeed() * rvRandom::flrand(0.0f, 0.1f) * SignZero(dot);
  3286.     SetSpeed( target->GetIdealSpeed() + deltaSpeed );
  3287.  
  3288.     return SRESULT_DELAY( MSEC_DELAY );
  3289. }
  3290.