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

  1. //----------------------------------------------------------------
  2. // VehicleParts.cpp
  3. //
  4. // Copyright 2002-2004 Raven Software
  5. //----------------------------------------------------------------
  6.  
  7. #include "../../idlib/precompiled.h"
  8. #pragma hdrstop
  9.  
  10. #include "../Game_local.h"
  11. #include "../Projectile.h"
  12. #include "../Effect.h"
  13. #include "Vehicle.h"
  14. #include "VehicleParts.h"
  15. #include "../ai/AI_Manager.h"
  16.  
  17. /***********************************************************************
  18.  
  19.                             rvVehiclePart
  20.  
  21. ***********************************************************************/
  22.  
  23. ABSTRACT_DECLARATION( idClass, rvVehiclePart )
  24. END_CLASS
  25.  
  26. /*
  27. =====================
  28. rvVehiclePart::Init
  29. =====================
  30. */
  31. bool rvVehiclePart::Init ( rvVehiclePosition* _position, const idDict& args, s_channelType _soundChannel ) {
  32.     spawnArgs.Copy ( args );
  33.     
  34.     soundChannel     = _soundChannel;
  35.     position         = _position;
  36.     parent             = position->GetParent();
  37.     fl.active         = false;
  38.     fl.useCenterMass = false;
  39.     
  40.     return true;
  41. }
  42.  
  43. /*
  44. =====================
  45. rvVehiclePart::Spawn
  46. =====================
  47. */
  48. void rvVehiclePart::Spawn ( void ) {
  49.     // Get joint for orienting the part
  50.     joint = parent->GetAnimator()->GetJointHandle ( spawnArgs.GetString ( "joint", "" ) );
  51.  
  52.     // More position information
  53.     fl.useCenterMass = spawnArgs.GetBool ( "useCenterMass", "0" );
  54.     spawnArgs.GetVector ( "offset", "0 0 0", localOffset );
  55.  
  56.     fl.active = false;
  57.     
  58.     fl.useViewAxis    = spawnArgs.GetBool( "useViewAxis", "0" );
  59.  
  60.     // Initialize the origin so we can determine which side of the vehicle we are on
  61.     UpdateOrigin ( );
  62.     
  63.     // Determine if this part is on the left and/or front side of the vehicle
  64.     idVec3 localOrigin;
  65.     localOrigin = (worldOrigin - parent->GetPhysics()->GetCenterMass());
  66.     fl.front = (localOrigin * parent->GetPhysics()->GetAxis()[0] < 0.0f); 
  67.     fl.left  = (localOrigin * parent->GetPhysics()->GetAxis()[1] < 0.0f); 
  68. }
  69.  
  70. /*
  71. ================
  72. rvVehiclePart::UpdateOrigin
  73. ================
  74. */
  75. void rvVehiclePart::UpdateOrigin ( void ) {    
  76.     if ( joint != INVALID_JOINT ) {
  77.         parent->GetJointWorldTransform ( joint, gameLocal.time, worldOrigin, worldAxis );
  78.     } else {
  79.         if ( fl.useViewAxis ) {
  80.             worldAxis    = parent->viewAxis;
  81.         } else {
  82.             worldAxis   = parent->GetPhysics()->GetAxis();
  83.         }
  84.         worldOrigin = fl.useCenterMass ? parent->GetPhysics()->GetCenterMass() : parent->GetPhysics()->GetOrigin();
  85.     }            
  86.     
  87.     worldOrigin += (localOffset * worldAxis);
  88.     
  89.     // Include this part in the total bounds 
  90.     // FIXME: bounds are local
  91.     parent->AddToBounds ( worldOrigin );
  92. }
  93.  
  94. /*
  95. ================
  96. rvVehiclePart::Save
  97. ================
  98. */
  99. void rvVehiclePart::Save ( idSaveGame* savefile ) const {
  100.     savefile->Write( &fl, sizeof( fl ) );
  101.  
  102.     savefile->WriteDict ( &spawnArgs );
  103.     parent.Save ( savefile );
  104.     savefile->WriteJoint ( joint );
  105.     savefile->WriteInt ( soundChannel );
  106.     savefile->WriteInt ( parent->GetPositionIndex ( position ) );
  107.     
  108.     savefile->WriteVec3 ( worldOrigin );
  109.     savefile->WriteMat3 ( worldAxis );
  110.     savefile->WriteVec3 ( localOffset );
  111. }
  112.  
  113. /*
  114. ================
  115. rvVehiclePart::Restore
  116. ================
  117. */
  118. void rvVehiclePart::Restore    ( idRestoreGame* savefile ) {
  119.     int temp;
  120.     
  121.     savefile->Read( &fl, sizeof( fl ) );
  122.  
  123.     savefile->ReadDict ( &spawnArgs );
  124.     parent.Restore ( savefile );
  125.     savefile->ReadJoint ( joint );
  126.     savefile->ReadInt ( soundChannel );
  127.  
  128.     savefile->ReadInt ( temp );
  129.     position = parent->GetPosition ( temp );
  130.     
  131.     savefile->ReadVec3 ( worldOrigin );
  132.     savefile->ReadMat3 ( worldAxis );
  133.     savefile->ReadVec3 ( localOffset );
  134. }
  135.  
  136. /***********************************************************************
  137.  
  138.                             rvVehicleSound
  139.  
  140. ***********************************************************************/
  141.  
  142. CLASS_DECLARATION( rvVehiclePart, rvVehicleSound )
  143. END_CLASS
  144.  
  145. /*
  146. =====================
  147. rvVehicleSound::rvVehicleSound
  148. =====================
  149. */
  150. rvVehicleSound::rvVehicleSound ( void ) {
  151.     memset( &refSound, 0, sizeof( refSound ) );
  152.     refSound.referenceSoundHandle = -1;
  153.     fade = false;
  154.     autoActivate = true;
  155. }
  156.  
  157. /*
  158. =====================
  159. rvVehicleSound::~rvVehicleSound
  160. =====================
  161. */
  162. rvVehicleSound::~rvVehicleSound ( void ) {
  163.     Stop();
  164.     soundSystem->FreeSoundEmitter( SOUNDWORLD_GAME, refSound.referenceSoundHandle, true );
  165.     refSound.referenceSoundHandle = -1;
  166. }
  167.  
  168. /*
  169. =====================
  170. rvVehiclePart::Spawn
  171. =====================
  172. */
  173. void rvVehicleSound::Spawn ( void ) {
  174.     soundName = spawnArgs.GetString ( "snd_loop" );
  175.     
  176.     spawnArgs.GetVec2 ( "freqShift", "1 1", freqShift );
  177.     spawnArgs.GetVec2 ( "volume", "0 0", volume );
  178.     
  179.     // Temp fix for volume
  180.     volume[0] = idMath::dBToScale( volume[0] );
  181.     volume[1] = idMath::dBToScale( volume[1] );
  182.  
  183.     declManager->FindSound ( soundName )->GetParms ( &refSound.parms );
  184. }
  185.  
  186. /*
  187. =====================
  188. rvVehicleSound::RunPostPhysics
  189. =====================
  190. */
  191. void rvVehicleSound::RunPostPhysics ( void ) {
  192.     Update ( );
  193. }
  194.  
  195. /*
  196. =====================
  197. rvVehicleSound::Activate
  198. =====================
  199. */
  200. void rvVehicleSound::Activate ( bool active ) {
  201.     rvVehiclePart::Activate ( active );
  202.  
  203.     if ( autoActivate ) {
  204.         if ( active ) {
  205.             Play ( );
  206.         } else {
  207.             Stop ( );
  208.         }
  209.     }
  210. }
  211.  
  212. /*
  213. =====================
  214. rvVehicleSound::Play
  215. =====================
  216. */
  217. void rvVehicleSound::Play ( void ) {
  218.     if ( !soundName.Length ( ) ) {
  219.         return;
  220.     }
  221.  
  222.     idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
  223.     if ( !emitter )  {
  224.         refSound.referenceSoundHandle = soundSystem->AllocSoundEmitter( SOUNDWORLD_GAME );
  225.     }
  226.  
  227.     Attenuate ( 0.0f, 0.0f );
  228.  
  229.     Update ( true );
  230.  
  231.     emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
  232.     if( emitter ) {
  233.         emitter->UpdateEmitter( refSound.origin, refSound.velocity, refSound.listenerId, &refSound.parms );
  234.         emitter->StartSound ( declManager->FindSound( soundName ), soundChannel, 0, 0 );
  235.     }
  236. }
  237.  
  238. /*
  239. =====================
  240. rvVehicleSound::Stop
  241. =====================
  242. */
  243. void rvVehicleSound::Stop ( void ) {
  244.     if ( IsPlaying ( ) ) {
  245.         idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
  246.         if( emitter ) {
  247.             emitter->StopSound( soundChannel );
  248.         }
  249.     }
  250. }
  251.  
  252. /*
  253. =====================
  254. rvVehicleSound::Update
  255. =====================
  256. */
  257. void rvVehicleSound::Update ( bool force ) {
  258.     if ( !force && !IsPlaying ( ) ) {
  259.         return;
  260.     }
  261.  
  262.     if ( fade && currentVolume.IsDone ( gameLocal.time ) ) {
  263.         Stop ( );
  264.         return;
  265.     }
  266.  
  267.     idSoundEmitter *emitter = soundSystem->EmitterForIndex( SOUNDWORLD_GAME, refSound.referenceSoundHandle );
  268.     if( !emitter ) {
  269.         return;
  270.     }
  271.  
  272.     UpdateOrigin ( );
  273.  
  274.     refSound.parms.volume            = currentVolume.GetCurrentValue ( gameLocal.time );
  275.     refSound.parms.frequencyShift = currentFreqShift.GetCurrentValue ( gameLocal.time );
  276.     emitter->ModifySound ( soundChannel, &refSound.parms );
  277.     
  278.     refSound.origin = worldOrigin;
  279.     // bdube: please put something sensible here if you want doppler from the sound system
  280.     refSound.velocity = vec3_origin;
  281.     emitter->UpdateEmitter( refSound.origin, refSound.velocity, parent->entityNumber + 1, &refSound.parms );
  282. }
  283.  
  284. /*
  285. =====================
  286. rvVehicleSound::Attenuate
  287. =====================
  288. */
  289. void rvVehicleSound::Attenuate ( float volumeAttenuate, float freqAttenuate ) {
  290.     float f;
  291.  
  292.     fade = false;
  293.     
  294.     f = volume[0] + (volume[1]-volume[0]) * volumeAttenuate;
  295.     currentVolume.Init ( gameLocal.time, 100, currentVolume.GetCurrentValue(gameLocal.time), f );
  296.  
  297.     f = freqShift[0] + (freqShift[1]-freqShift[0]) * freqAttenuate;
  298.     currentFreqShift.Init ( gameLocal.time, 100, currentFreqShift.GetCurrentValue(gameLocal.time), f );
  299. }
  300.  
  301. /*
  302. =====================
  303. rvVehicleSound::Fade
  304. =====================
  305. */
  306. void rvVehicleSound::Fade ( int duration, float toVolume, float toFreq ) {
  307.     currentVolume.Init ( gameLocal.time, duration, currentVolume.GetCurrentValue(gameLocal.time), toVolume );
  308.     currentFreqShift.Init ( gameLocal.time, duration, currentFreqShift.GetCurrentValue(gameLocal.time), toFreq );
  309. }
  310.  
  311. /*
  312. =====================
  313. rvVehicleSound::Save
  314. =====================
  315. */
  316. void rvVehicleSound::Save ( idSaveGame* savefile ) const {
  317.     savefile->WriteVec2 ( volume );
  318.     savefile->WriteVec2 ( freqShift );
  319.     savefile->WriteString ( soundName );
  320.     savefile->WriteRefSound ( refSound );
  321.     
  322.     savefile->WriteInterpolate ( currentVolume );
  323.     savefile->WriteInterpolate ( currentFreqShift );
  324.     
  325.     savefile->WriteBool ( fade );
  326.     savefile->WriteBool ( autoActivate );
  327. }    
  328.  
  329. /*
  330. =====================
  331. rvVehicleSound::Restore
  332. =====================
  333. */
  334. void rvVehicleSound::Restore ( idRestoreGame* savefile ) {
  335.     savefile->ReadVec2 ( volume );
  336.     savefile->ReadVec2 ( freqShift );
  337.     savefile->ReadString ( soundName );
  338.     savefile->ReadRefSound ( refSound );
  339.     
  340.     savefile->ReadInterpolate ( currentVolume );
  341.     savefile->ReadInterpolate ( currentFreqShift );
  342.  
  343.     savefile->ReadBool ( fade );
  344.     savefile->ReadBool ( autoActivate );
  345. }
  346.  
  347. //----------------------------------------------------------------
  348. //
  349. //                        rvVehicleLight
  350. //
  351. //----------------------------------------------------------------
  352.  
  353. CLASS_DECLARATION( rvVehiclePart, rvVehicleLight )
  354. END_CLASS
  355.  
  356. /*
  357. =====================
  358. rvVehicleLight::rvVehicleLight
  359. =====================
  360. */
  361. rvVehicleLight::rvVehicleLight ( void ) {
  362.     lightHandle = -1;
  363. }
  364.  
  365. /*
  366. =====================
  367. rvVehicleLight::~rvVehicleLight
  368. =====================
  369. */
  370. rvVehicleLight::~rvVehicleLight ( void ) {
  371.     if ( lightHandle != -1 ) {
  372.         gameRenderWorld->FreeLightDef( lightHandle );
  373.         lightHandle = -1;
  374.     }
  375. }
  376.  
  377. /*
  378. =====================
  379. rvVehicleLight::Spawn
  380. =====================
  381. */
  382. void rvVehicleLight::Spawn ( void ) {
  383.     idVec3        color;
  384.     const char* temp;
  385.  
  386.     memset ( &renderLight, 0, sizeof(renderLight) );
  387.  
  388.     renderLight.shader      = declManager->FindMaterial( spawnArgs.GetString ( "mtr_light", "lights/muzzleflash" ), false );
  389.     renderLight.pointLight  = spawnArgs.GetBool( "pointlight", "1" );
  390.  
  391.     spawnArgs.GetVector( "color", "0 0 0", color );
  392.     renderLight.shaderParms[ SHADERPARM_RED ]        = color[0];
  393.     renderLight.shaderParms[ SHADERPARM_GREEN ]        = color[1];
  394.     renderLight.shaderParms[ SHADERPARM_BLUE ]        = color[2];
  395.     renderLight.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
  396.  
  397. // RAVEN BEGIN
  398. // dluetscher: added detail levels to render lights
  399.     renderLight.detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  400. // RAVEN END
  401.  
  402.     renderLight.lightRadius[0] = renderLight.lightRadius[1] = 
  403.         renderLight.lightRadius[2] = (float)spawnArgs.GetInt( "radius" );
  404.  
  405.     if ( !renderLight.pointLight ) {
  406.         renderLight.target = spawnArgs.GetVector( "target" );
  407.         renderLight.up       = spawnArgs.GetVector( "up" );
  408.         renderLight.right  = spawnArgs.GetVector( "right" );
  409.         renderLight.end       = spawnArgs.GetVector( "target" );;
  410.     }
  411.  
  412.     // Hide flare surface if there is one
  413.     temp = spawnArgs.GetString ( "flaresurface", "" );
  414.     if ( temp && *temp ) {
  415.         parent->ProcessEvent ( &EV_HideSurface, temp );
  416.     }
  417.  
  418.     // Sounds shader when turning light
  419.     spawnArgs.GetString ( "snd_on", "", soundOn );
  420.     
  421.     // Sound shader when turning light off
  422.     spawnArgs.GetString ( "snd_off", "", soundOff);
  423.     
  424.     lightOn = spawnArgs.GetBool( "start_on", "1" );
  425.     lightHandle = -1;
  426. }
  427.  
  428. /*
  429. =====================
  430. rvVehicleLight::RunPostPhysics
  431. =====================
  432. */
  433. void rvVehicleLight::RunPostPhysics ( void ) {
  434.     if ( lightHandle == -1 || !lightOn ) {
  435.         return;
  436.     }
  437.     
  438.     UpdateLightDef ( );
  439. }
  440.  
  441. /*
  442. =====================
  443. rvVehicleLight::UpdateLightDef
  444. =====================
  445. */
  446. void rvVehicleLight::UpdateLightDef ( void ) {
  447.     UpdateOrigin ( );
  448.     
  449.     renderLight.origin = worldOrigin;
  450.     renderLight.axis   = worldAxis;    
  451.     
  452.     gameRenderWorld->UpdateLightDef( lightHandle, &renderLight );    
  453. }
  454.  
  455. /*
  456. =====================
  457. rvVehicleLight::Activate
  458. =====================
  459. */
  460. void rvVehicleLight::Activate ( bool active ) {
  461.     rvVehiclePart::Activate ( active );
  462.     
  463.     if ( active && lightOn ) {
  464.         TurnOn ( );
  465.     } else {
  466.         TurnOff ( );
  467.     }
  468. }
  469.  
  470. /*
  471. =====================
  472. rvVehicleLight::TurnOff
  473. =====================
  474. */
  475. void rvVehicleLight::TurnOff ( void ) {
  476.     const char* surface;
  477.  
  478.     if ( lightHandle != -1 ) {
  479.         gameRenderWorld->FreeLightDef( lightHandle );
  480.         lightHandle = -1;
  481.  
  482.         // Play off sound
  483.         if ( soundOff.Length() ) {
  484.             parent->StartSoundShader ( declManager->FindSound ( soundOff ), soundChannel, 0, false, NULL );
  485.         }
  486.     }
  487.     
  488.     // Hide flare surface if there is one
  489.     surface = spawnArgs.GetString ( "flaresurface", "" );
  490.     if ( surface && *surface ) {
  491.         parent->ProcessEvent ( &EV_HideSurface, surface );
  492.     }
  493. }
  494.  
  495. /*
  496. =====================
  497. rvVehicleLight::TurnOn
  498. =====================
  499. */
  500. void rvVehicleLight::TurnOn ( void ) {
  501.     const char* surface;
  502.  
  503.     if ( lightHandle == -1 ) {
  504.         lightHandle = gameRenderWorld->AddLightDef( &renderLight );
  505.     }
  506.  
  507.     // Play off sound
  508.     if ( soundOn.Length() ) {
  509.         parent->StartSoundShader ( declManager->FindSound ( soundOn ), soundChannel, 0, false, NULL );
  510.     }
  511.  
  512.     // Show flare surface if there is one
  513.     surface = spawnArgs.GetString ( "flaresurface", "" );
  514.     if ( surface && *surface ) {
  515.         parent->ProcessEvent ( &EV_ShowSurface, surface );
  516.     }
  517.     
  518.     UpdateLightDef ( );
  519. }
  520.  
  521. /*
  522. =====================
  523. rvVehicleLight::Impulse
  524. =====================
  525. */
  526. void rvVehicleLight::Impulse ( int impulse ) {
  527.     switch ( impulse ) {
  528.         case IMPULSE_50:
  529.             if ( lightOn ) {
  530.                 lightOn = false;
  531.                 TurnOff ( );
  532.             } else {
  533.                 lightOn = true;
  534.                 TurnOn ( );
  535.             }
  536.             break;
  537.     }
  538. }
  539.  
  540. /*
  541. =====================
  542. rvVehicleLight::Save
  543. =====================
  544. */
  545. void rvVehicleLight::Save ( idSaveGame* savefile ) const {
  546.     savefile->WriteRenderLight ( renderLight );
  547.     savefile->WriteInt ( lightHandle );
  548.     savefile->WriteBool ( lightOn );
  549.     savefile->WriteString ( soundOn );
  550.     savefile->WriteString ( soundOff );
  551. }
  552.  
  553. /*
  554. =====================
  555. rvVehicleLight::Restore
  556. =====================
  557. */
  558. void rvVehicleLight::Restore ( idRestoreGame* savefile ) {
  559.     savefile->ReadRenderLight ( renderLight );
  560.     savefile->ReadInt ( lightHandle );
  561.     if ( lightHandle != -1 ) {
  562.         //get the handle again as it's out of date after a restore!
  563.         lightHandle = gameRenderWorld->AddLightDef( &renderLight );
  564.     }
  565.  
  566.     savefile->ReadBool ( lightOn );
  567.     savefile->ReadString ( soundOn );
  568.     savefile->ReadString ( soundOff );
  569. }
  570.  
  571. /***********************************************************************
  572.  
  573.                             rvVehicleWeapon
  574.  
  575. ***********************************************************************/
  576.  
  577. CLASS_DECLARATION( rvVehiclePart, rvVehicleWeapon )
  578. END_CLASS
  579.  
  580. #define    WEAPON_DELAY_FIRE        500
  581.  
  582. /*
  583. =====================
  584. rvVehicleWeapon::rvVehicleWeapon
  585. =====================
  586. */
  587. rvVehicleWeapon::rvVehicleWeapon ( void ) {
  588. #ifdef _XENON
  589.     bestEnemy        = 0;
  590. #endif
  591.     nextFireTime    = 0;
  592.     fireDelay        = 0;
  593.     hitScanDef        = NULL;
  594.     projectileDef    = NULL;
  595.     animNum            = 0;
  596. }
  597.  
  598. /*
  599. =====================
  600. rvVehicleWeapon::~rvVehicleWeapon
  601. =====================
  602. */
  603. rvVehicleWeapon::~rvVehicleWeapon ( void ) {
  604.     if ( muzzleFlashHandle != -1 ) {
  605.         gameRenderWorld->FreeLightDef( muzzleFlashHandle );
  606.         muzzleFlashHandle = -1;
  607.     }
  608.     StopTargetEffect( );
  609. }
  610.  
  611. /*
  612. =====================
  613. rvVehicleWeapon::Spawn
  614. =====================
  615. */
  616. void rvVehicleWeapon::Spawn ( void ) {
  617.     int        i;
  618.     idStr    temp;
  619.     idVec3    color;
  620.  
  621. #ifdef _XENON
  622.     bestEnemy = 0;
  623. #endif
  624.  
  625.     launchFromJoint = spawnArgs.GetBool ( "launchFromJoint", "0" );
  626.     lockScanning = spawnArgs.GetBool ( "lockScanning", "0" );
  627.     lastLockTime = 0;
  628.     
  629.     if ( spawnArgs.GetString ( "def_hitscan", "", temp ) ) {
  630.         hitScanDef = gameLocal.FindEntityDefDict ( spawnArgs.GetString ( "def_hitscan" ) );
  631.     } else {
  632.         projectileDef = gameLocal.FindEntityDefDict ( spawnArgs.GetString ( "def_projectile" ) );
  633.     }
  634.     
  635.     fireDelay             = SEC2MS ( spawnArgs.GetFloat ( "firedelay" ) );
  636.     spread                 = spawnArgs.GetFloat ( "spread" );
  637.     jointIndex             = 0;
  638.     count                 = spawnArgs.GetInt ( "count", "1" );
  639.     lockRange             = spawnArgs.GetFloat ( "lockrange", "0" );
  640.     ammoPerCharge         = spawnArgs.GetInt ( "ammopercharge", "-1" );
  641.     chargeTime             = SEC2MS ( spawnArgs.GetFloat ( "chargetime" ) );
  642.     currentAmmo             = ammoPerCharge;
  643.     muzzleFlashHandle    = -1;
  644.     
  645.     if( spawnArgs.GetString("anim", "", temp) && *temp ) {
  646.         animNum = parent->GetAnimator()->GetAnim( temp );
  647.         animChannel = spawnArgs.GetInt( "animChannel" );
  648.     }
  649.  
  650.     spawnArgs.GetVector ( "force", "0 0 0", force );
  651.  
  652.     // Vehicle guns can have multiple joints to fire from.  They will be cycled through 
  653.     // when firing.
  654.     joints.Append ( joint );
  655.     for ( i = 2; ; i ++ ) {
  656.         jointHandle_t joint2;
  657.         joint2 = parent->GetAnimator()->GetJointHandle ( spawnArgs.GetString ( va("joint%d", i ) ) );
  658.         if ( joint2 == INVALID_JOINT ) {
  659.             break;
  660.         }
  661.         
  662.         joints.Append ( joint2 );
  663.     }
  664.  
  665.     // Muzzle Flash
  666.     memset ( &muzzleFlash, 0, sizeof(muzzleFlash) );
  667.  
  668.     spawnArgs.GetVector ( "flashOffset", "0 0 0", muzzleFlashOffset );
  669.     muzzleFlash.shader      = declManager->FindMaterial( spawnArgs.GetString ( "mtr_flashShader", "lights/muzzleflash" ), false );
  670.     muzzleFlash.pointLight = spawnArgs.GetBool( "flashPointLight", "1" );
  671. // RAVEN BEGIN
  672. // dluetscher: added detail levels to render lights
  673.     muzzleFlash.detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  674. // RAVEN END
  675.  
  676.     spawnArgs.GetVector( "flashColor", "0 0 0", color );
  677.     muzzleFlash.shaderParms[ SHADERPARM_RED ]         = color[0];
  678.     muzzleFlash.shaderParms[ SHADERPARM_GREEN ]     = color[1];
  679.     muzzleFlash.shaderParms[ SHADERPARM_BLUE ]         = color[2];
  680.     muzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
  681.  
  682.     muzzleFlash.lightRadius[0] = muzzleFlash.lightRadius[1] = muzzleFlash.lightRadius[2] =    (float)spawnArgs.GetInt( "flashRadius" );
  683.  
  684.     if ( !muzzleFlash.pointLight ) {
  685.         muzzleFlash.target    = spawnArgs.GetVector( "flashTarget" );
  686.         muzzleFlash.up        = spawnArgs.GetVector( "flashUp" );
  687.         muzzleFlash.right    = spawnArgs.GetVector( "flashRight" );
  688.         muzzleFlash.end        = spawnArgs.GetVector( "flashTarget" );
  689.     }
  690.     
  691.     shaderFire = declManager->FindSound ( spawnArgs.GetString ( "snd_fire" ), false );
  692.     shaderReload = declManager->FindSound ( spawnArgs.GetString ( "snd_reload" ), false );
  693.  
  694.     // get the brass def
  695.     idStr name;
  696.     brassDict = NULL;
  697.     if ( spawnArgs.GetString( "def_ejectBrass", "", name ) && *name ) {
  698.         brassDict = gameLocal.FindEntityDefDict( name, false );
  699.  
  700.         if ( !brassDict ) {
  701.             gameLocal.Warning( "Unknown brass def '%s' for weapon on vehicle '%s'", name, position->mParent->name );
  702.         }
  703.     }
  704.  
  705.     spawnArgs.GetVector ( "ejectOffset", "0 0 0", brassEjectOffset );
  706.     brassEjectJoint = parent->GetAnimator()->GetJointHandle( spawnArgs.GetString ( "joint_view_eject", "eject" ) );
  707.     if ( brassDict ) {
  708.         brassEjectDelay    = SEC2MS( brassDict->GetFloat( "delay", "0.01" ) );
  709.     }
  710.     brassEjectNext = 0;
  711.  
  712.     targetEnt = NULL;
  713.     targetJoint = INVALID_JOINT;
  714.     targetPos.Zero ();
  715.  
  716.     // Zoom
  717.     zoomFov = spawnArgs.GetInt( "zoomFov", "-1" );
  718.     zoomGui  = uiManager->FindGui ( spawnArgs.GetString ( "gui_zoom", "" ), true );
  719.     zoomTime = spawnArgs.GetFloat ( "zoomTime", ".15" );
  720. //    wfl.zoomHideCrosshair = spawnArgs.GetBool ( "zoomHideCrosshair", "1" );
  721. }
  722.  
  723. /*
  724. =====================
  725. rvVehicleWeapon::Activate
  726. =====================
  727. */
  728. void rvVehicleWeapon::Activate ( bool activate ) {
  729.     rvVehiclePart::Activate ( activate );
  730.     
  731.     nextFireTime = gameLocal.time + WEAPON_DELAY_FIRE;
  732.  
  733. #ifdef _XENON
  734.     bestEnemy = 0;
  735. #endif
  736. }
  737.  
  738. /*
  739. =====================
  740. rvVehicleWeapon::StopTargetEffect
  741. =====================
  742. */
  743. void rvVehicleWeapon::StopTargetEffect ( void ) {
  744.     if ( targetEffect ) {
  745.         targetEffect->Stop( );
  746.         targetEffect = NULL;
  747.     }
  748. }
  749.  
  750. /*
  751. =====================
  752. rvVehicleWeapon::UpdateLock
  753. =====================
  754. */
  755. void rvVehicleWeapon::UpdateLock ( void ) {
  756.     
  757. #ifdef _XENON
  758.  
  759.     bestEnemy = 0;
  760.     
  761.     // Handle auto-aim if it's enabled
  762.     if ( cvarSystem->GetCVarBool("pm_AimAssist") ) {
  763.     
  764.         const float testDist = 2000.0f; // huge because we're outdoors
  765.         
  766.         idPlayer *player = gameLocal.GetLocalPlayer();
  767.         if ( player && position->GetDriver() == player ) {
  768.         
  769.             idVec3 start = position->GetEyeOrigin();
  770.             idVec3 end = start + (position->GetEyeAxis().ToAngles().ToForward() * testDist);
  771.  
  772.             idBounds bounds( start );
  773.             bounds.AddPoint( end );
  774.  
  775.             idClipModel *clipModelList[ MAX_GENTITIES ];
  776.             int numClipModels = gameLocal.ClipModelsTouchingBounds( player, bounds, -1, clipModelList, MAX_GENTITIES );
  777.             
  778.             float bDist = testDist;
  779.             
  780.              float fovX, fovY;
  781.              gameLocal.CalcFov( player->CalcFov( true ), fovX, fovY );
  782.             float dNear = cvarSystem->GetCVarFloat( "r_znear" );
  783.             float dFar = testDist;
  784.             float size = dFar * idMath::Tan( DEG2RAD( fovY * 0.5f ) ) * float(cvarSystem->GetCVarInteger("pm_AimAssistFOV")) / 100.0f;
  785.             
  786.             idFrustum aimArea;
  787.             aimArea.SetOrigin( start );
  788.             aimArea.SetAxis( position->GetEyeAxis() );
  789.             aimArea.SetSize( dNear, dFar, size, size );
  790.  
  791.             for ( int i = 0; i < numClipModels; i++ ) {
  792.                 
  793.                 idClipModel *clip = clipModelList[ i ];
  794.                 idEntity *ent = clip->GetEntity();
  795.  
  796.                 if ( ent->IsHidden() ) {
  797.                     continue;
  798.                 }
  799.  
  800.                 bool isAI = ent->IsType( idAI::GetClassType() );
  801.                 bool isFriendly = false;
  802.                 
  803.                 if ( isAI ) {
  804.                     if ( static_cast<idAI *>( ent )->team == player->team ) {
  805.                         continue;
  806.                     }
  807.                 } else {
  808.                     continue;
  809.                 }
  810.                 
  811.                 float focusLength = (ent->GetPhysics()->GetOrigin() - start).LengthFast() - ent->GetPhysics()->GetBounds().GetRadius();
  812.             
  813.                 const idBounds &b = ent->GetPhysics()->GetAbsBounds();
  814.                 bool inside = aimArea.IntersectsBounds(b);
  815.                 
  816.                 if ( inside ) {
  817.                     float dist = b.ShortestDistance(start);
  818.                     if ( bDist > dist ) {
  819.                         bDist = dist;
  820.                         bestEnemy = (idActor *)ent;
  821.                     }
  822.                 }
  823.             }
  824.         }
  825.     }
  826. #endif
  827.     
  828.     if ( !position->GetDriver() || parent->health <= 0 || !lockScanning) {
  829.         StopTargetEffect( );
  830.     } else if ( lockScanning && position->GetDriver() && position->GetDriver()->IsType( idPlayer::GetClassType() ) ) {
  831.         //always update locking info
  832.         GetLockInfo( position->GetEyeOrigin(), position->GetEyeAxis() );
  833.         idPlayer *player = gameLocal.GetLocalPlayer();
  834.         if ( player && position->GetDriver() == player ) {
  835.             // Update the guide effect
  836.             if ( targetEnt && targetEnt.IsValid() && targetEnt->health > 0 && targetEnt->IsType( idActor::GetClassType() ) ) {
  837.                 idVec3 eyePos;
  838.                 if ( targetJoint != INVALID_JOINT ) {
  839.                     idMat3 jointAxis;
  840.                     targetEnt->GetAnimator()->GetJointTransform( targetJoint, gameLocal.GetTime(), eyePos, jointAxis );
  841.                     eyePos = targetEnt->GetRenderEntity()->origin + (eyePos*targetEnt->GetRenderEntity()->axis);
  842.                     if ( !targetPos.Compare( vec3_origin ) ) {
  843.                         jointAxis = jointAxis * targetEnt->GetRenderEntity()->axis;
  844.                         eyePos += jointAxis[0]*targetPos[0];
  845.                         eyePos += jointAxis[1]*targetPos[1];
  846.                         eyePos += jointAxis[2]*targetPos[2];
  847.                     }
  848.                 } else {
  849.                     eyePos = static_cast<idActor *>(targetEnt.GetEntity())->GetEyePosition();
  850.                     eyePos += targetEnt->GetPhysics()->GetAbsBounds().GetCenter ( );
  851.                     eyePos *= 0.5f;
  852.                 }
  853.                 if ( targetEffect ) {            
  854.                     targetEffect->SetOrigin ( eyePos );
  855.                     targetEffect->SetAxis ( player->firstPersonViewAxis.Transpose() );
  856.                 } else {
  857.                     targetEffect = gameLocal.PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_guide" ), eyePos, player->firstPersonViewAxis.Transpose(), true, vec3_origin, false );
  858.                     if ( targetEffect ) {
  859.                         targetEffect->GetRenderEffect()->weaponDepthHackInViewID = position->GetDriver()->entityNumber + 1;
  860.                         targetEffect->GetRenderEffect()->allowSurfaceInViewID = position->GetDriver()->entityNumber + 1;
  861.                     }
  862.                 }
  863.             } else {
  864.                 StopTargetEffect( );
  865.             }
  866.         }
  867.     }
  868. }
  869.  
  870. /*
  871. =====================
  872. rvVehicleWeapon::RunPostPhysics
  873. =====================
  874. */
  875. void rvVehicleWeapon::RunPostPhysics ( void ) {
  876.     if ( !IsActive() || !parent->IsShootingEnabled ( ) ) {
  877.         return;
  878.     }
  879.  
  880.     if ( currentAmmo <= 0 && currentCharge.IsDone( gameLocal.GetTime() ) ) {
  881.         currentAmmo = ammoPerCharge;
  882.     }
  883.  
  884.     UpdateLock();
  885.  
  886.     if ( !(position->mInputCmd.buttons & BUTTON_ATTACK ) ) {
  887.         return;
  888.     }
  889.  
  890.     if ( gameLocal.time <= nextFireTime || !currentCharge.IsDone(gameLocal.GetTime()) ) {
  891.         return;
  892.     }
  893.  
  894.     // Gun animation?
  895.     if ( animNum ) {
  896.         parent->GetAnimator()->PlayAnim ( animChannel, animNum, gameLocal.time, 0 );
  897.     }
  898.  
  899.     if( !spawnArgs.GetBool("launchOnFrameCommand") ) {
  900.         if (!Fire())
  901.         {
  902.             return;
  903.         }
  904.     }
  905.  
  906.     nextFireTime = gameLocal.time + fireDelay;
  907. }
  908.  
  909. /*
  910. ================
  911. rvVehicleWeapon::WeaponFeedback
  912. ================
  913. */
  914. void rvVehicleWeapon::WeaponFeedback( const idDict* dict ) {
  915.     if( !dict || !GetPosition() || !GetPosition()->IsOccupied() ) {
  916.         return;
  917.     }
  918.  
  919.     idActor* actor = GetPosition()->GetDriver();
  920.     if( !actor || !actor->IsType( idPlayer::GetClassType() ) ) {
  921.         return;
  922.     }
  923.  
  924.     //abahr: This feels like a hack.  I hate using def files for logic but it just seems the easiest way to do it 
  925.     idPlayer* player = static_cast<idPlayer*>( actor );
  926.     if( dict->GetInt("recoilTime") ) {
  927.         player->playerView.WeaponFireFeedback( dict );
  928.     }
  929.     if( dict->GetInt("shakeTime") ) {
  930.         player->playerView.SetShakeParms( MS2SEC(gameLocal.GetTime() + dict->GetInt("shakeTime")), dict->GetFloat("shakeMagnitude") );
  931.     }
  932.     EjectBrass();
  933. }
  934.  
  935. /*
  936. ================
  937. rvVehicleWeapon::MuzzleFlashLight
  938. ================
  939. */
  940. void rvVehicleWeapon::MuzzleFlashLight( const idVec3& origin, const idMat3& axis ) {
  941.     if ( !muzzleFlash.lightRadius[0] ) {
  942.         return;
  943.     }
  944.  
  945.     muzzleFlash.origin = origin;
  946.     muzzleFlash.axis   = axis;
  947.  
  948.     // these will be different each fire
  949.     muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ]    = -MS2SEC( gameLocal.time );
  950.     muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ]    = parent->GetRenderEntity()->shaderParms[ SHADERPARM_DIVERSITY ];
  951.  
  952.     // the light will be removed at this time
  953.     muzzleFlashEnd = gameLocal.time + muzzleFlashTime;
  954.  
  955.     if ( muzzleFlashHandle != -1 ) {
  956.         gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
  957.     } else {
  958.         muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
  959.     }
  960. }
  961.  
  962. /*
  963. =====================
  964. rvVehicleWeapon::UpdateCursorGUI
  965. =====================
  966. */
  967. void rvVehicleWeapon::UpdateCursorGUI ( idUserInterface* gui ) const {
  968.     // cnicholson: I do't see a reason why this if statement is here. Its ALWAYS  false and so this block never executed,
  969.     // thus if the player was in a vehicle, the crosshair was always the player's last held weapon, not the crosshair defined in the vehicle .def.
  970.     // So I commented the if part out. 
  971.     //if ( spawnArgs.GetBool( "hide_crosshair", "0" ) ) {
  972.         gui->SetStateString ( "crossImage", spawnArgs.GetString ( "mtr_crosshair" ) );
  973.         gui->SetStateString ( "crossColor", g_crosshairColor.GetString() );
  974.         gui->SetStateInt ( "crossOffsetX", spawnArgs.GetInt ( "crosshairOffsetX", "0" ) );
  975.         gui->SetStateInt ( "crossOffsetY", spawnArgs.GetInt ( "crosshairOffsetY", "0" ) );             
  976.          gui->StateChanged ( gameLocal.time );
  977.     //}
  978. }
  979.  
  980. /*
  981. =====================
  982. rvVehicleWeapon::Save
  983. =====================
  984. */
  985. void rvVehicleWeapon::Save ( idSaveGame* savefile ) const {
  986.     int i;
  987.  
  988.     savefile->WriteInt ( nextFireTime );
  989.     savefile->WriteInt ( fireDelay );
  990.     savefile->WriteInt ( count );
  991.     // TOSAVE: const idDict*            projectileDef;
  992.     // TOSVAE: const idDict*            hitScanDef;
  993.  
  994.     savefile->WriteFloat ( spread );
  995.     savefile->WriteBool ( launchFromJoint );
  996.     savefile->WriteBool ( lockScanning );
  997.     savefile->WriteInt ( lastLockTime );
  998.     savefile->WriteFloat ( lockRange );
  999.     
  1000.     savefile->WriteInt ( joints.Num() );
  1001.     for ( i = 0; i < joints.Num(); i ++ ) {
  1002.         savefile->WriteJoint ( joints[i] );
  1003.     }
  1004.     savefile->WriteInt ( jointIndex );
  1005.     
  1006.     savefile->WriteVec3 ( force );
  1007.     
  1008.     savefile->WriteInt ( ammoPerCharge );
  1009.     savefile->WriteInt ( chargeTime );
  1010.     savefile->WriteInt ( currentAmmo );
  1011.     savefile->WriteInterpolate ( currentCharge );
  1012.  
  1013.     savefile->WriteRenderLight ( muzzleFlash );
  1014.     savefile->WriteInt ( muzzleFlashHandle );
  1015.     savefile->WriteInt ( muzzleFlashEnd );
  1016.     savefile->WriteInt ( muzzleFlashTime );
  1017.     savefile->WriteVec3 ( muzzleFlashOffset );
  1018.     
  1019.     savefile->WriteInt ( animNum );
  1020.     savefile->WriteInt ( animChannel );
  1021.  
  1022.     targetEnt.Save( savefile );
  1023.     savefile->WriteJoint( targetJoint );
  1024.     savefile->WriteVec3( targetPos );
  1025.     targetEffect.Save( savefile );
  1026.     // Don't save shaderFire or shaderReload, they are setup in Restore
  1027.  
  1028.     savefile->WriteInt( zoomFov );
  1029.     savefile->WriteUserInterface( zoomGui, true );
  1030.     savefile->WriteFloat( zoomTime );
  1031. }
  1032.  
  1033. /*
  1034. =====================
  1035. rvVehicleWeapon::Restore
  1036. =====================
  1037. */
  1038. void rvVehicleWeapon::Restore ( idRestoreGame* savefile ) {
  1039.     int        i;
  1040.     int        num;
  1041.     idStr    temp;
  1042.  
  1043. #ifdef _XENON
  1044.     bestEnemy = 0;
  1045. #endif
  1046.  
  1047.     savefile->ReadInt ( nextFireTime );
  1048.     savefile->ReadInt ( fireDelay );
  1049.     savefile->ReadInt ( count );
  1050.     savefile->ReadFloat ( spread );
  1051.     savefile->ReadBool ( launchFromJoint );
  1052.     savefile->ReadBool ( lockScanning );
  1053.     savefile->ReadInt ( lastLockTime );
  1054.     savefile->ReadFloat ( lockRange );
  1055.     
  1056.     savefile->ReadInt ( num );
  1057.     joints.Clear ( );
  1058.     joints.SetNum ( num );
  1059.     for ( i = 0; i < num; i ++ ) {
  1060.         savefile->ReadJoint ( joints[i] );
  1061.     }
  1062.     savefile->ReadInt ( jointIndex );
  1063.     
  1064.     savefile->ReadVec3 ( force );
  1065.     
  1066.     savefile->ReadInt ( ammoPerCharge );
  1067.     savefile->ReadInt ( chargeTime );
  1068.     savefile->ReadInt ( currentAmmo );
  1069.     savefile->ReadInterpolate( currentCharge );
  1070.  
  1071.     savefile->ReadRenderLight ( muzzleFlash );
  1072.     savefile->ReadInt ( muzzleFlashHandle );
  1073.     if ( muzzleFlashHandle != -1 ) {
  1074.         //get the handle again as it's out of date after a restore!
  1075.         muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
  1076.     }
  1077.  
  1078.     savefile->ReadInt ( muzzleFlashEnd );
  1079.     savefile->ReadInt ( muzzleFlashTime );
  1080.     savefile->ReadVec3 ( muzzleFlashOffset );
  1081.  
  1082.     savefile->ReadInt ( animNum );    
  1083.     savefile->ReadInt ( animChannel );
  1084.     
  1085.     if ( spawnArgs.GetString ( "def_hitscan", "", temp ) ) {
  1086.         hitScanDef = gameLocal.FindEntityDefDict ( spawnArgs.GetString ( "def_hitscan" ) );
  1087.     } else {
  1088.         projectileDef = gameLocal.FindEntityDefDict ( spawnArgs.GetString ( "def_projectile" ) );
  1089.     }
  1090.  
  1091.     shaderFire = declManager->FindSound ( spawnArgs.GetString ( "snd_fire" ), false );
  1092.     shaderReload = declManager->FindSound ( spawnArgs.GetString ( "snd_reload" ), false );
  1093.  
  1094.     // Brass Def
  1095.     brassDict = gameLocal.FindEntityDefDict( spawnArgs.GetString( "def_ejectBrass" ), false );
  1096.     spawnArgs.GetVector ( "ejectOffset", "0 0 0", brassEjectOffset );
  1097.     brassEjectJoint = parent->GetAnimator()->GetJointHandle( spawnArgs.GetString ( "joint_view_eject", "eject" ) );
  1098.     if ( brassDict ) {
  1099.         brassEjectDelay    = SEC2MS( brassDict->GetFloat( "delay", "0.01" ) );
  1100.     }
  1101.     brassEjectNext = 0;
  1102.  
  1103.     targetEnt.Restore( savefile );
  1104.     savefile->ReadJoint( targetJoint );
  1105.     savefile->ReadVec3( targetPos );
  1106.     targetEffect.Restore( savefile );
  1107.  
  1108.     savefile->ReadInt( zoomFov );
  1109.     savefile->ReadUserInterface( zoomGui, &spawnArgs );
  1110.     savefile->ReadFloat( zoomTime );
  1111. }
  1112.  
  1113. /*
  1114. =====================
  1115. rvVehicleWeapon::Restore
  1116. =====================
  1117. */
  1118. bool rvVehicleWeapon::Fire() {
  1119.     if ( muzzleFlashHandle != -1 && gameLocal.time >= muzzleFlashEnd ) {
  1120.         gameRenderWorld->FreeLightDef( muzzleFlashHandle );
  1121.         muzzleFlashHandle = -1;
  1122.     }
  1123.  
  1124. //    twhitaker: moved to rvVehicleWeapon::RunPostPhysics so that the HUD would update without having to fire the gun
  1125. //    if ( currentAmmo == 0 ) {
  1126. //        currentAmmo = ammoPerCharge;
  1127. //    }
  1128.  
  1129.     LaunchProjectiles();
  1130.  
  1131.     if ( currentAmmo == -1 ) {
  1132.         currentCharge.Init ( gameLocal.time, fireDelay, 0.0, 1.0f );
  1133.     } else {
  1134.         currentAmmo--;
  1135.         if ( currentAmmo <= 0 ) {
  1136.             currentAmmo = 0;
  1137.             if ( chargeTime ) {
  1138.                 parent->StartSoundShader( shaderReload, SND_CHANNEL_WEAPON, 0, false, NULL );
  1139.                 currentCharge.Init ( gameLocal.time, chargeTime, 0.0f, 1.0f );
  1140.             }            
  1141.         }
  1142.     }
  1143.     return true;
  1144. }
  1145.  
  1146. #ifdef _XENON
  1147. /*
  1148. =====================
  1149. rvVehicleWeapon::AutoAim
  1150. =====================
  1151. */
  1152. void rvVehicleWeapon::AutoAim( idPlayer* player, const idVec3& origin, idVec3& dir ) {
  1153.     
  1154.     if ( player ) {
  1155.     
  1156.         // If we have a enemy selected, handle aim correction
  1157.         if ( bestEnemy ) {
  1158.         
  1159.             idVec3 dif = bestEnemy->GetRenderEntity()->origin - origin;
  1160.             idVec3 muzzleDest = origin + (dir * dif.Length() );
  1161.             
  1162.             // Transform start and end into the enemy's body space
  1163.             idVec3 localStart = bestEnemy->GetRenderEntity()->axis / (origin - bestEnemy->GetRenderEntity()->origin);
  1164.             idVec3 localEnd = bestEnemy->GetRenderEntity()->axis / (muzzleDest - bestEnemy->GetRenderEntity()->origin);
  1165.             
  1166.             if ( bestEnemy->GetAnimator() ) {
  1167.                 int numJoints = bestEnemy->GetAnimator()->NumJoints();
  1168.                 
  1169.                 if ( numJoints ) {
  1170.                 
  1171.                     jointHandle_t nearJoint = bestEnemy->GetAnimator()->GetNearestJoint( localStart, localEnd, gameLocal.time, 0, numJoints );
  1172.             
  1173.                     // If we found a valid joint to snap to...
  1174.                     if ( nearJoint > 0 ) {
  1175.                         idMat3 dummy;
  1176.                         bestEnemy->GetJointWorldTransform( nearJoint, gameLocal.time, muzzleDest, dummy );
  1177.                         dir = muzzleDest - origin;
  1178.                         dir.Normalize();
  1179.                     } else {
  1180.                         dir = bestEnemy->GetRenderEntity()->origin - origin;
  1181.                         dir.Normalize();
  1182.                     }
  1183.                 }
  1184.             } else {
  1185.                 dir = bestEnemy->GetRenderEntity()->origin - origin;
  1186.                 dir.Normalize();
  1187.             }
  1188.             
  1189.         }
  1190.     }
  1191. }
  1192. #endif
  1193.  
  1194. /*
  1195. =====================
  1196. rvVehicleWeapon::LaunchHitScan
  1197. =====================
  1198. */
  1199. void rvVehicleWeapon::LaunchHitScan( const idVec3& origin, const idVec3& _dir, const idVec3& jointOrigin ) {
  1200.     idPlayer* player = 0;
  1201.     
  1202.     idVec3 dir = _dir;
  1203.     
  1204.     // Let the AI know about the new attack
  1205.     if ( !gameLocal.isMultiplayer ) {
  1206.         if ( position->GetDriver() && position->GetDriver()->IsType( idPlayer::GetClassType() ) ) {
  1207.             player = dynamic_cast<idPlayer*>(position->GetDriver());
  1208.             if ( player ) {
  1209.                 aiManager.ReactToPlayerAttack ( player, origin, dir );
  1210.             }
  1211.         }
  1212.     }
  1213.     
  1214. #ifdef _XENON
  1215.  
  1216.     AutoAim( player, origin, dir );
  1217.  
  1218. #endif
  1219.     
  1220.     gameLocal.HitScan ( *hitScanDef, origin, dir, jointOrigin, position->GetDriver(), false, 1.0f, parent );
  1221. }
  1222.  
  1223. /*
  1224. =====================
  1225. rvVehicleWeapon::LaunchProjectile
  1226. =====================
  1227. */
  1228. void rvVehicleWeapon::LaunchProjectile( const idVec3& origin, const idVec3& _dir, const idVec3& pushVelocity ) {
  1229.     
  1230.     idPlayer *player = 0;
  1231.     idEntity*        ent;
  1232.     idProjectile*    projectile;
  1233.  
  1234.     idVec3 dir = _dir;
  1235.     
  1236.     gameLocal.SpawnEntityDef ( *projectileDef, &ent );
  1237.     if ( !ent ) {
  1238.         return;
  1239.     }
  1240.                                         
  1241.     if ( !gameLocal.isMultiplayer ) {
  1242.         if ( position->GetDriver() && position->GetDriver()->IsType( idPlayer::GetClassType() ) ) {
  1243.             player = dynamic_cast<idPlayer*>(position->GetDriver());
  1244.             if ( player ) {
  1245.                 aiManager.ReactToPlayerAttack ( player, origin, dir );
  1246.             }
  1247.         }
  1248.     }
  1249.     
  1250. #ifdef _XENON
  1251.  
  1252.     AutoAim( player, origin, dir );
  1253.  
  1254. #endif
  1255.  
  1256.  
  1257.     projectile = ( idProjectile * )ent;
  1258.     projectile->Create( position->GetDriver(), origin, dir, parent );
  1259.     projectile->Launch( origin, dir, pushVelocity, 0.0f, 1.0f );
  1260.     
  1261.     if ( projectile->IsType ( idGuidedProjectile::GetClassType() ) ) {
  1262.         if ( spawnArgs.GetBool("guideTowardsDir") && (!targetEnt || targetJoint == INVALID_JOINT) ) {
  1263. #ifndef _XENON            
  1264.             static_cast<idGuidedProjectile*>(projectile)->GuideTo ( position->GetEyeOrigin(), position->GetEyeAxis()[0] );
  1265. #else
  1266.             if ( bestEnemy ) {
  1267.                 static_cast<idGuidedProjectile*>(projectile)->GuideTo ( origin, dir );
  1268.             } else {
  1269.                 static_cast<idGuidedProjectile*>(projectile)->GuideTo ( position->GetEyeOrigin(), position->GetEyeAxis()[0] );
  1270.             }
  1271. #endif
  1272.         
  1273.         } else if ( targetEnt ) {
  1274.             static_cast<idGuidedProjectile*>(projectile)->GuideTo ( targetEnt, targetJoint, targetPos );
  1275.         } else {
  1276.             static_cast<idGuidedProjectile*>(projectile)->GuideTo ( targetPos );
  1277.         }
  1278.     }
  1279. }
  1280.  
  1281. /*
  1282. =====================
  1283. rvVehicleWeapon::LaunchProjectiles
  1284. =====================
  1285. */
  1286. void rvVehicleWeapon::LaunchProjectiles() {
  1287.     idVec3            jointOrigin;
  1288.     idMat3            jointAxis;
  1289.     idVec3            origin;
  1290.     idMat3            axis;
  1291.  
  1292.     float            spreadRad = DEG2RAD( spread );
  1293.     idVec3            dir;
  1294.     float            ang = 0.0f;
  1295.     float            spin = 0.0f;
  1296.  
  1297.     parent->GetJointWorldTransform ( joints[jointIndex], gameLocal.time, jointOrigin, jointAxis );
  1298.     MuzzleFlashLight ( jointOrigin + muzzleFlashOffset * jointAxis, jointAxis );
  1299.  
  1300.     if( launchFromJoint ) {                
  1301.         origin = jointOrigin;
  1302.         axis = jointAxis;
  1303.     } else {
  1304.         origin = position->GetEyeOrigin();
  1305.         axis = position->GetEyeAxis();
  1306.     }
  1307.  
  1308.     if ( !lockScanning || !targetEnt || !position->GetDriver() || !position->GetDriver()->IsType( idPlayer::GetClassType() ) ) {
  1309.         //don't do this continuously, so have to do it here
  1310.         GetLockInfo( position->GetEyeOrigin(), position->GetEyeAxis() );
  1311.     }
  1312.  
  1313.     parent->StartSoundShader( shaderFire, SND_CHANNEL_WEAPON, 0, false, NULL );
  1314.  
  1315.     parent->PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_muzzleflash" ), joints[jointIndex], vec3_origin, mat3_identity );
  1316.  
  1317.     gameLocal.AlertAI( parent );
  1318.  
  1319.     for( int i = 0; i < count; i ++ ) {
  1320.         ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
  1321.         spin = DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
  1322.         dir = axis[0] + axis[ 2 ] * ( ang * idMath::Sin( spin ) ) - axis[ 1 ] * ( ang * idMath::Cos( spin ) );
  1323.         dir.Normalize();
  1324.  
  1325.         if ( g_debugWeapon.GetBool() ) {
  1326.             gameRenderWorld->DebugLine ( colorBlue, origin, origin + dir * 10000.0f, 10000 );        
  1327.         }
  1328.  
  1329.         if ( hitScanDef ) {
  1330.             LaunchHitScan( origin, dir, jointOrigin );
  1331.         } else {
  1332.             LaunchProjectile( origin, dir, parent->GetPhysics()->GetPushedLinearVelocity() );
  1333.         }
  1334.     }
  1335.  
  1336.     jointIndex = (jointIndex+1) % joints.Num();
  1337.  
  1338.     parent->GetPhysics()->ApplyImpulse ( 0, origin, force * axis );
  1339.  
  1340.     WeaponFeedback( &spawnArgs );
  1341. }
  1342.  
  1343. /*
  1344. =====================
  1345. rvVehicleWeapon::GetLockInfo
  1346. =====================
  1347. */
  1348. void rvVehicleWeapon::GetLockInfo( const idVec3& eyeOrigin, const idMat3& eyeAxis ) {
  1349.     if ( lastLockTime < gameLocal.GetTime() - 2000 
  1350.         || (targetEnt && (!targetEnt.IsValid() || targetEnt.GetEntity()->health <= 0)) ) {
  1351.         targetEnt = NULL;
  1352.         targetJoint = INVALID_JOINT;
  1353.         targetPos.Zero ();
  1354.     }
  1355.     
  1356.     if ( lockRange > 0.0f ) {
  1357.         idVec3    end;
  1358.         trace_t    tr;
  1359.         bool lockFound = false;
  1360.         idEntity* newTargetEnt = NULL;
  1361.         jointHandle_t newTargetJoint = INVALID_JOINT;
  1362.  
  1363.         end = eyeOrigin + eyeAxis[0] * lockRange;        
  1364. //        gameLocal.TracePoint( parent.GetEntity(), tr, eyeOrigin, end, MASK_SHOT_BOUNDINGBOX, NULL );
  1365.         gameLocal.TracePoint( parent.GetEntity(), tr, eyeOrigin, end, MASK_SHOT_RENDERMODEL, NULL );
  1366.  
  1367.         if ( tr.fraction < 1.0 ) {
  1368.             newTargetEnt = gameLocal.entities[ tr.c.entityNum ];
  1369.             lockFound = true;
  1370.     
  1371.             // Make sure the target is an actor and is alive
  1372.             if ( !(newTargetEnt->IsType ( idActor::GetClassType() ) || newTargetEnt->IsType ( idProjectile::GetClassType() )) || newTargetEnt->health <= 0 ) {
  1373.                 newTargetEnt = NULL;
  1374.                 lockFound = false;
  1375.             } else {
  1376.                 //see if there's a joint to lock onto
  1377.                 if ( newTargetEnt->spawnArgs.MatchPrefix ( "lockJoint" ) ) {
  1378.                     jointHandle_t testJointHandle;
  1379.                     idVec3 testJointOffset;
  1380.                     idStr lockJointName;
  1381.                     idVec3 jointOrg;
  1382.                     idMat3 jointAxis;
  1383.  
  1384.                     int lockJointNum = 1;
  1385.                     float bestDist = 100.0f;
  1386.                     float dist = 0.0f;
  1387.  
  1388.                     lockJointName = newTargetEnt->spawnArgs.GetString( va("lockJoint%d",lockJointNum), NULL );
  1389.                     while ( lockJointName.Length() ) {
  1390.                         testJointHandle = newTargetEnt->GetAnimator()->GetJointHandle( lockJointName );
  1391.                         if ( testJointHandle != INVALID_JOINT ) {
  1392.                             newTargetEnt->GetAnimator()->GetJointTransform( testJointHandle, gameLocal.GetTime(), jointOrg, jointAxis );
  1393.                             jointOrg = newTargetEnt->GetRenderEntity()->origin + (jointOrg*newTargetEnt->GetRenderEntity()->axis);
  1394.                             jointAxis = jointAxis * newTargetEnt->GetRenderEntity()->axis;
  1395.                             testJointOffset = newTargetEnt->spawnArgs.GetVector( va("lockJointOffset%d",lockJointNum), "0 0 0" );
  1396.                             jointOrg += jointAxis*testJointOffset;
  1397.                             dist = (jointOrg - tr.endpos).Length();
  1398.                             if ( dist < bestDist ) {
  1399.                                 bestDist = dist;
  1400.                                 newTargetJoint = testJointHandle;
  1401.                                 targetPos = testJointOffset;
  1402.                             }
  1403.                         }
  1404.                         lockJointName = newTargetEnt->spawnArgs.GetString( va("lockJoint%d",++lockJointNum), NULL );
  1405.                     }
  1406.                 }
  1407.             }
  1408.             if ( spawnArgs.GetBool("guideTowardsDir") ) {
  1409.                 if ( newTargetJoint == INVALID_JOINT ) {
  1410.                     newTargetEnt = NULL;
  1411.                     lockFound = false;
  1412.                 }
  1413.             }
  1414.         } else if ( spawnArgs.GetBool( "guideTowardsDir" ) && !targetEnt ) {
  1415.             targetPos = tr.endpos;
  1416.         }
  1417.         if ( lockFound ) {
  1418.             targetEnt = newTargetEnt;
  1419.             targetJoint = newTargetJoint;
  1420.             lastLockTime = gameLocal.GetTime();
  1421.         }
  1422.  
  1423.         if ( g_debugVehicle.GetInteger() == 2 ) {
  1424.             gameRenderWorld->DebugArrow ( colorGreen, eyeOrigin, end, 3, 10000 );
  1425.             gameRenderWorld->DebugArrow ( colorWhite, eyeOrigin, tr.endpos, 4, 10000 );
  1426.         }                
  1427.     }
  1428. }
  1429.  
  1430. /*
  1431. =====================
  1432. rvVehicleWeapon::EjectBrass
  1433. =====================
  1434. */
  1435. void rvVehicleWeapon::EjectBrass ( void ) {
  1436.     if ( !brassDict || gameLocal.time > brassEjectNext || g_brassTime.GetFloat() <= 0.0f || gameLocal.isMultiplayer || brassEjectJoint == INVALID_JOINT || !brassDict->GetNumKeyVals() ) {
  1437.         return;
  1438.     }
  1439.  
  1440.     idMat3 axis;
  1441.     idVec3 origin;
  1442.     idVec3 linear_velocity;
  1443.     idVec3 angular_velocity;
  1444.     int       brassTime;
  1445.  
  1446.     if ( !parent->GetJointWorldTransform( brassEjectJoint, gameLocal.time, origin, axis ) ) {
  1447.         return;
  1448.     }
  1449.  
  1450.     brassEjectNext += brassEjectDelay;
  1451.  
  1452.     // Spawn the client side moveable for the brass
  1453.     idVec3 offset;
  1454.     idMat3 playerViewAxis;
  1455.     gameLocal.GetPlayerView( offset, playerViewAxis );
  1456.     rvClientMoveable* cent;
  1457.     cent = new rvClientMoveable;    
  1458.     cent->SetOrigin ( origin + axis * brassEjectOffset );
  1459.     cent->SetAxis ( playerViewAxis );    
  1460.     cent->Spawn ( brassDict, position->GetDriver() );
  1461.     
  1462.     // Depth hack the brass to make sure it clips in front of view weapon properly
  1463.     cent->GetRenderEntity()->weaponDepthHackInViewID = position->GetDriver()->entityNumber + 1;
  1464.     
  1465.     // Clear the depth hack soon after it clears the view
  1466.     cent->PostEventMS ( &CL_ClearDepthHack, 200 );
  1467.     
  1468.     // Fade the brass out so they dont accumulate
  1469.     brassTime =(int)SEC2MS(g_brassTime.GetFloat() / 6.0f);
  1470.     cent->PostEventMS ( &CL_FadeOut, brassTime, brassTime );
  1471.  
  1472.     // Generate a good velocity for the brass
  1473.     idVec3 linearVelocity = brassDict->GetVector("linear_velocity").Random( brassDict->GetVector("linear_velocity_range"), gameLocal.random );
  1474.     cent->GetPhysics()->SetLinearVelocity( position->GetDriver()->GetPhysics()->GetLinearVelocity() + linearVelocity * cent->GetPhysics()->GetAxis() );
  1475.     idAngles angularVelocity = brassDict->GetAngles("angular_velocity").Random( brassDict->GetVector("angular_velocity_range"), gameLocal.random );
  1476.     cent->GetPhysics()->SetAngularVelocity( angularVelocity.ToAngularVelocity() * cent->GetPhysics()->GetAxis() );
  1477. }
  1478.  
  1479. /***********************************************************************
  1480.  
  1481.                             rvVehicleTurret
  1482.  
  1483. ***********************************************************************/
  1484.  
  1485. #define    TURRET_MOVESOUND_FADE    200
  1486.  
  1487. CLASS_DECLARATION( rvVehiclePart, rvVehicleTurret )
  1488. END_CLASS
  1489.  
  1490. /*
  1491. =====================
  1492. rvVehicleTurret::rvVehicleTurret
  1493. =====================
  1494. */
  1495. rvVehicleTurret::rvVehicleTurret ( void ) {
  1496.     moveTime  = 0;
  1497.     soundPart = -1;
  1498. }
  1499.  
  1500. /*
  1501. =====================
  1502. rvVehicleTurret::Spawn
  1503. =====================
  1504. */
  1505. void rvVehicleTurret::Spawn ( void ) {
  1506.     angles[0][PITCH] = spawnArgs.GetFloat ( "minpitch", "0" );
  1507.     angles[1][PITCH] = spawnArgs.GetFloat ( "maxpitch", "0" );
  1508.     axisMap[PITCH]     = spawnArgs.GetInt ( "pitch", "-1" );
  1509.     invert[PITCH]     = spawnArgs.GetBool ( "pitchinvert", "0" ) ? -1.0f : 1.0f;
  1510.  
  1511.     angles[0][ROLL]  = spawnArgs.GetFloat ( "minroll", "0" );
  1512.     angles[1][ROLL]  = spawnArgs.GetFloat ( "maxroll", "0" );
  1513.     axisMap[ROLL]     = spawnArgs.GetInt ( "roll", "-1" );
  1514.     invert[ROLL]     = spawnArgs.GetBool ( "rollinvert", "0" ) ? -1.0f : 1.0f;
  1515.  
  1516.     angles[0][YAW]   = spawnArgs.GetFloat ( "minyaw", "0" );
  1517.     angles[1][YAW]   = spawnArgs.GetFloat ( "maxyaw", "0" );
  1518.     axisMap[YAW]     = spawnArgs.GetInt ( "yaw", "-1" );
  1519.     invert[YAW]         = spawnArgs.GetBool ( "yawinvert", "0" ) ? -1.0f : 1.0f;
  1520.     
  1521.     turnRate = spawnArgs.GetFloat ( "turnrate", "360" );
  1522.         
  1523.     currentAngles.Zero ( );
  1524.  
  1525.     //the parent is *not* stuck on spawn.
  1526.     parentStuck = false;
  1527.  
  1528.  
  1529.     // Find the vehicle part for the turret sound
  1530.     if ( *spawnArgs.GetString ( "snd_loop", "" ) ) {
  1531.         soundPart = position->AddPart ( rvVehicleSound::GetClassType(), spawnArgs );
  1532.         static_cast<rvVehicleSound*>(position->GetPart(soundPart))->SetAutoActivate ( false );
  1533.     }
  1534. }
  1535.  
  1536. /*
  1537. =====================
  1538. rvVehicleTurret::Activate
  1539. =====================
  1540. */
  1541. void rvVehicleTurret::Activate ( bool active ) {
  1542.     rvVehiclePart::Activate( active );
  1543.  
  1544.     if ( !IsActive() && soundPart >= 0 ) {
  1545.         static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Stop ( );
  1546.     }
  1547.  
  1548. }
  1549.  
  1550. /*
  1551. =====================
  1552. rvVehicleTurret::RunPostPhysics
  1553. =====================
  1554. */
  1555. void rvVehicleTurret::RunPostPhysics ( void ) {
  1556.     int            i;
  1557.     idAngles    inputAngles;
  1558.     idAngles    lastInputAngles;
  1559.     idMat3        mat[3];
  1560.     idAngles    oldAngles;
  1561.  
  1562.     if ( IsActive ( ) ) {    
  1563.         for ( i = 0; i < 3; i++ ) {
  1564.             inputAngles[i] = SHORT2ANGLE( position->mInputCmd.angles[i] );
  1565.             lastInputAngles[i] = SHORT2ANGLE( position->mOldInputCmd.angles[i] );
  1566.         }
  1567.     } else {
  1568.         inputAngles.Zero ( );
  1569.         lastInputAngles.Zero ( );
  1570.     }
  1571.  
  1572.     oldAngles = currentAngles;
  1573.  
  1574.     mat[PITCH].Identity();
  1575.     mat[YAW].Identity();
  1576.     mat[ROLL].Identity();
  1577.     for ( i = 0; i < 3; i ++ ) {
  1578.         if ( axisMap[i] != -1 ) {
  1579.             float diff = (invert[i] * idMath::AngleDelta ( inputAngles[i], lastInputAngles[i] ));
  1580.             
  1581.             diff = SignZero( diff ) * idMath::ClampFloat ( 0.0f, turnRate * MS2SEC(gameLocal.GetMSec()), idMath::Fabs ( diff ) );
  1582.             if ( angles[0][i] == angles[1][i] ) {
  1583.                 currentAngles[axisMap[i]] = idMath::AngleNormalize360( currentAngles[axisMap[i]] + diff );
  1584.             } else {
  1585.                 currentAngles[axisMap[i]] = idMath::ClampFloat ( angles[0][i], angles[1][i], currentAngles[axisMap[i]] + diff );
  1586.             }
  1587.             
  1588.             idAngles angles;
  1589.             angles.Zero();
  1590.             angles[axisMap[i]] = currentAngles[axisMap[i]];
  1591.             mat[axisMap[i]] = angles.ToMat3();
  1592.         }
  1593.     }    
  1594.  
  1595.     // Update the turret moving sound
  1596.     if ( soundPart >= 0 ) {
  1597.         float speed;
  1598.         speed = idMath::Fabs( idMath::AngleDelta( oldAngles[YAW], currentAngles[YAW] ) );
  1599.         speed = Max( speed, idMath::Fabs( idMath::AngleDelta( oldAngles[PITCH], currentAngles[PITCH] ) ) );
  1600.         speed = Max( speed, idMath::Fabs( idMath::AngleDelta( oldAngles[ROLL], currentAngles[ROLL] ) ) );
  1601.         
  1602.         if ( speed ) {
  1603.             if ( !moveTime ) {            
  1604.                 static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Play ( );
  1605.             }
  1606.             moveTime = gameLocal.time + TURRET_MOVESOUND_FADE;
  1607.         } else if ( moveTime && gameLocal.time > moveTime ) {
  1608.             static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Fade ( TURRET_MOVESOUND_FADE, 0.0f, 1.0f );
  1609.             moveTime = 0;
  1610.         }
  1611.         
  1612.         // Update the volume of the turret move sound using the current speed of the
  1613.         // turret as well as how long it has been since it has last moved.  
  1614.         if ( moveTime ) {
  1615.             float f;
  1616.             f = idMath::ClampFloat ( 0.0f, 1.0f, fabs(speed) / (turnRate * MS2SEC(gameLocal.GetMSec())) );
  1617.             static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Attenuate ( f, f );
  1618.         }        
  1619.     }
  1620.  
  1621.     // Rotate the turret with the mouse
  1622.     parent->GetAnimator()->SetJointAxis( joint, JOINTMOD_LOCAL, mat[YAW] * mat[PITCH] * mat[ROLL] );
  1623.  
  1624.     if ( g_vehicleMode.GetInteger() != 0 && joint != INVALID_JOINT && parent->GetAnimator()->GetJointHandle( spawnArgs.GetString( "alignment_joint" ) ) == joint ) {
  1625.         idMat3 turretAxis;
  1626.         idVec3 temp;
  1627.         parent->GetJointWorldTransform( joint, gameLocal.GetTime(), temp, turretAxis );
  1628.         const idMat3 & vehicleAxis    = parent->GetPhysics()->GetAxis();
  1629.         int alignmentAxis            = spawnArgs.GetInt( "alignment_axis" );
  1630.         float dotRight                = vehicleAxis[1] * turretAxis[alignmentAxis];
  1631.         float dotForward            = vehicleAxis[0] * turretAxis[alignmentAxis];
  1632.         float acosForward            = idMath::ACos( dotForward );
  1633.  
  1634.         idAngles oldAngles            = currentAngles;
  1635.         idMat3 oldAxis                = vehicleAxis;
  1636.  
  1637.         if ( idMath::Fabs(1.0f - dotForward) > VECTOR_EPSILON ) {
  1638.             float sinus = idMath::Cos( dotForward );
  1639.             float power = idMath::Pow( sinus, spawnArgs.GetFloat( "alignment_power", "5" ) );
  1640.             float deg = idMath::ClampFloat( 0.0f, spawnArgs.GetFloat( "alignment_delta", "2.4" ), RAD2DEG( acosForward ) ) * power;
  1641.             idAngles delta( 0, deg * SignZero(dotRight), 0 );
  1642.  
  1643.             //move the tank
  1644.             parent->GetPhysics()->SetAxis( (delta).ToMat3() * vehicleAxis );
  1645.             currentAngles -= delta;
  1646.             trace_t tr;
  1647.  
  1648.             //trace the tank against the world.
  1649.             idBounds traceBounds;
  1650.             idVec3 raiseVector( 0, 0, 80);
  1651.             idVec3 reduceVector( 0, 0, -16);
  1652.             idVec3 parentOrigin = parent->GetPhysics()->GetOrigin();
  1653.             idVec3 parentVecForward = parent->GetPhysics()->GetAxis()[0];
  1654.  
  1655.             traceBounds.Zero();
  1656.             traceBounds = parent->GetPhysics()->GetAbsBounds();
  1657.             traceBounds.ExpandSelf( reduceVector );
  1658.             
  1659.             //zero the tracebounds-- abs bounds is the right size but not the right location :)
  1660.             traceBounds.TranslateSelf( traceBounds.GetCenter() * -1 );
  1661.             
  1662.             //raise the bounds up so the hover tank can hover.
  1663.             traceBounds.TranslateSelf( raiseVector );
  1664.  
  1665.             gameLocal.TraceBounds( parent, tr, parentOrigin, parentOrigin, traceBounds, MASK_SOLID | CONTENTS_VEHICLECLIP ,parent);
  1666.  
  1667.             //draw the debug turning bounds if we need to. It will be redder if there's collision.
  1668.             if ( gameDebug.IsHudActive ( DBGHUD_VEHICLE, parent ) ) {
  1669.                 idVec4 vecColor(1.0f - ( tr.fraction) , 0.25f, (0.5f * tr.fraction), 1.0f);
  1670.                 gameRenderWorld->DebugBounds( vecColor, traceBounds, parentOrigin, 10, true );
  1671.             }
  1672.             
  1673.             //roll it back if it's colliding.
  1674.             if( tr.fraction < 1.0f) {
  1675.                 if ( gameDebug.IsHudActive ( DBGHUD_VEHICLE, parent ) ) {
  1676.                     gameLocal.Warning("Turret move caused vehicle block %d distance %f", tr.c.entityNum, tr.fraction );
  1677.                 }
  1678.                 parent->GetPhysics()->SetAxis( oldAxis );
  1679.                 currentAngles = oldAngles;
  1680.  
  1681.                 //apply a push to the parent based on the direction of the collision? 
  1682.                 parentStuck = true;
  1683.             }            
  1684.         }
  1685.  
  1686.     }
  1687. }
  1688. /*
  1689. =====================
  1690. rvVehicleTurret::RunPrePhysics
  1691. =====================
  1692. */
  1693. void rvVehicleTurret::RunPrePhysics ( void ) {
  1694.     
  1695.     //in case we're stuck, apply an impulse to the parent based on the direction the turret is facing. This will help us pull out in the right direction.
  1696.     if( parentStuck )    {
  1697.  
  1698.         idVec3 impulseForce( 0,0,0);
  1699.         idMat3 turretAxis;
  1700.         idVec3 temp;
  1701.         int alignmentAxis = spawnArgs.GetInt( "alignment_axis" );
  1702.         parent->GetJointWorldTransform( joint, gameLocal.GetTime(), temp, turretAxis );
  1703.         impulseForce += ( turretAxis[alignmentAxis] * 50 * MS2SEC(gameLocal.msec) * parent->GetPhysics()->GetMass());
  1704.         //impulseForce += ( position->mInputCmd.forwardmove / 127.0f ) * parent->GetPhysics()->GetAxis()[0] * 25 * MS2SEC(gameLocal.msec) * parent->GetPhysics()->GetMass ();
  1705.  
  1706.         // Apply the impulse to the parent entity
  1707.         
  1708.         parent->GetPhysics()->ApplyImpulse( 0, parent->GetPhysics()->GetOrigin(), impulseForce);
  1709.         
  1710.         parentStuck = false;
  1711.     }
  1712.  
  1713.  
  1714. }
  1715.  
  1716.  
  1717. /*
  1718. =====================
  1719. rvVehicleTurret::Save
  1720. =====================
  1721. */
  1722. void rvVehicleTurret::Save ( idSaveGame* savefile ) const {
  1723.     savefile->WriteBounds ( angles );
  1724.     savefile->WriteInt ( axisMap[0] );
  1725.     savefile->WriteInt ( axisMap[1] );
  1726.     savefile->WriteInt ( axisMap[2] );
  1727.  
  1728.     savefile->WriteFloat ( invert[0] );
  1729.     savefile->WriteFloat ( invert[1] );
  1730.     savefile->WriteFloat ( invert[2] );
  1731.  
  1732.     savefile->WriteAngles ( currentAngles );
  1733.  
  1734.     savefile->WriteInt ( moveTime );
  1735.     savefile->WriteFloat ( turnRate );
  1736.  
  1737.     savefile->WriteInt ( soundPart );
  1738. }
  1739.  
  1740. /*
  1741. =====================
  1742. rvVehicleTurret::Restore
  1743. =====================
  1744. */
  1745. void rvVehicleTurret::Restore ( idRestoreGame* savefile ) {
  1746.     savefile->ReadBounds ( angles );
  1747.     savefile->ReadInt ( axisMap[0] );
  1748.     savefile->ReadInt ( axisMap[1] );
  1749.     savefile->ReadInt ( axisMap[2] );
  1750.  
  1751.     savefile->ReadFloat ( invert[0] );
  1752.     savefile->ReadFloat ( invert[1] );
  1753.     savefile->ReadFloat ( invert[2] );
  1754.  
  1755.     savefile->ReadAngles ( currentAngles );
  1756.  
  1757.     savefile->ReadInt ( moveTime );
  1758.     savefile->ReadFloat ( turnRate );
  1759.  
  1760.     savefile->ReadInt ( soundPart );    
  1761. }
  1762.  
  1763. /***********************************************************************
  1764.  
  1765.                             rvVehicleHoverpad
  1766.  
  1767. ***********************************************************************/
  1768.  
  1769. CLASS_DECLARATION( rvVehiclePart, rvVehicleHoverpad )
  1770. END_CLASS
  1771.  
  1772. rvVehicleHoverpad::rvVehicleHoverpad ( void ) {
  1773.     clipModel  = NULL;
  1774.     effectDust = NULL;
  1775.     soundPart  = -1;
  1776.     effectDustMaterialType = NULL;
  1777. }
  1778.  
  1779. rvVehicleHoverpad::~rvVehicleHoverpad ( void ) {
  1780.     if ( effectDust ) {
  1781.         effectDust->Stop ( );
  1782.         effectDust = NULL;
  1783.     }        
  1784.  
  1785.     delete clipModel;
  1786. }
  1787.  
  1788. /*
  1789. ================
  1790. rvVehicleHoverpad::Spawn
  1791. ================
  1792. */
  1793. void rvVehicleHoverpad::Spawn ( void ) {
  1794.     float size;
  1795.         
  1796.     spawnArgs.GetFloat ( "size", "10", size );
  1797.     spawnArgs.GetFloat ( "height", "70", height );
  1798.     spawnArgs.GetFloat ( "dampen", "10", dampen );
  1799.     spawnArgs.GetFloat ( "forceRandom", "10", forceRandom );        
  1800.     spawnArgs.GetFloat ( "thrustForward", "0", thrustForward );
  1801.     spawnArgs.GetFloat ( "thrustLeft", "0", thrustLeft );
  1802.         
  1803.     maxRestAngle = idMath::Cos ( DEG2RAD(spawnArgs.GetFloat ( "maxRestAngle", "20" ) ) );
  1804.         
  1805.     fadeTime = SEC2MS(spawnArgs.GetFloat ( "fadetime", ".5" ));
  1806.                 
  1807.     effectDust = NULL;
  1808.     atRest        = true;
  1809.     
  1810.     delete clipModel;
  1811.     clipModel = new idClipModel ( idTraceModel ( idBounds ( spawnArgs.GetVector("mins"),spawnArgs.GetVector("maxs")) ) );
  1812.     
  1813.     force = spawnArgs.GetFloat ( "force", "1066" );
  1814.     forceUpTime   = SEC2MS ( spawnArgs.GetFloat ( "forceUpTime", "1" ) );
  1815.     forceDownTime = SEC2MS ( spawnArgs.GetFloat ( "forceDownTime", "1" ) );
  1816.     forceTable = static_cast<const idDeclTable *>( declManager->FindType( DECL_TABLE, spawnArgs.GetString ( "forceTable", "linear" ), false ) );
  1817.  
  1818.     currentForce.Init ( 0, 0, 0, 0 );                
  1819.     
  1820.     // Is a sound part specified?
  1821.     if ( *spawnArgs.GetString ( "snd_loop", "" ) ) {
  1822.         soundPart = position->AddPart ( rvVehicleSound::GetClassType(), spawnArgs );
  1823.         static_cast<rvVehicleSound*>(position->GetPart(soundPart))->SetAutoActivate ( false );
  1824.     }
  1825.     
  1826.     Activate ( false );
  1827. }
  1828.  
  1829. /*
  1830. ================
  1831. rvVehicleHoverpad::Activate
  1832. ================
  1833. */
  1834. void rvVehicleHoverpad::Activate ( bool active ) {
  1835.     rvVehiclePart::Activate ( active );
  1836.  
  1837.     if ( active ) {
  1838.         currentForce.Init ( gameLocal.time, forceUpTime, currentForce.GetCurrentValue( gameLocal.time ), force );
  1839.         atRest    = false;
  1840.     } else {
  1841.         currentForce.Init ( gameLocal.time, forceDownTime, currentForce.GetCurrentValue( gameLocal.time ), 0 );
  1842.     }    
  1843. }
  1844.  
  1845. /*
  1846. ================
  1847. rvVehicleHoverpad::RunPrePhysics
  1848.  
  1849. Called before RunPhysics.  Since impact velocities are altered by calls to ApplyImpulse
  1850. we need to cache them to ensure other hoverpads are not altering the data.  The start 
  1851. position of the hoverpad is also cached here for efficiency.
  1852. ================
  1853. */
  1854. void rvVehicleHoverpad::RunPrePhysics ( void ) {    
  1855.     impactInfo_t info;
  1856.  
  1857.     UpdateOrigin ( );
  1858.  
  1859.     // Cache velocity at start point
  1860.     parent->GetPhysics()->GetImpactInfo( 0, worldOrigin, &info );
  1861.     velocity = info.velocity;
  1862. }
  1863.  
  1864. /*
  1865. ================
  1866. rvVehicleHoverpad::RunPhysics
  1867. ================
  1868. */
  1869. void rvVehicleHoverpad::RunPhysics ( void ) {
  1870.     idVec3    end;
  1871.     idMat3    axis;
  1872.     trace_t    tr;
  1873.     idVec3  impulseForce;
  1874.     idVec3    dampingForce;
  1875.     float    hoverForce;
  1876.     float   curlength;        
  1877.  
  1878.     // Disable pad?
  1879.     if ( !IsActive() && !atRest ) {
  1880.         if ( parent->IsAtRest ( ) || currentForce.GetCurrentValue ( gameLocal.time ) <= 0.0f || parent->IsFlipped( ) || parent->IsStalled() ) {
  1881.             if ( soundPart >= 0 ) {
  1882.                 float fadeVolume;
  1883.                 fadeVolume = idMath::dBToScale ( spawnArgs.GetFloat ( "fadevolume", "0" ) );
  1884.                 static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Fade ( fadeTime, fadeVolume, spawnArgs.GetFloat ( "fadefreqshift", "0.1" ) );
  1885.             }
  1886.  
  1887.             if ( effectDust ) {
  1888.                 effectDust->Stop ( );
  1889.                 effectDust = NULL;            
  1890.             }
  1891.             
  1892.             atRest = true;
  1893.         }
  1894.     }
  1895.  
  1896.     // If the vehicle isnt activate then kill the effect on it and dont think
  1897.     if ( atRest ) {                        
  1898.         return;
  1899.     }
  1900.  
  1901.     // Prepare for trace            
  1902.     axis[0] = -parent->GetPhysics()->GetAxis()[2];
  1903.     axis[1] = parent->GetPhysics()->GetAxis()[0];    
  1904.     axis[2] = parent->GetPhysics()->GetAxis()[1];
  1905.  
  1906.     // Always point the hover jets towards gravity
  1907.     end  = worldOrigin + (parent->GetPhysics()->GetGravityNormal ( ) * height);    
  1908.  
  1909.     // Determine how far away the hoverpad is from the ground
  1910.     gameLocal.Translation ( parent.GetEntity(), tr, worldOrigin, end, clipModel, axis, MASK_SOLID|CONTENTS_VEHICLECLIP, parent );
  1911.     if ( tr.fraction >= 1.0f && tr.endpos == end ) {    
  1912.         UpdateDustEffect ( worldOrigin, axis, 0.0f, NULL );
  1913.         return;
  1914.     }
  1915.         
  1916.     // Determine spring properties from trace
  1917.     tr.c.point = worldOrigin + (end-worldOrigin) * (tr.fraction + 0.001f);
  1918.     curlength  = height - (worldOrigin - tr.c.point).Length ( );        
  1919.  
  1920.     // Dampening
  1921.     dampingForce = tr.c.point - worldOrigin;
  1922.     dampingForce = ( dampen * ( (velocity * dampingForce) / (dampingForce * dampingForce) ) ) * dampingForce;        
  1923.  
  1924.     // Random swap forces
  1925.     float rnd = 0.0f;
  1926.     if ( !position->mInputCmd.forwardmove && !position->mInputCmd.rightmove ) {
  1927.         float angle = DEG2RAD( ( (float)( gameLocal.time % 5000 ) / 5000.0f ) * 360.f );
  1928.         if ( fl.left ) {
  1929.             angle += 90;
  1930.         }
  1931.         if ( fl.front ) {
  1932.             angle += 180;
  1933.         }
  1934.         rnd = forceRandom * idMath::Sin( angle );
  1935.     }
  1936.  
  1937.     // Determine how much force should be applied at the center of the hover spring        
  1938.     hoverForce = (currentForce.GetCurrentValue(gameLocal.time) + rnd) * parent->GetPhysics()->GetMass () * MS2SEC(gameLocal.GetMSec());
  1939.  
  1940.     // If the slope under the hoverpad is < 30 degress then use the gravity vector to allow the hovertank to
  1941.     // stop on slopes, otherwise use the vehicles axis which will cause it to slide down steep slopes 
  1942.     float f = (-parent->GetPhysics()->GetGravityNormal ( ) * tr.c.normal);
  1943.     if ( f < maxRestAngle || (thrustForward && position->mInputCmd.forwardmove != 0 ) ) {
  1944.         impulseForce = (forceTable->TableLookup ( curlength / height ) * hoverForce) * -axis[0] - dampingForce;    
  1945.     } else {
  1946.         impulseForce = (forceTable->TableLookup ( curlength / height ) * hoverForce) * -parent->GetPhysics()->GetGravityNormal ( ) - dampingForce;    
  1947.     }
  1948.  
  1949.     // No thrust if movement is disabled
  1950.     if ( parent->IsMovementEnabled ( ) ) {
  1951.         idMat3 axis;
  1952.         idVec3 offset;
  1953.         jointHandle_t axisJoint = joint;
  1954.         if ( axisJoint == INVALID_JOINT ) {
  1955.             axisJoint = parent->GetAnimator()->GetJointHandle( "gun_pivot" );
  1956.         }
  1957.         parent->GetJointWorldTransform( axisJoint, gameLocal.time, offset, axis );
  1958.  
  1959.         // Add forward/backward thrust
  1960.         if ( thrustForward ) {
  1961.             if ( g_vehicleMode.GetInteger() == 2 ) {
  1962.                 impulseForce += ( position->mInputCmd.forwardmove / 127.0f ) * axis[0] * thrustForward * MS2SEC(gameLocal.msec) * parent->GetPhysics()->GetMass ();
  1963.             } else {
  1964.                 impulseForce += ( position->mInputCmd.forwardmove / 127.0f ) * parent->GetPhysics()->GetAxis()[0] * thrustForward * MS2SEC(gameLocal.msec) * parent->GetPhysics()->GetMass ();
  1965.             }
  1966.         }
  1967.  
  1968.         // Add right/left thrust.  The front pads will add force to the right if the right button is pressed and the rear will do the opposite.  If moving backward the directions are flipped again
  1969.         if ( thrustLeft ) {
  1970.             if ( !parent->IsStrafing() && g_vehicleMode.GetInteger() != 0 ) {
  1971.                 impulseForce += ( position->mInputCmd.rightmove / 127.0f ) * -axis[2] * thrustLeft * MS2SEC(gameLocal.msec) * parent->GetPhysics()->GetMass ();
  1972.             } else {
  1973.                 impulseForce += ( position->mInputCmd.rightmove / 127.0f ) * (position->mInputCmd.forwardmove<0?-1:1) * parent->GetPhysics()->GetAxis()[1] * thrustLeft * MS2SEC(gameLocal.msec) * parent->GetPhysics()->GetMass () * (fl.front ? 1 : -1);
  1974.             }
  1975.         }
  1976.     }
  1977.     
  1978.     // Apply the impulse to the parent entity
  1979.     parent->GetPhysics()->ApplyImpulse( 0, worldOrigin, impulseForce);
  1980.         
  1981.     // Update the position and intensity of the dust effect
  1982.     UpdateDustEffect ( tr.c.point, tr.c.normal.ToMat3( ), (curlength / height), tr.c.materialType );
  1983.  
  1984.     // Debug information
  1985.     if ( gameDebug.IsHudActive ( DBGHUD_VEHICLE, parent ) ) {
  1986.         idVec3 offset;
  1987.         offset = worldOrigin - parent->GetPhysics()->GetOrigin();
  1988.         offset *= parent->GetPhysics()->GetAxis().Transpose();        
  1989.         gameDebug.AppendList ( "hover", va("%c%c %d\t%0.1f\t%0.1f\t%d\t", (offset[0]>0?'F':'B'),(offset[1]<0?'R':'L'), (int)impulseForce.Length(), curlength, dampingForce.Length(), (int)(velocity*axis[0] ) ) );        
  1990.     }        
  1991.     
  1992.     // Draw debug information
  1993.     if ( g_debugVehicle.GetInteger() == 2 ) {
  1994.         collisionModelManager->DrawModel ( clipModel->GetCollisionModel(), tr.c.point, axis, vec3_origin, mat3_identity, 0.0f ); 
  1995.         gameRenderWorld->DebugCircle ( colorGreen, tr.c.point, axis[0], 5, 10 );                
  1996.         gameRenderWorld->DebugLine ( colorMagenta, worldOrigin, worldOrigin + idVec3(0,0,-height));
  1997.         gameRenderWorld->DebugLine ( colorWhite, worldOrigin, end );
  1998.         gameRenderWorld->DebugCircle ( colorBlue, worldOrigin, axis[0], 5, 10 );
  1999.         gameRenderWorld->DebugCircle ( colorBlue, end, axis[0], 5, 10 );
  2000.  
  2001.         if ( thrustForward && position->mInputCmd.forwardmove != 0 ) {
  2002.             gameRenderWorld->DebugArrow( colorCyan, worldOrigin, worldOrigin + parent->GetPhysics()->GetAxis()[1] * 50.0f * (fl.front ? 1 : -1) * ( position->mInputCmd.rightmove / 127.0f ), 10);
  2003.         }
  2004.  
  2005.         if ( thrustLeft && position->mInputCmd.rightmove != 0 ) {
  2006.             gameRenderWorld->DebugArrow( colorCyan, worldOrigin, worldOrigin + parent->GetPhysics()->GetAxis()[0] * 50.0f * ( position->mInputCmd.forwardmove / 127.0f ), 10);
  2007.         }
  2008.     }
  2009. }
  2010.  
  2011. /*
  2012. ================
  2013. rvVehicleHoverpad::UpdateDustEffect
  2014. ================
  2015. */
  2016. void rvVehicleHoverpad::UpdateDustEffect ( const idVec3& origin, const idMat3& axis, float attenuation, const rvDeclMatType* mtype ) {
  2017.     if ( !effectDust || (mtype && effectDustMaterialType != mtype) ) {    
  2018.         if ( effectDust ) {
  2019.             effectDust->Stop ( );
  2020.             effectDust = NULL;
  2021.         }        
  2022.  
  2023.         effectDust = gameLocal.PlayEffect ( gameLocal.GetEffect ( spawnArgs, "fx_dust", mtype ), origin, axis, true );
  2024.         effectDustMaterialType = mtype;
  2025.     }
  2026.  
  2027.     if ( effectDust ) {    
  2028.         effectDust->Attenuate ( attenuation );
  2029.         effectDust->SetOrigin ( origin );
  2030.         effectDust->SetAxis ( axis );
  2031.     }
  2032.  
  2033.     // If there is a sound playing update it as well    
  2034.     if ( soundPart >= 0 ) {
  2035.         if ( static_cast<rvVehicleSound*>(position->GetPart(soundPart))->IsPlaying ( ) ) {
  2036.             static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Attenuate ( attenuation, attenuation );
  2037.         } else {
  2038.             static_cast<rvVehicleSound*>(position->GetPart(soundPart))->Play ( );
  2039.         }
  2040.     }
  2041. }
  2042.  
  2043. /*
  2044. =====================
  2045. rvVehicleHoverpad::Save
  2046. =====================
  2047. */
  2048. void rvVehicleHoverpad::Save ( idSaveGame* savefile ) const {
  2049.     savefile->WriteFloat ( height );
  2050.     savefile->WriteFloat ( dampen );
  2051.     savefile->WriteBool ( atRest );
  2052.  
  2053.     savefile->WriteBounds ( clipModel->GetBounds ( ) );
  2054.  
  2055.     savefile->WriteInterpolate ( currentForce );
  2056.     savefile->WriteFloat ( force );
  2057.     savefile->WriteTable ( forceTable );
  2058.     savefile->WriteFloat ( forceRandom );
  2059.     
  2060.     savefile->WriteFloat ( fadeTime );
  2061.     savefile->WriteVec3 ( velocity );
  2062.  
  2063.     savefile->WriteFloat ( thrustForward );
  2064.     savefile->WriteFloat ( thrustLeft );
  2065.     
  2066.     savefile->WriteFloat ( maxRestAngle );
  2067.  
  2068.     savefile->WriteInt ( soundPart );
  2069.  
  2070.     savefile->WriteInt ( forceUpTime );
  2071.     savefile->WriteInt ( forceDownTime );
  2072.  
  2073.     effectDust.Save ( savefile );
  2074.     savefile->WriteMaterialType ( effectDustMaterialType );
  2075. }
  2076.  
  2077. /*
  2078. =====================
  2079. rvVehicleHoverpad::Restore
  2080. =====================
  2081. */
  2082. void rvVehicleHoverpad::Restore ( idRestoreGame* savefile ) {
  2083.     idBounds bounds;
  2084.  
  2085.     savefile->ReadFloat ( height );
  2086.     savefile->ReadFloat ( dampen );
  2087.     savefile->ReadBool ( atRest );
  2088.  
  2089.     delete clipModel;
  2090.     savefile->ReadBounds ( bounds );
  2091.     clipModel = new idClipModel ( idTraceModel ( bounds ) );
  2092.     
  2093.     savefile->ReadInterpolate ( currentForce );
  2094.     savefile->ReadFloat ( force );
  2095.     savefile->ReadTable ( forceTable );
  2096.     savefile->ReadFloat ( forceRandom );
  2097.  
  2098.     savefile->ReadFloat ( fadeTime );
  2099.     savefile->ReadVec3 ( velocity );
  2100.  
  2101.     savefile->ReadFloat ( thrustForward );
  2102.     savefile->ReadFloat ( thrustLeft );
  2103.     
  2104.     savefile->ReadFloat ( maxRestAngle );
  2105.  
  2106.     savefile->ReadInt ( soundPart );
  2107.     
  2108.     savefile->ReadInt ( forceUpTime );
  2109.     savefile->ReadInt ( forceDownTime );
  2110.  
  2111.     effectDust.Restore ( savefile );
  2112.     savefile->ReadMaterialType ( effectDustMaterialType );
  2113. }
  2114.  
  2115. /***********************************************************************
  2116.  
  2117.                             rvVehicleThruster
  2118.  
  2119. ***********************************************************************/
  2120.  
  2121. CLASS_DECLARATION( rvVehiclePart, rvVehicleThruster )
  2122. END_CLASS
  2123.  
  2124. rvVehicleThruster::rvVehicleThruster ( void ) {
  2125. }
  2126.  
  2127. rvVehicleThruster::~rvVehicleThruster ( void ) {
  2128. }
  2129.  
  2130. /*
  2131. =====================
  2132. rvVehicleThruster::Save
  2133. =====================
  2134. */
  2135. void rvVehicleThruster::Save ( idSaveGame* savefile ) const {
  2136.     savefile->WriteFloat ( force );
  2137.     savefile->WriteInt ( forceAxis );
  2138.     savefile->WriteInt ( key );
  2139. }
  2140.  
  2141. /*
  2142. =====================
  2143. rvVehicleThruster::Restore
  2144. =====================
  2145. */
  2146. void rvVehicleThruster::Restore ( idRestoreGame* savefile ) {
  2147.     savefile->ReadFloat ( force );
  2148.     savefile->ReadInt ( forceAxis );
  2149.     savefile->ReadInt ( (int&)key );
  2150. }
  2151.  
  2152. /*
  2153. ================
  2154. rvVehicleThruster::Spawn
  2155. ================
  2156. */
  2157. void rvVehicleThruster::Spawn ( void ) {
  2158.     idStr keyName;
  2159.  
  2160.     force        = spawnArgs.GetFloat ( "force", "0" );
  2161.     forceAxis    = spawnArgs.GetInt ( "forceAxis", "0" );
  2162.     
  2163.     // Determine which key controls the thruster
  2164.     spawnArgs.GetString ( "key", "", keyName );
  2165.     if ( !keyName.Icmp ( "forward" ) )     {
  2166.         key = KEY_FORWARD;
  2167.     } else if ( !keyName.Icmp ( "right" ) ) {
  2168.         key = KEY_RIGHT;
  2169.     } else if ( !keyName.Icmp ( "up" ) ) {
  2170.         key = KEY_UP;
  2171.     }            
  2172. }
  2173.  
  2174. /*
  2175. ================
  2176. rvVehicleThruster::RunPhysics
  2177. ================
  2178. */
  2179. void rvVehicleThruster::RunPhysics ( void ) {
  2180.     float mult;
  2181.     
  2182.     if ( !IsActive ( ) ) {
  2183.         return;
  2184.     }
  2185.     
  2186.     // Determine the force multiplier from the key being pressed
  2187.     mult = 0.0f;
  2188.     switch ( key )    {
  2189.         case KEY_FORWARD:
  2190.             mult = idMath::ClampFloat ( -1.0f, 1.0f, position->mInputCmd.forwardmove );
  2191.             break;
  2192.  
  2193.         case KEY_RIGHT:
  2194.             mult = idMath::ClampFloat ( -1.0f, 1.0f, position->mInputCmd.rightmove );
  2195.             break;
  2196.  
  2197.         case KEY_UP:
  2198.             mult = idMath::ClampFloat ( -1.0f, 1.0f, position->mInputCmd.upmove );
  2199.             break;
  2200.     }
  2201.     
  2202.     // No multiplier, no move
  2203.     if ( mult == 0.0f ) {
  2204.         return;
  2205.     }            
  2206.     
  2207.     UpdateOrigin ( );
  2208.     
  2209.     // Apply the force
  2210.     parent->GetPhysics()->ApplyImpulse ( 0, worldOrigin, worldAxis[forceAxis] * force * mult );
  2211.     
  2212.     // Debug Information
  2213.     if ( g_debugVehicle.GetInteger() == 2 ) {
  2214.         gameRenderWorld->DebugBounds ( colorCyan, idBounds(idVec3(-4,-4,-4), idVec3(4,4,4) ), worldOrigin );
  2215.         gameRenderWorld->DebugArrow ( colorCyan, worldOrigin, worldOrigin + worldAxis[forceAxis] * 100.0f * mult * (force < 0 ? -1 : 1), 10 );    
  2216.     }
  2217. }
  2218.  
  2219. /***********************************************************************
  2220.  
  2221.                         rvVehicleUserAnimated
  2222.  
  2223. ***********************************************************************/
  2224.  
  2225. CLASS_DECLARATION( rvVehiclePart, rvVehicleUserAnimated )
  2226.     EVENT( EV_PostSpawn, rvVehicleUserAnimated::Event_PostSpawn )
  2227. END_CLASS
  2228.  
  2229. rvVehicleUserAnimated::rvVehicleUserAnimated ( void ) {
  2230. }
  2231.  
  2232. /*
  2233. =====================
  2234. rvVehicleUserAnimated::Save
  2235. =====================
  2236. */
  2237. void rvVehicleUserAnimated::Save ( idSaveGame* savefile ) const {
  2238. }
  2239.  
  2240. /*
  2241. =====================
  2242. rvVehicleUserAnimated::Restore
  2243. =====================
  2244. */
  2245. void rvVehicleUserAnimated::Restore ( idRestoreGame* savefile ) {
  2246.     Event_PostSpawn();
  2247. }
  2248.  
  2249. /*
  2250. ================
  2251. rvVehicleUserAnimated::Spawn
  2252. ================
  2253. */
  2254. void rvVehicleUserAnimated::Spawn ( void ) {
  2255.     PostEventMS( &EV_PostSpawn, 0 );
  2256. }
  2257.  
  2258. /*
  2259. ================
  2260. rvVehicleUserAnimated::Event_PostSpawn
  2261. ================
  2262. */
  2263. void rvVehicleUserAnimated::Event_PostSpawn ( void ) {
  2264.     InitAnim( "forward",    anims[ VUAA_Forward    ] );
  2265.     InitAnim( "strafe",        anims[ VUAA_Strafe    ] );
  2266.     InitAnim( "crouch",        anims[ VUAA_Crouch    ] );
  2267.     InitAnim( "attack",        anims[ VUAA_Attack    ] );
  2268.  
  2269.     InitFunc( "forward",    funcs[ VUAF_Forward    ] );
  2270.     InitFunc( "strafe",        funcs[ VUAF_Strafe    ] );
  2271.     InitFunc( "crouch",        funcs[ VUAF_Crouch    ] );
  2272.     InitFunc( "attack",        funcs[ VUAF_Attack    ] );
  2273. }
  2274.  
  2275.  
  2276. /*
  2277. ================
  2278. rvVehicleUserAnimated::RunPrePhysics
  2279. ================
  2280. */
  2281. void rvVehicleUserAnimated::RunPrePhysics ( void ) {
  2282.     if ( !IsActive ( ) ) {
  2283.         return;
  2284.     }
  2285.  
  2286.     rvVehicle                *vehicle    = position->GetParent();
  2287.     const rvVehiclePosition    *position    = vehicle->GetPosition( 0 );
  2288.     const usercmd_t &        input        = position->mInputCmd;
  2289.  
  2290.     if ( position->IsOccupied() ) {
  2291.         LateralMove( input.forwardmove, VUAA_Forward,    VUAF_Forward );
  2292.         LateralMove( input.rightmove,    VUAA_Strafe,    VUAF_Strafe );
  2293.         LateralMove( input.upmove,        VUAA_Crouch,    VUAF_Crouch    );
  2294.  
  2295.         // cheat and use LateralMove for the attack
  2296.         LateralMove( input.buttons & BUTTON_ATTACK, VUAA_Attack, VUAF_Attack );
  2297.     }
  2298. }
  2299.  
  2300. /*
  2301. ================
  2302. rvVehicleUserAnimated::InitAnim
  2303. ================
  2304. */
  2305. void rvVehicleUserAnimated::InitAnim( const char * action, rvUserAnimatedAnim_t & anim ) {
  2306.     idStr prefix( action );
  2307.     rvVehicle * vehicle = position->GetParent();
  2308.  
  2309.     anim.index        = vehicle->GetAnimator()->GetAnim( spawnArgs.GetString( prefix + "_anim" ) );
  2310.     anim.rate        = spawnArgs.GetFloat( prefix + "_rate", "1" );
  2311.     anim.frame        = spawnArgs.GetInt( prefix + "_frame" ) * anim.rate;
  2312.     anim.channel    = spawnArgs.GetInt( prefix + "_channel" );
  2313.     anim.loop        = spawnArgs.GetBool( prefix + "_loop" );
  2314.     anim.length        = vehicle->GetAnimator()->NumFrames( anim.index );
  2315.  
  2316.     // NOTE: I may want to add the suffix "_play" in the InitAnim() function
  2317.     // this would specify that the animation should be played rather than stepped through
  2318.     // which could be useful especially for the attack command
  2319. }
  2320.  
  2321. /*
  2322. ================
  2323. rvVehicleUserAnimated::InitFunc
  2324. ================
  2325. */
  2326. void rvVehicleUserAnimated::InitFunc( const char * action, rvScriptFuncUtility & func ) {
  2327.     idStr temp = idStr( action ) + "_script";
  2328.  
  2329.     if ( spawnArgs.GetString( temp, NULL, temp ) ) {
  2330.         func.Init( temp );
  2331.     }
  2332. }
  2333.  
  2334. /*
  2335. ================
  2336. rvVehicleUserAnimated::SetFrame
  2337. ================
  2338. */
  2339. void rvVehicleUserAnimated::SetFrame( const rvUserAnimatedAnim_t & anim ) {
  2340.     float lerp                = anim.frame - int( anim.frame );
  2341.     frameBlend_t frameBlend    = { 0, anim.frame, anim.frame + 1.0f, 1.0f - lerp, lerp };
  2342.     position->GetParent()->GetAnimator()->SetFrame( anim.channel, anim.index, frameBlend );
  2343. }
  2344.  
  2345. /*
  2346. ================
  2347. rvVehicleUserAnimated::LateralMove
  2348. ================
  2349. */
  2350. void rvVehicleUserAnimated::LateralMove( signed char input, int anim, int func ) {
  2351.     if ( !input ) {
  2352.         return;
  2353.     }
  2354.  
  2355.     rvUserAnimatedAnim_t & theAnim = anims[ anim ];
  2356.  
  2357.     if ( theAnim.index ) {
  2358.  
  2359.         float sign        = ( ( input > 0 ) ? 1.0f : -1.0f );
  2360.         theAnim.frame    += theAnim.rate * sign;
  2361.  
  2362.         if ( theAnim.frame < 1 ) {
  2363.             theAnim.frame = ( ( theAnim.loop ) ? theAnim.length : 1 );
  2364.         } else if ( theAnim.frame > theAnim.length ) {
  2365.             theAnim.frame = ( ( theAnim.loop ) ? 1 : theAnim.length );
  2366.         }
  2367.  
  2368.         SetFrame( theAnim );
  2369.     }
  2370.  
  2371.     if ( funcs[ func ].Valid( ) )
  2372.         funcs[ func ].CallFunc( &spawnArgs );
  2373. }
  2374.