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

  1. #include "../../idlib/precompiled.h"
  2. #pragma hdrstop
  3.  
  4. #include "../Game_local.h"
  5. #include "../Weapon.h"
  6. #include "../client/ClientEffect.h"
  7. #include "../Projectile.h"
  8.  
  9. // Available drum speeds
  10. const int NAILGUN_DRUMSPEED_STOPPED        = 0;
  11. const int NAILGUN_DRUMSPEED_SLOW        = 1;
  12. const int NAILGUN_DRUMSPEED_FAST        = 2;
  13.  
  14. // Spinup and spindown times
  15. const int NAILGUN_SPINDOWN_TIME            = 1000;
  16. const int NAILGUN_SPINUP_TIME            = 1000;
  17.  
  18. // Nailgun shader parms
  19. const int NAILGUN_SPARM_PLAYLEADIN        = 7;
  20.  
  21. // Nailgun weapon modifications
  22. const int NAILGUN_MOD_ROF_AMMO            = BIT(0);    // power mod combines Rate of fire and ammoo
  23. const int NAILGUN_MOD_SEEK                = BIT(1);
  24. //const int NAILGUN_MOD_AMMO                = BIT(2);
  25.  
  26. const int NAILGUN_SPIN_SNDCHANNEL        = SND_CHANNEL_BODY2;
  27.  
  28. class rvWeaponNailgun : public rvWeapon {
  29. public:
  30.  
  31.     CLASS_PROTOTYPE( rvWeaponNailgun );
  32.  
  33.     rvWeaponNailgun ( void );
  34.     ~rvWeaponNailgun ( void );
  35.  
  36.     virtual void        Spawn                    ( void );
  37.     virtual void        Think                    ( void );
  38.     void                Save                ( idSaveGame *savefile ) const;
  39.     void                Restore                ( idRestoreGame *savefile );
  40.     void                PreSave                    ( void );
  41.     void                PostSave                ( void );
  42.  
  43. protected:
  44.  
  45.     idEntityPtr<idEntity>                guideEnt;
  46.     int                                    guideTime;
  47.     int                                    guideStartTime;
  48.     rvClientEntityPtr<rvClientEffect>    guideEffect;
  49.     bool                                guideLocked;
  50.     float                                guideRange;
  51.     int                                    guideHoldTime;
  52.     int                                    guideAquireTime;
  53.  
  54.     jointHandle_t                        jointDrumView;
  55.     jointHandle_t                        jointPinsView;
  56.     jointHandle_t                        jointSteamRightView;
  57.     jointHandle_t                        jointSteamLeftView;
  58.  
  59.     jointHandle_t                        jointGuideEnt;
  60.     
  61.     int                                    drumSpeed;
  62.     int                                    drumSpeedIdeal;
  63.     float                                drumMultiplier;
  64.  
  65.     virtual void        OnLaunchProjectile        ( idProjectile* proj );
  66.     
  67. private:
  68.  
  69.     void                UpdateGuideStatus        ( float range = 0.0f );
  70.     void                CancelGuide                ( void );    
  71.     bool                DrumSpin                ( int speed, int blendFrames );
  72.  
  73.     stateResult_t        State_Idle                ( const stateParms_t& parms );
  74.     stateResult_t        State_Fire                ( const stateParms_t& parms );
  75.     stateResult_t        State_Reload            ( const stateParms_t& parms );
  76.     stateResult_t        State_Raise                ( const stateParms_t& parms );
  77.     stateResult_t        State_Lower                ( const stateParms_t& parms );
  78.     
  79.     stateResult_t        State_DrumSpinDown        ( const stateParms_t& parms );
  80.     stateResult_t        State_DrumSpinUp        ( const stateParms_t& parms );
  81.  
  82.     stateResult_t        Frame_ClaspOpen            ( const stateParms_t& parms );
  83.     stateResult_t        Frame_ClaspClose        ( const stateParms_t& parms );    
  84.     
  85.     CLASS_STATES_PROTOTYPE ( rvWeaponNailgun );
  86. };
  87.  
  88. CLASS_DECLARATION( rvWeapon, rvWeaponNailgun )
  89. END_CLASS
  90.  
  91. /*
  92. ================
  93. rvWeaponNailgun::rvWeaponNailgun
  94. ================
  95. */
  96. rvWeaponNailgun::rvWeaponNailgun ( void ) {
  97. }
  98.  
  99. /*
  100. ================
  101. rvWeaponNailgun::~rvWeaponNailgun
  102. ================
  103. */
  104. rvWeaponNailgun::~rvWeaponNailgun ( void ) {
  105.     if ( guideEffect ) {
  106.         guideEffect->Stop();
  107.     }
  108.     if( viewModel ) {
  109.         viewModel->StopSound( NAILGUN_SPIN_SNDCHANNEL, false );
  110.     }
  111. }
  112.  
  113.  
  114. /*
  115. ================
  116. rvWeaponNailgun::Spawn
  117. ================
  118. */
  119. void rvWeaponNailgun::Spawn ( void ) {
  120.     spawnArgs.GetFloat ( "lockRange", "1000", guideRange );
  121.     guideHoldTime = SEC2MS ( spawnArgs.GetFloat ( "lockHoldTime", "10" ) );
  122.     guideAquireTime = SEC2MS ( spawnArgs.GetFloat ( "lockAquireTime", ".1" ) );
  123.     
  124.     jointDrumView        = viewAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_view_drum" ) );
  125.     jointPinsView        = viewAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_view_pins" ) );
  126.     jointSteamRightView = viewAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_view_steamRight" ) );
  127.     jointSteamLeftView  = viewAnimator->GetJointHandle ( spawnArgs.GetString ( "joint_view_steamLeft" ) );
  128.  
  129.     jointGuideEnt        = INVALID_JOINT;
  130.     
  131.     drumSpeed        = NAILGUN_DRUMSPEED_STOPPED;
  132.     drumSpeedIdeal    = drumSpeed;
  133.     drumMultiplier    = spawnArgs.GetFloat ( "drumSpeed" );
  134.     
  135.     ExecuteState ( "ClaspClose" );    
  136.     SetState ( "Raise", 0 );    
  137. }
  138.  
  139. /*
  140. ================
  141. rvWeaponNailgun::Save
  142. ================
  143. */
  144. void rvWeaponNailgun::Save ( idSaveGame *savefile ) const {
  145.     guideEnt.Save( savefile );
  146.     savefile->WriteInt( guideTime );
  147.     savefile->WriteInt( guideStartTime );
  148.     guideEffect.Save( savefile );
  149.     savefile->WriteBool ( guideLocked );
  150.     savefile->WriteFloat ( guideRange );
  151.     savefile->WriteInt ( guideHoldTime );
  152.     savefile->WriteInt ( guideAquireTime );
  153.  
  154.     savefile->WriteJoint ( jointDrumView );
  155.     savefile->WriteJoint ( jointPinsView );
  156.     savefile->WriteJoint ( jointSteamRightView );
  157.     savefile->WriteJoint ( jointSteamLeftView );
  158.     savefile->WriteJoint ( jointGuideEnt );
  159.     
  160.     savefile->WriteInt ( drumSpeed );
  161.     savefile->WriteInt ( drumSpeedIdeal );
  162.     savefile->WriteFloat ( drumMultiplier );
  163. }
  164.  
  165. /*
  166. ================
  167. rvWeaponNailgun::Restore
  168. ================
  169. */
  170. void rvWeaponNailgun::Restore ( idRestoreGame *savefile ) {
  171.     guideEnt.Restore( savefile );
  172.     savefile->ReadInt( guideTime );
  173.     savefile->ReadInt( guideStartTime );
  174.     guideEffect.Restore( savefile );
  175.     savefile->ReadBool ( guideLocked );
  176.     savefile->ReadFloat ( guideRange );
  177.     savefile->ReadInt ( guideHoldTime );
  178.     savefile->ReadInt ( guideAquireTime );
  179.  
  180.     savefile->ReadJoint ( jointDrumView );
  181.     savefile->ReadJoint ( jointPinsView );
  182.     savefile->ReadJoint ( jointSteamRightView );
  183.     savefile->ReadJoint ( jointSteamLeftView );
  184.     savefile->ReadJoint ( jointGuideEnt );
  185.  
  186.     
  187.     savefile->ReadInt ( drumSpeed );
  188.     savefile->ReadInt ( drumSpeedIdeal );
  189.     savefile->ReadFloat ( drumMultiplier );
  190. }
  191.  
  192. /*
  193. ================
  194. rvWeaponNailgun::PreSave
  195. ================
  196. */
  197. void rvWeaponNailgun::PreSave ( void ) {
  198.  
  199.     //disable sounds
  200.     StopSound( SND_CHANNEL_ANY, false);
  201.  
  202.     //remove the guide gui cursor if there is one.
  203.     if ( guideEffect ) {
  204.         guideEffect->Stop();
  205.         guideEffect->Event_Remove();
  206.         guideEffect = NULL;
  207.     }
  208. }
  209.  
  210. /*
  211. ================
  212. rvWeaponNailgun::PostSave
  213. ================
  214. */
  215. void rvWeaponNailgun::PostSave ( void ) {
  216.  
  217.     //restore sounds-- but which one?
  218.     if( drumSpeed == NAILGUN_DRUMSPEED_FAST )    {
  219.         viewModel->StartSound ( "snd_spinfast", NAILGUN_SPIN_SNDCHANNEL, 0, false, NULL );        
  220.     } else     if( drumSpeed == NAILGUN_DRUMSPEED_SLOW )    {
  221.         viewModel->StartSound ( "snd_spinslow", NAILGUN_SPIN_SNDCHANNEL, 0, false, NULL );        
  222.     }
  223.  
  224.     //the guide gui effect will restore itself naturally
  225. }
  226.  
  227. /*
  228. ================
  229. rvWeaponNailgun::CancelGuide
  230. ================
  231. */
  232. void rvWeaponNailgun::CancelGuide ( void ) {
  233.     if ( zoomGui && guideEnt ) {
  234.         zoomGui->HandleNamedEvent ( "lockStop" );
  235.     }
  236.  
  237.     guideEnt = NULL;
  238.     guideLocked = false;
  239.     jointGuideEnt = INVALID_JOINT;
  240.     UpdateGuideStatus ( );    
  241. }
  242.  
  243. /*
  244. ================
  245. rvWeaponNailgun::UpdateGuideStatus
  246. ================
  247. */
  248. void rvWeaponNailgun::UpdateGuideStatus ( float range ) {
  249.     // Update the zoom GUI variables
  250.     if ( zoomGui ) {
  251.         float lockStatus;
  252.         if ( guideEnt ) {
  253.             if ( guideLocked ) {
  254.                 lockStatus = 1.0f;
  255.             } else {
  256.                 lockStatus = Min((float)(gameLocal.time - guideStartTime)/(float)guideTime, 1.0f);
  257.             }
  258.         } else {
  259.             lockStatus = 0.0f;
  260.         }
  261.         
  262.         if ( owner == gameLocal.GetLocalPlayer( ) ) {
  263.             zoomGui->SetStateFloat ( "lockStatus", lockStatus );        
  264.             zoomGui->SetStateFloat ( "playerYaw", playerViewAxis.ToAngles().yaw );
  265.         }        
  266.  
  267.         if ( guideEnt ) {        
  268.             idVec3 diff;
  269.             diff = guideEnt->GetPhysics()->GetOrigin ( ) - playerViewOrigin;
  270.             diff.NormalizeFast ( );
  271.             zoomGui->SetStateFloat ( "lockYaw", idMath::AngleDelta ( diff.ToAngles ( ).yaw, playerViewAxis.ToAngles().yaw ) );
  272.             zoomGui->SetStateString ( "lockRange", va("%4.2f", range) );
  273.             zoomGui->SetStateString ( "lockTime", va("%02d", (int)MS2SEC(guideStartTime + guideTime - gameLocal.time) + 1) );
  274.         }
  275.     }
  276.     
  277.     // Update the guide effect
  278.     if ( guideEnt ) {
  279.         idVec3 eyePos = static_cast<idActor *>(guideEnt.GetEntity())->GetEyePosition();
  280.         if( jointGuideEnt == INVALID_JOINT )    {
  281.             eyePos += guideEnt->GetPhysics()->GetAbsBounds().GetCenter ( );
  282.         } else {
  283.             idMat3    jointAxis;
  284.             idVec3    jointLoc;
  285.             static_cast<idAnimatedEntity *>(guideEnt.GetEntity())->GetJointWorldTransform( jointGuideEnt, gameLocal.GetTime(),jointLoc,jointAxis );
  286.             eyePos += jointLoc;
  287.         }
  288.         eyePos *= 0.5f;
  289.         if ( guideEffect ) {            
  290.             guideEffect->SetOrigin ( eyePos );
  291.             guideEffect->SetAxis ( playerViewAxis.Transpose() );
  292.         } else {
  293.             guideEffect = gameLocal.PlayEffect ( 
  294.                                     gameLocal.GetEffect( spawnArgs, guideLocked ? "fx_guide" : "fx_guidestart" ), 
  295.                                     eyePos, playerViewAxis.Transpose(), true, vec3_origin, false );
  296.             if ( guideEffect ) {
  297.                 guideEffect->GetRenderEffect()->weaponDepthHackInViewID = owner->entityNumber + 1;
  298.                 guideEffect->GetRenderEffect()->allowSurfaceInViewID = owner->entityNumber + 1;
  299.             }
  300.         }
  301.     } else {
  302.         if ( guideEffect ) {
  303.             guideEffect->Stop();
  304.             guideEffect = NULL;
  305.         }
  306.     }
  307. }
  308.  
  309. /*
  310. ================
  311. rvWeaponNailgun::Think
  312. ================
  313. */
  314. void rvWeaponNailgun::Think ( void ) {
  315.     idEntity* ent;
  316.     trace_t      tr;
  317.  
  318.     // Let the real weapon think first
  319.     rvWeapon::Think ( );
  320.  
  321.     // If no guide range is set then we dont have the mod yet
  322.     if ( !guideRange ) {
  323.         return;
  324.     }
  325.  
  326.     // If the zoom button isnt down then dont update the lock
  327.     if ( !wsfl.zoom || IsReloading ( ) || IsHolstered ( ) ) {
  328.         CancelGuide ( );
  329.         return;
  330.     }
  331.  
  332.     // Dont update the target if the current target is still alive, unhidden and we have already locked
  333.     if ( guideEnt && guideEnt->health > 0 && !guideEnt->IsHidden() && guideLocked ) {
  334.         float    range;
  335.         range = (guideEnt->GetPhysics()->GetOrigin() - playerViewOrigin).LengthFast();
  336.         if ( range > guideRange || gameLocal.time > guideStartTime + guideTime ) {
  337.             CancelGuide ( );
  338.         } else {
  339.             UpdateGuideStatus ( range );
  340.             return;
  341.         }
  342.     }
  343.  
  344.     // Cast a ray out to the lock range
  345. // RAVEN BEGIN
  346. // ddynerman: multiple clip worlds
  347.     gameLocal.TracePoint(    owner, tr, 
  348.                             playerViewOrigin, 
  349.                             playerViewOrigin + playerViewAxis[0] * guideRange, 
  350.                             MASK_SHOT_BOUNDINGBOX, owner );
  351. // RAVEN END
  352.     
  353.     if ( tr.fraction >= 1.0f ) {
  354.         CancelGuide( );
  355.         return;
  356.     }
  357.     
  358.     ent = gameLocal.entities[tr.c.entityNum];
  359.     
  360.     //if we're using a target nailable...
  361.     if( ent->IsType ( rvTarget_Nailable::GetClassType()     )    )    {
  362.         const char* jointName = ent->spawnArgs.GetString("lock_joint");
  363.         ent = ent->targets[ 0 ].GetEntity();
  364.         if( !ent)    {
  365.             CancelGuide( );
  366.             return;
  367.         }
  368.  
  369.         if( ent->GetAnimator() )    {
  370.             jointGuideEnt = ent->GetAnimator()->GetJointHandle( jointName );
  371.         }
  372.     }
  373.  
  374.  
  375.     if ( !ent->IsType ( idActor::GetClassType() ) ) {
  376.         CancelGuide( );
  377.         return;
  378.     }
  379.     if ( gameLocal.GetLocalPlayer() && static_cast<idActor *>(ent)->team == gameLocal.GetLocalPlayer()->team ) {
  380.         CancelGuide( );
  381.         return;
  382.     }
  383.     
  384.     if ( guideEnt != ent ) {
  385.         guideStartTime = gameLocal.time;        
  386.         guideTime = guideAquireTime;
  387.         guideEnt = ent;
  388.         if ( zoomGui ) {
  389.             zoomGui->HandleNamedEvent ( "lockStart" );
  390.         }
  391.     } else if ( gameLocal.time > guideStartTime + guideTime ) {
  392.         // Stop the guide effect since it was just the guide_Start effect
  393.         if ( guideEffect ) {
  394.             guideEffect->Stop();
  395.             guideEffect = NULL;
  396.         }
  397.         guideLocked = true;
  398.         guideTime = guideHoldTime;
  399.         guideStartTime = gameLocal.time;
  400.         if ( zoomGui ) {
  401.             zoomGui->HandleNamedEvent ( "lockAquired" );
  402.         }
  403.     }
  404.     
  405.     UpdateGuideStatus ( (ent->GetPhysics()->GetOrigin() - playerViewOrigin).LengthFast() );
  406. }
  407.  
  408. /*
  409. ================
  410. rvWeaponNailgun::OnLaunchProjectile
  411. ================
  412. */
  413. void rvWeaponNailgun::OnLaunchProjectile ( idProjectile* proj ) {
  414.     rvWeapon::OnLaunchProjectile(proj);
  415.  
  416.     idGuidedProjectile* guided;
  417.     guided = dynamic_cast<idGuidedProjectile*>(proj);
  418.     if ( guided ) {
  419.         guided->GuideTo ( guideEnt, jointGuideEnt );
  420.     }
  421. }
  422.  
  423. /*
  424. ================
  425. rvWeaponNailgun::DrumSpin
  426.  
  427. Set the drum spin speed 
  428. ================
  429. */
  430. bool rvWeaponNailgun::DrumSpin ( int speed, int blendFrames ) {
  431.     // Dont bother if the drum is already spinning at the desired speed
  432.     if ( drumSpeedIdeal == speed ) {
  433.         return false;
  434.     }
  435.  
  436.     drumSpeedIdeal = speed;
  437.  
  438.     switch ( speed ) {
  439.         case NAILGUN_DRUMSPEED_STOPPED:
  440.             viewModel->StopSound ( NAILGUN_SPIN_SNDCHANNEL, false );
  441.             viewAnimator->SetJointAngularVelocity ( jointDrumView, idAngles(0,0,0), gameLocal.time, 250 );
  442.             viewAnimator->SetJointAngularVelocity ( jointPinsView, idAngles(0,0,0), gameLocal.time, 250 );
  443.         
  444.             // Spin the barrel down if we were spinning fast
  445.             if ( drumSpeed == NAILGUN_DRUMSPEED_FAST ) {
  446.                 PostState ( "DrumSpinDown", blendFrames );
  447.                 return true;
  448.             }
  449.             break;
  450.             
  451.         case NAILGUN_DRUMSPEED_SLOW:
  452.             viewAnimator->SetJointAngularVelocity ( jointDrumView, idAngles(0,0,45), gameLocal.time, NAILGUN_SPINDOWN_TIME );
  453.             viewAnimator->SetJointAngularVelocity ( jointPinsView, idAngles(0,0,-35), gameLocal.time, NAILGUN_SPINDOWN_TIME );        
  454.             viewModel->StopSound ( NAILGUN_SPIN_SNDCHANNEL, false );
  455.             viewModel->StartSound ( "snd_spinslow", NAILGUN_SPIN_SNDCHANNEL, 0, false, NULL );        
  456.  
  457.             // Spin the barrel down if we were spinning fast        
  458.             if ( drumSpeed == NAILGUN_DRUMSPEED_FAST ) {
  459.                 PostState ( "DrumSpinDown", blendFrames );
  460.                 return true;
  461.             }
  462.             break;
  463.         
  464.         case NAILGUN_DRUMSPEED_FAST:
  465.             // Start the barrel spinning faster
  466.             viewAnimator->SetJointAngularVelocity ( jointDrumView, idAngles(0,0,360.0f * drumMultiplier), gameLocal.time, NAILGUN_SPINUP_TIME );
  467.             viewAnimator->SetJointAngularVelocity ( jointPinsView, idAngles(0,0,-300.0f * drumMultiplier), gameLocal.time, NAILGUN_SPINUP_TIME );
  468.  
  469.             PostState ( "DrumSpinUp", blendFrames );
  470.             return true;
  471.     }
  472.     
  473.     drumSpeed = drumSpeedIdeal;
  474.     
  475.     return false;
  476. }
  477.  
  478. /*
  479. ===============================================================================
  480.  
  481.     States 
  482.  
  483. ===============================================================================
  484. */
  485.  
  486. CLASS_STATES_DECLARATION ( rvWeaponNailgun )
  487.     STATE ( "Raise",                        rvWeaponNailgun::State_Raise )
  488.     STATE ( "Lower",                        rvWeaponNailgun::State_Lower )
  489.     STATE ( "Idle",                            rvWeaponNailgun::State_Idle)
  490.     STATE ( "Fire",                            rvWeaponNailgun::State_Fire )
  491.     STATE ( "Reload",                        rvWeaponNailgun::State_Reload )
  492.     STATE ( "DrumSpinDown",                    rvWeaponNailgun::State_DrumSpinDown )
  493.     STATE ( "DrumSpinUp",                    rvWeaponNailgun::State_DrumSpinUp )
  494.     
  495.     STATE ( "ClaspOpen",                    rvWeaponNailgun::Frame_ClaspOpen )
  496.     STATE ( "ClaspClose",                    rvWeaponNailgun::Frame_ClaspClose )
  497. END_CLASS_STATES
  498.  
  499. /*
  500. ================
  501. rvWeaponNailgun::State_Raise
  502.  
  503. Raise the weapon
  504. ================
  505. */
  506. stateResult_t rvWeaponNailgun::State_Raise ( const stateParms_t& parms ) {
  507.     enum {
  508.         STAGE_INIT,
  509.         STAGE_WAIT,
  510.     };    
  511.     switch ( parms.stage ) {
  512.         // Start the weapon raising
  513.         case STAGE_INIT:
  514.             SetStatus ( WP_RISING );
  515.             PlayAnim( ANIMCHANNEL_LEGS, "raise", 0 );
  516.             DrumSpin ( NAILGUN_DRUMSPEED_SLOW, 0 );
  517.             return SRESULT_STAGE ( STAGE_WAIT );
  518.             
  519.         case STAGE_WAIT:
  520.             if ( AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
  521.                 SetState ( "Idle", 4 );
  522.                 return SRESULT_DONE;
  523.             }
  524.             if ( wsfl.lowerWeapon ) {
  525.                 SetState ( "Lower", 4 );
  526.                 return SRESULT_DONE;
  527.             }
  528.             return SRESULT_WAIT;
  529.     }
  530.     return SRESULT_ERROR;
  531. }
  532.  
  533. /*
  534. ================
  535. rvWeaponNailgun::State_Lower
  536.  
  537. Lower the weapon
  538. ================
  539. */
  540. stateResult_t rvWeaponNailgun::State_Lower ( const stateParms_t& parms ) {    
  541.     enum {
  542.         STAGE_INIT,
  543.         STAGE_WAIT,
  544.         STAGE_WAITRAISE
  545.     };    
  546.     switch ( parms.stage ) {
  547.         case STAGE_INIT:
  548.             // Stop any looping sounds
  549.             viewModel->StopSound ( NAILGUN_SPIN_SNDCHANNEL, false );
  550.             
  551.             SetStatus ( WP_LOWERING );
  552.             PlayAnim ( ANIMCHANNEL_LEGS, "putaway", parms.blendFrames );
  553.             return SRESULT_STAGE(STAGE_WAIT);
  554.             
  555.         case STAGE_WAIT:
  556.             if ( AnimDone ( ANIMCHANNEL_LEGS, 0 ) ) {
  557.                 SetStatus ( WP_HOLSTERED );
  558.                 return SRESULT_STAGE(STAGE_WAITRAISE);
  559.             }
  560.             return SRESULT_WAIT;
  561.         
  562.         case STAGE_WAITRAISE:
  563.             if ( wsfl.raiseWeapon ) {
  564.                 SetState ( "Raise", 0 );
  565.                 return SRESULT_DONE;
  566.             }
  567.             return SRESULT_WAIT;
  568.     }
  569.     return SRESULT_ERROR;
  570. }
  571.  
  572. /*
  573. ================
  574. rvWeaponNailgun::State_Idle
  575.  
  576. Manage the idle state of the weapon
  577. ================
  578. */
  579. stateResult_t rvWeaponNailgun::State_Idle( const stateParms_t& parms ) {
  580.     enum {
  581.         STAGE_INIT,
  582.         STAGE_WAIT,
  583.     };    
  584.     switch ( parms.stage ) {
  585.         case STAGE_INIT:
  586.             if ( !AmmoInClip ( ) ) {
  587.                 SetStatus ( WP_OUTOFAMMO );
  588.             } else {
  589.                 SetStatus ( WP_READY );
  590.             }            
  591.             // Do we need to spin the drum down?
  592.             if ( DrumSpin ( NAILGUN_DRUMSPEED_SLOW, parms.blendFrames ) ) {
  593.                 PostState ( "Idle", parms.blendFrames );
  594.                 return SRESULT_DONE;
  595.             }
  596.                 
  597.             PlayCycle( ANIMCHANNEL_LEGS, "idle", parms.blendFrames );
  598.             return SRESULT_STAGE ( STAGE_WAIT );
  599.  
  600.         case STAGE_WAIT:
  601.             if ( wsfl.lowerWeapon ) {
  602.                 SetState ( "Lower", 4 );
  603.                 return SRESULT_DONE;
  604.             }
  605.         
  606.             if ( !clipSize ) {
  607.                 if ( gameLocal.time > nextAttackTime && wsfl.attack && AmmoAvailable ( ) ) {
  608.                     SetState ( "Fire", 0 );
  609.                     return SRESULT_DONE;
  610.                 }
  611.             } else {
  612.                 if ( gameLocal.time > nextAttackTime && wsfl.attack && AmmoInClip ( ) ) {
  613.                     SetState ( "Fire", 0 );
  614.                     return SRESULT_DONE;
  615.                 }  
  616.                 if ( wsfl.attack && AutoReload() && !AmmoInClip ( ) && AmmoAvailable () ) {
  617.                     SetState ( "Reload", 4 );
  618.                     return SRESULT_DONE;            
  619.                 }
  620.                 if ( wsfl.netReload || (wsfl.reload && AmmoInClip() < ClipSize() && AmmoAvailable()>AmmoInClip()) ) {
  621.                     SetState ( "Reload", 4 );
  622.                     return SRESULT_DONE;            
  623.                 }                
  624.             }
  625.             return SRESULT_WAIT;
  626.     }
  627.     return SRESULT_ERROR;
  628. }
  629.  
  630. /*
  631. ================
  632. rvWeaponNailgun::State_Fire
  633.  
  634. Fire the weapon
  635. ================
  636. */
  637. stateResult_t rvWeaponNailgun::State_Fire( const stateParms_t& parms ) {
  638.     enum {
  639.         STAGE_INIT,
  640.         STAGE_FIRE,
  641.         STAGE_FIREWAIT,
  642.         STAGE_DONE,
  643.         STAGE_SPINEMPTY,        
  644.     };    
  645.     switch ( parms.stage ) {
  646.         case STAGE_INIT:
  647.             if ( !wsfl.attack ) {
  648.                 SetState ( "Idle", parms.blendFrames );                
  649.                 return SRESULT_DONE;
  650.             }
  651.             if ( DrumSpin ( NAILGUN_DRUMSPEED_FAST, 2 ) ) {
  652.                 PostState ( "Fire", 2 );
  653.                 return SRESULT_DONE;
  654.             }
  655.             nextAttackTime = gameLocal.time;
  656.             
  657.             return SRESULT_STAGE ( STAGE_FIRE );
  658.             
  659.         case STAGE_FIRE:
  660.             if ( !wsfl.attack || wsfl.reload || wsfl.lowerWeapon || AmmoInClip ( ) <= 0 ) {
  661.                 return SRESULT_STAGE ( STAGE_DONE );
  662.             }
  663.             if ( mods & NAILGUN_MOD_ROF_AMMO ) {
  664.                 PlayCycle ( ANIMCHANNEL_LEGS, "fire_fast", 4 );
  665.             } else {
  666.                 PlayCycle ( ANIMCHANNEL_LEGS, "fire_slow", 4 );
  667.             }
  668.  
  669.             if ( wsfl.zoom ) {                
  670.                 Attack ( true, 1, spread, 0.0f, 1.0f );
  671.                 nextAttackTime = gameLocal.time + (altFireRate * owner->PowerUpModifier ( PMOD_FIRERATE ));
  672.             } else {
  673.                 Attack ( false, 1, spread, 0.0f, 1.0f );
  674.                 nextAttackTime = gameLocal.time + (fireRate * owner->PowerUpModifier ( PMOD_FIRERATE ));
  675.             }
  676.             
  677.             // Play the exhaust effects
  678.             viewModel->PlayEffect ( "fx_exhaust", jointSteamRightView, false );
  679.             viewModel->PlayEffect ( "fx_exhaust", jointSteamLeftView, false );
  680.  
  681.             viewModel->StartSound ( "snd_fire", SND_CHANNEL_WEAPON,    0, false, NULL );
  682.             viewModel->StartSound ( "snd_fireStereo", SND_CHANNEL_ITEM, 0, false, NULL ); 
  683.                     
  684.             return SRESULT_STAGE ( STAGE_FIREWAIT );
  685.  
  686.         case STAGE_FIREWAIT:
  687.             if ( !wsfl.attack || wsfl.reload || wsfl.lowerWeapon || AmmoInClip ( ) <= 0 ) {
  688.                 return SRESULT_STAGE ( STAGE_DONE );
  689.             }
  690.             if ( gameLocal.time >= nextAttackTime ) {
  691.                 return SRESULT_STAGE ( STAGE_FIRE );
  692.             }
  693.             return SRESULT_WAIT;
  694.             
  695.         case STAGE_DONE:
  696.             if ( clipSize && wsfl.attack && !wsfl.lowerWeapon && !wsfl.reload ) {
  697.                 PlayCycle ( ANIMCHANNEL_LEGS, "spinempty", 4 );
  698.                 return SRESULT_STAGE ( STAGE_SPINEMPTY );
  699.             }
  700.             DrumSpin ( NAILGUN_DRUMSPEED_SLOW, 4 );
  701.             if ( !wsfl.attack && !AmmoInClip() && AmmoAvailable() && AutoReload ( ) && !wsfl.lowerWeapon ) {
  702.                 PostState ( "Reload", 4 );
  703.             } else {
  704.                 PostState ( "Idle", 4 );
  705.             }
  706.             return SRESULT_DONE;
  707.             
  708.         case STAGE_SPINEMPTY:
  709.             if ( !wsfl.attack || wsfl.reload || wsfl.lowerWeapon ) {
  710.                 return SRESULT_STAGE ( STAGE_DONE );
  711.             }
  712.             return SRESULT_WAIT;
  713.     }
  714.     return SRESULT_ERROR;
  715. }
  716.  
  717. /*
  718. ================
  719. rvWeaponNailgun::State_Reload
  720. ================
  721. */
  722. stateResult_t rvWeaponNailgun::State_Reload ( const stateParms_t& parms ) {
  723.     enum {
  724.         STAGE_INIT,
  725.         STAGE_RELOAD,
  726.         STAGE_RELOADWAIT,
  727.         STAGE_RELOADLEFT,
  728.         STAGE_RELOADLEFTWAIT,
  729.         STAGE_RELOADRIGHT,
  730.         STAGE_RELOADRIGHTWAIT,
  731.         STAGE_RELOADDONE,
  732.         STAGE_RELOADDONEWAIT,        
  733.     };    
  734.     switch ( parms.stage ) {
  735.         case STAGE_INIT:
  736.             if ( DrumSpin ( NAILGUN_DRUMSPEED_STOPPED, parms.blendFrames ) ) {
  737.                 PostState ( "reload", parms.blendFrames );
  738.                 return SRESULT_DONE;
  739.             }
  740.  
  741.             SetStatus ( WP_RELOAD );
  742.  
  743.             if ( wsfl.netReload ) {
  744.                 wsfl.netReload = false;
  745.             } else {
  746.                 NetReload ( );
  747.             }
  748.  
  749.             if ( mods & NAILGUN_MOD_ROF_AMMO ) {
  750.                 return SRESULT_STAGE( STAGE_RELOADLEFT );
  751.             }                        
  752.             return SRESULT_STAGE( STAGE_RELOAD );
  753.             
  754.         case STAGE_RELOAD:
  755.             PlayAnim( ANIMCHANNEL_LEGS, "reload", parms.blendFrames );
  756.             return SRESULT_STAGE( STAGE_RELOADWAIT );
  757.             
  758.         case STAGE_RELOADWAIT:
  759.             if ( AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
  760.                 AddToClip( ClipSize ( ) );
  761.                 SetState ( "Idle", 4 );
  762.                 return SRESULT_DONE;
  763.             }
  764.             if ( wsfl.lowerWeapon ) {
  765.                 SetState ( "Lower", 4 );
  766.                 return SRESULT_DONE;
  767.             }            
  768.             return SRESULT_WAIT;
  769.         
  770.         case STAGE_RELOADLEFT:
  771.             PlayAnim ( ANIMCHANNEL_LEGS, "reload_clip1hold", parms.blendFrames );
  772.             return SRESULT_STAGE ( STAGE_RELOADLEFTWAIT );
  773.             
  774.         case STAGE_RELOADLEFTWAIT:
  775.             if ( AnimDone ( ANIMCHANNEL_LEGS, 0 ) ) {
  776.                 AddToClip ( ClipSize() / 2 );                
  777.                 return SRESULT_STAGE ( STAGE_RELOADRIGHT );
  778.             }
  779.             if ( wsfl.lowerWeapon ) {
  780.                 SetState ( "Lower", 4 );
  781.                 return SRESULT_DONE;
  782.             }            
  783.             return SRESULT_WAIT;
  784.  
  785.         case STAGE_RELOADRIGHT:
  786.             if ( wsfl.attack || AmmoInClip() >= ClipSize() || !AmmoAvailable() ) {
  787.                 return SRESULT_STAGE ( STAGE_RELOADDONE );
  788.             }
  789.             PlayAnim ( ANIMCHANNEL_LEGS, "reload_clip2", 0 );
  790.             return SRESULT_STAGE ( STAGE_RELOADRIGHTWAIT );
  791.             
  792.         case STAGE_RELOADRIGHTWAIT:
  793.             if ( AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
  794.                 AddToClip( ClipSize() / 2 );
  795.                 SetState ( "Idle", 4 );
  796.                 return SRESULT_DONE;
  797.             }
  798.             if ( wsfl.lowerWeapon ) {
  799.                 SetState ( "Lower", 4 );
  800.                 return SRESULT_DONE;
  801.             }            
  802.             return SRESULT_WAIT;
  803.         
  804.         case STAGE_RELOADDONE:
  805.             PlayAnim ( ANIMCHANNEL_LEGS, "reload_clip1finish", 0 );
  806.             return SRESULT_STAGE ( STAGE_RELOADDONEWAIT );
  807.         
  808.         case STAGE_RELOADDONEWAIT:
  809.             if ( AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
  810.                 SetState ( "Idle", 4 );
  811.                 return SRESULT_DONE;
  812.             }
  813.             if ( wsfl.lowerWeapon ) {
  814.                 SetState ( "Lower", 4 );
  815.                 return SRESULT_DONE;
  816.             }            
  817.             return SRESULT_WAIT;
  818.     }
  819.     return SRESULT_ERROR;
  820. }
  821.  
  822. /*
  823. ================
  824. rvWeaponNailgun::State_DrumSpinUp
  825.  
  826. Spin the drum from a slow speed to a fast speed
  827. ================
  828. */
  829. stateResult_t rvWeaponNailgun::State_DrumSpinUp ( const stateParms_t& parms ) {
  830.     enum {
  831.         STAGE_INIT,
  832.         STAGE_WAIT,
  833.     };    
  834.     switch ( parms.stage ) {
  835.         case STAGE_INIT:
  836.             viewModel->StartSound ( "snd_spinup", NAILGUN_SPIN_SNDCHANNEL, 0, false, NULL);
  837.             PlayAnim ( ANIMCHANNEL_LEGS, "spinup", 4 );
  838.             return SRESULT_STAGE(STAGE_WAIT);
  839.         
  840.         case STAGE_WAIT:
  841.             if ( AnimDone ( ANIMCHANNEL_LEGS, 0 ) ) {
  842.                 viewModel->StartSound ( "snd_spinfast", NAILGUN_SPIN_SNDCHANNEL, 0, false, NULL );
  843.                 drumSpeed = drumSpeedIdeal;
  844.                 return SRESULT_DONE;
  845.             }
  846.             if ( !wsfl.attack ) {
  847.                 int oldSpeed = drumSpeed;
  848.                 drumSpeed = drumSpeedIdeal;
  849.                 DrumSpin ( oldSpeed, 0 );
  850.                 return SRESULT_DONE;
  851.             }
  852.             return SRESULT_WAIT;
  853.     }
  854.     return SRESULT_ERROR;
  855. }
  856.  
  857. /*
  858. ================
  859. rvWeaponNailgun::State_DrumSpinDown
  860.  
  861. Spin the drum down from a faster speed
  862. ================
  863. */
  864. stateResult_t rvWeaponNailgun::State_DrumSpinDown ( const stateParms_t& parms ) {
  865.     enum {
  866.         STAGE_INIT,
  867.         STAGE_WAIT,
  868.     };    
  869.     switch ( parms.stage ) {
  870.         case STAGE_INIT:
  871.             viewModel->StartSound ( "snd_spindown", SND_CHANNEL_ANY, 0, false, 0 );
  872.  
  873.             // Spin down animation    
  874.             PlayAnim( ANIMCHANNEL_LEGS, "spindown", parms.blendFrames );
  875.             return SRESULT_STAGE ( STAGE_WAIT );
  876.             
  877.         case STAGE_WAIT:
  878.             if ( AnimDone ( ANIMCHANNEL_LEGS, 4 ) ) {
  879.                 drumSpeed = drumSpeedIdeal;
  880.                 return SRESULT_DONE;
  881.             }
  882.             if ( wsfl.lowerWeapon ) {
  883.                 viewModel->StopSound ( NAILGUN_SPIN_SNDCHANNEL, false );
  884.                 SetState ( "Lower", 4 );
  885.                 return SRESULT_DONE;
  886.             }
  887.             if ( wsfl.attack && AmmoInClip ( ) ) {
  888.                 viewModel->StopSound ( NAILGUN_SPIN_SNDCHANNEL, false );
  889.                 SetState ( "Fire", 4 );
  890.                 return SRESULT_DONE;
  891.             }
  892.             if ( wsfl.netReload || (wsfl.reload && AmmoAvailable() > AmmoInClip() && AmmoInClip() < ClipSize()) ) {
  893.                 SetState ( "Reload", 4 );
  894.                 return SRESULT_DONE;
  895.             }
  896.             return SRESULT_WAIT;
  897.     }
  898.     return SRESULT_ERROR;
  899. }
  900.  
  901. /*
  902. ================
  903. rvWeaponNailgun::Frame_ClaspOpen
  904.  
  905. Open the clasp that holds in the clips
  906. ================
  907. */
  908. stateResult_t rvWeaponNailgun::Frame_ClaspOpen ( const stateParms_t& parms ) {
  909.     PlayAnim ( ANIMCHANNEL_TORSO, "clasp_open", 0 );
  910.     return SRESULT_OK;
  911. }
  912.  
  913. /*
  914. ================
  915. rvWeaponNailgun::Frame_ClaspOpen
  916.  
  917. Close the clasp that holds in the clips and make sure to use the
  918. correct positioning depending on whether you have one or two clips
  919. in the gun.
  920. ================
  921. */
  922. stateResult_t rvWeaponNailgun::Frame_ClaspClose ( const stateParms_t& parms ) {
  923.     if ( mods & NAILGUN_MOD_ROF_AMMO ) {
  924.         PlayAnim( ANIMCHANNEL_TORSO, "clasp_2clip", 0 );
  925.     } else {
  926.         PlayAnim( ANIMCHANNEL_TORSO, "clasp_1clip", 0 );
  927.     }
  928.     return SRESULT_OK;
  929. }
  930.