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

  1. // RAVEN BEGIN
  2. // bdube: note that this file is no longer merged with Doom3 updates
  3. //
  4. // MERGE_DATE 09/30/2004
  5.  
  6. #include "../idlib/precompiled.h"
  7. #pragma hdrstop
  8.  
  9. #include "Game_local.h"
  10. #include "ai/AI_Manager.h"
  11. #include "Projectile.h"
  12. #include "spawner.h"
  13.  
  14. /*
  15. ===============================================================================
  16.  
  17.     idProjectile
  18.  
  19. ===============================================================================
  20. */
  21.  
  22. static const float BOUNCE_SOUND_MIN_VELOCITY    = 200.0f;
  23. static const float BOUNCE_SOUND_MAX_VELOCITY    = 400.0f;
  24.  
  25. const idEventDef EV_Explode( "<explode>", NULL );
  26. const idEventDef EV_Fizzle( "<fizzle>", NULL );
  27. const idEventDef EV_RadiusDamage( "<radiusdmg>", "E" );
  28. const idEventDef EV_ResidualDamage ( "<residualdmg>", "E" );
  29.  
  30. CLASS_DECLARATION( idEntity, idProjectile )
  31.     EVENT( EV_Explode,            idProjectile::Event_Explode )
  32.     EVENT( EV_Fizzle,            idProjectile::Event_Fizzle )
  33.     EVENT( EV_Touch,            idProjectile::Event_Touch )
  34.     EVENT( EV_RadiusDamage,        idProjectile::Event_RadiusDamage )
  35.     EVENT( EV_ResidualDamage,    idProjectile::Event_ResidualDamage )
  36. END_CLASS
  37.  
  38. /*
  39. ================
  40. idProjectile::idProjectile
  41. ================
  42. */
  43. idProjectile::idProjectile( void ) {
  44.     methodOfDeath        = -1;
  45.     owner                = NULL;
  46.     memset( &projectileFlags, 0, sizeof( projectileFlags ) );
  47.     damagePower            = 1.0f;
  48.  
  49.     memset( &renderLight, 0, sizeof( renderLight ) );
  50.  
  51.     lightDefHandle        = -1;
  52.     lightOffset.Zero();
  53.     lightStartTime        = 0;
  54.     lightEndTime        = 0;
  55.     lightColor.Zero();
  56.  
  57.     visualAngles.Zero();
  58.     angularVelocity.Zero();
  59.     speed.Init( 0.0f, 0.0f, 0.0f, 0.0f );
  60.     updateVelocity        = false;
  61.  
  62.     rotation.Init( 0, 0.0f, mat3_identity.ToQuat(), mat3_identity.ToQuat() );
  63.  
  64.     flyEffect            = NULL;
  65.     flyEffectAttenuateSpeed = 0.0f;
  66.     bounceCount            = 0;
  67.     hitCount            = 0;
  68.     state                = SPAWNED;
  69.     
  70.     fl.networkSync        = true;
  71.  
  72.     prePredictTime        = 0;
  73.  
  74.     syncPhysics            = false;
  75.  
  76.     launchTime            = 0;
  77.     launchOrig            = vec3_origin;
  78.     launchDir            = vec3_origin;
  79.     launchSpeed            = 0.0f;
  80. }
  81.  
  82. /*
  83. ================
  84. idProjectile::Spawn
  85. ================
  86. */
  87. void idProjectile::Spawn( void ) {
  88.      physicsObj.SetSelf( this );
  89. // RAVEN BEGIN
  90. // mwhitlock: Dynamic memory consolidation
  91.     RV_PUSH_HEAP_MEM(this);
  92. // RAVEN END
  93.      physicsObj.SetClipModel( new idClipModel( GetPhysics()->GetClipModel() ), 1.0f );
  94. // RAVEN BEGIN
  95. // mwhitlock: Dynamic memory consolidation
  96.     RV_POP_HEAP();
  97. // RAVEN END
  98.      physicsObj.SetContents( 0 );
  99.      physicsObj.SetClipMask( 0 );
  100.      physicsObj.PutToRest();
  101.      SetPhysics( &physicsObj );
  102.     prePredictTime = spawnArgs.GetInt( "predictTime", "0" );
  103.     syncPhysics = spawnArgs.GetBool( "net_syncPhysics", "0" );
  104.  
  105.     if ( gameLocal.isClient ) {
  106.         Hide();
  107.     }
  108. }
  109.  
  110. /*
  111. ================
  112. idProjectile::Save
  113. ================
  114. */
  115. void idProjectile::Save( idSaveGame *savefile ) const {
  116.     
  117.     savefile->WriteInt( methodOfDeath );                // cnicholson: Added unsaved var
  118.     owner.Save( savefile );
  119.     savefile->Write( &projectileFlags, sizeof( projectileFlags ) );
  120.     savefile->WriteFloat( damagePower );
  121.  
  122.        savefile->WriteRenderLight( renderLight );
  123.  
  124.        savefile->WriteInt( ( int )lightDefHandle );
  125.     savefile->WriteVec3( lightOffset );
  126.      savefile->WriteInt( lightStartTime );
  127.      savefile->WriteInt( lightEndTime );
  128.      savefile->WriteVec3( lightColor );
  129.  
  130.     savefile->WriteStaticObject( physicsObj );
  131.  
  132.     savefile->WriteAngles( visualAngles );                // cnicholson: added unsaved var
  133.     savefile->WriteAngles( angularVelocity );            // cnicholson: moved var
  134.  
  135.     // Save speed
  136.     savefile->WriteBool ( updateVelocity );
  137.     savefile->WriteFloat( speed.GetStartTime() );
  138.     savefile->WriteFloat( speed.GetDuration() );
  139.     savefile->WriteFloat( speed.GetStartValue() );
  140.     savefile->WriteFloat( speed.GetEndValue() );    
  141.  
  142.     // rotation; this is a class, so it doesnt get saved here
  143.  
  144.     flyEffect.Save( savefile );                            // cnicholson: added unsaved var
  145.     savefile->WriteFloat( flyEffectAttenuateSpeed );    // cnicholson: added unsaved var
  146.     savefile->WriteInt ( bounceCount );
  147.     savefile->WriteInt ( hitCount );
  148.  
  149.      savefile->WriteInt( (int)state );
  150. }
  151.  
  152. /*
  153. ================
  154. idProjectile::Restore
  155. ================
  156. */
  157. void idProjectile::Restore( idRestoreGame *savefile ) {
  158.     float    fset;
  159.     idVec3    temp;
  160.  
  161.     savefile->ReadInt( methodOfDeath );                    // cnicholson: Added unrestored var
  162.     owner.Restore( savefile );
  163.     savefile->Read( &projectileFlags, sizeof( projectileFlags ) );
  164.     savefile->ReadFloat( damagePower );
  165.  
  166.     savefile->ReadRenderLight( renderLight );
  167.  
  168.     savefile->ReadInt( (int &)lightDefHandle );
  169.     if ( lightDefHandle != -1 ) {
  170.         //get the handle again as it's out of date after a restore!
  171.         lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
  172.     }
  173.     savefile->ReadVec3( lightOffset );
  174.      savefile->ReadInt( lightStartTime );
  175.      savefile->ReadInt( lightEndTime );
  176.      savefile->ReadVec3( lightColor );
  177.  
  178.     // Restore the physics
  179.     savefile->ReadStaticObject( physicsObj );
  180.     RestorePhysics ( &physicsObj );
  181.  
  182.     savefile->ReadAngles( visualAngles );                // cnicholson: added unrestored var
  183.     savefile->ReadAngles( angularVelocity );            // cnicholson: moved var
  184.  
  185.     // Restore speed
  186.     savefile->ReadBool ( updateVelocity );
  187.     savefile->ReadFloat( fset );
  188.     speed.SetStartTime( fset );
  189.     savefile->ReadFloat( fset );
  190.     speed.SetDuration( fset );
  191.     savefile->ReadFloat( fset );
  192.     speed.SetStartValue( fset );
  193.     savefile->ReadFloat( fset );
  194.     speed.SetEndValue( fset );
  195.     
  196.     // rotation?
  197.  
  198.     flyEffect.Restore( savefile );                        // cnicholson: added unrestored var
  199.     savefile->ReadFloat( flyEffectAttenuateSpeed );        // cnicholson: added unsaved var
  200.     savefile->ReadInt ( bounceCount );
  201.     savefile->ReadInt ( hitCount );
  202.  
  203.     savefile->ReadInt( (int &)state );
  204. }
  205.  
  206. /*
  207. ================
  208. idProjectile::GetOwner
  209. ================
  210. */
  211. idEntity *idProjectile::GetOwner( void ) const {
  212.     return owner.GetEntity();
  213. }
  214.  
  215. /*
  216. ================
  217. idProjectile::SetSpeed
  218. ================
  219. */
  220. void idProjectile::SetSpeed( float s, int accelTime ) {
  221.     idVec3 vel;
  222.     vel = physicsObj.GetLinearVelocity();
  223.     vel.Normalize();
  224.     speed.Init( gameLocal.time, accelTime, speed.GetCurrentValue(gameLocal.time), s );
  225.  
  226.     if ( accelTime > 0 ) {
  227.         updateVelocity = true;
  228.     } else {
  229.         updateVelocity = false;
  230.     }
  231.  
  232.     // Update the velocity to match the direction we are facing and include any accelerations
  233.     physicsObj.SetLinearVelocity( speed.GetCurrentValue( gameLocal.time ) * vel );            
  234. }
  235.  
  236. /*
  237. ================
  238. idProjectile::Create
  239. ================
  240. */
  241. void idProjectile::Create( idEntity* _owner, const idVec3 &start, const idVec3 &dir, idEntity* ignore, idEntity* extraPassEntity ) {
  242.     idDict        args;
  243.     idStr        shaderName;
  244.     idVec3        light_color;
  245.     idVec3        light_offset;
  246.     idVec3        tmp;
  247.     idMat3        axis;
  248.     
  249.     Unbind();
  250.  
  251.     axis = dir.ToMat3();
  252.  
  253.      physicsObj.SetOrigin( start );
  254.      physicsObj.SetAxis( axis );
  255.  
  256.      physicsObj.GetClipModel()->SetOwner( ignore ? ignore : _owner );
  257.     physicsObj.extraPassEntity = extraPassEntity;
  258.  
  259.     owner = _owner;
  260.  
  261.     memset( &renderLight, 0, sizeof( renderLight ) );
  262.     shaderName = spawnArgs.GetString( "mtr_light_shader" );
  263.     if ( *shaderName ) {
  264.         renderLight.shader = declManager->FindMaterial( shaderName, false );
  265.         renderLight.pointLight = true;
  266.         renderLight.lightRadius[0] =
  267.         renderLight.lightRadius[1] =
  268.         renderLight.lightRadius[2] = spawnArgs.GetFloat( "light_radius" );
  269.         spawnArgs.GetVector( "light_color", "1 1 1", light_color );
  270.         renderLight.shaderParms[0] = light_color[0];
  271.         renderLight.shaderParms[1] = light_color[1];
  272.         renderLight.shaderParms[2] = light_color[2];
  273.         renderLight.shaderParms[3] = 1.0f;
  274. // RAVEN BEGIN
  275. // dluetscher: added detail levels to render lights
  276.         renderLight.detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  277. // dluetscher: set the projectile lights to be no shadows
  278.         renderLight.noShadows = cvarSystem->GetCVarInteger("com_machineSpec") < 3;
  279. // RAVEN END
  280.     }
  281.  
  282.     spawnArgs.GetVector( "light_offset", "0 0 0", lightOffset );
  283.  
  284.     lightStartTime = 0;
  285.     lightEndTime = 0;
  286.  
  287.     damagePower = 1.0f;
  288.  
  289.      UpdateVisuals();
  290.  
  291.     state = CREATED;
  292. }
  293.  
  294. /*
  295. =================
  296. idProjectile::~idProjectile
  297. =================
  298. */
  299. idProjectile::~idProjectile() {
  300.     StopSound( SND_CHANNEL_ANY, false );
  301.     FreeLightDef();
  302.     SetPhysics( NULL );
  303. }
  304.  
  305. /*
  306. =================
  307. idProjectile::FreeLightDef
  308. =================
  309. */
  310. void idProjectile::FreeLightDef( void ) {
  311.     if ( lightDefHandle != -1 ) {
  312.         gameRenderWorld->FreeLightDef( lightDefHandle );
  313.         lightDefHandle = -1;
  314.     }
  315. }
  316.  
  317. /*
  318. =================
  319. idProjectile::Launch
  320. =================
  321. */
  322. void idProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, const float dmgPower ) {
  323.     float            fuse;
  324.     idVec3            velocity;
  325.     float            linear_friction;
  326.     float            angular_friction;
  327.     float            contact_friction;
  328.     float            bounce;
  329.     float            mass;
  330.     float            gravity;
  331.     float            temp, temp2;
  332.     idVec3            gravVec;
  333.     idVec3            tmp;
  334.     int                contents;
  335.      int                clipMask;
  336.  
  337.      // allow characters to throw projectiles during cinematics, but not the player
  338.      if ( owner.GetEntity() && !owner.GetEntity()->IsType( idPlayer::GetClassType() ) ) {
  339.          cinematic = owner.GetEntity()->cinematic;
  340.      } else {
  341.          cinematic = false;
  342.      } 
  343.  
  344.     // Set the damage
  345.     damagePower = dmgPower;
  346.  
  347.     if ( !spawnArgs.GetFloat( "speed", "0", temp ) ) {
  348.         spawnArgs.GetVector( "velocity", "0 0 0", tmp );
  349.         temp = tmp[0];
  350.     } else {
  351.         float speedRandom;
  352.         if ( !spawnArgs.GetFloat( "speedRandom", "0", speedRandom ) ) {
  353.             temp += gameLocal.random.CRandomFloat()*speedRandom;
  354.         }
  355.     }
  356.     if ( !spawnArgs.GetFloat( "speed_end", "0", temp2 ) ) {
  357.         temp2 = temp;
  358.     }
  359.     float speedDuration;
  360.     speedDuration = SEC2MS( spawnArgs.GetFloat( "speed_duration", "0" ) );
  361.     speed.Init( gameLocal.time, speedDuration, temp, temp2 );
  362.     if ( speedDuration > 0 && temp != temp2 ) {
  363.         // only support constant velocity projectiles in MP
  364.         // ( we also assume that no MP projectiles use speedRandom )
  365.         assert( !gameLocal.isServer );
  366.         updateVelocity = true;
  367.     }
  368.     launchSpeed = temp;
  369.  
  370.     spawnArgs.GetAngles( "angular_velocity", "0 0 0", angularVelocity );
  371.  
  372.     linear_friction        = spawnArgs.GetFloat( "linear_friction" );
  373.     angular_friction    = spawnArgs.GetFloat( "angular_friction" );
  374.     contact_friction    = spawnArgs.GetFloat( "contact_friction" );
  375.     bounce                = spawnArgs.GetFloat( "bounce" );
  376.     mass                = spawnArgs.GetFloat( "mass" );
  377.     gravity                = spawnArgs.GetFloat( "gravity" );
  378.     fuse                = spawnArgs.GetFloat( "fuse" ) + ( spawnArgs.GetFloat( "fuse_random", "0" ) * gameLocal.random.RandomFloat() );
  379.     bounceCount            = spawnArgs.GetInt( "bounce_count", "-1" );
  380.     
  381.     projectileFlags.detonate_on_world    = spawnArgs.GetBool( "detonate_on_world" );
  382.     projectileFlags.detonate_on_actor    = spawnArgs.GetBool( "detonate_on_actor" );
  383.     projectileFlags.randomShaderSpin    = spawnArgs.GetBool( "random_shader_spin" );
  384.     projectileFlags.detonate_on_bounce  = spawnArgs.GetBool( "detonate_on_bounce" );
  385.  
  386.     lightStartTime = 0;
  387.     lightEndTime = 0;
  388.  
  389.     if ( health ) {
  390.         fl.takedamage = true;
  391.     }
  392.  
  393. // RAVEN BEGIN
  394. // abahr:
  395.     gravVec = ( idMath::Fabs(gravity) > VECTOR_EPSILON ) ? gameLocal.GetCurrentGravity(this) * gravity : vec3_zero;
  396. // RAVEN END
  397.  
  398.     Unbind();
  399.  
  400.     contents = 0;
  401.     clipMask = spawnArgs.GetBool( "clipmask_rendermodel", "1" ) ? MASK_SHOT_RENDERMODEL : MASK_SHOT_BOUNDINGBOX;
  402.     if ( spawnArgs.GetBool( "clipmask_largeshot", "1" ) ) {
  403.         clipMask |= CONTENTS_LARGESHOTCLIP;
  404.     }
  405.     if ( spawnArgs.GetBool( "clipmask_moveable", "0" ) ) {
  406.         clipMask |= CONTENTS_MOVEABLECLIP;
  407.     }
  408.     if ( spawnArgs.GetBool( "clipmask_monsterclip", "0" ) ) {
  409.         clipMask |= CONTENTS_MONSTERCLIP;
  410.     }
  411.     
  412.     if ( spawnArgs.GetBool( "detonate_on_trigger" ) ) {
  413.         contents |= CONTENTS_TRIGGER;
  414.     }
  415.  
  416.      if ( !spawnArgs.GetBool( "no_contents", "1" ) ) {
  417.          contents |= CONTENTS_PROJECTILE;
  418.      }
  419.  
  420.     clipMask |= CONTENTS_PROJECTILE;
  421.  
  422.     // don't do tracers on client, we don't know origin and direction
  423.     if ( spawnArgs.GetBool( "tracers" ) && gameLocal.random.RandomFloat() > 0.5f ) {
  424.         SetModel( spawnArgs.GetString( "model_tracer" ) );
  425.         projectileFlags.isTracer = true;
  426.     }
  427.  
  428.     physicsObj.SetMass( mass );
  429.     physicsObj.SetFriction( linear_friction, angular_friction, contact_friction );
  430.     physicsObj.SetBouncyness( bounce, !projectileFlags.detonate_on_bounce );
  431.     physicsObj.SetGravity( gravVec );
  432.     physicsObj.SetContents( contents );
  433.      physicsObj.SetClipMask( clipMask | CONTENTS_WATER );
  434.     physicsObj.SetLinearVelocity( dir * speed.GetCurrentValue(gameLocal.time) + pushVelocity );
  435.     physicsObj.SetOrigin( start );
  436.     physicsObj.SetAxis( dir.ToMat3() );
  437.  
  438.     if ( !gameLocal.isClient ) {
  439.         if ( fuse <= 0 ) {
  440.             // run physics for 1 second
  441.             RunPhysics();
  442.             PostEventMS( &EV_Remove, spawnArgs.GetInt( "remove_time", "1500" ) );
  443.         } else if ( spawnArgs.GetBool( "detonate_on_fuse" ) ) {
  444.             fuse -= timeSinceFire;
  445.             if ( fuse < 0.0f ) {
  446.                 fuse = 0.0f;
  447.             }
  448.             PostEventSec( &EV_Explode, fuse );
  449.         } else {
  450.             fuse -= timeSinceFire;
  451.             if ( fuse < 0.0f ) {
  452.                 fuse = 0.0f;
  453.             }
  454.             PostEventSec( &EV_Fizzle, fuse );
  455.         }
  456.     }
  457.  
  458.     idQuat q( dir.ToMat3().ToQuat() );
  459.     rotation.Init( gameLocal.GetTime(), 0.0f, q, q );
  460.  
  461.     if ( projectileFlags.isTracer ) {
  462.         StartSound( "snd_tracer", SND_CHANNEL_BODY, 0, false, NULL );
  463.     } else {
  464.         StartSound( "snd_fly", SND_CHANNEL_BODY, 0, false, NULL );
  465.     }
  466.  
  467.     // used for the plasma bolts but may have other uses as well
  468.     if ( projectileFlags.randomShaderSpin ) {
  469.         float f = gameLocal.random.RandomFloat();
  470.         f *= 0.5f;
  471.         renderEntity.shaderParms[SHADERPARM_DIVERSITY] = f;
  472.     }
  473.  
  474.      UpdateVisuals();
  475.  
  476.     // Make sure these come after update visuals so the origin and axis are correct
  477.     PlayEffect( "fx_launch", renderEntity.origin, renderEntity.axis );
  478.     
  479.     flyEffect = PlayEffect( "fx_fly", renderEntity.origin, renderEntity.axis, true );
  480.     flyEffectAttenuateSpeed = spawnArgs.GetFloat( "flyEffectAttenuateSpeed", "0" );
  481.  
  482.     state = LAUNCHED;
  483.  
  484.     hitCount = 0;
  485.  
  486.     predictTime = prePredictTime;
  487.  
  488.     if ( gameLocal.isServer ) {
  489.         // store launch information for networking
  490.         launchTime = gameLocal.time;
  491.         launchOrig = physicsObj.GetOrigin();
  492.         launchDir = dir;
  493.     }
  494.  
  495.     if ( g_perfTest_noProjectiles.GetBool() ) {
  496.         PostEventMS( &EV_Remove, 0 );
  497.     }
  498. }
  499.  
  500. /*
  501. ================
  502. idProjectile::Think
  503. ================
  504. */
  505. void idProjectile::Think( void ) {
  506.     // run physics
  507.     if ( thinkFlags & TH_PHYSICS ) {
  508.  
  509.         // Update the velocity to match the changing speed
  510.         if ( updateVelocity ) {
  511.             idVec3 vel;
  512.             vel = physicsObj.GetLinearVelocity ( );
  513.             vel.Normalize ( );
  514.             physicsObj.SetLinearVelocity ( speed.GetCurrentValue ( gameLocal.time ) * vel );            
  515.             if ( speed.IsDone ( gameLocal.time ) ) {
  516.                 updateVelocity = false;
  517.             }
  518.         }
  519.         
  520.         RunPhysics();
  521.         
  522.         // If we werent at rest and are now then start the atrest fuse
  523.         if ( physicsObj.IsAtRest( ) ) {
  524.             float fuse = spawnArgs.GetFloat( "fuse_atrest" );
  525.             if ( fuse > 0.0f ) {
  526.                 if ( spawnArgs.GetBool( "detonate_on_fuse" ) ) {
  527.                     CancelEvents( &EV_Explode );
  528.                     PostEventSec( &EV_Explode, fuse );
  529.                 } else {
  530.                     CancelEvents( &EV_Fizzle );
  531.                     PostEventSec( &EV_Fizzle, fuse );
  532.                 }
  533.             }
  534.         }
  535.  
  536.         // Stop the trail effect if the physics flag was removed
  537.         if ( flyEffect && flyEffectAttenuateSpeed > 0.0f ) {
  538.             if ( physicsObj.IsAtRest( ) ) {
  539.                 flyEffect->Stop( );
  540.                 flyEffect = NULL;                
  541.             } else {
  542.                 float speed;
  543.                 speed = idMath::ClampFloat( 0, flyEffectAttenuateSpeed, physicsObj.GetLinearVelocity ( ).LengthFast ( ) );
  544.                 flyEffect->Attenuate( speed / flyEffectAttenuateSpeed );
  545.             }
  546.         }
  547.  
  548.         UpdateVisualAngles();
  549.     }
  550.         
  551.     Present();
  552.  
  553.     // add the light
  554.      if ( renderLight.lightRadius.x > 0.0f && g_projectileLights.GetBool() ) {
  555.         renderLight.origin = GetPhysics()->GetOrigin() + GetPhysics()->GetAxis() * lightOffset;
  556.         renderLight.axis = GetPhysics()->GetAxis();
  557.         if ( ( lightDefHandle != -1 ) ) {
  558.             if ( lightEndTime > 0 && gameLocal.time <= lightEndTime + gameLocal.GetMSec() ) {
  559.                 idVec3 color( 0, 0, 0 );
  560.                 if ( gameLocal.time < lightEndTime ) {
  561.                     float frac = ( float )( gameLocal.time - lightStartTime ) / ( float )( lightEndTime - lightStartTime );
  562.                     color.Lerp( lightColor, color, frac );
  563.                 } 
  564.                 renderLight.shaderParms[SHADERPARM_RED] = color.x;
  565.                 renderLight.shaderParms[SHADERPARM_GREEN] = color.y;
  566.                 renderLight.shaderParms[SHADERPARM_BLUE] = color.z;
  567.             } 
  568.             gameRenderWorld->UpdateLightDef( lightDefHandle, &renderLight );
  569.         } else {
  570.             lightDefHandle = gameRenderWorld->AddLightDef( &renderLight );
  571.         }
  572.     }
  573. }
  574.  
  575. /*
  576. =================
  577. idProjectile::UpdateVisualAngles
  578. =================
  579. */
  580. void idProjectile::UpdateVisualAngles() {
  581.     idVec3 linearVelocity( GetPhysics()->GetLinearVelocity() );
  582.  
  583.     if( angularVelocity.Compare(ang_zero, VECTOR_EPSILON) ) {
  584.         rotation.Init( gameLocal.GetTime(), 0.0f, rotation.GetCurrentValue(gameLocal.GetTime()), linearVelocity.ToNormal().ToMat3().ToQuat()  );
  585.         return;
  586.     }
  587.  
  588.     if( physicsObj.GetNumContacts() ) {
  589.         return;
  590.     }
  591.  
  592.     if( !rotation.IsDone(gameLocal.GetTime()) ) {
  593.         return;
  594.     }
  595.  
  596.     if( linearVelocity.Length() <= BOUNCE_SOUND_MIN_VELOCITY ) {
  597.         return;
  598.     }
  599.  
  600.     visualAngles += angularVelocity;
  601.     idQuat q = visualAngles.ToQuat() * linearVelocity.ToNormal().ToMat3().ToQuat();
  602.     rotation.Init( gameLocal.GetTime(), gameLocal.GetMSec(), rotation.GetCurrentValue(gameLocal.GetTime()), q  );
  603. }
  604.  
  605. /*
  606. =================
  607. idProjectile::Collide
  608. =================
  609. */
  610. bool idProjectile::Collide( const trace_t &collision, const idVec3 &velocity ) {
  611.      idEntity*    ent;
  612.     idEntity*    actualHitEnt = NULL;
  613.      idEntity*    ignore;
  614.      const char*    damageDefName;
  615.      idVec3        dir;
  616.      bool        canDamage;
  617.  
  618.     if ( state == EXPLODED || state == FIZZLED ) {
  619.         return true;
  620.     }
  621.  
  622.     if ( gameLocal.isClient ) {
  623.         // do not try to predict detonates, that causes projectiles disappearing
  624.         // no bouncing projectiles in MP atm
  625.         return false;
  626.     }
  627.  
  628.     // allow projectiles to hit triggers (teleports)
  629.     if( collision.c.contents & CONTENTS_TRIGGER ) {
  630.         idEntity* trigger = gameLocal.entities[ collision.c.entityNum ];
  631.         if( trigger ) {
  632.             if ( trigger->RespondsTo( EV_Touch ) || trigger->HasSignal( SIG_TOUCH ) ) {
  633.                 trace_t trace;
  634.                 
  635.                 trace.endpos = physicsObj.GetOrigin();
  636.                 trace.endAxis = physicsObj.GetAxis();
  637.  
  638.                 trace.c.contents = collision.c.contents;
  639.                 trace.c.entityNum = collision.c.entityNum;
  640.                 if( trigger->GetPhysics()->GetClipModel() ) {
  641.                     trace.c.id = trigger->GetPhysics()->GetClipModel()->GetId();
  642.                 } else {
  643.                     trace.c.id = 0;
  644.                 }
  645.                 
  646.  
  647.                 trigger->Signal( SIG_TOUCH );
  648.                 trigger->ProcessEvent( &EV_Touch, this, &trace );
  649.             }
  650.         }
  651.  
  652.         // when we hit a trigger, align our velocity to the trigger's coordinate plane
  653.         idVec3 up( 0.0f, 0.0f, 1.0f );
  654.         idVec3 right = collision.c.normal.Cross( up );
  655.         idMat3 mat( collision.c.normal, right, up );
  656.  
  657.         physicsObj.SetLinearVelocity( -1.0f * (physicsObj.GetLinearVelocity() * mat.Transpose()) );
  658.         physicsObj.SetLinearVelocity( idVec3( physicsObj.GetLinearVelocity()[ 0 ], -1.0 *physicsObj.GetLinearVelocity()[ 1 ], -1.0 * physicsObj.GetLinearVelocity()[ 2 ] ) );
  659.  
  660.         // update the projectile's launchdir and launch origin
  661.         // this will propagate the change to the clients for prediction
  662.         // re-launch the projectile
  663.         idVec3 dir = physicsObj.GetLinearVelocity();
  664.         dir.Normalize();
  665.         launchTime = gameLocal.time;
  666.         launchDir = dir;
  667.         physicsObj.SetOrigin( physicsObj.GetOrigin() + idVec3( 0.0f, 0.0f, 32.0f ) );
  668.         launchOrig = physicsObj.GetOrigin();        
  669.  
  670.         if( !(collision.c.contents & CONTENTS_SOLID) ) {
  671.             return false;
  672.         }
  673.     }
  674.  
  675.      // remove projectile when a 'noimpact' surface is hit
  676.      if ( ( collision.c.material != NULL ) && ( collision.c.material->GetSurfaceFlags() & SURF_NOIMPACT ) ) {
  677.          PostEventMS( &EV_Remove, 0 );
  678.         StopEffect( "fx_fly" );
  679.         if( flyEffect)    {
  680.             //flyEffect->Event_Remove();
  681.         }
  682.          return true;
  683.      }
  684.  
  685.     // get the entity the projectile collided with
  686.     ent = gameLocal.entities[ collision.c.entityNum ];
  687.     if ( ent == owner.GetEntity() ) {
  688.         // assert( 0 );        // twhitaker: this isn't necessary
  689.         return true;
  690.     }
  691.  
  692.      // just get rid of the projectile when it hits a player in noclip
  693.      if ( ent->IsType( idPlayer::GetClassType() ) && static_cast<idPlayer *>( ent )->noclip ) {
  694.            PostEventMS( &EV_Remove, 0 );
  695.           common->DPrintf( "Projectile collision no impact\n" );
  696.            return true;
  697.        }
  698.  
  699.     // If the hit entity is bound to an actor use the actor instead
  700.     if ( ent->GetTeamMaster( ) && ent->GetTeamMaster( )->IsType ( idActor::GetClassType() ) ) {
  701.         actualHitEnt = ent;
  702.         ent = ent->GetTeamMaster( );
  703.     }
  704.  
  705.     // Can the projectile damage?  
  706.     canDamage = ent->fl.takedamage && !(( collision.c.material != NULL ) && ( collision.c.material->GetSurfaceFlags() & SURF_NODAMAGE ));
  707.   
  708.      // direction of projectile
  709.      dir = velocity;
  710.      dir.Normalize();
  711.  
  712.      // projectiles can apply an additional impulse next to the rigid body physics impulse
  713. // RAVEN BEGIN
  714. // abahr: added call to SkipDamageImpulse changed where push comes from
  715.     damageDefName = NULL;
  716.     if ( collision.c.materialType ) {
  717.         damageDefName = spawnArgs.GetString( va("def_damage_%s", collision.c.materialType->GetName()) );
  718.     }
  719.     if ( !damageDefName || !*damageDefName ) {
  720.         damageDefName = spawnArgs.GetString ( "def_damage" );
  721.     }
  722.  
  723.     if( damageDefName && damageDefName[0] ) {
  724.         const idDict* dict = gameLocal.FindEntityDefDict( damageDefName, false );
  725.         if ( dict ) {
  726.              ent->ApplyImpulse( this, collision.c.id, collision.endpos, dir, dict );
  727.          }
  728.     }
  729. // RAVEN END
  730.  
  731.     // MP: projectiles open doors
  732.     if ( gameLocal.isMultiplayer && ent->IsType( idDoor::GetClassType() ) && !static_cast< idDoor * >(ent)->IsOpen() && !ent->spawnArgs.GetBool( "no_touch" ) ) {
  733.         ent->ProcessEvent( &EV_Activate , this );
  734.     }
  735.  
  736.     // If the projectile hits water then we need to let the projectile keep going
  737.     if ( ent->GetPhysics()->GetContents() & CONTENTS_WATER ) {
  738.         if ( !physicsObj.IsInWater( ) ) {
  739.             StopEffect( "fx_fly" );
  740.             if( flyEffect)    {
  741.                 //flyEffect->Event_Remove();
  742.             }
  743.         }
  744.         // Pass through water
  745.         return false;
  746.     } else if ( canDamage && ent->IsType( idActor::GetClassType() ) ) {
  747.         if ( !projectileFlags.detonate_on_actor ) {
  748.             return false;
  749.         }
  750.     } else {
  751.         bool bounce = false;
  752.         
  753.         // Determine if the projectile should bounce
  754.         bounce = !physicsObj.IsInWater() && !projectileFlags.detonate_on_world && !canDamage;
  755.         bounce = bounce && (bounceCount == -1 || bounceCount > 0);
  756.         //assert(collision.c.material);
  757.         if ( !bounce && collision.c.material && (collision.c.material->GetSurfaceFlags() & SURF_BOUNCE) ) {
  758.             bounce = !projectileFlags.detonate_on_bounce;
  759.         }
  760.         
  761.         if ( bounce ) {
  762.             if ( bounceCount != -1 ) {
  763.                 bounceCount--;
  764.             }
  765.             
  766.             StartSound( "snd_ricochet", SND_CHANNEL_ITEM, 0, true, NULL );
  767.  
  768.             float len = velocity.Length();
  769.             if ( len > BOUNCE_SOUND_MIN_VELOCITY ) {
  770.                 if ( ent->IsType ( idMover::GetClassType ( ) ) ) {            
  771.                     ent->PlayEffect( 
  772.                         gameLocal.GetEffect(spawnArgs,"fx_bounce",collision.c.materialType), 
  773.                         collision.c.point, collision.c.normal.ToMat3(), 
  774.                         false, vec3_origin, true );
  775.                 } else {
  776.                     gameLocal.PlayEffect( 
  777.                         gameLocal.GetEffect(spawnArgs,"fx_bounce",collision.c.materialType), 
  778.                         collision.c.point, collision.c.normal.ToMat3(), 
  779.                         false, vec3_origin, true );
  780.                 }            
  781.             } else {
  782.                 // FIXME: clean up
  783.                 idMat3 axis( rotation.GetCurrentValue(gameLocal.GetTime()).ToMat3() );
  784.                 axis[0].ProjectOntoPlane( collision.c.normal );
  785.                 axis[0].Normalize();
  786.                 axis[2] = collision.c.normal;
  787.                 axis[1] = axis[2].Cross( axis[0] ).ToNormal();
  788.  
  789.                 rotation.Init( gameLocal.GetTime(), SEC2MS(spawnArgs.GetFloat("settle_duration")), rotation.GetCurrentValue(gameLocal.GetTime()), axis.ToQuat() );
  790.             }
  791.             if ( actualHitEnt
  792.                 && actualHitEnt != ent
  793.                 && actualHitEnt->spawnArgs.GetBool( "takeBounceDamage" ) )
  794.             {//bleh...
  795.                  if ( damageDefName[0] != '\0' ) {
  796.                     idVec3 dir = velocity;
  797.                     dir.Normalize();
  798.                     actualHitEnt->Damage( this, owner, dir, damageDefName, damagePower, CLIPMODEL_ID_TO_JOINT_HANDLE( collision.c.id ) );
  799.                 }
  800.             }
  801.             return false;        
  802.         }
  803.     }
  804.  
  805.     SetOrigin( collision.endpos );
  806. //    SetAxis( collision.endAxis );
  807.  
  808.     // unlink the clip model because we no longer need it
  809.     GetPhysics()->UnlinkClip();
  810.  
  811.     ignore = NULL;
  812.  
  813. // RAVEN BEGIN
  814. // jshepard: Single Player- if the the player is the attacker and the victim is teammate, don't play any blood effects.
  815.     bool willPlayDamageEffect = true;
  816.  
  817. // jnewquist: Use accessor for static class type 
  818.         if ( owner.GetEntity() && owner.GetEntity()->IsType( idPlayer::GetClassType() ) ) {
  819.             // if the projectile hit an ai
  820.             if ( ent->IsType( idAI::GetClassType() ) ) {
  821.                 idPlayer *player = static_cast<idPlayer *>( owner.GetEntity() );
  822.                 player->AddProjectileHits( 1 );
  823.  
  824. // jshepard: Single Player- if the the player is the attacker and the victim is teammate, don't play any blood effects.
  825.                 idAI * ai_ent = static_cast<idAI* >(ent);
  826.                 if( ai_ent->team == player->team)    {
  827.                     willPlayDamageEffect = false;
  828.                 }
  829.             }
  830.         }
  831.  
  832. // RAVEN END
  833.  
  834.     // if the hit entity takes damage
  835.     if ( canDamage ) {
  836.  
  837.          if ( damageDefName[0] != '\0' ) {
  838.             idVec3 dir = velocity;
  839.             dir.Normalize();
  840. // RAVEN BEGIN
  841. // jdischler: code from the 'other' project..to ensure that if an attached head is hit, the body will use the head joint
  842. //    otherwise damage zones for head attachments no-worky
  843.             int hitJoint = CLIPMODEL_ID_TO_JOINT_HANDLE(collision.c.id);
  844.             if ( ent->IsType(idActor::GetClassType()) )
  845.             {
  846.                 idActor* entActor = static_cast<idActor*>(ent);
  847.                 if ( entActor && entActor->GetHead() && entActor->GetHead()->IsType(idAFAttachment::GetClassType()) )
  848.                 {
  849.                     idAFAttachment* headEnt = static_cast<idAFAttachment*>(entActor->GetHead());
  850.                     if ( headEnt && headEnt->entityNumber == collision.c.entityNum )
  851.                     {//hit ent's head, get the proper joint for the head
  852.                         hitJoint = entActor->GetAnimator()->GetJointHandle("head");
  853.                     }
  854.                 }
  855.             }    
  856. // RAVEN END
  857.              ent->Damage( this, owner, dir, damageDefName, damagePower, hitJoint );
  858.             
  859.             if( owner && owner->IsType( idPlayer::GetClassType() ) && ent->IsType( idActor::GetClassType() ) ) {
  860.                 statManager->WeaponHit( (const idActor*)(owner.GetEntity()), ent, methodOfDeath, hitCount == 0 );            
  861.                 hitCount++;
  862.             }
  863.         }
  864.     }
  865.     
  866.     ignore = ent;
  867.  
  868.     // TODO: fixme
  869.     ent->AddDamageEffect ( collision, velocity, damageDefName, owner );
  870.  
  871. // RAVEN BEGIN
  872. // jshepard: Single Player- if the the player is the attacker and the victim is teammate, don't play any effects.
  873. // this should make sure that only explosion effects play when the player shoots his comrades. 
  874.     if( willPlayDamageEffect || spawnArgs.GetBool( "friendly_impact") )    {
  875.         DefaultDamageEffect( collision, velocity, damageDefName );
  876.     }
  877. // RAVEN END
  878.  
  879. /*
  880.     // if the projectile causes a damage effect
  881.     bool explodeFX = true;
  882.      if ( spawnArgs.GetBool( "impact_damage_effect" ) ) {
  883.         // if the hit entity has a special damage effect
  884.         if ( ent->spawnArgs.GetBool( "bleed" ) ) {
  885.              ent->AddDamageEffect( collision, velocity, damageDefName );
  886.         } else {
  887.              DefaultDamageEffect( collision, velocity, damageDefName );
  888.             explodeFX = false;
  889.         }
  890.     }
  891. */
  892.  
  893.     Explode( &collision, false, ignore );
  894.  
  895.     return true;
  896. }
  897.  
  898. /*
  899. =================
  900. idProjectile::DefaultDamageEffect
  901. =================
  902. */
  903. void idProjectile::DefaultDamageEffect( const trace_t &tr, const idVec3 &velocity, const char *damageDefName ) {
  904.     idEntity* ent;
  905.     idMat3      axis;
  906.     ent = gameLocal.entities[ tr.c.entityNum ];    
  907.  
  908.     // Make sure we want to play effects
  909.     if  ( (!*spawnArgs.GetString( "def_splash_damage" ) || spawnArgs.GetBool( "bloodyImpactEffect" ))
  910.         && owner.GetEntity( ) && !ent->CanPlayImpactEffect( owner, ent ) ) {
  911.         return;
  912.     }
  913.  
  914.     // Effect axis when hitting actors is along the direction of impact because actor models are 
  915.     // very detailed.
  916.     if ( ent->IsType( idActor::GetClassType() ) || ent->IsType( idAFAttachment::GetClassType() ) ) {
  917.         idVec3 dir;
  918.         dir = velocity;
  919.         dir.Normalize ( );
  920.         axis = ((-dir + tr.c.normal) * 0.5f).ToMat3();
  921.         
  922.         // Play an actor specific impact effect?
  923.         const idDecl *actorImpactEffect = gameLocal.GetEffect( spawnArgs, "fx_impact_actor", tr.c.materialType );
  924.         if ( actorImpactEffect ) {
  925.             gameLocal.PlayEffect( actorImpactEffect, tr.c.point, axis, false, vec3_origin, true );
  926.             return;
  927.         }
  928.     } else {
  929.         axis = tr.c.normal.ToMat3();
  930.     }
  931.     
  932.     // Play an impact effect on the entity that got hit
  933.     if ( ent->IsType( idMover::GetClassType ( ) ) ) {
  934.         ent->PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_impact", tr.c.materialType ), tr.c.point, axis, false, vec3_origin, true );
  935.     } else { 
  936.         gameLocal.PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_impact", tr.c.materialType ), tr.c.point, axis, false, vec3_origin, true );
  937.     }
  938. }
  939.  
  940. /*
  941. ================
  942. idProjectile::Killed
  943. ================
  944. */
  945. void idProjectile::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  946.     if ( spawnArgs.GetBool( "detonate_on_death" ) ) {
  947.         Explode( NULL, true );
  948.         physicsObj.ClearContacts();
  949.         physicsObj.PutToRest();
  950.     } else {
  951.         Fizzle();
  952.     }
  953. }
  954.  
  955. /*
  956. ================
  957. idProjectile::Fizzle
  958. ================
  959. */
  960. void idProjectile::Fizzle( void ) {
  961.  
  962.     if ( gameLocal.isClient || state == EXPLODED || state == FIZZLED ) {
  963.         return;
  964.     }
  965.  
  966.     StopSound( SND_CHANNEL_BODY, false );
  967.     StartSound( "snd_fizzle", SND_CHANNEL_BODY, 0, false, NULL );
  968.     
  969.     gameLocal.PlayEffect( spawnArgs, "fx_fuse", GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
  970.  
  971.     fl.takedamage = false;
  972.     physicsObj.SetContents( 0 );
  973.     physicsObj.GetClipModel()->Unlink();
  974.     physicsObj.PutToRest();
  975.  
  976.     // No more fly effects
  977.     StopEffect( "fx_fly" );
  978.     if( flyEffect)    {
  979.         //flyEffect->Event_Remove();
  980.     }
  981.  
  982.     Hide();
  983.     FreeLightDef();
  984.  
  985.     state = FIZZLED;
  986.  
  987.      CancelEvents( &EV_Fizzle );
  988.     PostEventMS( &EV_Remove, spawnArgs.GetInt( "remove_time", "1500" ) );
  989. }
  990.  
  991. /*
  992. ================
  993. idProjectile::Event_RadiusDamage
  994. ================
  995. */
  996. void idProjectile::Event_RadiusDamage( idEntity *ignore ) {
  997.     const char *splash_damage = spawnArgs.GetString( "def_splash_damage" );
  998.     if ( splash_damage[0] != '\0' ) {
  999.         gameLocal.RadiusDamage( physicsObj.GetOrigin(), this, owner, ignore, this, splash_damage, damagePower, &hitCount );
  1000.     }
  1001. }
  1002.  
  1003. /*
  1004. ================
  1005. idProjectile::Event_ResidualDamage
  1006. ================
  1007. */
  1008. void idProjectile::Event_ResidualDamage ( idEntity* ignore ) {
  1009.     const char *residual_damage = spawnArgs.GetString( "def_residual_damage" );
  1010.     if ( residual_damage[0] != '\0' ) {
  1011.         gameLocal.RadiusDamage( physicsObj.GetOrigin(), this, owner, ignore, this, residual_damage, damagePower, &hitCount );
  1012.     }
  1013.  
  1014.     // Keep the loop going
  1015.     PostEventSec ( &EV_ResidualDamage, spawnArgs.GetFloat ( "delay_residual" ), ignore );
  1016. }
  1017.  
  1018. /*
  1019. ================
  1020. idProjectile::Explode
  1021. ================
  1022. */
  1023. void idProjectile::Explode( const trace_t *collision, const bool showExplodeFX, idEntity *ignore, const char *sndExplode ) {
  1024.     idVec3        normal, endpos;
  1025.     int            removeTime;
  1026.  
  1027.     if ( state == EXPLODED || state == FIZZLED ) {
  1028.         return;
  1029.     }
  1030.  
  1031.     if ( spawnArgs.GetVector( "detonation_axis", "", normal ) ) {
  1032.         GetPhysics()->SetAxis( normal.ToMat3() );
  1033.     } else {
  1034.         normal = collision ? collision->c.normal : idVec3( 0, 0, 1 );
  1035.     }
  1036.     endpos = ( collision ) ? collision->endpos : GetPhysics()->GetOrigin();
  1037.  
  1038.     removeTime = spawnArgs.GetInt( "remove_time", "1500" );
  1039.  
  1040.     // play sound
  1041.     StopSound( SND_CHANNEL_BODY, false );
  1042.     StartSound( sndExplode, SND_CHANNEL_BODY, 0, false, NULL );
  1043.  
  1044.     if ( showExplodeFX ) {
  1045.         idVec3 fxDir;
  1046.         if ( physicsObj.GetGravityNormal( ) != vec3_zero ) {
  1047.             fxDir = -physicsObj.GetGravityNormal( );
  1048.         } else { 
  1049.             fxDir = -physicsObj.GetLinearVelocity( );
  1050.             fxDir.Normalize( );
  1051.         }
  1052.         // FIXME: This should be done in a better way
  1053.         PlayDetonateEffect( endpos, fxDir.ToMat3() );
  1054.     }
  1055.  
  1056.     // Stop the fly effect without destroying particles to ensure the trail within can persist.
  1057.     StopEffect( "fx_fly" );    
  1058.     if( flyEffect)    {
  1059.         //flyEffect->Event_Remove();
  1060.     }
  1061.     
  1062.     // Stop the remaining particles
  1063.     StopAllEffects( );
  1064.  
  1065.     Hide();
  1066.     FreeLightDef();
  1067.  
  1068.     GetPhysics()->SetOrigin( GetPhysics()->GetOrigin() + 8.0f * normal );
  1069.  
  1070.     fl.takedamage = false;
  1071.     physicsObj.SetContents( 0 );
  1072.     physicsObj.PutToRest();
  1073.  
  1074.     state = EXPLODED;
  1075.  
  1076.     if ( gameLocal.isClient ) {
  1077.         return;
  1078.     }
  1079.  
  1080.      // alert the ai
  1081.      gameLocal.AlertAI( owner.GetEntity() );
  1082.  
  1083.     // bind the projectile to the impact entity if necesary
  1084.     if ( collision && gameLocal.entities[collision->c.entityNum] && spawnArgs.GetBool( "bindOnImpact" ) ) {
  1085.         Bind( gameLocal.entities[collision->c.entityNum], true );
  1086.     }
  1087.  
  1088.     // splash damage
  1089.     removeTime = 0;
  1090.     float delay = spawnArgs.GetFloat( "delay_splash" );
  1091.     if ( delay ) {
  1092.         if ( removeTime < delay * 1000 ) {
  1093.             removeTime = ( delay + 0.10 ) * 1000;
  1094.         }
  1095.         PostEventSec( &EV_RadiusDamage, delay, ignore );
  1096.     } else {
  1097.         Event_RadiusDamage( ignore );
  1098.     }
  1099.  
  1100.     // Residual damage (damage over time)
  1101.     delay = SEC2MS ( spawnArgs.GetFloat ( "delay_residual" ) );
  1102.     if ( delay > 0.0f ) {
  1103.         PostEventMS ( &EV_ResidualDamage, delay, ignore );
  1104.  
  1105.         // Keep the projectile around until the residual damage is done        
  1106.         delay = SEC2MS ( spawnArgs.GetFloat ( "residual_time" ) );
  1107.         if ( removeTime < delay ) {
  1108.             removeTime = delay;
  1109.         }
  1110.     }
  1111.             
  1112.      CancelEvents( &EV_Explode );
  1113.     PostEventMS( &EV_Remove, removeTime );
  1114. }
  1115.  
  1116. /*
  1117. ================
  1118. idProjectile::GetVelocity
  1119. ================
  1120. */
  1121. idVec3 idProjectile::GetVelocity( const idDict *projectile ) {
  1122.     idVec3 velocity;    
  1123.  
  1124.     velocity.Zero ( );
  1125.     if ( projectile && !projectile->GetFloat ( "speed", "0", velocity.x ) ) {
  1126.         projectile->GetVector( "velocity", "0 0 0", velocity );
  1127.     }
  1128.     return velocity;
  1129. }
  1130.  
  1131. /*
  1132. ================
  1133. idProjectile::GetGravity
  1134. ================
  1135. */
  1136. idVec3 idProjectile::GetGravity( const idDict *projectile ) {
  1137.     if ( projectile ) {
  1138.         return gameLocal.GetGravity ( ) * projectile->GetFloat( "gravity" );
  1139.     }
  1140.     return vec3_origin;
  1141. }
  1142.  
  1143. /*
  1144. ================
  1145. idProjectile::PlayPainEffect
  1146. ================
  1147. */
  1148. void idProjectile::PlayPainEffect ( idEntity* ent, int damage, const rvDeclMatType* materialType, const idVec3& origin, const idVec3& dir )  {
  1149.     static int  damageTable[] = { 100, 50, 25, 10, 0 };
  1150.     int            index;
  1151.  
  1152.     // Normalize the damage value to the damage table    
  1153.     for ( index = 0; damage < damageTable[index] && damageTable[index]; index ++ );
  1154.     
  1155.     // loop until we find a pain effect, trying lower damage numbers if needed
  1156.     for ( ; damageTable[index]; index ++ ) {
  1157.         // Try the pain effect for the  current damage value and if it plays then
  1158.         // we are done
  1159.         if ( ent->PlayEffect( gameLocal.GetEffect( spawnArgs, va( "fx_pain%d", damageTable[index] ), materialType ), origin, dir.ToMat3()  ) ) {
  1160.             return;
  1161.         }
  1162.     }
  1163.  
  1164.     // Play the default pain effect        
  1165.     ent->PlayEffect( gameLocal.GetEffect ( spawnArgs, "fx_pain", materialType ), origin, dir.ToMat3() );
  1166. }
  1167.  
  1168. /*
  1169. ================
  1170. idProjectile::PlayDetonateEffect
  1171. ================
  1172. */
  1173. void idProjectile::PlayDetonateEffect( const idVec3& origin, const idMat3& axis ) {
  1174.     if( physicsObj.HasGroundContacts() ) {
  1175.         if ( spawnArgs.GetBool( "detonateTestGroundMaterial" ) ) {
  1176.             trace_t tr;
  1177.             idVec3 down;
  1178.             down = GetPhysics()->GetOrigin() + GetPhysics()->GetGravityNormal()*8.0f;
  1179.             gameLocal.Translation( this, tr, GetPhysics()->GetOrigin(), down, GetPhysics()->GetClipModel(), GetPhysics()->GetClipModel()->GetAxis(), GetPhysics()->GetClipMask(), this );
  1180.             gameLocal.PlayEffect( gameLocal.GetEffect( spawnArgs, "fx_impact", tr.c.materialType ), origin, axis, false, vec3_origin, true );
  1181.         } else {
  1182.             gameLocal.PlayEffect( spawnArgs, "fx_impact", origin, axis, false, vec3_origin, true );
  1183.         }
  1184.         return;
  1185.     }
  1186.  
  1187.     gameLocal.PlayEffect( spawnArgs, "fx_detonate", origin, axis, false, vec3_origin, true );
  1188. }
  1189.  
  1190. /*
  1191. ================
  1192. idProjectile::Event_Explode
  1193. ================
  1194. */
  1195. void idProjectile::Event_Explode( void ) {
  1196.     // events are processed outside of the think loop, so set the current thinking ent appropriately
  1197.     idEntity* think = gameLocal.currentThinkingEntity;
  1198.     gameLocal.currentThinkingEntity = this;
  1199.     Explode( NULL, true );
  1200.     gameLocal.currentThinkingEntity = think;
  1201. }
  1202.  
  1203. /*
  1204. ================
  1205. idProjectile::Event_Fizzle
  1206. ================
  1207. */
  1208. void idProjectile::Event_Fizzle( void ) {
  1209.     idEntity* think = gameLocal.currentThinkingEntity;
  1210.     gameLocal.currentThinkingEntity = this;
  1211.     Fizzle();
  1212.     gameLocal.currentThinkingEntity = think;
  1213. }
  1214.  
  1215. /*
  1216. ================
  1217. idProjectile::Event_Touch
  1218. ================
  1219. */
  1220. void idProjectile::Event_Touch( idEntity *other, trace_t *trace ) {
  1221.     if ( IsHidden() ) {
  1222.         return;
  1223.     }
  1224.  
  1225.     if ( other != owner.GetEntity() ) {
  1226.         idEntity* think = gameLocal.currentThinkingEntity;
  1227.         gameLocal.currentThinkingEntity = this;
  1228.  
  1229.         trace_t collision;
  1230.  
  1231.         memset( &collision, 0, sizeof( collision ) );
  1232.         collision.c.point = GetPhysics()->GetOrigin();
  1233.         collision.c.normal.Set( 0, 0, 1 );
  1234.         DefaultDamageEffect( collision, collision.c.normal, NULL );
  1235.         Explode( NULL, true );
  1236.  
  1237.         gameLocal.currentThinkingEntity = think;
  1238.     }
  1239. }
  1240.  
  1241. /*
  1242. ================
  1243. idProjectile::ClientPredictionThink
  1244. ================
  1245. */
  1246. void idProjectile::ClientPredictionThink( void ) {
  1247.     if ( !renderEntity.hModel && clientEntities.IsListEmpty() ) {
  1248.         return;
  1249.     }
  1250.     if ( !syncPhysics && state == LAUNCHED ) {
  1251.         idMat3 axis = launchDir.ToMat3();
  1252.         idVec3 origin( launchOrig );
  1253.         origin += ( ( gameLocal.time - launchTime ) / 1000.0f ) * launchSpeed * launchDir;
  1254.         physicsObj.SetAxis( axis );
  1255.         physicsObj.SetOrigin( origin );
  1256.     }
  1257.     Think();
  1258. }
  1259.  
  1260. /*
  1261. ================
  1262. idProjectile::WriteToSnapshot
  1263. ================
  1264. */
  1265. void idProjectile::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1266.     if ( syncPhysics ) {
  1267.         physicsObj.WriteToSnapshot( msg );
  1268.     }
  1269.  
  1270.     msg.WriteBits( state, 3 );
  1271.     if ( state >= LAUNCHED ) {
  1272.         // feed the client with start position, direction and time. let the client do everything else
  1273.         // this won't change during projectile life and be completely deltified away
  1274.         msg.WriteLong( launchTime );
  1275.         msg.WriteFloat( launchOrig[ 0 ] );
  1276.         msg.WriteFloat( launchOrig[ 1 ] );
  1277.         msg.WriteFloat( launchOrig[ 2 ] );
  1278.         msg.WriteDir( launchDir, 24 );
  1279.     }
  1280. }
  1281.  
  1282. /*
  1283. ================
  1284. idProjectile::ReadFromSnapshot
  1285. ================
  1286. */
  1287. void idProjectile::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1288.     projectileState_t newState;
  1289.  
  1290.     if ( syncPhysics ) {
  1291.         physicsObj.ReadFromSnapshot( msg );
  1292.     }
  1293.  
  1294.     newState = (projectileState_t) msg.ReadBits( 3 );
  1295.     if ( newState >= LAUNCHED ) {
  1296.         launchTime = msg.ReadLong( );
  1297.         launchOrig[ 0 ] = msg.ReadFloat();
  1298.         launchOrig[ 1 ] = msg.ReadFloat();
  1299.         launchOrig[ 2 ] = msg.ReadFloat();
  1300.         launchDir = msg.ReadDir( 24 );
  1301.     }
  1302.     // we always create and launch at the same time
  1303.     // state on a client can be SPAWNED, LAUNCHED, EXPLODED
  1304.     // expect never to get a CREATED projectile, they should launch right away
  1305.     assert( state != CREATED && state != FIZZLED && newState != CREATED );
  1306.     if ( newState != state ) {
  1307.         switch ( newState ) {
  1308.         case LAUNCHED:
  1309.             Create( NULL, launchOrig, launchDir );
  1310.             Launch( launchOrig, launchDir, vec3_origin );
  1311.             Show();
  1312.             break;
  1313.         case FIZZLED:
  1314.         case EXPLODED:
  1315.             if ( state != EXPLODED ) {
  1316.                 StopSound( SND_CHANNEL_BODY, false );
  1317.                 StopAllEffects();
  1318.                 Hide();
  1319.                 FreeLightDef();
  1320.                 state = EXPLODED;
  1321.             }
  1322.             break;
  1323.         }
  1324.     }
  1325.  
  1326.     if ( msg.HasChanged() ) {
  1327.         UpdateVisuals();
  1328.     }
  1329. }
  1330.  
  1331. /*
  1332. ===============
  1333. idProjectile::ClientStale
  1334. ===============
  1335. */
  1336. bool idProjectile::ClientStale( void ) {
  1337.     // delete stale projectile ents. if they pop back in pvs, they will be re-spawned ( rare case anyway )
  1338.     StopAllEffects();
  1339.     return true;
  1340. }
  1341.  
  1342. /*
  1343. ================
  1344. idProjectile::GetPhysicsToVisualTransform
  1345. ================
  1346. */
  1347. bool idProjectile::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
  1348.     axis = rotation.GetCurrentValue( gameLocal.GetTime() ).ToMat3() * GetPhysics()->GetAxis().Transpose();
  1349.  
  1350.     origin.Zero();
  1351.     return true;
  1352. }
  1353.  
  1354. /*
  1355. ===============================================================================
  1356.  
  1357.     idGuidedProjectile
  1358.  
  1359. ===============================================================================
  1360. */
  1361.  
  1362. CLASS_DECLARATION( idProjectile, idGuidedProjectile )
  1363. END_CLASS
  1364.  
  1365. /*
  1366. ================
  1367. idGuidedProjectile::idGuidedProjectile( void )
  1368. ================
  1369. */
  1370. idGuidedProjectile::idGuidedProjectile( void ) {
  1371.     guideType = GUIDE_NONE;
  1372.     turn_max.Init ( gameLocal.time, 0, 0.0f, 0.0f );
  1373. }
  1374.  
  1375. /*
  1376. =================
  1377. idGuidedProjectile::~idGuidedProjectile
  1378. =================
  1379. */
  1380. idGuidedProjectile::~idGuidedProjectile() {
  1381. }
  1382.  
  1383. /*
  1384. ================
  1385. idGuidedProjectile::Save
  1386. ================
  1387. */
  1388. void idGuidedProjectile::Save( idSaveGame *savefile ) const {
  1389.     savefile->WriteInt ( guideType );
  1390.     guideEnt.Save( savefile );
  1391.     savefile->WriteVec3 ( guideDir );
  1392.     savefile->WriteVec3 ( guidePos );
  1393.     savefile->WriteJoint ( guideJoint );
  1394.     savefile->WriteFloat( guideMinDist ); // cnicholson: Added unsaved var
  1395.  
  1396.     savefile->WriteInt ( driftTime );
  1397.     savefile->WriteInt ( driftRate );
  1398.     savefile->WriteFloat ( driftRange );
  1399.     savefile->WriteFloat ( driftRadius );
  1400.     savefile->WriteFloat ( driftDiversity ); // cnicholson: Added unsaved var
  1401.     savefile->WriteFloat ( driftAngle );
  1402.     savefile->WriteFloat ( driftAngleStep );
  1403.     savefile->WriteFloat ( driftProjectRange );
  1404.  
  1405.     savefile->WriteFloat( turn_max.GetStartTime() );
  1406.     savefile->WriteFloat( turn_max.GetDuration() );
  1407.     savefile->WriteFloat( turn_max.GetStartValue() );
  1408.     savefile->WriteFloat( turn_max.GetEndValue() );
  1409.  
  1410.     savefile->WriteInt ( launchTime );
  1411.     savefile->WriteInt ( guideDelay );
  1412.     savefile->WriteInt ( driftDelay );
  1413. }
  1414.  
  1415. /*
  1416. ================
  1417. idGuidedProjectile::Restore
  1418. ================
  1419. */
  1420. void idGuidedProjectile::Restore( idRestoreGame *savefile ) {
  1421.     float set;
  1422.     savefile->ReadInt ( guideType );
  1423.     guideEnt.Restore( savefile );
  1424.     savefile->ReadVec3 ( guideDir );
  1425.     savefile->ReadVec3 ( guidePos );
  1426.     savefile->ReadJoint ( guideJoint );
  1427.     savefile->ReadFloat( guideMinDist ); // cnicholson: Added unrestored var
  1428.     
  1429.     savefile->ReadInt ( driftTime );
  1430.     savefile->ReadInt ( driftRate );
  1431.     savefile->ReadFloat ( driftRange );
  1432.     savefile->ReadFloat ( driftRadius );
  1433.     savefile->ReadFloat ( driftDiversity ); // cnicholson: Added unrestored var
  1434.     savefile->ReadFloat ( driftAngle );
  1435.     savefile->ReadFloat ( driftAngleStep );
  1436.     savefile->ReadFloat ( driftProjectRange );
  1437.     
  1438.     savefile->ReadFloat( set );
  1439.     turn_max.SetStartTime( set );
  1440.     savefile->ReadFloat( set );
  1441.     turn_max.SetDuration( set );
  1442.     savefile->ReadFloat( set );
  1443.     turn_max.SetStartValue( set );
  1444.     savefile->ReadFloat( set );
  1445.     turn_max.SetEndValue( set );
  1446.  
  1447.     savefile->ReadInt ( launchTime );
  1448.     savefile->ReadInt ( guideDelay );
  1449.     savefile->ReadInt ( driftDelay );
  1450. }
  1451.  
  1452. /*
  1453. ================
  1454. idGuidedProjectile::GetGuideDir
  1455. ================
  1456. */
  1457. bool idGuidedProjectile::GetGuideDir ( idVec3 &outDir, float& outDist  ) {
  1458.     // Dont start guiding immeidately?
  1459.     if ( gameLocal.GetTime() - launchTime < guideDelay ) {
  1460.         return false;
  1461.     }
  1462.     
  1463.     switch ( guideType ) {
  1464.         case GUIDE_ENTITY:
  1465.             // If the guide entity is gone or dead then cancel the guide
  1466.             if ( !guideEnt.GetEntity ( ) || (guideEnt->fl.takedamage && guideEnt->health <= 0 ) ) {
  1467.                 CancelGuide ( );
  1468.                 return false;
  1469.             }
  1470.             // Use eye position for actors and center of bounds for everything else
  1471.             if ( guideJoint != INVALID_JOINT ) {
  1472.                 idMat3 jointAxis;
  1473.                 guideEnt->GetAnimator()->GetJointTransform( guideJoint, gameLocal.GetTime(), outDir, jointAxis );
  1474.                 outDir = guideEnt->GetRenderEntity()->origin + (outDir*guideEnt->GetRenderEntity()->axis);
  1475.                 if ( !guidePos.Compare( vec3_origin ) ) {
  1476.                     jointAxis = jointAxis * guideEnt->GetRenderEntity()->axis;
  1477.                     outDir += jointAxis[0]*guidePos[0];
  1478.                     outDir += jointAxis[1]*guidePos[1];
  1479.                     outDir += jointAxis[2]*guidePos[2];
  1480.                 }
  1481.             } else {
  1482.                 outDir = guideEnt->GetPhysics()->GetAbsBounds().GetCenter();
  1483.                 if ( guideEnt->IsType( idActor::GetClassType() ) ) {
  1484.                     outDir += static_cast<idActor *>(guideEnt.GetEntity())->GetEyePosition();
  1485.                     outDir *= 0.5f;
  1486.                 }
  1487.             }
  1488.             outDir -= physicsObj.GetOrigin();
  1489.             break;
  1490.  
  1491.         case GUIDE_POS:
  1492.             outDir = guidePos - physicsObj.GetOrigin();
  1493.             break;
  1494.             
  1495.         case GUIDE_DIR:
  1496.             // Project our current position on to the desired direction
  1497.             outDir = guidePos + guideDir * ((physicsObj.GetOrigin() - guidePos) * guideDir);
  1498.             
  1499.             // Seek towards a point forward along our desired direction
  1500.             outDir = (outDir + guideDir * (guideMinDist * 1.10f)) - physicsObj.GetOrigin();
  1501.             break;
  1502.             
  1503.         default:
  1504.             return false;
  1505.     }
  1506.  
  1507.     // Add drifting
  1508.     if ( driftRate && gameLocal.GetTime() - launchTime > driftDelay ) {        
  1509.         idMat3 axis;
  1510.         
  1511.         outDist = outDir.NormalizeFast();
  1512.         axis    = outDir.ToMat3();            
  1513.             
  1514.         if ( gameLocal.time > driftTime ) {                
  1515.             driftRadius    = driftRange + gameLocal.random.RandomFloat ( ) * driftRange * driftDiversity;
  1516.             driftTime    = gameLocal.time + driftRate;
  1517.         } else {
  1518.             driftAngle += driftAngleStep * MS2SEC ( gameLocal.msec );
  1519.             idMath::AngleNormalize360 ( driftAngle );
  1520.         }
  1521.  
  1522.         float angle;
  1523.         angle = DEG2RAD ( driftAngle );
  1524.         outDir = physicsObj.GetOrigin ( ) + outDir * Min( outDist, driftProjectRange );
  1525.         outDir += axis[2] * (driftRadius * idMath::Sin ( angle ));
  1526.         outDir += axis[1] * (driftRadius * idMath::Cos ( angle ));
  1527.  
  1528.         outDir -= physicsObj.GetOrigin();
  1529.     }
  1530.  
  1531.     outDist = outDir.Normalize ( );
  1532.             
  1533.     return true;
  1534. }
  1535.  
  1536. /*
  1537. ================
  1538. idGuidedProjectile::Think
  1539. ================
  1540. */
  1541. void idGuidedProjectile::Think( void ) {
  1542.  
  1543.     if ( state == LAUNCHED ) {
  1544.         idVec3    dir;
  1545.         idVec3    vel;
  1546.         float    angle;
  1547.         float    maxangle;
  1548.         idMat3    axis;
  1549.         float    dist;
  1550.  
  1551.         // crank up to normal speed ?
  1552.         if ( guideDelay && gameLocal.GetTime() - launchTime >= guideDelay ) {
  1553.             float newSpeed = spawnArgs.GetFloat( "speed" );
  1554.             float newSpeed2;
  1555.             if ( !spawnArgs.GetFloat ( "speed_end", "0", newSpeed2 ) ) {
  1556.                 newSpeed2 = newSpeed;
  1557.             }
  1558.             float newSpeedDuration;
  1559.             newSpeedDuration = SEC2MS( spawnArgs.GetFloat ( "speed_duration", "0" ) );
  1560.             speed.Init ( gameLocal.time, newSpeedDuration, newSpeed, newSpeed2 );
  1561.             guideDelay = 0;
  1562.         }
  1563.  
  1564.         if ( !GetGuideDir( dir, dist ) ) {
  1565.             idProjectile::Think();
  1566.             return;
  1567.         }
  1568.                         
  1569.         // Direction of travel
  1570.         vel = physicsObj.GetLinearVelocity();
  1571.         vel.Normalize();
  1572.  
  1573.         // Calculate the angle between the current projectile direction and where we want to go                    
  1574.         angle = RAD2DEG( idMath::ACos( dir * vel ) );
  1575.                                 
  1576.         // Make sure the angle doesnt cross our max turn radius                                
  1577.         maxangle = turn_max.GetCurrentValue( gameLocal.time );
  1578.         if ( angle < -maxangle ) {
  1579.             angle = -maxangle;
  1580.         } else if ( angle > maxangle ) {
  1581.             angle = maxangle;
  1582.         }
  1583.  
  1584.         // Debug information
  1585.         if ( g_debugWeapon.GetBool ( ) ) {
  1586.             gameRenderWorld->DebugArrow( colorCyan, physicsObj.GetOrigin(), physicsObj.GetOrigin() + vel * 50.0f, 10.0f );
  1587.             gameRenderWorld->DebugArrow( colorMagenta, physicsObj.GetOrigin(), physicsObj.GetOrigin() + dir * 50.0f, 10.0f );
  1588.         }
  1589.                     
  1590.         // Calculate the new axis by rotating the current forward vector around the cross of the forward
  1591.         // vector and the direction vector.
  1592.         vel = vel * idRotation( vec3_origin, dir.Cross ( vel ), angle );
  1593.         physicsObj.SetLinearVelocity( vel * GetSpeed ( ) );
  1594.  
  1595.         // If within the minium distance to the target anything over a 45 degree change will cancel the guide
  1596.         if ( guideMinDist != 0.0f && dist < guideMinDist ) {
  1597.             // Stop guiding if we have passed our target
  1598.             vel = physicsObj.GetLinearVelocity ( );
  1599.             vel.Normalize( );        
  1600.             if ( vel * dir < 0.7f ) {
  1601.                 guideType = GUIDE_NONE;
  1602.             }
  1603.         }
  1604.         
  1605.         idProjectile::Think();                        
  1606.     } else { 
  1607.         idProjectile::Think();
  1608.     }
  1609. }
  1610.  
  1611. /*
  1612. =================
  1613. idGuidedProjectile::Launch
  1614. =================
  1615. */
  1616. void idGuidedProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, float dmgPower ) {
  1617.     idProjectile::Launch( start, dir, pushVelocity, timeSinceFire, dmgPower );
  1618.     
  1619.     launchTime = gameLocal.GetTime();
  1620.  
  1621.     if ( owner.GetEntity() ) {
  1622.         if ( owner.GetEntity()->IsType( idAI::GetClassType() ) ) {
  1623.             GuideTo ( static_cast<idAI *>( owner.GetEntity() )->GetEnemy() );
  1624.         }
  1625.     }
  1626.  
  1627.     guideMinDist    = spawnArgs.GetFloat ( "min_dist", "128" );
  1628.     guideDelay        = SEC2MS(spawnArgs.GetFloat ( "delayGuide" ) + ( gameLocal.random.RandomFloat ( ) * spawnArgs.GetFloat ( "delayGuide_random")) );
  1629.  
  1630.     if ( guideDelay ) {
  1631.         float delaySpeed;
  1632.         if ( spawnArgs.GetFloat( "delaySpeed", "0", delaySpeed ) )    {
  1633.             float delaySpeed2;
  1634.             if ( !spawnArgs.GetFloat ( "delaySpeed_end", "0", delaySpeed2 ) ) {
  1635.                 delaySpeed2 = delaySpeed;
  1636.             }
  1637.             float delaySpeedDuration;
  1638.             delaySpeedDuration = SEC2MS( spawnArgs.GetFloat ( "delaySpeed_duration", "0" ) );
  1639.             speed.Init( gameLocal.time, delaySpeedDuration, delaySpeed, delaySpeed2 );
  1640.             physicsObj.SetLinearVelocity( dir * speed.GetCurrentValue(gameLocal.time) + pushVelocity );
  1641.         }
  1642.     }
  1643.  
  1644.     driftTime        = 0;
  1645.     driftRate        = SEC2MS ( spawnArgs.GetFloat ( "driftRate", "0" ) );
  1646.     driftRange        = spawnArgs.GetFloat ( "driftRange" );
  1647.     driftDiversity    = spawnArgs.GetFloat ( "driftDiversity", ".5" );
  1648.     driftAngle        = gameLocal.random.RandomFloat ( ) * 360.0f;
  1649.     driftAngleStep    = spawnArgs.GetFloat ( "driftRotate" );
  1650.     driftAngleStep += gameLocal.random.CRandomFloat ( ) * (driftAngleStep * driftDiversity) * (gameLocal.random.RandomFloat()<0.5f?-1.0f:1.0f);
  1651.     driftDelay        = SEC2MS(spawnArgs.GetFloat ( "driftDelay" ));
  1652.     
  1653.     driftProjectRange = spawnArgs.GetFloat ( "driftProjectRange", "128" );
  1654.  
  1655.     // Turn rate can be ramped up over time
  1656.     turn_max.Init ( gameLocal.time, 
  1657.                     SEC2MS(spawnArgs.GetFloat ( "turn_accel", "0" )), 
  1658.                     0, 
  1659.                     spawnArgs.GetFloat( "turn_max", "180" ) / ( float )gameLocal.GetMHz() );
  1660.  
  1661.      UpdateVisuals();
  1662. }
  1663.  
  1664. /*
  1665. =================
  1666. idGuidedProjectile::Launch
  1667. =================
  1668. */
  1669. void idGuidedProjectile::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  1670.     if ( guideEnt.IsValid() ) {
  1671.         guideEnt->GuidedProjectileIncoming( NULL );
  1672.     }
  1673.     idProjectile::Killed( inflictor, attacker, damage, dir, location );
  1674. }
  1675.  
  1676. /*
  1677. ===============================================================================
  1678.  
  1679.     rvDriftingProjectile
  1680.  
  1681. ===============================================================================
  1682. */
  1683.  
  1684. CLASS_DECLARATION( idProjectile, rvDriftingProjectile )
  1685. END_CLASS
  1686.  
  1687. /*
  1688. ================
  1689. rvDriftingProjectile::rvDriftingProjectile( void )
  1690. ================
  1691. */
  1692. rvDriftingProjectile::rvDriftingProjectile( void ) {
  1693. }
  1694.  
  1695. /*
  1696. =================
  1697. rvDriftingProjectile::~rvDriftingProjectile
  1698. =================
  1699. */
  1700. rvDriftingProjectile::~rvDriftingProjectile ( void ) {
  1701. }
  1702.  
  1703. /*
  1704. ================
  1705. rvDriftingProjectile::Save
  1706. ================
  1707. */
  1708. void rvDriftingProjectile::Save( idSaveGame *savefile ) const {
  1709.     savefile->WriteVec3 ( startDir );
  1710.     savefile->WriteVec3 ( startOrigin );
  1711.     savefile->WriteMat3 ( startAxis );
  1712.     savefile->WriteFloat ( startSpeed );
  1713.     savefile->WriteFloat ( driftOffsetMax );
  1714.     savefile->WriteFloat ( driftSpeedMax );
  1715.     savefile->WriteFloat ( driftTime );
  1716.  
  1717.     savefile->WriteInterpolate( driftSpeed );
  1718.     for( int ix = 0; ix < 2; ++ix ) {
  1719.         savefile->WriteInterpolate( driftOffset[ix] );
  1720.     }
  1721. }
  1722.  
  1723. /*
  1724. ================
  1725. rvDriftingProjectile::Restore
  1726. ================
  1727. */
  1728. void rvDriftingProjectile::Restore( idRestoreGame *savefile ) {
  1729.     savefile->ReadVec3 ( startDir );
  1730.     savefile->ReadVec3 ( startOrigin );
  1731.     savefile->ReadMat3 ( startAxis );
  1732.     savefile->ReadFloat ( startSpeed );
  1733.     savefile->ReadFloat ( driftOffsetMax );
  1734.     savefile->ReadFloat ( driftSpeedMax );
  1735.     savefile->ReadFloat ( driftTime );
  1736.  
  1737.     savefile->ReadInterpolate( driftSpeed );
  1738.     for( int ix = 0; ix < 2; ++ix ) {
  1739.         savefile->ReadInterpolate( driftOffset[ix] );
  1740.     }
  1741. }
  1742.  
  1743. /*
  1744. ================
  1745. rvDriftingProjectile::Think
  1746. ================
  1747. */
  1748. void rvDriftingProjectile::Think( void ) {
  1749.     idVec3 diff;
  1750.     idVec3 origin;
  1751.     idVec3 oldOrigin;
  1752.     idVec3 dir;
  1753.     float  dist;
  1754.     
  1755.     oldOrigin = GetPhysics()->GetOrigin ( );
  1756.     
  1757.     diff   = oldOrigin - startOrigin;
  1758.     dist   = diff.Length ( );
  1759.     origin = startOrigin + startDir * dist;
  1760.     
  1761.     if ( driftSpeed.IsDone ( gameLocal.time ) ) {
  1762.         driftSpeed.Init ( gameLocal.time, driftTime / 4.0f, driftTime / 4.0f, driftTime + gameLocal.random.RandomFloat() * driftTime,
  1763.                           driftSpeed.GetCurrentValue ( gameLocal.time ),
  1764.                           gameLocal.random.RandomFloat() * driftSpeedMax * (driftSpeed.GetCurrentValue ( gameLocal.time )<0?1:-1) );
  1765.     }
  1766.     
  1767.     if ( driftOffset[0].IsDone ( gameLocal.time ) ) {
  1768.         driftOffset[0].Init ( gameLocal.time, driftTime / 4.0f, driftTime / 4.0f, driftTime + gameLocal.random.RandomFloat() * driftTime, 
  1769.                         driftOffset[0].GetCurrentValue ( gameLocal.time ),
  1770.                         gameLocal.random.RandomFloat() * driftOffsetMax * (driftOffset[0].GetCurrentValue ( gameLocal.time )<0?1:-1) );
  1771.     }
  1772.  
  1773.     if ( driftOffset[1].IsDone ( gameLocal.time ) ) {
  1774.         driftOffset[1].Init ( gameLocal.time, driftTime / 4.0f, driftTime / 4.0f, driftTime + gameLocal.random.RandomFloat()*driftTime, 
  1775.                         driftOffset[1].GetCurrentValue ( gameLocal.time ),
  1776.                         gameLocal.random.RandomFloat() * driftOffsetMax * (driftOffset[1].GetCurrentValue ( gameLocal.time )<0?1:-1) );
  1777.     }
  1778.  
  1779.     origin += startAxis[1] * driftOffset[0].GetCurrentValue ( gameLocal.time );
  1780.     origin += startAxis[2] * driftOffset[1].GetCurrentValue ( gameLocal.time );
  1781.     
  1782.     GetPhysics ( )->SetOrigin ( origin );
  1783.     GetPhysics ( )->SetLinearVelocity ( startDir * (startSpeed + driftSpeed.GetCurrentValue ( gameLocal.time )) );
  1784.     
  1785.     idProjectile::Think();
  1786.  
  1787.     // Now orient the projectile using the old origin
  1788.     dir = GetPhysics()->GetOrigin() - oldOrigin;
  1789.     dir.Normalize ( );    
  1790.     GetPhysics ( )->SetAxis ( dir.ToMat3 ( ) );
  1791. }
  1792.  
  1793. /*
  1794. =================
  1795. rvDriftingProjectile::Launch
  1796. =================
  1797. */
  1798. void rvDriftingProjectile::Launch( const idVec3 &start, const idVec3 &dir, const idVec3 &pushVelocity, const float timeSinceFire, float dmgPower) {
  1799.     startDir    = dir;    
  1800.     startOrigin = start;
  1801.     startAxis   = dir.ToMat3();
  1802.  
  1803.     driftOffsetMax    = spawnArgs.GetFloat ( "driftOffset", "50" );
  1804.     driftSpeedMax    = spawnArgs.GetFloat ( "driftSpeed", "50" );
  1805.     driftTime    = SEC2MS ( spawnArgs.GetFloat ( "driftTime", ".5" ) );
  1806.  
  1807.     idProjectile::Launch ( start, dir, pushVelocity, timeSinceFire, dmgPower );
  1808.  
  1809.     startSpeed = GetPhysics()->GetLinearVelocity().Length ( );
  1810. }
  1811.  
  1812. /*
  1813. =================
  1814. rvDriftingProjectile::Launch
  1815. =================
  1816. */
  1817. void rvDriftingProjectile::UpdateVisualAngles ( void ) {
  1818.     rotation.Init( gameLocal.GetTime(), 0.0f, rotation.GetCurrentValue(gameLocal.GetTime()), GetPhysics()->GetAxis().ToQuat()  );
  1819. }
  1820.  
  1821. /*
  1822. ===============================================================================
  1823.  
  1824.     rvSpawnerProjectile
  1825.  
  1826. ===============================================================================
  1827. */
  1828.  
  1829. CLASS_DECLARATION( idProjectile, rvSpawnerProjectile )
  1830.     EVENT( EV_PostSpawn,        rvSpawnerProjectile::Event_PostSpawn )
  1831. END_CLASS
  1832.  
  1833. /*
  1834. ================
  1835. rvSpawnerProjectile::rvSpawnerProjectile( void )
  1836. ================
  1837. */
  1838. rvSpawnerProjectile::rvSpawnerProjectile( void ) {
  1839.     spawnState = STATE_NONE;
  1840. }
  1841.  
  1842. /*
  1843. =================
  1844. rvSpawnerProjectile::~rvSpawnerProjectile
  1845. =================
  1846. */
  1847. rvSpawnerProjectile::~rvSpawnerProjectile ( void ) {
  1848.     if ( spawnState == STATE_ADDED && spawner ) {
  1849.         spawner->RemoveSpawnPoint ( this );
  1850.     }
  1851. }
  1852.  
  1853. /*
  1854. =================
  1855. rvSpawnerProjectile::SetSpawner
  1856. =================
  1857. */
  1858. void rvSpawnerProjectile::Spawn ( void ) {
  1859.     if ( *spawnArgs.GetString ( "spawner" ) ) {
  1860.         PostEventMS ( &EV_PostSpawn, 0 );
  1861.     }
  1862. }
  1863.  
  1864. /*
  1865. =================
  1866. rvSpawnerProjectile::SetSpawner
  1867. =================
  1868. */
  1869. void rvSpawnerProjectile::SetSpawner ( rvSpawner* _spawner ) {
  1870.     spawner = _spawner;
  1871. }
  1872.  
  1873. /*
  1874. =================
  1875. rvSpawnerProjectile::Think
  1876. =================
  1877. */
  1878. void rvSpawnerProjectile::Think ( void ) {
  1879.     idProjectile::Think ( );
  1880.     
  1881.     if ( physicsObj.IsAtRest ( ) ) {
  1882.         if ( spawnState == STATE_NONE && spawner ) {
  1883.             spawner->AddSpawnPoint ( this );
  1884.             spawnState = STATE_ADDED;
  1885.         }
  1886.     }
  1887. }
  1888.  
  1889. /*
  1890. =================
  1891. rvSpawnerProjectile::Event_PostSpawn
  1892. =================
  1893. */
  1894. void rvSpawnerProjectile::Event_PostSpawn ( void ) {
  1895.     const char* temp;
  1896.     temp = spawnArgs.GetString ( "spawner" );
  1897.     if ( temp && *temp ) {
  1898.         idEntity* ent;
  1899.         ent = gameLocal.FindEntity ( temp );
  1900.         if ( !ent ) {
  1901.             gameLocal.Warning ( "spawner entity ('%s') not found for rvSpawnerProjectile '%s'", temp, GetName ( ) ); 
  1902.         } else if ( !ent->IsType ( rvSpawner::GetClassType() ) )  {
  1903.             gameLocal.Warning ( "spawner entity ('%s') is not of type rvSpawner for rvSpawnerProjectile '%s'", temp, GetName ( ) ); 
  1904.         } else {
  1905.             SetSpawner ( static_cast<rvSpawner*>(ent) );
  1906.         }
  1907.     }
  1908. }
  1909.  
  1910.  
  1911. /*
  1912. ===============================================================================
  1913.  
  1914.     rvMIRVProjectile
  1915.  
  1916. ===============================================================================
  1917. */
  1918. idEventDef        EV_LaunchWarheads( "launchWarheads" );
  1919.  
  1920. CLASS_DECLARATION( idProjectile, rvMIRVProjectile )
  1921.     EVENT( EV_LaunchWarheads,        rvMIRVProjectile::Event_LaunchWarheads )
  1922. END_CLASS
  1923.  
  1924. /*
  1925. ================
  1926. rvMIRVProjectile::rvMIRVProjectile( void )
  1927. ================
  1928. */
  1929. rvMIRVProjectile::rvMIRVProjectile( void ) {
  1930.  
  1931. }
  1932.  
  1933. /*
  1934. =================
  1935. rvMIRVProjectile::~rvMIRVProjectile
  1936. =================
  1937. */
  1938. rvMIRVProjectile::~rvMIRVProjectile ( void ) {
  1939.     
  1940. }
  1941.  
  1942. /*
  1943. ================
  1944. void rvMIRVProjectile::Spawn( void ) 
  1945. ================
  1946. */
  1947. void rvMIRVProjectile::Spawn( void ) {
  1948.     
  1949.     float launchDelay = spawnArgs.GetFloat("warhead_fuse", "0");
  1950.     //post event for warhead launch
  1951.     PostEventSec( &EV_LaunchWarheads, launchDelay );
  1952. }
  1953.  
  1954. /*
  1955. ================
  1956. void rvMIRVProjectile::Event_LaunchWarheads( void )
  1957. ================
  1958. */
  1959. void rvMIRVProjectile::Event_LaunchWarheads( void ) {
  1960.  
  1961.     const char* warhead;
  1962.     int count;
  1963.     
  1964.     warhead = spawnArgs.GetString("def_warhead","");
  1965.     count = spawnArgs.GetFloat("warhead_count","0");
  1966.  
  1967.     if( warhead && warhead[0] )    {
  1968.         
  1969.         //start launching!
  1970.         float angle = (360.0f / count);
  1971.         idMat3 normalMat = this->GetPhysics()->GetAxis( );
  1972.         idVec3 normal = normalMat[0];
  1973.         idVec3 axis = normalMat[1];
  1974.  
  1975.         float t;
  1976.         idMat3 axisMat;
  1977.         idProjectile * warheadEntity;
  1978.         
  1979.         //hey how 'bout that.
  1980.         normal.Normalize();
  1981.  
  1982.         for( t = 0; t< count; t++)    {
  1983.             
  1984.             //rotate axis around normal by angle degrees.
  1985.             axisMat = axis.ToMat3();
  1986.             axisMat.RotateArbitrary( normal, angle * t);
  1987.             warheadEntity =  static_cast<idProjectile*>(gameLocal.SpawnEntityDef( warhead));
  1988.             warheadEntity->Create( this->GetOwner(), this->GetPhysics()->GetOrigin(), axisMat[0], this );
  1989.             warheadEntity->Launch( this->GetPhysics()->GetOrigin(), axisMat[0], axisMat[0] );
  1990.  
  1991.         }
  1992.         //foom!
  1993.         gameLocal.PlayEffect( spawnArgs, "fx_impact", GetPhysics()->GetOrigin(), GetPhysics()->GetAxis() );
  1994.         PostEventSec( &EV_Explode, 0 );
  1995.     }
  1996.  
  1997.  
  1998.  
  1999. }
  2000. // RAVEN END
  2001.