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.

766 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "ai_default.h"
  10. #include "ai_task.h"
  11. #include "ai_schedule.h"
  12. #include "ai_node.h"
  13. #include "ai_hull.h"
  14. #include "ai_hint.h"
  15. #include "ai_memory.h"
  16. #include "ai_route.h"
  17. #include "ai_motor.h"
  18. #include "soundent.h"
  19. #include "game.h"
  20. #include "npcevent.h"
  21. #include "entitylist.h"
  22. #include "activitylist.h"
  23. #include "hl1_basegrenade.h"
  24. #include "animation.h"
  25. #include "IEffects.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "ammodef.h"
  29. #include "soundenvelope.h"
  30. #include "hl1_CBaseHelicopter.h"
  31. #include "ndebugoverlay.h"
  32. #include "smoke_trail.h"
  33. #include "beam_shared.h"
  34. #include "grenade_homer.h"
  35. #define HOMER_TRAIL0_LIFE 0.1
  36. #define HOMER_TRAIL1_LIFE 0.2
  37. #define HOMER_TRAIL2_LIFE 3.0// 1.0
  38. #define SF_NOTRANSITION 128
  39. extern short g_sModelIndexFireball;
  40. class CNPC_Apache : public CBaseHelicopter
  41. {
  42. DECLARE_CLASS( CNPC_Apache, CBaseHelicopter );
  43. public:
  44. DECLARE_DATADESC();
  45. void Spawn( void );
  46. void Precache( void );
  47. int BloodColor( void ) { return DONT_BLEED; }
  48. Class_T Classify( void ) { return CLASS_HUMAN_MILITARY; };
  49. void InitializeRotorSound( void );
  50. void LaunchRocket( Vector &viewDir, int damage, int radius, Vector vecLaunchPoint );
  51. void Flight( void );
  52. bool FireGun( void );
  53. void AimRocketGun( void );
  54. void FireRocket( void );
  55. void DyingThink( void );
  56. int ObjectCaps( void );
  57. void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  58. /* bool OnInternalDrawModel( ClientModelRenderInfo_t *pInfo )
  59. {
  60. BaseClass::OnInteralDrawModel( pInfo );
  61. Vector origin = GetAbsOrigin();
  62. origin.z += 32;
  63. SetAbsOrigin( origin );
  64. }*/
  65. /*( void SetAbsOrigin( const Vector& absOrigin )
  66. {
  67. ((Vector&)absOrigin).z += 32;
  68. BaseClass::SetAbsOrigin( absOrigin );
  69. }*/
  70. /* int Save( CSave &save );
  71. int Restore( CRestore &restore );
  72. static TYPEDESCRIPTION m_SaveData[];
  73. void Spawn( void );
  74. void Precache( void );
  75. void Killed( entvars_t *pevAttacker, int iGib );
  76. void GibMonster( void );
  77. void EXPORT HuntThink( void );
  78. void EXPORT FlyTouch( CBaseEntity *pOther );
  79. void EXPORT CrashTouch( CBaseEntity *pOther );
  80. void EXPORT DyingThink( void );
  81. void EXPORT StartupUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  82. void EXPORT NullThink( void );
  83. void ShowDamage( void );
  84. void Flight( void );
  85. void FireRocket( void );
  86. BOOL FireGun( void );
  87. int TakeDamage( entvars_t* pevInflictor, entvars_t* pevAttacker, float flDamage, int bitsDamageType );
  88. void TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType);*/
  89. int m_iRockets;
  90. float m_flForce;
  91. float m_flNextRocket;
  92. int m_iAmmoType;
  93. Vector m_vecTarget;
  94. Vector m_posTarget;
  95. Vector m_vecDesired;
  96. Vector m_posDesired;
  97. Vector m_vecGoal;
  98. QAngle m_angGun;
  99. int m_iSoundState; // don't save this
  100. int m_iExplode;
  101. int m_iBodyGibs;
  102. int m_nDebrisModel;
  103. float m_flGoalSpeed;
  104. CHandle<SmokeTrail> m_hSmoke;
  105. CBeam *m_pBeam;
  106. };
  107. BEGIN_DATADESC( CNPC_Apache )
  108. DEFINE_FIELD( m_iRockets, FIELD_INTEGER ),
  109. DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
  110. DEFINE_FIELD( m_flNextRocket, FIELD_TIME ),
  111. DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
  112. DEFINE_FIELD( m_posTarget, FIELD_POSITION_VECTOR ),
  113. DEFINE_FIELD( m_vecDesired, FIELD_VECTOR ),
  114. DEFINE_FIELD( m_posDesired, FIELD_POSITION_VECTOR ),
  115. DEFINE_FIELD( m_vecGoal, FIELD_VECTOR ),
  116. DEFINE_FIELD( m_angGun, FIELD_VECTOR ),
  117. DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
  118. DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
  119. // DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ),
  120. // DEFINE_FIELD( m_iSpriteTexture, FIELD_INTEGER ),
  121. // DEFINE_FIELD( m_iExplode, FIELD_INTEGER ),
  122. // DEFINE_FIELD( m_iBodyGibs, FIELD_INTEGER ),
  123. DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ),
  124. DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
  125. DEFINE_FIELD( m_hSmoke, FIELD_EHANDLE ),
  126. END_DATADESC()
  127. ConVar sk_apache_health( "sk_apache_health","100");
  128. static Vector s_vecSurroundingMins( -300, -300, -172);
  129. static Vector s_vecSurroundingMaxs(300, 300, 8);
  130. void CNPC_Apache::Spawn( void )
  131. {
  132. Precache( );
  133. SetModel( "models/apache.mdl" );
  134. BaseClass::Spawn();
  135. AddFlag( FL_NPC );
  136. SetSolid( SOLID_BBOX );
  137. SetMoveType( MOVETYPE_STEP );
  138. AddFlag( FL_FLY );
  139. m_iHealth = sk_apache_health.GetFloat();
  140. m_flFieldOfView = -0.707; // 270 degrees
  141. m_fHelicopterFlags = BITS_HELICOPTER_MISSILE_ON | BITS_HELICOPTER_GUN_ON;
  142. InitBoneControllers();
  143. SetRenderColor( 255, 255, 255, 255 );
  144. m_iRockets = 10;
  145. UTIL_SetSize( this, Vector( -32, -32, -32 ), Vector( 32, 32, 32 ) );
  146. //CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &s_vecSurroundingMins, &s_vecSurroundingMaxs );
  147. //AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
  148. m_hSmoke = NULL;
  149. }
  150. LINK_ENTITY_TO_CLASS ( monster_apache, CNPC_Apache );
  151. int CNPC_Apache::ObjectCaps( void )
  152. {
  153. if ( GetSpawnFlags() & SF_NOTRANSITION )
  154. return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION;
  155. else
  156. return BaseClass::ObjectCaps();
  157. }
  158. void CNPC_Apache::Precache( void )
  159. {
  160. // Get to tha chopper!
  161. PrecacheModel( "models/apache.mdl" );
  162. PrecacheScriptSound( "Apache.Rotor" );
  163. m_nDebrisModel = PrecacheModel( "models/metalplategibs_green.mdl" );
  164. // Gun
  165. PrecacheScriptSound( "Apache.FireGun" );
  166. m_iAmmoType = GetAmmoDef()->Index("9mmRound");
  167. // Rockets
  168. UTIL_PrecacheOther( "grenade_homer" );
  169. PrecacheScriptSound( "Apache.RPG" );
  170. PrecacheModel( "models/weapons/w_missile.mdl" );
  171. BaseClass::Precache();
  172. }
  173. void CNPC_Apache::InitializeRotorSound( void )
  174. {
  175. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  176. CPASAttenuationFilter filter( this );
  177. m_pRotorSound = controller.SoundCreate( filter, entindex(), "Apache.Rotor" );
  178. BaseClass::InitializeRotorSound();
  179. }
  180. void CNPC_Apache::Flight( void )
  181. {
  182. StudioFrameAdvance( );
  183. float flDistToDesiredPosition = (GetAbsOrigin() - m_vecDesiredPosition).Length();
  184. // NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0.1);
  185. if (m_flGoalSpeed < 800 )
  186. m_flGoalSpeed += GetAcceleration();
  187. // Vector vecGoalOrientation;
  188. if (flDistToDesiredPosition > 250) // 500
  189. {
  190. Vector v1 = (m_vecTargetPosition - GetAbsOrigin());
  191. Vector v2 = (m_vecDesiredPosition - GetAbsOrigin());
  192. VectorNormalize( v1 );
  193. VectorNormalize( v2 );
  194. if (m_flLastSeen + 90 > gpGlobals->curtime && DotProduct( v1, v2 ) > 0.25)
  195. {
  196. m_vecGoalOrientation = ( m_vecTargetPosition - GetAbsOrigin());
  197. }
  198. else
  199. {
  200. m_vecGoalOrientation = (m_vecDesiredPosition - GetAbsOrigin());
  201. }
  202. VectorNormalize( m_vecGoalOrientation );
  203. }
  204. else
  205. {
  206. AngleVectors( GetGoalEnt()->GetAbsAngles(), &m_vecGoalOrientation );
  207. }
  208. // SetGoalOrientation( vecGoalOrientation );
  209. if (GetGoalEnt())
  210. {
  211. // ALERT( at_console, "%.0f\n", flLength );
  212. if ( HasReachedTarget() )
  213. {
  214. // If we get this close to the desired position, it's assumed that we've reached
  215. // the desired position, so move on.
  216. // Fire target that I've reached my goal
  217. m_AtTarget.FireOutput( GetGoalEnt(), this );
  218. OnReachedTarget( GetGoalEnt() );
  219. SetGoalEnt( gEntList.FindEntityByName( NULL, GetGoalEnt()->m_target ) );
  220. if (GetGoalEnt())
  221. {
  222. m_vecDesiredPosition = GetGoalEnt()->GetAbsOrigin();
  223. // Vector vecGoalOrientation;
  224. AngleVectors( GetGoalEnt()->GetAbsAngles(), &m_vecGoalOrientation );
  225. // SetGoalOrientation( vecGoalOrientation );
  226. flDistToDesiredPosition = (GetAbsOrigin() - m_vecDesiredPosition).Length();
  227. }
  228. }
  229. }
  230. else
  231. {
  232. // If we can't find a new target, just stay where we are.
  233. m_vecDesiredPosition = GetAbsOrigin();
  234. }
  235. // tilt model 5 degrees
  236. QAngle angAdj = QAngle( 5.0, 0, 0 );
  237. // estimate where I'll be facing in one seconds
  238. Vector forward, right, up;
  239. AngleVectors( GetAbsAngles() + GetLocalAngularVelocity() * 2 + angAdj, &forward, &right, &up );
  240. // Vector vecEst1 = GetAbsOrigin() + pev->velocity + gpGlobals->v_up * m_flForce - Vector( 0, 0, 384 );
  241. // float flSide = DotProduct( m_posDesired - vecEst1, gpGlobals->v_right );
  242. QAngle angVel = GetLocalAngularVelocity();
  243. float flSide = DotProduct( m_vecGoalOrientation, right );
  244. if (flSide < 0)
  245. {
  246. if ( angVel.y < 60)
  247. {
  248. angVel.y += 8; // 9 * (3.0/2.0);
  249. }
  250. }
  251. else
  252. {
  253. if ( angVel.y > -60)
  254. {
  255. angVel.y -= 8; // 9 * (3.0/2.0);
  256. }
  257. }
  258. angVel.y *= 0.98;
  259. SetLocalAngularVelocity( angVel );
  260. Vector vecVel = GetAbsVelocity();
  261. // estimate where I'll be in two seconds
  262. AngleVectors( GetAbsAngles() + GetLocalAngularVelocity() * 1 + angAdj, &forward, &right, &up );
  263. Vector vecEst = GetAbsOrigin() + vecVel * 2.0 + up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
  264. // add immediate force
  265. AngleVectors( GetAbsAngles() + angAdj, &forward, &right, &up );
  266. vecVel.x += up.x * m_flForce;
  267. vecVel.y += up.y * m_flForce;
  268. vecVel.z += up.z * m_flForce;
  269. // add gravity
  270. vecVel.z -= 38.4; // 32ft/sec
  271. float flSpeed = vecVel.Length();
  272. float flDir = DotProduct( Vector( forward.x, forward.y, 0 ), Vector( vecVel.x, vecVel.y, 0 ) );
  273. if (flDir < 0)
  274. flSpeed = -flSpeed;
  275. float flDist = DotProduct( m_vecDesiredPosition - vecEst, forward );
  276. // float flSlip = DotProduct( pev->velocity, gpGlobals->v_right );
  277. float flSlip = -DotProduct( m_vecDesiredPosition - vecEst, right );
  278. angVel = GetLocalAngularVelocity();
  279. // fly sideways
  280. if (flSlip > 0)
  281. {
  282. if (GetAbsAngles().z > -30 && angVel.z > -15)
  283. angVel.z -= 4;
  284. else
  285. angVel.z += 2;
  286. }
  287. else
  288. {
  289. if (GetAbsAngles().z < 30 && angVel.z < 15)
  290. angVel.z += 4;
  291. else
  292. angVel.z -= 2;
  293. }
  294. SetLocalAngularVelocity( angVel );
  295. // sideways drag
  296. vecVel.x = vecVel.x * (1.0 - fabs( right.x ) * 0.05);
  297. vecVel.y = vecVel.y * (1.0 - fabs( right.y ) * 0.05);
  298. vecVel.z = vecVel.z * (1.0 - fabs( right.z ) * 0.05);
  299. // general drag
  300. vecVel = vecVel * 0.995;
  301. // Set final computed velocity
  302. SetAbsVelocity( vecVel );
  303. // apply power to stay correct height
  304. if (m_flForce < 80 && vecEst.z < m_vecDesiredPosition.z)
  305. {
  306. m_flForce += 12;
  307. }
  308. else if (m_flForce > 30)
  309. {
  310. if (vecEst.z > m_vecDesiredPosition.z)
  311. m_flForce -= 8;
  312. }
  313. angVel = GetLocalAngularVelocity();
  314. // pitch forward or back to get to target
  315. if (flDist > 0 && flSpeed < m_flGoalSpeed && GetAbsAngles().x + angVel.x < 40)
  316. {
  317. // ALERT( at_console, "F " );
  318. // lean forward
  319. angVel.x += 12.0;
  320. }
  321. else if (flDist < 0 && flSpeed > -50 && GetAbsAngles().x + angVel.x > -20)
  322. {
  323. // ALERT( at_console, "B " );
  324. // lean backward
  325. angVel.x -= 12.0;
  326. }
  327. else if (GetAbsAngles().x + angVel.x < 0)
  328. {
  329. // ALERT( at_console, "f " );
  330. angVel.x += 4.0;
  331. }
  332. else if (GetAbsAngles().x + angVel.x > 0)
  333. {
  334. // ALERT( at_console, "b " );
  335. angVel.x -= 4.0;
  336. }
  337. // Set final computed angular velocity
  338. SetLocalAngularVelocity( angVel );
  339. // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", GetAbsOrigin().x, pev->velocity.x, flDist, flSpeed, GetAbsAngles().x, pev->avelocity.x, m_flForce );
  340. // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", GetAbsOrigin().z, pev->velocity.z, vecEst.z, m_posDesired.z, m_flForce );
  341. }
  342. //------------------------------------------------------------------------------
  343. // Purpose :
  344. // Input :
  345. // Output :
  346. //------------------------------------------------------------------------------
  347. #define CHOPPER_AP_GUN_TIP 0
  348. #define CHOPPER_AP_GUN_BASE 1
  349. #define CHOPPER_BC_GUN_YAW 0
  350. #define CHOPPER_BC_GUN_PITCH 1
  351. #define CHOPPER_BC_POD_PITCH 2
  352. bool CNPC_Apache::FireGun( )
  353. {
  354. if ( !GetEnemy() )
  355. return false;
  356. Vector vForward, vRight, vUp;
  357. AngleVectors( GetAbsAngles(), &vForward, &vUp, &vRight );
  358. Vector posGun;
  359. QAngle angGun;
  360. GetAttachment( 1, posGun, angGun );
  361. Vector vecTarget = (m_vecTargetPosition - posGun);
  362. VectorNormalize( vecTarget );
  363. Vector vecOut;
  364. vecOut.x = DotProduct( vForward, vecTarget );
  365. vecOut.y = -DotProduct( vUp, vecTarget );
  366. vecOut.z = DotProduct( vRight, vecTarget );
  367. QAngle angles;
  368. VectorAngles( vecOut, angles );
  369. angles.y = AngleNormalize(angles.y);
  370. angles.x = AngleNormalize(angles.x);
  371. if (angles.x > m_angGun.x)
  372. m_angGun.x = MIN( angles.x, m_angGun.x + 12 );
  373. if (angles.x < m_angGun.x)
  374. m_angGun.x = MAX( angles.x, m_angGun.x - 12 );
  375. if (angles.y > m_angGun.y)
  376. m_angGun.y = MIN( angles.y, m_angGun.y + 12 );
  377. if (angles.y < m_angGun.y)
  378. m_angGun.y = MAX( angles.y, m_angGun.y - 12 );
  379. // hacks - shouldn't be hardcoded, oh well.
  380. // limit it so it doesn't pop if you try to set it to the max value
  381. m_angGun.y = clamp( m_angGun.y, -89.9, 89.9 );
  382. m_angGun.x = clamp( m_angGun.x, -9.9, 44.9 );
  383. m_angGun.y = SetBoneController( 0, m_angGun.y );
  384. m_angGun.x = SetBoneController( 1, m_angGun.x );
  385. Vector posBarrel;
  386. QAngle angBarrel;
  387. GetAttachment( 0, posBarrel, angBarrel );
  388. Vector forward;
  389. AngleVectors( angBarrel + m_angGun, &forward );
  390. Vector2D vec2LOS = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).AsVector2D();
  391. vec2LOS.NormalizeInPlace();
  392. float flDot = vec2LOS.Dot( forward.AsVector2D() );
  393. //forward
  394. // NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + ( forward * 200 ), 255,0,0, false, 0.1);
  395. //LOS
  396. // NDebugOverlay::Line( posGun, m_vecTargetPosition , 0,0,255, false, 0.1);
  397. // NDebugOverlay::Box( GetAbsOrigin(), s_vecSurroundingMins, s_vecSurroundingMaxs, 0, 255,0, false, 0.1);
  398. if ( flDot > 0.98 )
  399. {
  400. CPASAttenuationFilter filter( this, 0.2f );
  401. EmitSound( filter, entindex(), "Apache.FireGun" );//<<TEMP>>temp sound
  402. // gun is a bit dodgy, just fire at the target if we are close
  403. FireBullets( 1, posGun, vecTarget, VECTOR_CONE_4DEGREES, 8192, m_iAmmoType, 2 );
  404. return true;
  405. }
  406. return false;
  407. }
  408. void CNPC_Apache::FireRocket( void )
  409. {
  410. static float side = 1.0;
  411. static int count;
  412. Vector vForward, vRight, vUp;
  413. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  414. Vector vecSrc = GetAbsOrigin() + 1.5 * ( vForward * 21 + vRight * 70 * side + vUp * -79 );
  415. // pick firing pod to launch from
  416. switch( m_iRockets % 5)
  417. {
  418. case 0: vecSrc = vecSrc + vRight * 10; break;
  419. case 1: vecSrc = vecSrc - vRight * 10; break;
  420. case 2: vecSrc = vecSrc + vUp * 10; break;
  421. case 3: vecSrc = vecSrc - vUp * 10; break;
  422. case 4: break;
  423. }
  424. Vector vTargetDir = m_vecTargetPosition - GetAbsOrigin();
  425. VectorNormalize ( vTargetDir );
  426. LaunchRocket( vTargetDir, 100, 150, vecSrc);
  427. m_iRockets--;
  428. side = - side;
  429. }
  430. void CNPC_Apache::AimRocketGun( void )
  431. {
  432. Vector vForward, vRight, vUp;
  433. if (m_iRockets <= 0)
  434. return;
  435. Vector vTargetDir = m_vecTargetPosition - GetAbsOrigin();
  436. VectorNormalize ( vTargetDir );
  437. AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
  438. Vector vecEst = ( vForward * 800 + GetAbsVelocity());
  439. VectorNormalize ( vecEst );
  440. trace_t tr1;
  441. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1);
  442. // NDebugOverlay::Line(GetAbsOrigin(), tr1.endpos, 255,255,0, false, 0.1);
  443. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vTargetDir * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr1);
  444. // NDebugOverlay::Line(GetAbsOrigin(), tr1.endpos, 0,255,0, false, 0.1);
  445. // ALERT( at_console, "%d %d %d %4.2f\n", GetAbsAngles().x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->curtime, DotProduct( m_vecTarget, vecEst ) );
  446. if ((m_iRockets % 2) == 1)
  447. {
  448. FireRocket( );
  449. m_flNextRocket = gpGlobals->curtime + 0.5;
  450. if (m_iRockets <= 0)
  451. {
  452. m_flNextRocket = gpGlobals->curtime + 10;
  453. m_iRockets = 10;
  454. }
  455. }
  456. else if (DotProduct( GetAbsVelocity(), vForward ) > -100 && m_flNextRocket < gpGlobals->curtime)
  457. {
  458. if (m_flLastSeen + 60 > gpGlobals->curtime)
  459. {
  460. if (GetEnemy() != NULL)
  461. {
  462. // make sure it's a good shot
  463. //if (DotProduct( vTargetDir, vecEst) > 0.5)
  464. {
  465. trace_t tr;
  466. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
  467. // NDebugOverlay::Line(GetAbsOrigin(), tr.endpos, 255,0,255, false, 5);
  468. // if ((tr.endpos - m_vecTargetPosition).Length() < 512)
  469. if ((tr.endpos - m_vecTargetPosition).Length() < 1024)
  470. FireRocket( );
  471. }
  472. }
  473. else
  474. {
  475. trace_t tr;
  476. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecEst * 4096, MASK_ALL, this, COLLISION_GROUP_NONE, &tr);
  477. // just fire when close
  478. if ((tr.endpos - m_vecTargetPosition).Length() < 512)
  479. FireRocket( );
  480. }
  481. }
  482. }
  483. }
  484. #define MISSILE_HOMING_STRENGTH 0.3
  485. #define MISSILE_HOMING_DELAY 5.0
  486. #define MISSILE_HOMING_RAMP_UP 0.5
  487. #define MISSILE_HOMING_DURATION 1.0
  488. #define MISSILE_HOMING_RAMP_DOWN 0.5
  489. void CNPC_Apache::LaunchRocket( Vector &viewDir, int damage, int radius, Vector vecLaunchPoint )
  490. {
  491. CGrenadeHomer *pGrenade = CGrenadeHomer::CreateGrenadeHomer(
  492. MAKE_STRING("models/weapons/w_missile.mdl"),
  493. MAKE_STRING( "Apache.RPG" ),
  494. vecLaunchPoint, vec3_angle, edict() );
  495. pGrenade->Spawn( );
  496. pGrenade->SetHoming(MISSILE_HOMING_STRENGTH, MISSILE_HOMING_DELAY,
  497. MISSILE_HOMING_RAMP_UP, MISSILE_HOMING_DURATION,
  498. MISSILE_HOMING_RAMP_DOWN);
  499. pGrenade->SetDamage( damage );
  500. pGrenade->m_DmgRadius = radius;
  501. pGrenade->Launch( this, GetEnemy(), viewDir * 1500, 500, 0, HOMER_SMOKE_TRAIL_ON );
  502. }
  503. //-----------------------------------------------------------------------------
  504. // Purpose: Lame, temporary death
  505. //-----------------------------------------------------------------------------
  506. void CNPC_Apache::DyingThink( void )
  507. {
  508. StudioFrameAdvance( );
  509. SetNextThink( gpGlobals->curtime + 0.1f );
  510. if( gpGlobals->curtime > m_flNextCrashExplosion )
  511. {
  512. CPASFilter pasFilter( GetAbsOrigin() );
  513. Vector pos;
  514. pos = GetAbsOrigin();
  515. pos.x += random->RandomFloat( -150, 150 );
  516. pos.y += random->RandomFloat( -150, 150 );
  517. pos.z += random->RandomFloat( -150, -50 );
  518. te->Explosion( pasFilter, 0.0, &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
  519. Vector vecSize = Vector( 500, 500, 60 );
  520. CPVSFilter pvsFilter( GetAbsOrigin() );
  521. te->BreakModel( pvsFilter, 0.0, GetAbsOrigin(), vec3_angle, vecSize, vec3_origin,
  522. m_nDebrisModel, 100, 0, 2.5, BREAK_METAL );
  523. m_flNextCrashExplosion = gpGlobals->curtime + random->RandomFloat( 0.3, 0.5 );
  524. }
  525. QAngle angVel = GetLocalAngularVelocity();
  526. if( angVel.y < 400 )
  527. {
  528. angVel.y *= 1.1;
  529. SetLocalAngularVelocity( angVel );
  530. }
  531. Vector vecImpulse( 0, 0, -38.4 ); // gravity - 32ft/sec
  532. ApplyAbsVelocityImpulse( vecImpulse );
  533. if( m_hSmoke )
  534. {
  535. m_hSmoke->SetLifetime(0.1f);
  536. m_hSmoke = NULL;
  537. }
  538. }
  539. void CNPC_Apache::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  540. {
  541. CTakeDamageInfo dmgInfo = info;
  542. // HITGROUPS don't work currently.
  543. // ignore blades
  544. //if (ptr->hitgroup == 6 && (info.GetDamageType() & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB)))
  545. // return;
  546. // hit hard, hits cockpit
  547. if (info.GetDamage() > 50 || ptr->hitgroup == 1 || ptr->hitgroup == 2 || ptr->hitgroup == 3 )
  548. {
  549. // ALERT( at_console, "%map .0f\n", flDamage );
  550. AddMultiDamage( dmgInfo, this );
  551. if ( info.GetDamage() > 50 )
  552. {
  553. if ( m_hSmoke == NULL && (m_hSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
  554. {
  555. m_hSmoke->m_Opacity = 1.0f;
  556. m_hSmoke->m_SpawnRate = 60;
  557. m_hSmoke->m_ParticleLifetime = 1.3f;
  558. m_hSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
  559. m_hSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
  560. m_hSmoke->m_StartSize = 12;
  561. m_hSmoke->m_EndSize = 64;
  562. m_hSmoke->m_SpawnRadius = 8;
  563. m_hSmoke->m_MinSpeed = 2;
  564. m_hSmoke->m_MaxSpeed = 24;
  565. m_hSmoke->SetLifetime( 1e6 );
  566. m_hSmoke->FollowEntity( this );
  567. }
  568. }
  569. }
  570. else
  571. {
  572. // do half damage in the body
  573. dmgInfo.ScaleDamage(0.5);
  574. AddMultiDamage( dmgInfo, this );
  575. g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
  576. }
  577. if ( m_iHealth < 10 )
  578. {
  579. if ( m_hSmoke == NULL && (m_hSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
  580. {
  581. m_hSmoke->m_Opacity = 1.0f;
  582. m_hSmoke->m_SpawnRate = 60;
  583. m_hSmoke->m_ParticleLifetime = 1.3f;
  584. m_hSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
  585. m_hSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
  586. m_hSmoke->m_StartSize = 12;
  587. m_hSmoke->m_EndSize = 64;
  588. m_hSmoke->m_SpawnRadius = 8;
  589. m_hSmoke->m_MinSpeed = 2;
  590. m_hSmoke->m_MaxSpeed = 24;
  591. m_hSmoke->SetLifetime( 1e6 );
  592. m_hSmoke->FollowEntity( this );
  593. }
  594. }
  595. }