Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1105 lines
33 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "vehicle_apc.h"
  9. #include "ammodef.h"
  10. #include "IEffects.h"
  11. #include "engine/IEngineSound.h"
  12. #include "weapon_rpg.h"
  13. #include "in_buttons.h"
  14. #include "globalstate.h"
  15. #include "soundent.h"
  16. #include "ai_basenpc.h"
  17. #include "ndebugoverlay.h"
  18. #include "gib.h"
  19. #include "EntityFlame.h"
  20. #include "smoke_trail.h"
  21. #include "explode.h"
  22. #include "effect_dispatch_data.h"
  23. #include "te_effect_dispatch.h"
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. #define ROCKET_ATTACK_RANGE_MAX 5500.0f
  27. #define ROCKET_ATTACK_RANGE_MIN 1250.0f
  28. #define MACHINE_GUN_ATTACK_RANGE_MAX 1250.0f
  29. #define MACHINE_GUN_ATTACK_RANGE_MIN 0.0f
  30. #define MACHINE_GUN_MAX_UP_PITCH 30
  31. #define MACHINE_GUN_MAX_DOWN_PITCH 10
  32. #define MACHINE_GUN_MAX_LEFT_YAW 30
  33. #define MACHINE_GUN_MAX_RIGHT_YAW 30
  34. #define MACHINE_GUN_BURST_SIZE 10
  35. #define MACHINE_GUN_BURST_TIME 0.075f
  36. #define MACHINE_GUN_BURST_PAUSE_TIME 2.0f
  37. #define ROCKET_SALVO_SIZE 5
  38. #define ROCKET_DELAY_TIME 1.5
  39. #define ROCKET_MIN_BURST_PAUSE_TIME 3
  40. #define ROCKET_MAX_BURST_PAUSE_TIME 4
  41. #define ROCKET_SPEED 800
  42. #define DEATH_VOLLEY_ROCKET_COUNT 4
  43. #define DEATH_VOLLEY_MIN_FIRE_TIME 0.333
  44. #define DEATH_VOLLEY_MAX_FIRE_TIME 0.166
  45. extern short g_sModelIndexFireball; // Echh...
  46. ConVar sk_apc_health( "sk_apc_health", "750" );
  47. #define APC_MAX_CHUNKS 3
  48. static const char *s_pChunkModelName[APC_MAX_CHUNKS] =
  49. {
  50. "models/gibs/helicopter_brokenpiece_01.mdl",
  51. "models/gibs/helicopter_brokenpiece_02.mdl",
  52. "models/gibs/helicopter_brokenpiece_03.mdl",
  53. };
  54. #define APC_MAX_GIBS 6
  55. static const char *s_pGibModelName[APC_MAX_GIBS] =
  56. {
  57. "models/combine_apc_destroyed_gib01.mdl",
  58. "models/combine_apc_destroyed_gib02.mdl",
  59. "models/combine_apc_destroyed_gib03.mdl",
  60. "models/combine_apc_destroyed_gib04.mdl",
  61. "models/combine_apc_destroyed_gib05.mdl",
  62. "models/combine_apc_destroyed_gib06.mdl",
  63. };
  64. LINK_ENTITY_TO_CLASS( prop_vehicle_apc, CPropAPC );
  65. BEGIN_DATADESC( CPropAPC )
  66. DEFINE_FIELD( m_flDangerSoundTime, FIELD_TIME ),
  67. DEFINE_FIELD( m_flHandbrakeTime, FIELD_TIME ),
  68. DEFINE_FIELD( m_bInitialHandbrake, FIELD_BOOLEAN ),
  69. DEFINE_FIELD( m_nSmokeTrailCount, FIELD_INTEGER ),
  70. DEFINE_FIELD( m_flMachineGunTime, FIELD_TIME ),
  71. DEFINE_FIELD( m_iMachineGunBurstLeft, FIELD_INTEGER ),
  72. // DEFINE_FIELD( m_nMachineGunMuzzleAttachment, FIELD_INTEGER ),
  73. // DEFINE_FIELD( m_nMachineGunBaseAttachment, FIELD_INTEGER ),
  74. // DEFINE_FIELD( m_vecBarrelPos, FIELD_VECTOR ),
  75. DEFINE_FIELD( m_bInFiringCone, FIELD_BOOLEAN ),
  76. // DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ),
  77. DEFINE_FIELD( m_hRocketTarget, FIELD_EHANDLE ),
  78. DEFINE_FIELD( m_iRocketSalvoLeft, FIELD_INTEGER ),
  79. DEFINE_FIELD( m_flRocketTime, FIELD_TIME ),
  80. // DEFINE_FIELD( m_nRocketAttachment, FIELD_INTEGER ),
  81. DEFINE_FIELD( m_nRocketSide, FIELD_INTEGER ),
  82. DEFINE_FIELD( m_hSpecificRocketTarget, FIELD_EHANDLE ),
  83. DEFINE_KEYFIELD( m_strMissileHint, FIELD_STRING, "missilehint" ),
  84. DEFINE_INPUTFUNC( FIELD_VOID, "Destroy", InputDestroy ),
  85. DEFINE_INPUTFUNC( FIELD_STRING, "FireMissileAt", InputFireMissileAt ),
  86. DEFINE_OUTPUT( m_OnDeath, "OnDeath" ),
  87. DEFINE_OUTPUT( m_OnFiredMissile, "OnFiredMissile" ),
  88. DEFINE_OUTPUT( m_OnDamaged, "OnDamaged" ),
  89. DEFINE_OUTPUT( m_OnDamagedByPlayer, "OnDamagedByPlayer" ),
  90. END_DATADESC()
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. void CPropAPC::Precache( void )
  95. {
  96. BaseClass::Precache();
  97. int i;
  98. for ( i = 0; i < APC_MAX_CHUNKS; ++i )
  99. {
  100. PrecacheModel( s_pChunkModelName[i] );
  101. }
  102. for ( i = 0; i < APC_MAX_GIBS; ++i )
  103. {
  104. PrecacheModel( s_pGibModelName[i] );
  105. }
  106. PrecacheScriptSound( "Weapon_AR2.Single" );
  107. PrecacheScriptSound( "PropAPC.FireRocket" );
  108. PrecacheScriptSound( "combine.door_lock" );
  109. }
  110. //------------------------------------------------
  111. // Spawn
  112. //------------------------------------------------
  113. void CPropAPC::Spawn( void )
  114. {
  115. BaseClass::Spawn();
  116. SetBlocksLOS( true );
  117. m_iHealth = m_iMaxHealth = sk_apc_health.GetFloat();
  118. SetCycle( 0 );
  119. m_iMachineGunBurstLeft = MACHINE_GUN_BURST_SIZE;
  120. m_iRocketSalvoLeft = ROCKET_SALVO_SIZE;
  121. m_nRocketSide = 0;
  122. m_lifeState = LIFE_ALIVE;
  123. m_bInFiringCone = false;
  124. m_flHandbrakeTime = gpGlobals->curtime + 0.1;
  125. m_bInitialHandbrake = false;
  126. // Reset the gun to a default pose.
  127. SetPoseParameter( "vehicle_weapon_pitch", 0 );
  128. SetPoseParameter( "vehicle_weapon_yaw", 90 );
  129. CreateAPCLaserDot();
  130. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  131. {
  132. AddFlag( FL_AIMTARGET );
  133. }
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose: Create a laser
  137. //-----------------------------------------------------------------------------
  138. void CPropAPC::CreateAPCLaserDot( void )
  139. {
  140. // Create a laser if we don't have one
  141. if ( m_hLaserDot == NULL )
  142. {
  143. m_hLaserDot = CreateLaserDot( GetAbsOrigin(), this, false );
  144. }
  145. }
  146. //-----------------------------------------------------------------------------
  147. //-----------------------------------------------------------------------------
  148. bool CPropAPC::ShouldAttractAutoAim( CBaseEntity *pAimingEnt )
  149. {
  150. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE && pAimingEnt->IsPlayer() && GetDriver() )
  151. {
  152. return true;
  153. }
  154. return BaseClass::ShouldAttractAutoAim( pAimingEnt );
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. //-----------------------------------------------------------------------------
  159. void CPropAPC::Activate()
  160. {
  161. BaseClass::Activate();
  162. m_nRocketAttachment = LookupAttachment( "cannon_muzzle" );
  163. m_nMachineGunMuzzleAttachment = LookupAttachment( "muzzle" );
  164. m_nMachineGunBaseAttachment = LookupAttachment( "gun_base" );
  165. // NOTE: gun_ref must have the same position as gun_base, but rotates with the gun
  166. int nMachineGunRefAttachment = LookupAttachment( "gun_def" );
  167. Vector vecWorldBarrelPos;
  168. matrix3x4_t matRefToWorld;
  169. GetAttachment( m_nMachineGunMuzzleAttachment, vecWorldBarrelPos );
  170. GetAttachment( nMachineGunRefAttachment, matRefToWorld );
  171. VectorITransform( vecWorldBarrelPos, matRefToWorld, m_vecBarrelPos );
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose:
  175. //-----------------------------------------------------------------------------
  176. void CPropAPC::UpdateOnRemove( void )
  177. {
  178. if ( m_hLaserDot )
  179. {
  180. UTIL_Remove( m_hLaserDot );
  181. m_hLaserDot = NULL;
  182. }
  183. BaseClass::UpdateOnRemove();
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. //-----------------------------------------------------------------------------
  188. void CPropAPC::CreateServerVehicle( void )
  189. {
  190. // Create our armed server vehicle
  191. m_pServerVehicle = new CAPCFourWheelServerVehicle();
  192. m_pServerVehicle->SetVehicle( this );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose:
  196. // Input : *pMoveData -
  197. //-----------------------------------------------------------------------------
  198. Class_T CPropAPC::ClassifyPassenger( CBaseCombatCharacter *pPassenger, Class_T defaultClassification )
  199. {
  200. return CLASS_COMBINE;
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose: Damage events as modified for the passenger of the APC, not the APC itself
  204. //-----------------------------------------------------------------------------
  205. float CPropAPC::PassengerDamageModifier( const CTakeDamageInfo &info )
  206. {
  207. CTakeDamageInfo DmgInfo = info;
  208. // bullets, slashing and headbutts don't hurt us in the apc, neither do rockets
  209. if( (DmgInfo.GetDamageType() & DMG_BULLET) || (DmgInfo.GetDamageType() & DMG_SLASH) ||
  210. (DmgInfo.GetDamageType() & DMG_CLUB) || (DmgInfo.GetDamageType() & DMG_BLAST) )
  211. return (0);
  212. // Accept everything else by default
  213. return 1.0;
  214. }
  215. //-----------------------------------------------------------------------------
  216. // position of eyes
  217. //-----------------------------------------------------------------------------
  218. Vector CPropAPC::EyePosition( )
  219. {
  220. Vector vecEyePosition;
  221. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5, 0.5, 1.0 ), &vecEyePosition );
  222. return vecEyePosition;
  223. }
  224. //-----------------------------------------------------------------------------
  225. //-----------------------------------------------------------------------------
  226. Vector CPropAPC::BodyTarget( const Vector &posSrc, bool bNoisy )
  227. {
  228. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  229. {
  230. return WorldSpaceCenter();
  231. }
  232. return BaseClass::BodyTarget( posSrc, bNoisy );
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Add a smoke trail since we've taken more damage
  236. //-----------------------------------------------------------------------------
  237. void CPropAPC::AddSmokeTrail( const Vector &vecPos )
  238. {
  239. // Start this trail out with a bang!
  240. ExplosionCreate( vecPos, vec3_angle, this, 1000, 500.0f, SF_ENVEXPLOSION_NODAMAGE |
  241. SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE |
  242. SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 );
  243. UTIL_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
  244. if ( m_nSmokeTrailCount == MAX_SMOKE_TRAILS )
  245. return;
  246. SmokeTrail *pSmokeTrail = SmokeTrail::CreateSmokeTrail();
  247. if( !pSmokeTrail )
  248. return;
  249. // See if there's an attachment for this smoke trail
  250. char buf[32];
  251. Q_snprintf( buf, 32, "damage%d", m_nSmokeTrailCount );
  252. int nAttachment = LookupAttachment( buf );
  253. ++m_nSmokeTrailCount;
  254. pSmokeTrail->m_SpawnRate = 4;
  255. pSmokeTrail->m_ParticleLifetime = 5.0f;
  256. pSmokeTrail->m_StartColor.Init( 0.7f, 0.7f, 0.7f );
  257. pSmokeTrail->m_EndColor.Init( 0.6, 0.6, 0.6 );
  258. pSmokeTrail->m_StartSize = 32;
  259. pSmokeTrail->m_EndSize = 64;
  260. pSmokeTrail->m_SpawnRadius = 4;
  261. pSmokeTrail->m_Opacity = 0.5f;
  262. pSmokeTrail->m_MinSpeed = 16;
  263. pSmokeTrail->m_MaxSpeed = 16;
  264. pSmokeTrail->m_MinDirectedSpeed = 16.0f;
  265. pSmokeTrail->m_MaxDirectedSpeed = 16.0f;
  266. pSmokeTrail->SetLifetime( 5 );
  267. pSmokeTrail->SetParent( this, nAttachment );
  268. Vector vecForward( 0, 0, 1 );
  269. QAngle angles;
  270. VectorAngles( vecForward, angles );
  271. if ( nAttachment == 0 )
  272. {
  273. pSmokeTrail->SetAbsOrigin( vecPos );
  274. pSmokeTrail->SetAbsAngles( angles );
  275. }
  276. else
  277. {
  278. pSmokeTrail->SetLocalOrigin( vec3_origin );
  279. pSmokeTrail->SetLocalAngles( angles );
  280. }
  281. pSmokeTrail->SetMoveType( MOVETYPE_NONE );
  282. }
  283. //------------------------------------------------------------------------------
  284. // Pow!
  285. //------------------------------------------------------------------------------
  286. void CPropAPC::ExplodeAndThrowChunk( const Vector &vecExplosionPos )
  287. {
  288. ExplosionCreate( vecExplosionPos, vec3_angle, this, 1000, 500.0f,
  289. SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS |
  290. SF_ENVEXPLOSION_NOSMOKE | SF_ENVEXPLOSION_NOFIREBALLSMOKE, 0 );
  291. UTIL_ScreenShake( vecExplosionPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
  292. // Drop a flaming, smoking chunk.
  293. CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
  294. pChunk->Spawn( "models/gibs/hgibs.mdl" );
  295. pChunk->SetBloodColor( DONT_BLEED );
  296. QAngle vecSpawnAngles;
  297. vecSpawnAngles.Random( -90, 90 );
  298. pChunk->SetAbsOrigin( vecExplosionPos );
  299. pChunk->SetAbsAngles( vecSpawnAngles );
  300. int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 );
  301. pChunk->Spawn( s_pChunkModelName[nGib] );
  302. pChunk->SetOwnerEntity( this );
  303. pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
  304. pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  305. IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
  306. // Set the velocity
  307. if ( pPhysicsObject )
  308. {
  309. pPhysicsObject->EnableMotion( true );
  310. Vector vecVelocity;
  311. QAngle angles;
  312. angles.x = random->RandomFloat( -40, 0 );
  313. angles.y = random->RandomFloat( 0, 360 );
  314. angles.z = 0.0f;
  315. AngleVectors( angles, &vecVelocity );
  316. vecVelocity *= random->RandomFloat( 300, 900 );
  317. vecVelocity += GetAbsVelocity();
  318. AngularImpulse angImpulse;
  319. angImpulse = RandomAngularImpulse( -180, 180 );
  320. pChunk->SetAbsVelocity( vecVelocity );
  321. pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
  322. }
  323. CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
  324. if ( pFlame != NULL )
  325. {
  326. pFlame->SetLifetime( pChunk->m_lifeTime );
  327. }
  328. }
  329. //-----------------------------------------------------------------------------
  330. // Should we trigger a damage effect?
  331. //-----------------------------------------------------------------------------
  332. inline bool CPropAPC::ShouldTriggerDamageEffect( int nPrevHealth, int nEffectCount ) const
  333. {
  334. int nPrevRange = (int)( ((float)nPrevHealth / (float)GetMaxHealth()) * nEffectCount );
  335. int nRange = (int)( ((float)GetHealth() / (float)GetMaxHealth()) * nEffectCount );
  336. return ( nRange != nPrevRange );
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose:
  340. //-----------------------------------------------------------------------------
  341. void CPropAPC::Event_Killed( const CTakeDamageInfo &info )
  342. {
  343. m_OnDeath.FireOutput( info.GetAttacker(), this );
  344. Vector vecAbsMins, vecAbsMaxs;
  345. CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  346. Vector vecNormalizedMins, vecNormalizedMaxs;
  347. CollisionProp()->WorldToNormalizedSpace( vecAbsMins, &vecNormalizedMins );
  348. CollisionProp()->WorldToNormalizedSpace( vecAbsMaxs, &vecNormalizedMaxs );
  349. Vector vecAbsPoint;
  350. CPASFilter filter( GetAbsOrigin() );
  351. for (int i = 0; i < 5; i++)
  352. {
  353. CollisionProp()->RandomPointInBounds( vecNormalizedMins, vecNormalizedMaxs, &vecAbsPoint );
  354. te->Explosion( filter, random->RandomFloat( 0.0, 1.0 ), &vecAbsPoint,
  355. g_sModelIndexFireball, random->RandomInt( 4, 10 ),
  356. random->RandomInt( 8, 15 ),
  357. ( i < 2 ) ? TE_EXPLFLAG_NODLIGHTS : TE_EXPLFLAG_NOPARTICLES | TE_EXPLFLAG_NOFIREBALLSMOKE | TE_EXPLFLAG_NODLIGHTS,
  358. 100, 0 );
  359. }
  360. // TODO: make the gibs spawn in sync with the delayed explosions
  361. int nGibs = random->RandomInt( 1, 4 );
  362. for ( int i = 0; i < nGibs; i++)
  363. {
  364. // Throw a flaming, smoking chunk.
  365. CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
  366. pChunk->Spawn( "models/gibs/hgibs.mdl" );
  367. pChunk->SetBloodColor( DONT_BLEED );
  368. QAngle vecSpawnAngles;
  369. vecSpawnAngles.Random( -90, 90 );
  370. pChunk->SetAbsOrigin( vecAbsPoint );
  371. pChunk->SetAbsAngles( vecSpawnAngles );
  372. int nGib = random->RandomInt( 0, APC_MAX_CHUNKS - 1 );
  373. pChunk->Spawn( s_pChunkModelName[nGib] );
  374. pChunk->SetOwnerEntity( this );
  375. pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
  376. pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  377. IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
  378. // Set the velocity
  379. if ( pPhysicsObject )
  380. {
  381. pPhysicsObject->EnableMotion( true );
  382. Vector vecVelocity;
  383. QAngle angles;
  384. angles.x = random->RandomFloat( -20, 20 );
  385. angles.y = random->RandomFloat( 0, 360 );
  386. angles.z = 0.0f;
  387. AngleVectors( angles, &vecVelocity );
  388. vecVelocity *= random->RandomFloat( 300, 900 );
  389. vecVelocity += GetAbsVelocity();
  390. AngularImpulse angImpulse;
  391. angImpulse = RandomAngularImpulse( -180, 180 );
  392. pChunk->SetAbsVelocity( vecVelocity );
  393. pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
  394. }
  395. CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
  396. if ( pFlame != NULL )
  397. {
  398. pFlame->SetLifetime( pChunk->m_lifeTime );
  399. }
  400. }
  401. UTIL_ScreenShake( vecAbsPoint, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
  402. if( hl2_episodic.GetBool() )
  403. {
  404. // EP1 perf hit
  405. Ignite( 6, false );
  406. }
  407. else
  408. {
  409. Ignite( 60, false );
  410. }
  411. m_lifeState = LIFE_DYING;
  412. // Spawn a lesser amount if the player is close
  413. m_iRocketSalvoLeft = DEATH_VOLLEY_ROCKET_COUNT;
  414. m_flRocketTime = gpGlobals->curtime;
  415. }
  416. //-----------------------------------------------------------------------------
  417. // Purpose: Blows it up!
  418. //-----------------------------------------------------------------------------
  419. void CPropAPC::InputDestroy( inputdata_t &inputdata )
  420. {
  421. CTakeDamageInfo info( this, this, m_iHealth, DMG_BLAST );
  422. info.SetDamagePosition( WorldSpaceCenter() );
  423. info.SetDamageForce( Vector( 0, 0, 1 ) );
  424. TakeDamage( info );
  425. }
  426. //-----------------------------------------------------------------------------
  427. // Aim the next rocket at a specific target
  428. //-----------------------------------------------------------------------------
  429. void CPropAPC::InputFireMissileAt( inputdata_t &inputdata )
  430. {
  431. string_t strMissileTarget = MAKE_STRING( inputdata.value.String() );
  432. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, strMissileTarget, NULL, inputdata.pActivator, inputdata.pCaller );
  433. if ( pTarget == NULL )
  434. {
  435. DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( strMissileTarget ) );
  436. return;
  437. }
  438. m_hSpecificRocketTarget = pTarget;
  439. }
  440. //-----------------------------------------------------------------------------
  441. // Purpose:
  442. //-----------------------------------------------------------------------------
  443. int CPropAPC::OnTakeDamage( const CTakeDamageInfo &info )
  444. {
  445. if ( m_iHealth == 0 )
  446. return 0;
  447. m_OnDamaged.FireOutput( info.GetAttacker(), this );
  448. if ( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
  449. {
  450. m_OnDamagedByPlayer.FireOutput( info.GetAttacker(), this );
  451. }
  452. CTakeDamageInfo dmgInfo = info;
  453. if ( dmgInfo.GetDamageType() & (DMG_BLAST | DMG_AIRBOAT) )
  454. {
  455. int nPrevHealth = GetHealth();
  456. m_iHealth -= dmgInfo.GetDamage();
  457. if ( m_iHealth <= 0 )
  458. {
  459. m_iHealth = 0;
  460. Event_Killed( dmgInfo );
  461. return 0;
  462. }
  463. // Chain
  464. // BaseClass::OnTakeDamage( dmgInfo );
  465. // Spawn damage effects
  466. if ( nPrevHealth != GetHealth() )
  467. {
  468. if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) )
  469. {
  470. AddSmokeTrail( dmgInfo.GetDamagePosition() );
  471. }
  472. if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) )
  473. {
  474. ExplodeAndThrowChunk( dmgInfo.GetDamagePosition() );
  475. }
  476. }
  477. }
  478. return 1;
  479. }
  480. //-----------------------------------------------------------------------------
  481. // Purpose:
  482. // Input : *pMoveData -
  483. //-----------------------------------------------------------------------------
  484. void CPropAPC::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
  485. {
  486. BaseClass::ProcessMovement( pPlayer, pMoveData );
  487. if ( m_flDangerSoundTime > gpGlobals->curtime )
  488. return;
  489. QAngle vehicleAngles = GetLocalAngles();
  490. Vector vecStart = GetAbsOrigin();
  491. Vector vecDir;
  492. GetVectors( &vecDir, NULL, NULL );
  493. // Make danger sounds ahead of the APC
  494. trace_t tr;
  495. Vector vecSpot, vecLeftDir, vecRightDir;
  496. // lay down sound path
  497. vecSpot = vecStart + vecDir * 600;
  498. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this );
  499. // put sounds a bit to left and right but slightly closer to APC to make a "cone" of sound
  500. // in front of it
  501. QAngle leftAngles = vehicleAngles;
  502. leftAngles[YAW] += 20;
  503. VehicleAngleVectors( leftAngles, &vecLeftDir, NULL, NULL );
  504. vecSpot = vecStart + vecLeftDir * 400;
  505. UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  506. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this );
  507. QAngle rightAngles = vehicleAngles;
  508. rightAngles[YAW] -= 20;
  509. VehicleAngleVectors( rightAngles, &vecRightDir, NULL, NULL );
  510. vecSpot = vecStart + vecRightDir * 400;
  511. UTIL_TraceLine( vecStart, vecSpot, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  512. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this);
  513. m_flDangerSoundTime = gpGlobals->curtime + 0.3;
  514. }
  515. //-----------------------------------------------------------------------------
  516. // Purpose:
  517. //-----------------------------------------------------------------------------
  518. void CPropAPC::Think( void )
  519. {
  520. BaseClass::Think();
  521. SetNextThink( gpGlobals->curtime );
  522. if ( !m_bInitialHandbrake ) // after initial timer expires, set the handbrake
  523. {
  524. m_bInitialHandbrake = true;
  525. m_VehiclePhysics.SetHandbrake( true );
  526. m_VehiclePhysics.Think();
  527. }
  528. StudioFrameAdvance();
  529. if ( IsSequenceFinished() )
  530. {
  531. int iSequence = SelectWeightedSequence( ACT_IDLE );
  532. if ( iSequence > ACTIVITY_NOT_AVAILABLE )
  533. {
  534. SetCycle( 0 );
  535. m_flAnimTime = gpGlobals->curtime;
  536. ResetSequence( iSequence );
  537. ResetClientsideFrame();
  538. }
  539. }
  540. if (m_debugOverlays & OVERLAY_NPC_KILL_BIT)
  541. {
  542. CTakeDamageInfo info( this, this, m_iHealth, DMG_BLAST );
  543. info.SetDamagePosition( WorldSpaceCenter() );
  544. info.SetDamageForce( Vector( 0, 0, 1 ) );
  545. TakeDamage( info );
  546. }
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Aims the secondary weapon at a target
  550. //-----------------------------------------------------------------------------
  551. void CPropAPC::AimSecondaryWeaponAt( CBaseEntity *pTarget )
  552. {
  553. m_hRocketTarget = pTarget;
  554. // Update the rocket target
  555. CreateAPCLaserDot();
  556. if ( m_hRocketTarget )
  557. {
  558. m_hLaserDot->SetAbsOrigin( m_hRocketTarget->BodyTarget( WorldSpaceCenter(), false ) );
  559. }
  560. SetLaserDotTarget( m_hLaserDot, m_hRocketTarget );
  561. EnableLaserDot( m_hLaserDot, m_hRocketTarget != NULL );
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose:
  565. //-----------------------------------------------------------------------------
  566. void CPropAPC::DriveVehicle( float flFrameTime, CUserCmd *ucmd, int iButtonsDown, int iButtonsReleased )
  567. {
  568. switch( m_lifeState )
  569. {
  570. case LIFE_ALIVE:
  571. {
  572. int iButtons = ucmd->buttons;
  573. if ( iButtons & IN_ATTACK )
  574. {
  575. FireMachineGun();
  576. }
  577. else if ( iButtons & IN_ATTACK2 )
  578. {
  579. FireRocket();
  580. }
  581. }
  582. break;
  583. case LIFE_DYING:
  584. FireDying( );
  585. break;
  586. case LIFE_DEAD:
  587. return;
  588. }
  589. BaseClass::DriveVehicle( flFrameTime, ucmd, iButtonsDown, iButtonsReleased );
  590. }
  591. void CPropAPC::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  592. {
  593. BaseClass::Use( pActivator, pCaller, useType, value );
  594. if ( pActivator->IsPlayer() )
  595. {
  596. EmitSound ( "combine.door_lock" );
  597. }
  598. }
  599. //-----------------------------------------------------------------------------
  600. // Primary gun
  601. //-----------------------------------------------------------------------------
  602. void CPropAPC::AimPrimaryWeapon( const Vector &vecWorldTarget )
  603. {
  604. EntityMatrix parentMatrix;
  605. parentMatrix.InitFromEntity( this, m_nMachineGunBaseAttachment );
  606. Vector target = parentMatrix.WorldToLocal( vecWorldTarget );
  607. float quadTarget = target.LengthSqr();
  608. float quadTargetXY = target.x*target.x + target.y*target.y;
  609. // Target is too close! Can't aim at it
  610. if ( quadTarget > m_vecBarrelPos.LengthSqr() )
  611. {
  612. // We're trying to aim the offset barrel at an arbitrary point.
  613. // To calculate this, I think of the target as being on a sphere with
  614. // it's center at the origin of the gun.
  615. // The rotation we need is the opposite of the rotation that moves the target
  616. // along the surface of that sphere to intersect with the gun's shooting direction
  617. // To calculate that rotation, we simply calculate the intersection of the ray
  618. // coming out of the barrel with the target sphere (that's the new target position)
  619. // and use atan2() to get angles
  620. // angles from target pos to center
  621. float targetToCenterYaw = atan2( target.y, target.x );
  622. float centerToGunYaw = atan2( m_vecBarrelPos.y, sqrt( quadTarget - (m_vecBarrelPos.y*m_vecBarrelPos.y) ) );
  623. float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) );
  624. float centerToGunPitch = atan2( -m_vecBarrelPos.z, sqrt( quadTarget - (m_vecBarrelPos.z*m_vecBarrelPos.z) ) );
  625. QAngle angles;
  626. angles.Init( -RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 );
  627. SetPoseParameter( "vehicle_weapon_yaw", angles.y );
  628. SetPoseParameter( "vehicle_weapon_pitch", angles.x );
  629. StudioFrameAdvance();
  630. float curPitch = GetPoseParameter( "vehicle_weapon_pitch" );
  631. float curYaw = GetPoseParameter( "vehicle_weapon_yaw" );
  632. m_bInFiringCone = (fabs(curPitch - angles.x) < 1e-3) && (fabs(curYaw - angles.y) < 1e-3);
  633. }
  634. else
  635. {
  636. m_bInFiringCone = false;
  637. }
  638. }
  639. //-----------------------------------------------------------------------------
  640. // Purpose:
  641. //-----------------------------------------------------------------------------
  642. const char *CPropAPC::GetTracerType( void )
  643. {
  644. return "HelicopterTracer";
  645. }
  646. //-----------------------------------------------------------------------------
  647. // Allows the shooter to change the impact effect of his bullets
  648. //-----------------------------------------------------------------------------
  649. void CPropAPC::DoImpactEffect( trace_t &tr, int nDamageType )
  650. {
  651. UTIL_ImpactTrace( &tr, nDamageType, "HelicopterImpact" );
  652. }
  653. //-----------------------------------------------------------------------------
  654. // Purpose:
  655. //-----------------------------------------------------------------------------
  656. void CPropAPC::DoMuzzleFlash( void )
  657. {
  658. CEffectData data;
  659. data.m_nEntIndex = entindex();
  660. data.m_nAttachmentIndex = m_nMachineGunMuzzleAttachment;
  661. data.m_flScale = 1.0f;
  662. DispatchEffect( "ChopperMuzzleFlash", data );
  663. BaseClass::DoMuzzleFlash();
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Purpose:
  667. //-----------------------------------------------------------------------------
  668. void CPropAPC::FireMachineGun( void )
  669. {
  670. if ( m_flMachineGunTime > gpGlobals->curtime )
  671. return;
  672. // If we're still firing the salvo, fire quickly
  673. m_iMachineGunBurstLeft--;
  674. if ( m_iMachineGunBurstLeft > 0 )
  675. {
  676. m_flMachineGunTime = gpGlobals->curtime + MACHINE_GUN_BURST_TIME;
  677. }
  678. else
  679. {
  680. // Reload the salvo
  681. m_iMachineGunBurstLeft = MACHINE_GUN_BURST_SIZE;
  682. m_flMachineGunTime = gpGlobals->curtime + MACHINE_GUN_BURST_PAUSE_TIME;
  683. }
  684. Vector vecMachineGunShootPos;
  685. Vector vecMachineGunDir;
  686. GetAttachment( m_nMachineGunMuzzleAttachment, vecMachineGunShootPos, &vecMachineGunDir );
  687. // Fire the round
  688. int bulletType = GetAmmoDef()->Index("AR2");
  689. FireBullets( 1, vecMachineGunShootPos, vecMachineGunDir, VECTOR_CONE_8DEGREES, MAX_TRACE_LENGTH, bulletType, 1 );
  690. DoMuzzleFlash();
  691. EmitSound( "Weapon_AR2.Single" );
  692. }
  693. //-----------------------------------------------------------------------------
  694. // Purpose:
  695. //-----------------------------------------------------------------------------
  696. void CPropAPC::GetRocketShootPosition( Vector *pPosition )
  697. {
  698. GetAttachment( m_nRocketAttachment, *pPosition );
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Create a corpse
  702. //-----------------------------------------------------------------------------
  703. void CPropAPC::CreateCorpse( )
  704. {
  705. m_lifeState = LIFE_DEAD;
  706. for ( int i = 0; i < APC_MAX_GIBS; ++i )
  707. {
  708. CPhysicsProp *pGib = assert_cast<CPhysicsProp*>(CreateEntityByName( "prop_physics" ));
  709. pGib->SetAbsOrigin( GetAbsOrigin() );
  710. pGib->SetAbsAngles( GetAbsAngles() );
  711. pGib->SetAbsVelocity( GetAbsVelocity() );
  712. pGib->SetModel( s_pGibModelName[i] );
  713. pGib->Spawn();
  714. pGib->SetMoveType( MOVETYPE_VPHYSICS );
  715. float flMass = pGib->GetMass();
  716. if ( flMass < 200 )
  717. {
  718. Vector vecVelocity;
  719. pGib->GetMassCenter( &vecVelocity );
  720. vecVelocity -= WorldSpaceCenter();
  721. vecVelocity.z = fabs(vecVelocity.z);
  722. VectorNormalize( vecVelocity );
  723. // Apply a force that would make a 100kg mass travel 150 - 300 m/s
  724. float flRandomVel = random->RandomFloat( 150, 300 );
  725. vecVelocity *= (100 * flRandomVel) / flMass;
  726. vecVelocity.z += 100.0f;
  727. AngularImpulse angImpulse = RandomAngularImpulse( -500, 500 );
  728. IPhysicsObject *pObj = pGib->VPhysicsGetObject();
  729. if ( pObj != NULL )
  730. {
  731. pObj->AddVelocity( &vecVelocity, &angImpulse );
  732. }
  733. pGib->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  734. }
  735. if( hl2_episodic.GetBool() )
  736. {
  737. // EP1 perf hit
  738. pGib->Ignite( 6, false );
  739. }
  740. else
  741. {
  742. pGib->Ignite( 60, false );
  743. }
  744. }
  745. AddSolidFlags( FSOLID_NOT_SOLID );
  746. AddEffects( EF_NODRAW );
  747. UTIL_Remove( this );
  748. }
  749. //-----------------------------------------------------------------------------
  750. // Death volley
  751. //-----------------------------------------------------------------------------
  752. void CPropAPC::FireDying( )
  753. {
  754. if ( m_flRocketTime > gpGlobals->curtime )
  755. return;
  756. Vector vecRocketOrigin;
  757. GetRocketShootPosition( &vecRocketOrigin );
  758. Vector vecDir;
  759. vecDir.Random( -1.0f, 1.0f );
  760. if ( vecDir.z < 0.0f )
  761. {
  762. vecDir.z *= -1.0f;
  763. }
  764. VectorNormalize( vecDir );
  765. Vector vecVelocity;
  766. VectorMultiply( vecDir, ROCKET_SPEED * random->RandomFloat( 0.75f, 1.25f ), vecVelocity );
  767. QAngle angles;
  768. VectorAngles( vecDir, angles );
  769. CAPCMissile *pRocket = (CAPCMissile *) CAPCMissile::Create( vecRocketOrigin, angles, vecVelocity, this );
  770. float flDeathTime = random->RandomFloat( 0.3f, 0.5f );
  771. if ( random->RandomFloat( 0.0f, 1.0f ) < 0.3f )
  772. {
  773. pRocket->ExplodeDelay( flDeathTime );
  774. }
  775. else
  776. {
  777. pRocket->AugerDelay( flDeathTime );
  778. }
  779. // Make erratic firing
  780. m_flRocketTime = gpGlobals->curtime + random->RandomFloat( DEATH_VOLLEY_MIN_FIRE_TIME, DEATH_VOLLEY_MAX_FIRE_TIME );
  781. if ( --m_iRocketSalvoLeft <= 0 )
  782. {
  783. CreateCorpse();
  784. }
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose:
  788. //-----------------------------------------------------------------------------
  789. void CPropAPC::FireRocket( void )
  790. {
  791. if ( m_flRocketTime > gpGlobals->curtime )
  792. return;
  793. // If we're still firing the salvo, fire quickly
  794. m_iRocketSalvoLeft--;
  795. if ( m_iRocketSalvoLeft > 0 )
  796. {
  797. m_flRocketTime = gpGlobals->curtime + ROCKET_DELAY_TIME;
  798. }
  799. else
  800. {
  801. // Reload the salvo
  802. m_iRocketSalvoLeft = ROCKET_SALVO_SIZE;
  803. m_flRocketTime = gpGlobals->curtime + random->RandomFloat( ROCKET_MIN_BURST_PAUSE_TIME, ROCKET_MAX_BURST_PAUSE_TIME );
  804. }
  805. Vector vecRocketOrigin;
  806. GetRocketShootPosition( &vecRocketOrigin );
  807. static float s_pSide[] = { 0.966, 0.866, 0.5, -0.5, -0.866, -0.966 };
  808. Vector forward;
  809. GetVectors( &forward, NULL, NULL );
  810. Vector vecDir;
  811. CrossProduct( Vector( 0, 0, 1 ), forward, vecDir );
  812. vecDir.z = 1.0f;
  813. vecDir.x *= s_pSide[m_nRocketSide];
  814. vecDir.y *= s_pSide[m_nRocketSide];
  815. if ( ++m_nRocketSide >= 6 )
  816. {
  817. m_nRocketSide = 0;
  818. }
  819. VectorNormalize( vecDir );
  820. Vector vecVelocity;
  821. VectorMultiply( vecDir, ROCKET_SPEED, vecVelocity );
  822. QAngle angles;
  823. VectorAngles( vecDir, angles );
  824. CAPCMissile *pRocket = (CAPCMissile *)CAPCMissile::Create( vecRocketOrigin, angles, vecVelocity, this );
  825. pRocket->IgniteDelay();
  826. if ( m_hSpecificRocketTarget )
  827. {
  828. pRocket->AimAtSpecificTarget( m_hSpecificRocketTarget );
  829. m_hSpecificRocketTarget = NULL;
  830. }
  831. else if ( m_strMissileHint != NULL_STRING )
  832. {
  833. pRocket->SetGuidanceHint( STRING( m_strMissileHint ) );
  834. }
  835. EmitSound( "PropAPC.FireRocket" );
  836. m_OnFiredMissile.FireOutput( this, this );
  837. }
  838. //-----------------------------------------------------------------------------
  839. // Purpose:
  840. //-----------------------------------------------------------------------------
  841. float CPropAPC::MaxAttackRange() const
  842. {
  843. return ROCKET_ATTACK_RANGE_MAX;
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose:
  847. //-----------------------------------------------------------------------------
  848. void CPropAPC::OnRestore( void )
  849. {
  850. IServerVehicle *pServerVehicle = GetServerVehicle();
  851. if ( pServerVehicle != NULL )
  852. {
  853. // Restore the passenger information we're holding on to
  854. pServerVehicle->RestorePassengerInfo();
  855. }
  856. }
  857. //========================================================================================================================================
  858. // APC FOUR WHEEL PHYSICS VEHICLE SERVER VEHICLE
  859. //========================================================================================================================================
  860. //-----------------------------------------------------------------------------
  861. // Purpose:
  862. //-----------------------------------------------------------------------------
  863. void CAPCFourWheelServerVehicle::NPC_AimPrimaryWeapon( Vector vecTarget )
  864. {
  865. CPropAPC *pAPC = ((CPropAPC*)m_pVehicle);
  866. pAPC->AimPrimaryWeapon( vecTarget );
  867. }
  868. //-----------------------------------------------------------------------------
  869. // Purpose:
  870. //-----------------------------------------------------------------------------
  871. void CAPCFourWheelServerVehicle::NPC_AimSecondaryWeapon( Vector vecTarget )
  872. {
  873. // Add some random noise
  874. // Vector vecOffset = vecTarget + RandomVector( -128, 128 );
  875. // ((CPropAPC*)m_pVehicle)->AimSecondaryWeaponAt( vecOffset );
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose:
  879. //-----------------------------------------------------------------------------
  880. void CAPCFourWheelServerVehicle::Weapon_PrimaryRanges( float *flMinRange, float *flMaxRange )
  881. {
  882. *flMinRange = MACHINE_GUN_ATTACK_RANGE_MIN;
  883. *flMaxRange = MACHINE_GUN_ATTACK_RANGE_MAX;
  884. }
  885. //-----------------------------------------------------------------------------
  886. // Purpose:
  887. //-----------------------------------------------------------------------------
  888. void CAPCFourWheelServerVehicle::Weapon_SecondaryRanges( float *flMinRange, float *flMaxRange )
  889. {
  890. *flMinRange = ROCKET_ATTACK_RANGE_MIN;
  891. *flMaxRange = ROCKET_ATTACK_RANGE_MAX;
  892. }
  893. //-----------------------------------------------------------------------------
  894. // Purpose: Return the time at which this vehicle's primary weapon can fire again
  895. //-----------------------------------------------------------------------------
  896. float CAPCFourWheelServerVehicle::Weapon_PrimaryCanFireAt( void )
  897. {
  898. return ((CPropAPC*)m_pVehicle)->PrimaryWeaponFireTime();
  899. }
  900. //-----------------------------------------------------------------------------
  901. // Purpose: Return the time at which this vehicle's secondary weapon can fire again
  902. //-----------------------------------------------------------------------------
  903. float CAPCFourWheelServerVehicle::Weapon_SecondaryCanFireAt( void )
  904. {
  905. return ((CPropAPC*)m_pVehicle)->SecondaryWeaponFireTime();
  906. }