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.

1597 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Base class for helicopters & helicopter-type vehicles
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_network.h"
  9. #include "ai_default.h"
  10. #include "ai_schedule.h"
  11. #include "ai_hull.h"
  12. #include "ai_node.h"
  13. #include "ai_task.h"
  14. #include "ai_senses.h"
  15. #include "ai_memory.h"
  16. #include "entitylist.h"
  17. #include "soundenvelope.h"
  18. #include "gamerules.h"
  19. #include "grenade_homer.h"
  20. #include "ndebugoverlay.h"
  21. #include "cbasehelicopter.h"
  22. #include "soundflags.h"
  23. #include "rope.h"
  24. #include "saverestore_utlvector.h"
  25. #include "collisionutils.h"
  26. #include "coordsize.h"
  27. #include "effects.h"
  28. #include "rotorwash.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. void ExpandBBox(Vector &vecMins, Vector &vecMaxs);
  32. #if 0
  33. virtual void NullThink( void );
  34. #endif //0
  35. #define HELICOPTER_THINK_INTERVAL 0.1
  36. #define HELICOPTER_ROTORWASH_THINK_INTERVAL 0.01
  37. #define BASECHOPPER_DEBUG_WASH 1
  38. ConVar g_debug_basehelicopter( "g_debug_basehelicopter", "0", FCVAR_CHEAT );
  39. //---------------------------------------------------------
  40. //---------------------------------------------------------
  41. // TODOs
  42. //
  43. // -Member function: CHANGE MOVE GOAL
  44. //
  45. // -Member function: GET GRAVITY (or GetMaxThrust)
  46. //
  47. //---------------------------------------------------------
  48. //---------------------------------------------------------
  49. static const char *s_pRotorWashThinkContext = "RotorWashThink";
  50. static const char *s_pDelayedKillThinkContext = "DelayedKillThink";
  51. //------------------------------------------------------------------------------
  52. // Purpose :
  53. // Input :
  54. // Output :
  55. //------------------------------------------------------------------------------
  56. BEGIN_DATADESC_NO_BASE( washentity_t )
  57. DEFINE_FIELD( hEntity, FIELD_EHANDLE ),
  58. DEFINE_FIELD( flWashStartTime, FIELD_TIME ),
  59. END_DATADESC()
  60. BEGIN_DATADESC( CBaseHelicopter )
  61. DEFINE_THINKFUNC( HelicopterThink ),
  62. DEFINE_THINKFUNC( RotorWashThink ),
  63. DEFINE_THINKFUNC( CallDyingThink ),
  64. DEFINE_THINKFUNC( DelayedKillThink ),
  65. DEFINE_ENTITYFUNC( CrashTouch ),
  66. DEFINE_ENTITYFUNC( FlyTouch ),
  67. DEFINE_SOUNDPATCH( m_pRotorSound ),
  68. DEFINE_SOUNDPATCH( m_pRotorBlast ),
  69. DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
  70. DEFINE_FIELD( m_fHelicopterFlags, FIELD_INTEGER),
  71. DEFINE_FIELD( m_vecDesiredFaceDir, FIELD_VECTOR ),
  72. DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
  73. DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
  74. // DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ), // Don't save, precached
  75. DEFINE_FIELD( m_vecTargetPosition, FIELD_POSITION_VECTOR ),
  76. DEFINE_FIELD( m_hRotorWash, FIELD_EHANDLE ),
  77. DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
  78. DEFINE_FIELD( m_flMaxSpeedFiring, FIELD_FLOAT ),
  79. DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
  80. DEFINE_KEYFIELD( m_flInitialSpeed, FIELD_FLOAT, "InitialSpeed" ),
  81. DEFINE_FIELD( m_flRandomOffsetTime, FIELD_TIME ),
  82. DEFINE_FIELD( m_vecRandomOffset, FIELD_VECTOR ),
  83. DEFINE_FIELD( m_flRotorWashEntitySearchTime, FIELD_TIME ),
  84. DEFINE_FIELD( m_bSuppressSound, FIELD_BOOLEAN ),
  85. DEFINE_FIELD( m_flStartupTime, FIELD_TIME ),
  86. DEFINE_FIELD( m_cullBoxMins, FIELD_VECTOR ),
  87. DEFINE_FIELD( m_cullBoxMaxs, FIELD_VECTOR ),
  88. DEFINE_UTLVECTOR( m_hEntitiesPushedByWash, FIELD_EMBEDDED ),
  89. // Inputs
  90. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate),
  91. DEFINE_INPUTFUNC( FIELD_VOID, "GunOn", InputGunOn ),
  92. DEFINE_INPUTFUNC( FIELD_VOID, "GunOff", InputGunOff ),
  93. DEFINE_INPUTFUNC( FIELD_VOID, "MissileOn", InputMissileOn ),
  94. DEFINE_INPUTFUNC( FIELD_VOID, "MissileOff", InputMissileOff ),
  95. DEFINE_INPUTFUNC( FIELD_VOID, "EnableRotorWash", InputEnableRotorWash ),
  96. DEFINE_INPUTFUNC( FIELD_VOID, "DisableRotorWash", InputDisableRotorWash ),
  97. DEFINE_INPUTFUNC( FIELD_VOID, "MoveTopSpeed", InputMoveTopSpeed ),
  98. DEFINE_INPUTFUNC( FIELD_FLOAT, "MoveSpecifiedSpeed", InputMoveSpecifiedSpeed ),
  99. DEFINE_INPUTFUNC( FIELD_STRING, "SetAngles", InputSetAngles ),
  100. DEFINE_INPUTFUNC( FIELD_VOID, "EnableRotorSound", InputEnableRotorSound ),
  101. DEFINE_INPUTFUNC( FIELD_VOID, "DisableRotorSound", InputDisableRotorSound ),
  102. DEFINE_INPUTFUNC( FIELD_VOID, "Kill", InputKill ),
  103. END_DATADESC()
  104. IMPLEMENT_SERVERCLASS_ST( CBaseHelicopter, DT_BaseHelicopter )
  105. SendPropTime( SENDINFO( m_flStartupTime ) ),
  106. END_SEND_TABLE()
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. //-----------------------------------------------------------------------------
  110. CBaseHelicopter::CBaseHelicopter( void )
  111. {
  112. m_cullBoxMins = vec3_origin;
  113. m_cullBoxMaxs = vec3_origin;
  114. m_hRotorWash = NULL;
  115. }
  116. //------------------------------------------------------------------------------
  117. // Purpose :
  118. // Input :
  119. // Output :
  120. // Notes : Have your derived Helicopter's Spawn() function call this one FIRST
  121. //------------------------------------------------------------------------------
  122. void CBaseHelicopter::Precache( void )
  123. {
  124. }
  125. //------------------------------------------------------------------------------
  126. // Purpose :
  127. // Input :
  128. // Output :
  129. // Notes : Have your derived Helicopter's Spawn() function call this one FIRST
  130. //------------------------------------------------------------------------------
  131. void CBaseHelicopter::Spawn( void )
  132. {
  133. Precache( );
  134. SetSolid( SOLID_BBOX );
  135. SetMoveType( MOVETYPE_STEP );
  136. AddFlag( FL_FLY );
  137. SetState( NPC_STATE_IDLE );
  138. m_lifeState = LIFE_ALIVE;
  139. // motor
  140. //******
  141. // All of this stuff is specific to the individual type of aircraft. Handle it yourself.
  142. //******
  143. // m_iAmmoType = g_pGameRules->GetAmmoDef()->Index("AR2");
  144. // SetModel( "models/attack_helicopter.mdl" );
  145. // UTIL_SetSize( this, Vector( -32, -32, -64 ), Vector( 32, 32, 0 ) );
  146. // UTIL_SetOrigin( this, GetLocalOrigin() );
  147. // m_iHealth = 100;
  148. // m_flFieldOfView = -0.707; // 270 degrees
  149. // InitBoneControllers();
  150. // m_iRockets = 10;
  151. // Get the rotor sound started up.
  152. // This base class assumes the helicopter has no guns or missiles.
  153. // Set the appropriate flags in your derived class' Spawn() function.
  154. m_fHelicopterFlags &= ~BITS_HELICOPTER_MISSILE_ON;
  155. m_fHelicopterFlags &= ~BITS_HELICOPTER_GUN_ON;
  156. m_pRotorSound = NULL;
  157. m_pRotorBlast = NULL;
  158. SetCycle( 0 );
  159. ResetSequenceInfo();
  160. AddFlag( FL_NPC );
  161. m_flMaxSpeed = BASECHOPPER_MAX_SPEED;
  162. m_flMaxSpeedFiring = BASECHOPPER_MAX_FIRING_SPEED;
  163. m_takedamage = DAMAGE_AIM;
  164. // Don't start up if the level designer has asked the
  165. // helicopter to start disabled.
  166. if ( !(m_spawnflags & SF_AWAITINPUT) )
  167. {
  168. Startup();
  169. SetNextThink( gpGlobals->curtime + 1.0f );
  170. }
  171. else
  172. {
  173. m_flStartupTime = FLT_MAX;
  174. }
  175. InitPathingData( 0, BASECHOPPER_MIN_CHASE_DIST_DIFF, BASECHOPPER_AVOID_DIST );
  176. // Setup collision hull
  177. ExpandBBox( m_cullBoxMins, m_cullBoxMaxs );
  178. CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &m_cullBoxMins, &m_cullBoxMaxs );
  179. AddSolidFlags( FSOLID_CUSTOMRAYTEST | FSOLID_CUSTOMBOXTEST );
  180. m_flRandomOffsetTime = -1.0f;
  181. m_vecRandomOffset.Init( 0, 0, 0 );
  182. }
  183. //------------------------------------------------------------------------------
  184. // Cleanup
  185. //------------------------------------------------------------------------------
  186. void CBaseHelicopter::UpdateOnRemove()
  187. {
  188. StopRotorWash();
  189. BaseClass::UpdateOnRemove();
  190. }
  191. //------------------------------------------------------------------------------
  192. // Gets the max speed of the helicopter
  193. //------------------------------------------------------------------------------
  194. float CBaseHelicopter::GetMaxSpeed()
  195. {
  196. // If our last path_track has specified a speed, use that instead of ours
  197. if ( GetPathMaxSpeed() )
  198. return GetPathMaxSpeed();
  199. return m_flMaxSpeed;
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. //-----------------------------------------------------------------------------
  204. float CBaseHelicopter::GetMaxSpeedFiring()
  205. {
  206. // If our last path_track has specified a speed, use that instead of ours
  207. if ( GetPathMaxSpeed() )
  208. return GetPathMaxSpeed();
  209. return m_flMaxSpeedFiring;
  210. }
  211. //------------------------------------------------------------------------------
  212. // Enemy methods
  213. //------------------------------------------------------------------------------
  214. bool CBaseHelicopter::GetTrackPatherTarget( Vector *pPos )
  215. {
  216. if ( GetEnemy() )
  217. {
  218. *pPos = GetEnemy()->BodyTarget( GetAbsOrigin(), false );
  219. return true;
  220. }
  221. return false;
  222. }
  223. CBaseEntity *CBaseHelicopter::GetTrackPatherTargetEnt()
  224. {
  225. return GetEnemy();
  226. }
  227. //------------------------------------------------------------------------------
  228. // Purpose :
  229. // Input :
  230. // Output :
  231. //------------------------------------------------------------------------------
  232. bool CBaseHelicopter::FireGun( void )
  233. {
  234. return true;
  235. }
  236. //------------------------------------------------------------------------------
  237. // Purpose : The main think function for the helicopters
  238. // Input :
  239. // Output :
  240. //------------------------------------------------------------------------------
  241. void CBaseHelicopter::HelicopterThink( void )
  242. {
  243. CheckPVSCondition();
  244. SetNextThink( gpGlobals->curtime + HELICOPTER_THINK_INTERVAL );
  245. // Don't keep this around for more than one frame.
  246. ClearCondition( COND_ENEMY_DEAD );
  247. // Animate and dispatch animation events.
  248. StudioFrameAdvance( );
  249. DispatchAnimEvents( this );
  250. PrescheduleThink();
  251. if ( IsMarkedForDeletion() )
  252. return;
  253. ShowDamage( );
  254. // -----------------------------------------------
  255. // If AI is disabled, kill any motion and return
  256. // -----------------------------------------------
  257. if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
  258. {
  259. SetAbsVelocity( vec3_origin );
  260. SetLocalAngularVelocity( vec3_angle );
  261. SetNextThink( gpGlobals->curtime + HELICOPTER_THINK_INTERVAL );
  262. return;
  263. }
  264. Hunt();
  265. // Finally, forget dead enemies, or ones we've been told to ignore.
  266. if( GetEnemy() != NULL && (!GetEnemy()->IsAlive() || GetEnemy()->GetFlags() & FL_NOTARGET || IRelationType( GetEnemy() ) == D_NU ) )
  267. {
  268. SetEnemy( NULL );
  269. }
  270. HelicopterPostThink();
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Rotor wash think
  274. //-----------------------------------------------------------------------------
  275. void CBaseHelicopter::RotorWashThink( void )
  276. {
  277. if ( m_lifeState == LIFE_ALIVE || m_lifeState == LIFE_DYING )
  278. {
  279. DrawRotorWash( BASECHOPPER_WASH_ALTITUDE, GetAbsOrigin() );
  280. SetContextThink( &CBaseHelicopter::RotorWashThink, gpGlobals->curtime + HELICOPTER_ROTORWASH_THINK_INTERVAL, s_pRotorWashThinkContext );
  281. }
  282. else
  283. {
  284. SetContextThink( NULL, gpGlobals->curtime, s_pRotorWashThinkContext );
  285. }
  286. }
  287. //-----------------------------------------------------------------------------
  288. // Purpose:
  289. //-----------------------------------------------------------------------------
  290. void CBaseHelicopter::DrawRotorWash( float flAltitude, const Vector &vecRotorOrigin )
  291. {
  292. // Shake any ropes nearby
  293. if ( random->RandomInt( 0, 2 ) == 0 )
  294. {
  295. CRopeKeyframe::ShakeRopes( GetAbsOrigin(), flAltitude, 128 );
  296. }
  297. if ( m_spawnflags & SF_NOROTORWASH )
  298. return;
  299. DoRotorPhysicsPush( vecRotorOrigin, flAltitude );
  300. if ( m_flRotorWashEntitySearchTime > gpGlobals->curtime )
  301. return;
  302. // Only push every half second
  303. m_flRotorWashEntitySearchTime = gpGlobals->curtime + 0.5f;
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Purpose: Push an airboat in our wash
  307. //-----------------------------------------------------------------------------
  308. #define MAX_AIRBOAT_ROLL_ANGLE 20.0f
  309. #define MAX_AIRBOAT_ROLL_COSANGLE 0.866f
  310. #define MAX_AIRBOAT_ROLL_COSANGLE_X2 0.5f
  311. void CBaseHelicopter::DoWashPushOnAirboat( CBaseEntity *pAirboat,
  312. const Vector &vecWashToAirboat, float flWashAmount )
  313. {
  314. // For the airboat, simply produce a small roll and a push outwards.
  315. // But don't produce a roll if we're too rolled in that direction already.
  316. // Get the actual up direction vector
  317. Vector vecUp;
  318. pAirboat->GetVectors( NULL, NULL, &vecUp );
  319. if ( vecUp.z < MAX_AIRBOAT_ROLL_COSANGLE )
  320. return;
  321. // Compute roll direction so that we get pushed down on the side where the rotor wash is.
  322. Vector vecRollNormal;
  323. CrossProduct( vecWashToAirboat, Vector( 0, 0, 1 ), vecRollNormal );
  324. // Project it into the plane of the roll normal
  325. VectorMA( vecUp, -DotProduct( vecUp, vecRollNormal ), vecRollNormal, vecUp );
  326. VectorNormalize( vecUp );
  327. // Compute a vector which is the max direction we can roll given the roll constraint
  328. Vector vecExtremeUp;
  329. VMatrix rot;
  330. MatrixBuildRotationAboutAxis( rot, vecRollNormal, MAX_AIRBOAT_ROLL_ANGLE );
  331. MatrixGetColumn( rot, 2, &vecExtremeUp );
  332. // Find the angle between how vertical we are and how vertical we should be
  333. float flCosDelta = DotProduct( vecExtremeUp, vecUp );
  334. float flDelta = acos(flCosDelta) * 180.0f / M_PI;
  335. flDelta = clamp( flDelta, 0.0f, MAX_AIRBOAT_ROLL_ANGLE );
  336. flDelta = SimpleSplineRemapVal( flDelta, 0.0f, MAX_AIRBOAT_ROLL_ANGLE, 0.0f, 1.0f );
  337. float flForce = 12.0f * flWashAmount * flDelta;
  338. Vector vecWashOrigin;
  339. Vector vecForce;
  340. VectorMultiply( Vector( 0, 0, -1 ), flForce, vecForce );
  341. VectorMA( pAirboat->GetAbsOrigin(), -200.0f, vecWashToAirboat, vecWashOrigin );
  342. pAirboat->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) );
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose: Push a physics object in our wash. Return false if it's now out of our wash
  346. //-----------------------------------------------------------------------------
  347. bool CBaseHelicopter::DoWashPush( washentity_t *pWash, const Vector &vecWashOrigin )
  348. {
  349. if ( !pWash || !pWash->hEntity.Get() )
  350. return false;
  351. // Make sure the entity is still within our wash's radius
  352. CBaseEntity *pEntity = pWash->hEntity;
  353. // This can happen because we can dynamically turn this flag on and off
  354. if ( pEntity->IsEFlagSet( EFL_NO_ROTORWASH_PUSH ))
  355. return false;
  356. Vector vecSpot = pEntity->BodyTarget( vecWashOrigin );
  357. Vector vecToSpot = ( vecSpot - vecWashOrigin );
  358. vecToSpot.z = 0;
  359. float flDist = VectorNormalize( vecToSpot );
  360. if ( flDist > BASECHOPPER_WASH_RADIUS )
  361. return false;
  362. IRotorWashShooter *pShooter = GetRotorWashShooter( pEntity );
  363. IPhysicsObject *pPhysObject;
  364. float flPushTime = (gpGlobals->curtime - pWash->flWashStartTime);
  365. flPushTime = clamp( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME );
  366. float flWashAmount = RemapVal( flPushTime, 0, BASECHOPPER_WASH_RAMP_TIME, BASECHOPPER_WASH_PUSH_MIN, BASECHOPPER_WASH_PUSH_MAX );
  367. if ( pShooter )
  368. {
  369. Vector vecForce = (0.015f / 0.1f) * flWashAmount * vecToSpot * phys_pushscale.GetFloat();
  370. pEntity = pShooter->DoWashPush( pWash->flWashStartTime, vecForce );
  371. if ( !pEntity )
  372. return true;
  373. washentity_t Wash;
  374. Wash.hEntity = pEntity;
  375. Wash.flWashStartTime = pWash->flWashStartTime;
  376. int i = m_hEntitiesPushedByWash.AddToTail( Wash );
  377. pWash = &m_hEntitiesPushedByWash[i];
  378. pPhysObject = pEntity->VPhysicsGetObject();
  379. if ( !pPhysObject )
  380. return true;
  381. }
  382. else
  383. {
  384. // Airboat gets special treatment
  385. if ( FClassnameIs( pEntity, "prop_vehicle_airboat" ) )
  386. {
  387. DoWashPushOnAirboat( pEntity, vecToSpot, flWashAmount );
  388. return true;
  389. }
  390. pPhysObject = pEntity->VPhysicsGetObject();
  391. if ( !pPhysObject )
  392. return false;
  393. }
  394. // Push it away from the center of the wash
  395. float flMass = pPhysObject->GetMass();
  396. // This used to be mass independent, which is a bad idea because it blows 200kg engine blocks
  397. // as much as it blows cardboard and soda cans. Make this force mass-independent, but clamp at
  398. // 30kg.
  399. flMass = MIN( flMass, 30.0f );
  400. Vector vecForce = (0.015f / 0.1f) * flWashAmount * flMass * vecToSpot * phys_pushscale.GetFloat();
  401. pEntity->VPhysicsTakeDamage( CTakeDamageInfo( this, this, vecForce, vecWashOrigin, flWashAmount, DMG_BLAST ) );
  402. // Debug
  403. if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH )
  404. {
  405. NDebugOverlay::Cross3D( pEntity->GetAbsOrigin(), -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.1f );
  406. NDebugOverlay::Line( pEntity->GetAbsOrigin(), pEntity->GetAbsOrigin() + vecForce, 255, 255, 0, true, 0.1f );
  407. IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
  408. Msg("Pushed %s (index %d) (mass %f) with force %f (min %.2f max %.2f) at time %.2f\n",
  409. pEntity->GetClassname(), pEntity->entindex(), pPhysObject->GetMass(), flWashAmount,
  410. BASECHOPPER_WASH_PUSH_MIN * flMass, BASECHOPPER_WASH_PUSH_MAX * flMass, gpGlobals->curtime );
  411. }
  412. // If we've pushed this thing for some time, remove it to give us a chance to find lighter things nearby
  413. if ( flPushTime > 2.0 )
  414. return false;
  415. return true;
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Purpose:
  419. //-----------------------------------------------------------------------------
  420. void CBaseHelicopter::DoRotorPhysicsPush( const Vector &vecRotorOrigin, float flAltitude )
  421. {
  422. CBaseEntity *pEntity = NULL;
  423. trace_t tr;
  424. // First, trace down and find out where the was is hitting the ground
  425. UTIL_TraceLine( vecRotorOrigin, vecRotorOrigin+Vector(0,0,-flAltitude), (MASK_SOLID_BRUSHONLY|CONTENTS_WATER), NULL, COLLISION_GROUP_NONE, &tr );
  426. // Always raise the physics origin a bit
  427. Vector vecPhysicsOrigin = tr.endpos + Vector(0,0,64);
  428. // Debug
  429. if ( g_debug_basehelicopter.GetInt() == BASECHOPPER_DEBUG_WASH )
  430. {
  431. NDebugOverlay::Cross3D( vecPhysicsOrigin, -Vector(16,16,16), Vector(16,16,16), 0, 255, 255, true, 0.1f );
  432. }
  433. // Push entities that we've pushed before, and are still within range
  434. // Walk backwards because they may be removed if they're now out of range
  435. int iCount = m_hEntitiesPushedByWash.Count();
  436. bool bWasPushingObjects = (iCount > 0);
  437. for ( int i = (iCount-1); i >= 0; i-- )
  438. {
  439. if ( !DoWashPush( &(m_hEntitiesPushedByWash[i]), vecPhysicsOrigin ) )
  440. {
  441. // Out of range now, so remove
  442. m_hEntitiesPushedByWash.Remove(i);
  443. }
  444. }
  445. if ( m_flRotorWashEntitySearchTime > gpGlobals->curtime )
  446. return;
  447. // Any spare slots?
  448. iCount = m_hEntitiesPushedByWash.Count();
  449. if ( iCount >= BASECHOPPER_WASH_MAX_OBJECTS )
  450. return;
  451. // Find the lightest physics entity below us and add it to our list to push around
  452. CBaseEntity *pLightestEntity = NULL;
  453. float flLightestMass = 9999;
  454. while ((pEntity = gEntList.FindEntityInSphere(pEntity, vecPhysicsOrigin, BASECHOPPER_WASH_RADIUS )) != NULL)
  455. {
  456. IRotorWashShooter *pShooter = GetRotorWashShooter( pEntity );
  457. if ( pEntity->IsEFlagSet( EFL_NO_ROTORWASH_PUSH ))
  458. continue;
  459. if ( pShooter || pEntity->GetMoveType() == MOVETYPE_VPHYSICS || (pEntity->VPhysicsGetObject() && !pEntity->IsPlayer()) )
  460. {
  461. // Make sure it's not already in our wash
  462. bool bAlreadyPushing = false;
  463. for ( int i = 0; i < iCount; i++ )
  464. {
  465. if ( m_hEntitiesPushedByWash[i].hEntity == pEntity )
  466. {
  467. bAlreadyPushing = true;
  468. break;
  469. }
  470. }
  471. if ( bAlreadyPushing )
  472. continue;
  473. float flMass = FLT_MAX;
  474. if ( pShooter )
  475. {
  476. flMass = 1.0f;
  477. }
  478. else
  479. {
  480. // Don't try to push anything too big
  481. IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
  482. if ( pPhysObject )
  483. {
  484. flMass = pPhysObject->GetMass();
  485. if ( flMass > BASECHOPPER_WASH_MAX_MASS )
  486. continue;
  487. }
  488. }
  489. // Ignore anything bigger than the one we've already found
  490. if ( flMass > flLightestMass )
  491. continue;
  492. Vector vecSpot = pEntity->BodyTarget( vecPhysicsOrigin );
  493. // Don't push things too far below our starting point (helps reduce through-roof cases w/o doing a trace)
  494. if ( fabs( vecSpot.z - vecPhysicsOrigin.z ) > 96 )
  495. continue;
  496. Vector vecToSpot = ( vecSpot - vecPhysicsOrigin );
  497. vecToSpot.z = 0;
  498. float flDist = VectorNormalize( vecToSpot );
  499. if ( flDist > BASECHOPPER_WASH_RADIUS )
  500. continue;
  501. // Try to cast to the helicopter; if we can't, then we can't be hit.
  502. if ( pEntity->GetServerVehicle() )
  503. {
  504. UTIL_TraceLine( vecSpot, vecPhysicsOrigin, MASK_SOLID_BRUSHONLY, pEntity, COLLISION_GROUP_NONE, &tr );
  505. if ( tr.fraction != 1.0f )
  506. continue;
  507. }
  508. flLightestMass = flMass;
  509. pLightestEntity = pEntity;
  510. washentity_t Wash;
  511. Wash.hEntity = pLightestEntity;
  512. Wash.flWashStartTime = gpGlobals->curtime;
  513. m_hEntitiesPushedByWash.AddToTail( Wash );
  514. // Can we fit more after adding this one? No? Then we are done.
  515. iCount = m_hEntitiesPushedByWash.Count();
  516. if ( iCount >= BASECHOPPER_WASH_MAX_OBJECTS )
  517. break;
  518. }
  519. }
  520. // Handle sound.
  521. // If we just started pushing objects, ramp the blast sound up.
  522. if ( !bWasPushingObjects && m_hEntitiesPushedByWash.Count() )
  523. {
  524. if ( m_pRotorBlast )
  525. {
  526. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  527. controller.SoundChangeVolume( m_pRotorBlast, 1.0, 1.0 );
  528. }
  529. }
  530. else if ( bWasPushingObjects && m_hEntitiesPushedByWash.Count() == 0 )
  531. {
  532. if ( m_pRotorBlast )
  533. {
  534. // We just stopped pushing objects, so fade the blast sound out.
  535. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  536. controller.SoundChangeVolume( m_pRotorBlast, 0, 1.0 );
  537. }
  538. }
  539. }
  540. //------------------------------------------------------------------------------
  541. // Updates the enemy
  542. //------------------------------------------------------------------------------
  543. float CBaseHelicopter::EnemySearchDistance( )
  544. {
  545. return 4092;
  546. }
  547. //------------------------------------------------------------------------------
  548. // Updates the enemy
  549. //------------------------------------------------------------------------------
  550. void CBaseHelicopter::UpdateEnemy()
  551. {
  552. if( HasCondition( COND_ENEMY_DEAD ) )
  553. {
  554. SetEnemy( NULL );
  555. }
  556. // Look for my best enemy. If I change enemies,
  557. // be sure and change my prevseen/lastseen timers.
  558. if( m_lifeState == LIFE_ALIVE )
  559. {
  560. GetSenses()->Look( EnemySearchDistance() );
  561. GetEnemies()->RefreshMemories();
  562. ChooseEnemy();
  563. if( HasEnemy() )
  564. {
  565. CBaseEntity *pEnemy = GetEnemy();
  566. GatherEnemyConditions( pEnemy );
  567. if ( FVisible( pEnemy ) )
  568. {
  569. if (m_flLastSeen < gpGlobals->curtime - 2)
  570. {
  571. m_flPrevSeen = gpGlobals->curtime;
  572. }
  573. m_flLastSeen = gpGlobals->curtime;
  574. m_vecTargetPosition = pEnemy->WorldSpaceCenter();
  575. }
  576. }
  577. else
  578. {
  579. // look at where we're going instead
  580. m_vecTargetPosition = GetDesiredPosition();
  581. }
  582. }
  583. else
  584. {
  585. // If we're dead or dying, forget our enemy and don't look for new ones(sjb)
  586. SetEnemy( NULL );
  587. }
  588. }
  589. //------------------------------------------------------------------------------
  590. // Purpose : Override the desired position if your derived helicopter is doing something special
  591. //------------------------------------------------------------------------------
  592. void CBaseHelicopter::UpdateDesiredPosition( void )
  593. {
  594. }
  595. //------------------------------------------------------------------------------
  596. // Updates the facing direction
  597. //------------------------------------------------------------------------------
  598. void CBaseHelicopter::UpdateFacingDirection()
  599. {
  600. if ( 1 )
  601. {
  602. Vector targetDir = m_vecTargetPosition - GetAbsOrigin();
  603. Vector desiredDir = GetDesiredPosition() - GetAbsOrigin();
  604. VectorNormalize( targetDir );
  605. VectorNormalize( desiredDir );
  606. if ( !IsCrashing() && m_flLastSeen + 5 > gpGlobals->curtime ) //&& DotProduct( targetDir, desiredDir) > 0.25)
  607. {
  608. // If we've seen the target recently, face the target.
  609. //Msg( "Facing Target \n" );
  610. m_vecDesiredFaceDir = targetDir;
  611. }
  612. else
  613. {
  614. // Face our desired position.
  615. // Msg( "Facing Position\n" );
  616. m_vecDesiredFaceDir = desiredDir;
  617. }
  618. }
  619. else
  620. {
  621. // Face the way the path corner tells us to.
  622. //Msg( "Facing my path corner\n" );
  623. m_vecDesiredFaceDir = GetGoalOrientation();
  624. }
  625. }
  626. //------------------------------------------------------------------------------
  627. // Fire weapons
  628. //------------------------------------------------------------------------------
  629. void CBaseHelicopter::FireWeapons()
  630. {
  631. // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->curtime, m_flLastSeen, m_flPrevSeen );
  632. if (m_fHelicopterFlags & BITS_HELICOPTER_GUN_ON)
  633. {
  634. //if ( (m_flLastSeen + 1 > gpGlobals->curtime) && (m_flPrevSeen + 2 < gpGlobals->curtime) )
  635. {
  636. if (FireGun( ))
  637. {
  638. // slow down if we're firing
  639. if (m_flGoalSpeed > GetMaxSpeedFiring() )
  640. {
  641. m_flGoalSpeed = GetMaxSpeedFiring();
  642. }
  643. }
  644. }
  645. }
  646. if (m_fHelicopterFlags & BITS_HELICOPTER_MISSILE_ON)
  647. {
  648. AimRocketGun();
  649. }
  650. }
  651. //------------------------------------------------------------------------------
  652. // Purpose :
  653. // Input :
  654. // Output :
  655. //------------------------------------------------------------------------------
  656. void CBaseHelicopter::Hunt( void )
  657. {
  658. UpdateEnemy();
  659. UpdateTrackNavigation( );
  660. UpdateDesiredPosition();
  661. UpdateFacingDirection();
  662. Flight();
  663. UpdatePlayerDopplerShift( );
  664. FireWeapons();
  665. }
  666. //------------------------------------------------------------------------------
  667. // Purpose :
  668. // Input :
  669. // Output :
  670. //------------------------------------------------------------------------------
  671. void CBaseHelicopter::UpdatePlayerDopplerShift( )
  672. {
  673. // -----------------------------
  674. // make rotor, engine sounds
  675. // -----------------------------
  676. if (m_iSoundState == 0)
  677. {
  678. // Sound startup.
  679. InitializeRotorSound();
  680. }
  681. else
  682. {
  683. CBaseEntity *pPlayer = NULL;
  684. // UNDONE: this needs to send different sounds to every player for multiplayer.
  685. // FIXME: this isn't the correct way to find a player!!!
  686. pPlayer = gEntList.FindEntityByName( NULL, "!player" );
  687. if (pPlayer)
  688. {
  689. Vector dir;
  690. VectorSubtract( pPlayer->GetAbsOrigin(), GetAbsOrigin(), dir );
  691. VectorNormalize(dir);
  692. #if 1
  693. float velReceiver = DotProduct( pPlayer->GetAbsVelocity(), dir );
  694. float velTransmitter = -DotProduct( GetAbsVelocity(), dir );
  695. // speed of sound == 13049in/s
  696. int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049));
  697. #else
  698. // This is a bogus doppler shift, but I like it better
  699. float relV = DotProduct( GetAbsVelocity() - pPlayer->GetAbsVelocity(), dir );
  700. int iPitch = (int)(100 + relV / 50.0);
  701. #endif
  702. // clamp pitch shifts
  703. if (iPitch > 250)
  704. {
  705. iPitch = 250;
  706. }
  707. if (iPitch < 50)
  708. {
  709. iPitch = 50;
  710. }
  711. UpdateRotorSoundPitch( iPitch );
  712. // Msg( "Pitch:%d\n", iPitch );
  713. }
  714. else
  715. {
  716. Msg( "Chopper didn't find a player!\n" );
  717. }
  718. }
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Computes the actual position to fly to
  722. //-----------------------------------------------------------------------------
  723. void CBaseHelicopter::ComputeActualTargetPosition( float flSpeed, float flTime, float flPerpDist, Vector *pDest, bool bApplyNoise )
  724. {
  725. // This is used to make the helicopter drift around a bit.
  726. if ( bApplyNoise && m_flRandomOffsetTime <= gpGlobals->curtime )
  727. {
  728. m_vecRandomOffset.Random( -25.0f, 25.0f );
  729. m_flRandomOffsetTime = gpGlobals->curtime + 1.0f;
  730. }
  731. if ( IsLeading() && GetEnemy() && IsOnPathTrack() )
  732. {
  733. ComputePointAlongCurrentPath( flSpeed * flTime, flPerpDist, pDest );
  734. *pDest += m_vecRandomOffset;
  735. return;
  736. }
  737. *pDest = GetDesiredPosition() - GetAbsOrigin();
  738. float flDistToDesired = pDest->Length();
  739. if (flDistToDesired > flSpeed * flTime)
  740. {
  741. float scale = flSpeed * flTime / flDistToDesired;
  742. *pDest *= scale;
  743. }
  744. else if ( IsOnPathTrack() )
  745. {
  746. // Blend in a fake destination point based on the dest velocity
  747. Vector vecDestVelocity;
  748. ComputeNormalizedDestVelocity( &vecDestVelocity );
  749. vecDestVelocity *= flSpeed;
  750. float flBlendFactor = 1.0f - flDistToDesired / (flSpeed * flTime);
  751. VectorMA( *pDest, flTime * flBlendFactor, vecDestVelocity, *pDest );
  752. }
  753. *pDest += GetAbsOrigin();
  754. if ( bApplyNoise )
  755. {
  756. // ComputePointAlongCurrentPath( flSpeed * flTime, flPerpDist, pDest );
  757. *pDest += m_vecRandomOffset;
  758. }
  759. }
  760. //------------------------------------------------------------------------------
  761. //------------------------------------------------------------------------------
  762. void CBaseHelicopter::Flight( void )
  763. {
  764. if( GetFlags() & FL_ONGROUND )
  765. {
  766. //This would be really bad.
  767. SetGroundEntity( NULL );
  768. }
  769. // Generic speed up
  770. if (m_flGoalSpeed < GetMaxSpeed())
  771. {
  772. m_flGoalSpeed += GetAcceleration();
  773. }
  774. //NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0.1);
  775. // tilt model 5 degrees (why?! sjb)
  776. QAngle vecAdj = QAngle( 5.0, 0, 0 );
  777. // estimate where I'll be facing in one seconds
  778. Vector forward, right, up;
  779. AngleVectors( GetLocalAngles() + GetLocalAngularVelocity() * 2 + vecAdj, &forward, &right, &up );
  780. // Vector vecEst1 = GetLocalOrigin() + GetAbsVelocity() + up * m_flForce - Vector( 0, 0, 384 );
  781. // float flSide = DotProduct( m_vecDesiredPosition - vecEst1, right );
  782. QAngle angVel = GetLocalAngularVelocity();
  783. float flSide = DotProduct( m_vecDesiredFaceDir, right );
  784. if (flSide < 0)
  785. {
  786. if (angVel.y < 60)
  787. {
  788. angVel.y += 8;
  789. }
  790. }
  791. else
  792. {
  793. if (angVel.y > -60)
  794. {
  795. angVel.y -= 8;
  796. }
  797. }
  798. angVel.y *= ( 0.98 ); // why?! (sjb)
  799. // estimate where I'll be in two seconds
  800. AngleVectors( GetLocalAngles() + angVel * 1 + vecAdj, NULL, NULL, &up );
  801. Vector vecEst = GetAbsOrigin() + GetAbsVelocity() * 2.0 + up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
  802. // add immediate force
  803. AngleVectors( GetLocalAngles() + vecAdj, &forward, &right, &up );
  804. Vector vecImpulse( 0, 0, 0 );
  805. vecImpulse.x += up.x * m_flForce;
  806. vecImpulse.y += up.y * m_flForce;
  807. vecImpulse.z += up.z * m_flForce;
  808. // add gravity
  809. vecImpulse.z -= 38.4; // 32ft/sec
  810. ApplyAbsVelocityImpulse( vecImpulse );
  811. float flSpeed = GetAbsVelocity().Length();
  812. float flDir = DotProduct( Vector( forward.x, forward.y, 0 ), Vector( GetAbsVelocity().x, GetAbsVelocity().y, 0 ) );
  813. if (flDir < 0)
  814. {
  815. flSpeed = -flSpeed;
  816. }
  817. float flDist = DotProduct( GetDesiredPosition() - vecEst, forward );
  818. // float flSlip = DotProduct( GetAbsVelocity(), right );
  819. float flSlip = -DotProduct( GetDesiredPosition() - vecEst, right );
  820. // fly sideways
  821. if (flSlip > 0)
  822. {
  823. if (GetLocalAngles().z > -30 && angVel.z > -15)
  824. angVel.z -= 4;
  825. else
  826. angVel.z += 2;
  827. }
  828. else
  829. {
  830. if (GetLocalAngles().z < 30 && angVel.z < 15)
  831. angVel.z += 4;
  832. else
  833. angVel.z -= 2;
  834. }
  835. // These functions contain code Ken wrote that used to be right here as part of the flight model,
  836. // but we want different helicopter vehicles to have different drag characteristics, so I made
  837. // them virtual functions (sjb)
  838. ApplySidewaysDrag( right );
  839. ApplyGeneralDrag();
  840. // apply power to stay correct height
  841. // FIXME: these need to be per class variables
  842. #define MAX_FORCE 80
  843. #define FORCE_POSDELTA 12
  844. #define FORCE_NEGDELTA 8
  845. if (m_flForce < MAX_FORCE && vecEst.z < GetDesiredPosition().z)
  846. {
  847. m_flForce += FORCE_POSDELTA;
  848. }
  849. else if (m_flForce > 30)
  850. {
  851. if (vecEst.z > GetDesiredPosition().z)
  852. m_flForce -= FORCE_NEGDELTA;
  853. }
  854. // pitch forward or back to get to target
  855. //-----------------------------------------
  856. // Pitch is reversed since Half-Life! (sjb)
  857. //-----------------------------------------
  858. if (flDist > 0 && flSpeed < m_flGoalSpeed /* && flSpeed < flDist */ && GetLocalAngles().x + angVel.x < 40)
  859. {
  860. // ALERT( at_console, "F " );
  861. // lean forward
  862. angVel.x += 12.0;
  863. }
  864. else if (flDist < 0 && flSpeed > -50 && GetLocalAngles().x + angVel.x > -20)
  865. {
  866. // ALERT( at_console, "B " );
  867. // lean backward
  868. angVel.x -= 12.0;
  869. }
  870. else if (GetLocalAngles().x + angVel.x < 0)
  871. {
  872. // ALERT( at_console, "f " );
  873. angVel.x += 4.0;
  874. }
  875. else if (GetLocalAngles().x + angVel.x > 0)
  876. {
  877. // ALERT( at_console, "b " );
  878. angVel.x -= 4.0;
  879. }
  880. SetLocalAngularVelocity( angVel );
  881. // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", GetAbsOrigin().x, GetAbsVelocity().x, flDist, flSpeed, GetLocalAngles().x, m_vecAngVelocity.x, m_flForce );
  882. // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", GetAbsOrigin().z, GetAbsVelocity().z, vecEst.z, m_vecDesiredPosition.z, m_flForce );
  883. }
  884. //------------------------------------------------------------------------------
  885. // Updates the rotor wash volume
  886. //------------------------------------------------------------------------------
  887. void CBaseHelicopter::UpdateRotorWashVolume()
  888. {
  889. if ( !m_pRotorSound )
  890. return;
  891. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  892. float flVolDelta = GetRotorVolume() - controller.SoundGetVolume( m_pRotorSound );
  893. if ( flVolDelta )
  894. {
  895. // We can change from 0 to 1 in 3 seconds.
  896. // Figure out how many seconds flVolDelta will take.
  897. float flRampTime = fabs( flVolDelta ) * 3.0f;
  898. controller.SoundChangeVolume( m_pRotorSound, GetRotorVolume(), flRampTime );
  899. }
  900. }
  901. //------------------------------------------------------------------------------
  902. // For scripted times where it *has* to shoot
  903. //------------------------------------------------------------------------------
  904. float CBaseHelicopter::GetRotorVolume( void )
  905. {
  906. return m_bSuppressSound ? 0.0f : 1.0f;
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Rotor sound
  910. //-----------------------------------------------------------------------------
  911. void CBaseHelicopter::InputEnableRotorSound( inputdata_t &inputdata )
  912. {
  913. m_bSuppressSound = false;
  914. }
  915. void CBaseHelicopter::InputDisableRotorSound( inputdata_t &inputdata )
  916. {
  917. m_bSuppressSound = true;
  918. }
  919. //-----------------------------------------------------------------------------
  920. // Purpose: Marks the entity for deletion
  921. //-----------------------------------------------------------------------------
  922. void CBaseHelicopter::InputKill( inputdata_t &inputdata )
  923. {
  924. StopRotorWash();
  925. m_bSuppressSound = true;
  926. SetContextThink( &CBaseHelicopter::DelayedKillThink, gpGlobals->curtime + 3.0f, s_pDelayedKillThinkContext );
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Purpose:
  930. //-----------------------------------------------------------------------------
  931. void CBaseHelicopter::StopRotorWash( void )
  932. {
  933. if ( m_hRotorWash )
  934. {
  935. UTIL_Remove( m_hRotorWash );
  936. m_hRotorWash = NULL;
  937. }
  938. }
  939. //-----------------------------------------------------------------------------
  940. // Purpose: Marks the entity for deletion
  941. //-----------------------------------------------------------------------------
  942. void CBaseHelicopter::DelayedKillThink( )
  943. {
  944. // tell owner ( if any ) that we're dead.This is mostly for NPCMaker functionality.
  945. CBaseEntity *pOwner = GetOwnerEntity();
  946. if ( pOwner )
  947. {
  948. pOwner->DeathNotice( this );
  949. SetOwnerEntity( NULL );
  950. }
  951. UTIL_Remove( this );
  952. }
  953. //------------------------------------------------------------------------------
  954. // Purpose :
  955. // Input :
  956. // Output :
  957. //------------------------------------------------------------------------------
  958. void CBaseHelicopter::InitializeRotorSound( void )
  959. {
  960. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  961. if ( m_pRotorSound )
  962. {
  963. // Get the rotor sound started up.
  964. controller.Play( m_pRotorSound, 0.0, 100 );
  965. UpdateRotorWashVolume();
  966. }
  967. if ( m_pRotorBlast )
  968. {
  969. // Start the blast sound and then immediately drop it to 0 (starting it at 0 wouldn't start it)
  970. controller.Play( m_pRotorBlast, 1.0, 100 );
  971. controller.SoundChangeVolume(m_pRotorBlast, 0, 0.0);
  972. }
  973. m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
  974. }
  975. //------------------------------------------------------------------------------
  976. // Purpose :
  977. // Input :
  978. // Output :
  979. //------------------------------------------------------------------------------
  980. void CBaseHelicopter::UpdateRotorSoundPitch( int iPitch )
  981. {
  982. if (m_pRotorSound)
  983. {
  984. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  985. controller.SoundChangePitch( m_pRotorSound, iPitch, 0.1 );
  986. UpdateRotorWashVolume();
  987. }
  988. }
  989. //------------------------------------------------------------------------------
  990. // Purpose :
  991. // Input :
  992. // Output :
  993. //------------------------------------------------------------------------------
  994. void CBaseHelicopter::FlyTouch( CBaseEntity *pOther )
  995. {
  996. // bounce if we hit something solid
  997. if ( pOther->GetSolid() == SOLID_BSP)
  998. {
  999. // trace_t tr;
  1000. // tr = CBaseEntity::GetTouchTrace();
  1001. // UNDONE, do a real bounce
  1002. // FIXME: This causes bad problems, so we just ignore it right now
  1003. //ApplyAbsVelocityImpulse( tr.plane.normal * (GetAbsVelocity().Length() + 200) );
  1004. }
  1005. }
  1006. //------------------------------------------------------------------------------
  1007. // Purpose :
  1008. // Input :
  1009. // Output :
  1010. //------------------------------------------------------------------------------
  1011. void CBaseHelicopter::CrashTouch( CBaseEntity *pOther )
  1012. {
  1013. // only crash if we hit something solid
  1014. if ( pOther->GetSolid() == SOLID_BSP)
  1015. {
  1016. SetTouch( NULL );
  1017. SetNextThink( gpGlobals->curtime );
  1018. }
  1019. }
  1020. //------------------------------------------------------------------------------
  1021. // Purpose :
  1022. // Input :
  1023. // Output :
  1024. //------------------------------------------------------------------------------
  1025. void CBaseHelicopter::DyingThink( void )
  1026. {
  1027. StudioFrameAdvance( );
  1028. SetNextThink( gpGlobals->curtime + 0.1f );
  1029. SetLocalAngularVelocity( GetLocalAngularVelocity() * 1.02 );
  1030. }
  1031. //-----------------------------------------------------------------------------
  1032. // Purpose: Override base class to add display of fly direction
  1033. // Input :
  1034. // Output :
  1035. //-----------------------------------------------------------------------------
  1036. void CBaseHelicopter::DrawDebugGeometryOverlays(void)
  1037. {
  1038. if (m_pfnThink!= NULL)
  1039. {
  1040. // ------------------------------
  1041. // Draw route if requested
  1042. // ------------------------------
  1043. if (m_debugOverlays & OVERLAY_NPC_ROUTE_BIT)
  1044. {
  1045. NDebugOverlay::Line(GetAbsOrigin(), GetDesiredPosition(), 0,0,255, true, 0);
  1046. }
  1047. }
  1048. BaseClass::DrawDebugGeometryOverlays();
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. // Purpose:
  1052. // Input :
  1053. // Output :
  1054. //-----------------------------------------------------------------------------
  1055. void CBaseHelicopter::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  1056. {
  1057. // Take no damage from trace attacks unless it's blast damage. RadiusDamage() sometimes calls
  1058. // TraceAttack() as a means for delivering blast damage. Usually when the explosive penetrates
  1059. // the target. (RPG missiles do this sometimes).
  1060. if( info.GetDamageType() & (DMG_BLAST|DMG_AIRBOAT) )
  1061. {
  1062. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  1063. }
  1064. }
  1065. //------------------------------------------------------------------------------
  1066. // Purpose :
  1067. // Input :
  1068. // Output :
  1069. //------------------------------------------------------------------------------
  1070. void CBaseHelicopter::NullThink( void )
  1071. {
  1072. StudioFrameAdvance( );
  1073. SetNextThink( gpGlobals->curtime + 0.5f );
  1074. }
  1075. void CBaseHelicopter::Startup( void )
  1076. {
  1077. StopRotorWash();
  1078. if ( !( m_spawnflags & SF_NOROTORWASH ) )
  1079. {
  1080. m_hRotorWash = CreateRotorWashEmitter( GetAbsOrigin(), GetAbsAngles(), this, BASECHOPPER_WASH_ALTITUDE );
  1081. }
  1082. // Fade in the blades
  1083. m_flStartupTime = gpGlobals->curtime;
  1084. m_flGoalSpeed = m_flInitialSpeed;
  1085. SetThink( &CBaseHelicopter::HelicopterThink );
  1086. SetTouch( &CBaseHelicopter::FlyTouch );
  1087. SetNextThink( gpGlobals->curtime + 0.1f );
  1088. m_flRotorWashEntitySearchTime = gpGlobals->curtime;
  1089. SetContextThink( &CBaseHelicopter::RotorWashThink, gpGlobals->curtime, s_pRotorWashThinkContext );
  1090. }
  1091. void CBaseHelicopter::StopLoopingSounds()
  1092. {
  1093. // Kill the rotor sounds
  1094. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1095. controller.SoundDestroy( m_pRotorSound );
  1096. controller.SoundDestroy( m_pRotorBlast );
  1097. m_pRotorSound = NULL;
  1098. m_pRotorBlast = NULL;
  1099. BaseClass::StopLoopingSounds();
  1100. }
  1101. void CBaseHelicopter::Event_Killed( const CTakeDamageInfo &info )
  1102. {
  1103. m_lifeState = LIFE_DYING;
  1104. SetMoveType( MOVETYPE_FLYGRAVITY );
  1105. SetGravity( UTIL_ScaleForGravity( 240 ) ); // use a lower gravity
  1106. StopLoopingSounds();
  1107. UTIL_SetSize( this, Vector( -32, -32, -64), Vector( 32, 32, 0) );
  1108. SetThink( &CBaseHelicopter::CallDyingThink );
  1109. SetTouch( &CBaseHelicopter::CrashTouch );
  1110. SetNextThink( gpGlobals->curtime + 0.1f );
  1111. m_iHealth = 0;
  1112. m_takedamage = DAMAGE_NO;
  1113. /*
  1114. if (m_spawnflags & SF_NOWRECKAGE)
  1115. {
  1116. m_flNextRocket = gpGlobals->curtime + 4.0;
  1117. }
  1118. else
  1119. {
  1120. m_flNextRocket = gpGlobals->curtime + 15.0;
  1121. }
  1122. */
  1123. StopRotorWash();
  1124. m_OnDeath.FireOutput( info.GetAttacker(), this );
  1125. }
  1126. void CBaseHelicopter::GibMonster( void )
  1127. {
  1128. }
  1129. //-----------------------------------------------------------------------------
  1130. // Purpose: Call Startup for a helicopter that's been flagged to start disabled
  1131. //-----------------------------------------------------------------------------
  1132. void CBaseHelicopter::InputActivate( inputdata_t &inputdata )
  1133. {
  1134. if( m_spawnflags & SF_AWAITINPUT )
  1135. {
  1136. Startup();
  1137. // Now clear the spawnflag to protect from
  1138. // subsequent calls.
  1139. m_spawnflags &= ~SF_AWAITINPUT;
  1140. }
  1141. }
  1142. //------------------------------------------------------------------------------
  1143. // Purpose : Turn the gun on
  1144. //------------------------------------------------------------------------------
  1145. void CBaseHelicopter::InputGunOn( inputdata_t &inputdata )
  1146. {
  1147. m_fHelicopterFlags |= BITS_HELICOPTER_GUN_ON;
  1148. }
  1149. //-----------------------------------------------------------------------------
  1150. // Purpose: Turn the gun off
  1151. //-----------------------------------------------------------------------------
  1152. void CBaseHelicopter::InputGunOff( inputdata_t &inputdata )
  1153. {
  1154. m_fHelicopterFlags &= ~BITS_HELICOPTER_GUN_ON;
  1155. }
  1156. //------------------------------------------------------------------------------
  1157. // Purpose : Turn the missile on
  1158. //------------------------------------------------------------------------------
  1159. void CBaseHelicopter::InputMissileOn( inputdata_t &inputdata )
  1160. {
  1161. m_fHelicopterFlags |= BITS_HELICOPTER_MISSILE_ON;
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. // Purpose: Turn the missile off
  1165. //-----------------------------------------------------------------------------
  1166. void CBaseHelicopter::InputMissileOff( inputdata_t &inputdata )
  1167. {
  1168. m_fHelicopterFlags &= ~BITS_HELICOPTER_MISSILE_ON;
  1169. }
  1170. //-----------------------------------------------------------------------------
  1171. // Enable, disable rotor wash
  1172. //-----------------------------------------------------------------------------
  1173. void CBaseHelicopter::InputEnableRotorWash( inputdata_t &inputdata )
  1174. {
  1175. m_spawnflags &= ~SF_NOROTORWASH;
  1176. }
  1177. void CBaseHelicopter::InputDisableRotorWash( inputdata_t &inputdata )
  1178. {
  1179. m_spawnflags |= SF_NOROTORWASH;
  1180. }
  1181. //-----------------------------------------------------------------------------
  1182. // Causes the helicopter to immediately accelerate to its desired velocity
  1183. //-----------------------------------------------------------------------------
  1184. void CBaseHelicopter::InputMoveTopSpeed( inputdata_t &inputdata )
  1185. {
  1186. Vector vecVelocity;
  1187. ComputeActualTargetPosition( GetMaxSpeed(), 1.0f, 0.0f, &vecVelocity, false );
  1188. vecVelocity -= GetAbsOrigin();
  1189. float flLength = VectorNormalize( vecVelocity );
  1190. if (flLength < 1e-3)
  1191. {
  1192. GetVectors( &vecVelocity, NULL, NULL );
  1193. }
  1194. vecVelocity *= GetMaxSpeed();
  1195. SetAbsVelocity( vecVelocity );
  1196. }
  1197. //-----------------------------------------------------------------------------
  1198. // Cause helicopter to immediately accelerate to specified velocity
  1199. //-----------------------------------------------------------------------------
  1200. void CBaseHelicopter::InputMoveSpecifiedSpeed( inputdata_t &inputdata )
  1201. {
  1202. Vector vecVelocity;
  1203. ComputeActualTargetPosition( GetMaxSpeed(), 1.0f, 0.0f, &vecVelocity, false );
  1204. vecVelocity -= GetAbsOrigin();
  1205. float flLength = VectorNormalize( vecVelocity );
  1206. if (flLength < 1e-3)
  1207. {
  1208. GetVectors( &vecVelocity, NULL, NULL );
  1209. }
  1210. float flSpeed = inputdata.value.Float();
  1211. vecVelocity *= flSpeed;
  1212. SetAbsVelocity( vecVelocity );
  1213. }
  1214. //------------------------------------------------------------------------------
  1215. // Input values
  1216. //------------------------------------------------------------------------------
  1217. void CBaseHelicopter::InputSetAngles( inputdata_t &inputdata )
  1218. {
  1219. const char *pAngles = inputdata.value.String();
  1220. QAngle angles;
  1221. UTIL_StringToVector( angles.Base(), pAngles );
  1222. SetAbsAngles( angles );
  1223. }
  1224. //-----------------------------------------------------------------------------
  1225. // Purpose:
  1226. // Input :
  1227. // Output :
  1228. //-----------------------------------------------------------------------------
  1229. void CBaseHelicopter::ApplySidewaysDrag( const Vector &vecRight )
  1230. {
  1231. Vector vecNewVelocity = GetAbsVelocity();
  1232. vecNewVelocity.x *= 1.0 - fabs( vecRight.x ) * 0.05;
  1233. vecNewVelocity.y *= 1.0 - fabs( vecRight.y ) * 0.05;
  1234. vecNewVelocity.z *= 1.0 - fabs( vecRight.z ) * 0.05;
  1235. SetAbsVelocity( vecNewVelocity );
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose:
  1239. // Input :
  1240. // Output :
  1241. //-----------------------------------------------------------------------------
  1242. void CBaseHelicopter::ApplyGeneralDrag( void )
  1243. {
  1244. Vector vecNewVelocity = GetAbsVelocity();
  1245. vecNewVelocity *= 0.995;
  1246. SetAbsVelocity( vecNewVelocity );
  1247. }
  1248. //-----------------------------------------------------------------------------
  1249. // Purpose:
  1250. // Input :
  1251. // Output :
  1252. //-----------------------------------------------------------------------------
  1253. bool CBaseHelicopter::ChooseEnemy( void )
  1254. {
  1255. // See if there's a new enemy.
  1256. CBaseEntity *pNewEnemy;
  1257. pNewEnemy = BestEnemy();
  1258. if ( pNewEnemy != GetEnemy() )
  1259. {
  1260. if ( pNewEnemy != NULL )
  1261. {
  1262. // New enemy! Clear the timers and set conditions.
  1263. SetEnemy( pNewEnemy );
  1264. m_flLastSeen = m_flPrevSeen = gpGlobals->curtime;
  1265. }
  1266. else
  1267. {
  1268. SetEnemy( NULL );
  1269. SetState( NPC_STATE_ALERT );
  1270. }
  1271. return true;
  1272. }
  1273. else
  1274. {
  1275. ClearCondition( COND_NEW_ENEMY );
  1276. return false;
  1277. }
  1278. }
  1279. //-----------------------------------------------------------------------------
  1280. // Purpose:
  1281. // Input :
  1282. // Output :
  1283. //-----------------------------------------------------------------------------
  1284. void CBaseHelicopter::GatherEnemyConditions( CBaseEntity *pEnemy )
  1285. {
  1286. // -------------------
  1287. // If enemy is dead
  1288. // -------------------
  1289. if ( !pEnemy->IsAlive() )
  1290. {
  1291. SetCondition( COND_ENEMY_DEAD );
  1292. ClearCondition( COND_SEE_ENEMY );
  1293. ClearCondition( COND_ENEMY_OCCLUDED );
  1294. return;
  1295. }
  1296. }
  1297. //-----------------------------------------------------------------------------
  1298. // Purpose:
  1299. // Input : *pInfo -
  1300. // bAlways -
  1301. //-----------------------------------------------------------------------------
  1302. void CBaseHelicopter::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
  1303. {
  1304. // Are we already marked for transmission?
  1305. if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
  1306. return;
  1307. BaseClass::SetTransmit( pInfo, bAlways );
  1308. // Make our smoke trail always come with us
  1309. if ( m_hRotorWash )
  1310. {
  1311. m_hRotorWash->SetTransmit( pInfo, bAlways );
  1312. }
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Purpose:
  1316. //-----------------------------------------------------------------------------
  1317. void ExpandBBox(Vector &vecMins, Vector &vecMaxs)
  1318. {
  1319. // expand for *any* rotation
  1320. float maxval = 0;
  1321. for (int i = 0; i < 3; i++)
  1322. {
  1323. float v = fabs( vecMins[i]);
  1324. if (v > maxval)
  1325. maxval = v;
  1326. v = fabs( vecMaxs[i]);
  1327. if (v > maxval)
  1328. maxval = v;
  1329. }
  1330. vecMins.Init(-maxval, -maxval, -maxval);
  1331. vecMaxs.Init(maxval, maxval, maxval);
  1332. }