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.

3004 lines
92 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Large vehicle what delivers combine troops.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_default.h"
  9. #include "ai_basenpc.h"
  10. #include "soundenvelope.h"
  11. #include "cbasehelicopter.h"
  12. #include "ai_schedule.h"
  13. #include "engine/IEngineSound.h"
  14. #include "smoke_trail.h"
  15. #include "IEffects.h"
  16. #include "props.h"
  17. #include "TemplateEntities.h"
  18. #include "baseanimating.h"
  19. #include "ai_senses.h"
  20. #include "entitylist.h"
  21. #include "ammodef.h"
  22. #include "ndebugoverlay.h"
  23. #include "npc_combines.h"
  24. #include "soundent.h"
  25. #include "mapentities.h"
  26. #include "npc_rollermine.h"
  27. #include "scripted.h"
  28. #include "explode.h"
  29. #include "gib.h"
  30. #include "EntityFlame.h"
  31. #include "entityblocker.h"
  32. #include "eventqueue.h"
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. // Spawnflags
  36. #define SF_DROPSHIP_WAIT_FOR_DROPOFF_INPUT ( 1 << 15 )
  37. #define DROPSHIP_ACCEL_RATE 300
  38. // Timers
  39. #define DROPSHIP_LANDING_HOVER_TIME 5 // Time to spend on the ground if we have no troops to drop
  40. #define DROPSHIP_TIME_BETWEEN_MINES 0.5f
  41. // Special actions
  42. #define DROPSHIP_DEFAULT_SOLDIERS 4
  43. #define DROPSHIP_MAX_SOLDIERS 6
  44. // Movement
  45. #define DROPSHIP_BUFF_TIME 0.3f
  46. #define DROPSHIP_MAX_LAND_TILT 2.5f
  47. #define DROPSHIP_CONTAINER_HEIGHT 130.0f
  48. #define DROPSHIP_MAX_SPEED (60 * 17.6) // 120 miles per hour.
  49. // Pathing data
  50. #define DROPSHIP_LEAD_DISTANCE 800.0f
  51. #define DROPSHIP_MIN_CHASE_DIST_DIFF 128.0f // Distance threshold used to determine when a target has moved enough to update our navigation to it
  52. #define DROPSHIP_AVOID_DIST 256.0f
  53. #define DROPSHIP_ARRIVE_DIST 128.0f
  54. #define CRATE_BBOX_MIN (Vector( -100, -80, -60 ))
  55. #define CRATE_BBOX_MAX (Vector( 100, 80, 80 ))
  56. // Size
  57. // With crate
  58. #define DROPSHIP_BBOX_CRATE_MIN (-Vector(40,40,60))
  59. #define DROPSHIP_BBOX_CRATE_MAX (Vector(40,40,40))
  60. // Without crate
  61. #define DROPSHIP_BBOX_MIN (-Vector(40,40,0))
  62. #define DROPSHIP_BBOX_MAX (Vector(40,40,40))
  63. // Container gun
  64. #define DROPSHIP_GUN_SPEED 10 // Rotation speed
  65. #define DROPSHIP_CRATE_ROCKET_HITS 4
  66. enum DROP_STATES
  67. {
  68. DROP_IDLE = 0,
  69. DROP_NEXT,
  70. };
  71. enum CRATE_TYPES
  72. {
  73. CRATE_JEEP = -3,
  74. CRATE_APC = -2,
  75. CRATE_STRIDER = -1,
  76. CRATE_ROLLER_HOPPER,
  77. CRATE_SOLDIER,
  78. CRATE_NONE,
  79. };
  80. ConVar g_debug_dropship( "g_debug_dropship", "0" );
  81. ConVar sk_dropship_container_health( "sk_dropship_container_health", "750" );
  82. ConVar sk_npc_dmg_dropship( "sk_npc_dmg_dropship","5", FCVAR_NONE, "Dropship container cannon damage." );
  83. //=====================================
  84. // Animation Events
  85. //=====================================
  86. #define AE_DROPSHIP_RAMP_OPEN 1 // the tailgate is open.
  87. //=====================================
  88. // Custom activities
  89. //=====================================
  90. // Without Cargo
  91. Activity ACT_DROPSHIP_FLY_IDLE; // Flying. Vertical aspect
  92. Activity ACT_DROPSHIP_FLY_IDLE_EXAGG; // Exaggerated version of the flying idle
  93. // With Cargo
  94. Activity ACT_DROPSHIP_FLY_IDLE_CARGO; // Flying. Vertical aspect
  95. Activity ACT_DROPSHIP_DESCEND_IDLE; // waiting to touchdown
  96. Activity ACT_DROPSHIP_DEPLOY_IDLE; // idle on the ground with door open. Troops are leaving.
  97. Activity ACT_DROPSHIP_LIFTOFF; // transition back to FLY IDLE
  98. enum LandingState_t
  99. {
  100. LANDING_NO = 0,
  101. // Dropoff
  102. LANDING_LEVEL_OUT, // Heading to a point above the dropoff point
  103. LANDING_DESCEND, // Descending from to the dropoff point
  104. LANDING_TOUCHDOWN,
  105. LANDING_UNLOADING,
  106. LANDING_UNLOADED,
  107. LANDING_LIFTOFF,
  108. // Pickup
  109. LANDING_SWOOPING, // Swooping down to the target
  110. // Hovering, which we're saying is a type of landing since there's so much landing code to leverage
  111. LANDING_START_HOVER,
  112. LANDING_HOVER_LEVEL_OUT,
  113. LANDING_HOVER_DESCEND,
  114. LANDING_HOVER_TOUCHDOWN,
  115. LANDING_END_HOVER,
  116. };
  117. #define DROPSHIP_NEAR_SOUND_MIN_DISTANCE 1000
  118. #define DROPSHIP_NEAR_SOUND_MAX_DISTANCE 2500
  119. #define DROPSHIP_GROUND_WASH_MIN_ALTITUDE 100.0f
  120. #define DROPSHIP_GROUND_WASH_MAX_ALTITUDE 750.0f
  121. //=============================================================================
  122. // The combine dropship container
  123. //=============================================================================
  124. #define DROPSHIP_CONTAINER_MODEL "models/combine_dropship_container.mdl"
  125. #define DROPSHIP_CONTAINER_MAX_CHUNKS 3
  126. static const char *s_pChunkModelName[DROPSHIP_CONTAINER_MAX_CHUNKS] =
  127. {
  128. "models/gibs/helicopter_brokenpiece_01.mdl",
  129. "models/gibs/helicopter_brokenpiece_02.mdl",
  130. "models/gibs/helicopter_brokenpiece_03.mdl",
  131. };
  132. #define DROPSHIP_CONTAINER_MAX_GIBS 1
  133. static const char *s_pGibModelName[DROPSHIP_CONTAINER_MAX_GIBS] =
  134. {
  135. "models/combine_dropship_container.mdl",
  136. };
  137. class CCombineDropshipContainer : public CPhysicsProp
  138. {
  139. DECLARE_CLASS( CCombineDropshipContainer, CPhysicsProp );
  140. DECLARE_DATADESC();
  141. public:
  142. void Precache();
  143. virtual void Spawn();
  144. virtual bool OverridePropdata( void );
  145. virtual int OnTakeDamage( const CTakeDamageInfo &info );
  146. virtual void Event_Killed( const CTakeDamageInfo &info );
  147. private:
  148. enum
  149. {
  150. MAX_SMOKE_TRAILS = 4,
  151. MAX_EXPLOSIONS = 4,
  152. };
  153. // Should we trigger a damage effect?
  154. bool ShouldTriggerDamageEffect( int nPrevHealth, int nEffectCount ) const;
  155. // Add a smoke trail since we've taken more damage
  156. void AddSmokeTrail( const Vector &vecPos );
  157. // Pow!
  158. void ThrowFlamingGib();
  159. // Create a corpse
  160. void CreateCorpse();
  161. private:
  162. int m_nSmokeTrailCount;
  163. EHANDLE m_hLastInflictor;
  164. float m_flLastHitTime;
  165. };
  166. //=============================================================================
  167. // The combine dropship
  168. //=============================================================================
  169. class CNPC_CombineDropship : public CBaseHelicopter
  170. {
  171. DECLARE_CLASS( CNPC_CombineDropship, CBaseHelicopter );
  172. public:
  173. ~CNPC_CombineDropship();
  174. // Setup
  175. void Spawn( void );
  176. void Precache( void );
  177. void Activate( void );
  178. // Thinking/init
  179. void InitializeRotorSound( void );
  180. void StopLoopingSounds();
  181. void PrescheduleThink( void );
  182. // Flight/sound
  183. void Hunt( void );
  184. void Flight( void );
  185. float GetAltitude( void );
  186. void DoRotorWash( void );
  187. void UpdateRotorSoundPitch( int iPitch );
  188. void UpdatePickupNavigation( void );
  189. void UpdateLandTargetNavigation( void );
  190. void CalculateSoldierCount( int iSoldiers );
  191. // Updates the facing direction
  192. virtual void UpdateFacingDirection();
  193. // Combat
  194. void GatherEnemyConditions( CBaseEntity *pEnemy );
  195. void DoCombatStuff( void );
  196. void SpawnTroop( void );
  197. void DropMine( void );
  198. void UpdateContainerGunFacing( Vector &vecMuzzle, Vector &vecToTarget, Vector &vecAimDir, float *flTargetRange );
  199. bool FireCannonRound( void );
  200. void DoImpactEffect( trace_t &tr, int nDamageType );
  201. void StartCannon( void );
  202. void StopCannon( void );
  203. void MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType );
  204. int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
  205. // Input handlers.
  206. void InputLandLeave( inputdata_t &inputdata );
  207. void InputLandTake( inputdata_t &inputdata );
  208. void InputSetLandTarget( inputdata_t &inputdata );
  209. void InputDropMines( inputdata_t &inputdata );
  210. void InputDropStrider( inputdata_t &inputdata );
  211. void InputDropAPC( inputdata_t &inputdata );
  212. void InputPickup( inputdata_t &inputdata );
  213. void InputSetGunRange( inputdata_t &inputdata );
  214. void InputNPCFinishDustoff( inputdata_t &inputdata );
  215. void InputStopWaitingForDropoff( inputdata_t &inputdata );
  216. void InputHover( inputdata_t &inputdata );
  217. // From AI_TrackPather
  218. virtual void InputFlyToPathTrack( inputdata_t &inputdata );
  219. Vector GetDropoffFinishPosition( Vector vecOrigin, CAI_BaseNPC *pNPC, Vector vecMins, Vector vecMaxs );
  220. void LandCommon( bool bHover = false );
  221. Class_T Classify( void ) { return CLASS_COMBINE_GUNSHIP; }
  222. // Drop the soldier container
  223. void DropSoldierContainer( );
  224. // Sounds
  225. virtual void UpdateRotorWashVolume();
  226. private:
  227. void SetLandingState( LandingState_t landingState );
  228. LandingState_t GetLandingState() const { return (LandingState_t)m_iLandState; }
  229. bool IsHovering();
  230. void UpdateGroundRotorWashSound( float flAltitude );
  231. void UpdateRotorWashVolume( CSoundPatch *pRotorSound, float flVolume, float flDeltaTime );
  232. private:
  233. // Timers
  234. float m_flTimeTakeOff;
  235. float m_flNextTroopSpawnAttempt;
  236. float m_flDropDelay; // delta between each mine
  237. float m_flTimeNextAttack;
  238. float m_flLastTime;
  239. // States and counters
  240. int m_iMineCount; // index for current mine # being deployed
  241. int m_totalMinesToDrop; // total # of mines to drop as a group (based upon triggered input)
  242. int m_soldiersToDrop;
  243. int m_iDropState;
  244. int m_iLandState;
  245. float m_engineThrust; // for tracking sound volume/pitch
  246. float m_existPitch;
  247. float m_existRoll;
  248. bool m_bDropMines; // signal to drop mines
  249. bool m_bIsFiring;
  250. int m_iBurstRounds;
  251. bool m_leaveCrate;
  252. bool m_bHasDroppedOff;
  253. int m_iCrateType;
  254. float m_flLandingSpeed;
  255. float m_flGunRange;
  256. bool m_bInvulnerable;
  257. QAngle m_vecAngAcceleration;
  258. // Misc Vars
  259. CHandle<CBaseAnimating> m_hContainer;
  260. EHANDLE m_hPickupTarget;
  261. int m_iContainerMoveType;
  262. bool m_bWaitForDropoffInput;
  263. DECLARE_DATADESC();
  264. DEFINE_CUSTOM_AI;
  265. EHANDLE m_hLandTarget;
  266. string_t m_iszLandTarget;
  267. string_t m_iszAPCVehicleName;
  268. // Templates for soldier's dropped off
  269. string_t m_sNPCTemplate[ DROPSHIP_MAX_SOLDIERS ];
  270. string_t m_sNPCTemplateData[ DROPSHIP_MAX_SOLDIERS ];
  271. string_t m_sDustoffPoints[ DROPSHIP_MAX_SOLDIERS ];
  272. int m_iCurrentTroopExiting;
  273. EHANDLE m_hLastTroopToLeave;
  274. // Template for rollermines dropped by this dropship
  275. string_t m_sRollermineTemplate;
  276. string_t m_sRollermineTemplateData;
  277. // Cached attachment points
  278. int m_iMuzzleAttachment;
  279. int m_iMachineGunBaseAttachment;
  280. int m_iMachineGunRefAttachment;
  281. int m_iAttachmentTroopDeploy;
  282. int m_iAttachmentDeployStart;
  283. // Sounds
  284. CSoundPatch *m_pCannonSound;
  285. CSoundPatch *m_pRotorOnGroundSound;
  286. CSoundPatch *m_pDescendingWarningSound;
  287. CSoundPatch *m_pNearRotorSound;
  288. // Outputs
  289. COutputEvent m_OnFinishedDropoff;
  290. COutputEvent m_OnFinishedPickup;
  291. COutputFloat m_OnContainerShotDownBeforeDropoff;
  292. COutputEvent m_OnContainerShotDownAfterDropoff;
  293. protected:
  294. // Because the combine dropship is a leaf class, we can use
  295. // static variables to store this information, and save some memory.
  296. // Should the dropship end up having inheritors, their activate may
  297. // stomp these numbers, in which case you should make these ordinary members
  298. // again.
  299. static int m_poseBody_Accel, m_poseBody_Sway, m_poseCargo_Body_Accel, m_poseCargo_Body_Sway,
  300. m_poseWeapon_Pitch, m_poseWeapon_Yaw;
  301. static bool m_sbStaticPoseParamsLoaded;
  302. virtual void PopulatePoseParameters( void );
  303. };
  304. bool CNPC_CombineDropship::m_sbStaticPoseParamsLoaded = false;
  305. int CNPC_CombineDropship::m_poseBody_Accel = 0;
  306. int CNPC_CombineDropship::m_poseBody_Sway = 0;
  307. int CNPC_CombineDropship::m_poseCargo_Body_Accel = 0;
  308. int CNPC_CombineDropship::m_poseCargo_Body_Sway = 0;
  309. int CNPC_CombineDropship::m_poseWeapon_Pitch = 0;
  310. int CNPC_CombineDropship::m_poseWeapon_Yaw = 0;
  311. //-----------------------------------------------------------------------------
  312. // Purpose: Cache whatever pose parameters we intend to use
  313. //-----------------------------------------------------------------------------
  314. void CNPC_CombineDropship::PopulatePoseParameters( void )
  315. {
  316. if (!m_sbStaticPoseParamsLoaded)
  317. {
  318. m_poseBody_Accel = LookupPoseParameter( "body_accel");
  319. m_poseBody_Sway = LookupPoseParameter( "body_sway" );
  320. m_poseCargo_Body_Accel = LookupPoseParameter( "cargo_body_accel" );
  321. m_poseCargo_Body_Sway = LookupPoseParameter( "cargo_body_sway" );
  322. m_poseWeapon_Pitch = LookupPoseParameter( "weapon_pitch" );
  323. m_poseWeapon_Yaw = LookupPoseParameter( "weapon_yaw" );
  324. m_sbStaticPoseParamsLoaded = true;
  325. }
  326. BaseClass::PopulatePoseParameters();
  327. }
  328. //------------------------------------------------------------------------------
  329. //
  330. // Combine Dropship Container implementation:
  331. //
  332. //------------------------------------------------------------------------------
  333. LINK_ENTITY_TO_CLASS( prop_dropship_container, CCombineDropshipContainer )
  334. BEGIN_DATADESC( CCombineDropshipContainer )
  335. DEFINE_FIELD( m_nSmokeTrailCount, FIELD_INTEGER ),
  336. DEFINE_FIELD( m_hLastInflictor, FIELD_EHANDLE ),
  337. DEFINE_FIELD( m_flLastHitTime, FIELD_TIME ),
  338. END_DATADESC()
  339. //-----------------------------------------------------------------------------
  340. // Precache
  341. //-----------------------------------------------------------------------------
  342. void CCombineDropshipContainer::Precache()
  343. {
  344. PrecacheModel( DROPSHIP_CONTAINER_MODEL );
  345. // Set this here to quiet base prop warnings
  346. SetModel( DROPSHIP_CONTAINER_MODEL );
  347. BaseClass::Precache();
  348. int i;
  349. for ( i = 0; i < DROPSHIP_CONTAINER_MAX_CHUNKS; ++i )
  350. {
  351. PrecacheModel( s_pChunkModelName[i] );
  352. }
  353. for ( i = 0; i < DROPSHIP_CONTAINER_MAX_GIBS; ++i )
  354. {
  355. PrecacheModel( s_pGibModelName[i] );
  356. }
  357. PropBreakablePrecacheAll( GetModelName() );
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Spawn
  361. //-----------------------------------------------------------------------------
  362. void CCombineDropshipContainer::Spawn()
  363. {
  364. // NOTE: Model must be set before spawn
  365. SetModel( DROPSHIP_CONTAINER_MODEL );
  366. SetSolid( SOLID_VPHYSICS );
  367. BaseClass::Spawn();
  368. #ifdef _XBOX
  369. AddEffects( EF_NOSHADOW );
  370. #endif //_XBOX
  371. m_iHealth = m_iMaxHealth = sk_dropship_container_health.GetFloat();
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Allows us to use vphysics
  375. //-----------------------------------------------------------------------------
  376. bool CCombineDropshipContainer::OverridePropdata( void )
  377. {
  378. return true;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Should we trigger a damage effect?
  382. //-----------------------------------------------------------------------------
  383. inline bool CCombineDropshipContainer::ShouldTriggerDamageEffect( int nPrevHealth, int nEffectCount ) const
  384. {
  385. int nPrevRange = (int)( ((float)nPrevHealth / (float)GetMaxHealth()) * nEffectCount );
  386. int nRange = (int)( ((float)GetHealth() / (float)GetMaxHealth()) * nEffectCount );
  387. return ( nRange != nPrevRange );
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Character killed (only fired once)
  391. //-----------------------------------------------------------------------------
  392. void CCombineDropshipContainer::CreateCorpse()
  393. {
  394. m_lifeState = LIFE_DEAD;
  395. Vector vecNormalizedMins, vecNormalizedMaxs;
  396. Vector vecAbsMins, vecAbsMaxs;
  397. CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  398. CollisionProp()->WorldToNormalizedSpace( vecAbsMins, &vecNormalizedMins );
  399. CollisionProp()->WorldToNormalizedSpace( vecAbsMaxs, &vecNormalizedMaxs );
  400. // Explode
  401. Vector vecAbsPoint;
  402. CPASFilter filter( GetAbsOrigin() );
  403. CollisionProp()->RandomPointInBounds( vecNormalizedMins, vecNormalizedMaxs, &vecAbsPoint);
  404. te->Explosion( filter, 0.0f, &vecAbsPoint, g_sModelIndexFireball,
  405. random->RandomInt( 4, 10 ), random->RandomInt( 8, 15 ), TE_EXPLFLAG_NOPARTICLES, 100, 0 );
  406. // Break into chunks
  407. Vector angVelocity;
  408. QAngleToAngularImpulse( GetLocalAngularVelocity(), angVelocity );
  409. PropBreakableCreateAll( GetModelIndex(), VPhysicsGetObject(), GetAbsOrigin(), GetAbsAngles(), GetAbsVelocity(), angVelocity, 1.0, 250, COLLISION_GROUP_NPC, this );
  410. // Create flaming gibs
  411. int iChunks = random->RandomInt( 4, 6 );
  412. for ( int i = 0; i < iChunks; i++ )
  413. {
  414. ThrowFlamingGib();
  415. }
  416. AddSolidFlags( FSOLID_NOT_SOLID );
  417. AddEffects( EF_NODRAW );
  418. UTIL_Remove( this );
  419. }
  420. //-----------------------------------------------------------------------------
  421. // Character killed (only fired once)
  422. //-----------------------------------------------------------------------------
  423. void CCombineDropshipContainer::ThrowFlamingGib( void )
  424. {
  425. Vector vecAbsMins, vecAbsMaxs;
  426. CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
  427. Vector vecNormalizedMins, vecNormalizedMaxs;
  428. CollisionProp()->WorldToNormalizedSpace( vecAbsMins, &vecNormalizedMins );
  429. CollisionProp()->WorldToNormalizedSpace( vecAbsMaxs, &vecNormalizedMaxs );
  430. Vector vecAbsPoint;
  431. CPASFilter filter( GetAbsOrigin() );
  432. CollisionProp()->RandomPointInBounds( vecNormalizedMins, vecNormalizedMaxs, &vecAbsPoint);
  433. // Throw a flaming, smoking chunk.
  434. CGib *pChunk = CREATE_ENTITY( CGib, "gib" );
  435. pChunk->Spawn( "models/gibs/hgibs.mdl" );
  436. pChunk->SetBloodColor( DONT_BLEED );
  437. QAngle vecSpawnAngles;
  438. vecSpawnAngles.Random( -90, 90 );
  439. pChunk->SetAbsOrigin( vecAbsPoint );
  440. pChunk->SetAbsAngles( vecSpawnAngles );
  441. int nGib = random->RandomInt( 0, DROPSHIP_CONTAINER_MAX_CHUNKS - 1 );
  442. pChunk->Spawn( s_pChunkModelName[nGib] );
  443. pChunk->SetOwnerEntity( this );
  444. pChunk->m_lifeTime = random->RandomFloat( 6.0f, 8.0f );
  445. pChunk->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  446. IPhysicsObject *pPhysicsObject = pChunk->VPhysicsInitNormal( SOLID_VPHYSICS, pChunk->GetSolidFlags(), false );
  447. // Set the velocity
  448. if ( pPhysicsObject )
  449. {
  450. pPhysicsObject->EnableMotion( true );
  451. Vector vecVelocity;
  452. QAngle angles;
  453. angles.x = random->RandomFloat( -20, 20 );
  454. angles.y = random->RandomFloat( 0, 360 );
  455. angles.z = 0.0f;
  456. AngleVectors( angles, &vecVelocity );
  457. vecVelocity *= random->RandomFloat( 300, 900 );
  458. vecVelocity += GetAbsVelocity();
  459. AngularImpulse angImpulse;
  460. angImpulse = RandomAngularImpulse( -180, 180 );
  461. pChunk->SetAbsVelocity( vecVelocity );
  462. pPhysicsObject->SetVelocity(&vecVelocity, &angImpulse );
  463. }
  464. CEntityFlame *pFlame = CEntityFlame::Create( pChunk, false );
  465. if ( pFlame != NULL )
  466. {
  467. pFlame->SetLifetime( pChunk->m_lifeTime );
  468. }
  469. SmokeTrail *pSmokeTrail = SmokeTrail::CreateSmokeTrail();
  470. if( pSmokeTrail )
  471. {
  472. pSmokeTrail->m_SpawnRate = 80;
  473. pSmokeTrail->m_ParticleLifetime = 0.8f;
  474. pSmokeTrail->m_StartColor.Init(0.3, 0.3, 0.3);
  475. pSmokeTrail->m_EndColor.Init(0.5, 0.5, 0.5);
  476. pSmokeTrail->m_StartSize = 10;
  477. pSmokeTrail->m_EndSize = 40;
  478. pSmokeTrail->m_SpawnRadius = 5;
  479. pSmokeTrail->m_Opacity = 0.4;
  480. pSmokeTrail->m_MinSpeed = 15;
  481. pSmokeTrail->m_MaxSpeed = 25;
  482. pSmokeTrail->SetLifetime( pChunk->m_lifeTime );
  483. pSmokeTrail->SetParent( pChunk, 0 );
  484. pSmokeTrail->SetLocalOrigin( vec3_origin );
  485. pSmokeTrail->SetMoveType( MOVETYPE_NONE );
  486. }
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Character killed (only fired once)
  490. //-----------------------------------------------------------------------------
  491. void CCombineDropshipContainer::Event_Killed( const CTakeDamageInfo &info )
  492. {
  493. if ( GetOwnerEntity() )
  494. {
  495. CNPC_CombineDropship *pDropship = assert_cast<CNPC_CombineDropship *>(GetOwnerEntity() );
  496. pDropship->DropSoldierContainer();
  497. }
  498. CreateCorpse();
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Damage effects
  502. //-----------------------------------------------------------------------------
  503. int CCombineDropshipContainer::OnTakeDamage( const CTakeDamageInfo &info )
  504. {
  505. if ( m_iHealth == 0 )
  506. return 0;
  507. // Airboat guns + explosive damage is all that can hurt it
  508. if (( info.GetDamageType() & (DMG_BLAST | DMG_AIRBOAT) ) == 0 )
  509. return 0;
  510. CTakeDamageInfo dmgInfo = info;
  511. int nPrevHealth = GetHealth();
  512. if ( info.GetDamageType() & DMG_BLAST )
  513. {
  514. // This check is necessary to prevent double-counting of rocket damage
  515. // from the blast hitting both the dropship + the container
  516. if ( (info.GetInflictor() != m_hLastInflictor) || (gpGlobals->curtime != m_flLastHitTime) )
  517. {
  518. m_iHealth -= (m_iMaxHealth / DROPSHIP_CRATE_ROCKET_HITS) + 1;
  519. m_hLastInflictor = info.GetInflictor();
  520. m_flLastHitTime = gpGlobals->curtime;
  521. }
  522. }
  523. else
  524. {
  525. m_iHealth -= dmgInfo.GetDamage();
  526. }
  527. if ( m_iHealth <= 0 )
  528. {
  529. m_iHealth = 0;
  530. Event_Killed( dmgInfo );
  531. return 0;
  532. }
  533. // Spawn damage effects
  534. if ( nPrevHealth != GetHealth() )
  535. {
  536. if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_SMOKE_TRAILS ) )
  537. {
  538. AddSmokeTrail( dmgInfo.GetDamagePosition() );
  539. }
  540. if ( ShouldTriggerDamageEffect( nPrevHealth, MAX_EXPLOSIONS ) )
  541. {
  542. ExplosionCreate( dmgInfo.GetDamagePosition(), vec3_angle, this, 1000, 500.0f,
  543. SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0 );
  544. UTIL_ScreenShake( dmgInfo.GetDamagePosition(), 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
  545. ThrowFlamingGib();
  546. }
  547. }
  548. return 1;
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Add a smoke trail since we've taken more damage
  552. //-----------------------------------------------------------------------------
  553. void CCombineDropshipContainer::AddSmokeTrail( const Vector &vecPos )
  554. {
  555. // Start this trail out with a bang!
  556. ExplosionCreate( vecPos, vec3_angle, this, 1000, 500.0f, SF_ENVEXPLOSION_NODAMAGE |
  557. SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0 );
  558. UTIL_ScreenShake( vecPos, 25.0, 150.0, 1.0, 750.0f, SHAKE_START );
  559. if ( m_nSmokeTrailCount == MAX_SMOKE_TRAILS )
  560. return;
  561. SmokeTrail *pSmokeTrail = SmokeTrail::CreateSmokeTrail();
  562. if( !pSmokeTrail )
  563. return;
  564. // See if there's an attachment for this smoke trail
  565. char buf[32];
  566. Q_snprintf( buf, 32, "damage%d", m_nSmokeTrailCount );
  567. int nAttachment = LookupAttachment( buf );
  568. ++m_nSmokeTrailCount;
  569. pSmokeTrail->m_SpawnRate = 20;
  570. pSmokeTrail->m_ParticleLifetime = 4.0f;
  571. pSmokeTrail->m_StartColor.Init( 0.7f, 0.7f, 0.7f );
  572. pSmokeTrail->m_EndColor.Init( 0.6, 0.6, 0.6 );
  573. pSmokeTrail->m_StartSize = 15;
  574. pSmokeTrail->m_EndSize = 50;
  575. pSmokeTrail->m_SpawnRadius = 15;
  576. pSmokeTrail->m_Opacity = 0.75f;
  577. pSmokeTrail->m_MinSpeed = 10;
  578. pSmokeTrail->m_MaxSpeed = 20;
  579. pSmokeTrail->m_MinDirectedSpeed = 100.0f;
  580. pSmokeTrail->m_MaxDirectedSpeed = 120.0f;
  581. pSmokeTrail->SetLifetime( 5 );
  582. pSmokeTrail->SetParent( this, nAttachment );
  583. if ( nAttachment == 0 )
  584. {
  585. pSmokeTrail->SetAbsOrigin( vecPos );
  586. }
  587. else
  588. {
  589. pSmokeTrail->SetLocalOrigin( vec3_origin );
  590. }
  591. Vector vecForward( -1, 0, 0 );
  592. QAngle angles;
  593. VectorAngles( vecForward, angles );
  594. pSmokeTrail->SetAbsAngles( angles );
  595. pSmokeTrail->SetMoveType( MOVETYPE_NONE );
  596. }
  597. //------------------------------------------------------------------------------
  598. //
  599. // Combine Dropship implementation:
  600. //
  601. //------------------------------------------------------------------------------
  602. LINK_ENTITY_TO_CLASS( npc_combinedropship, CNPC_CombineDropship );
  603. BEGIN_DATADESC( CNPC_CombineDropship )
  604. DEFINE_FIELD( m_flTimeTakeOff, FIELD_TIME ),
  605. DEFINE_FIELD( m_flNextTroopSpawnAttempt, FIELD_TIME ),
  606. DEFINE_FIELD( m_flDropDelay, FIELD_TIME ),
  607. DEFINE_FIELD( m_flTimeNextAttack, FIELD_TIME ),
  608. DEFINE_FIELD( m_flLastTime, FIELD_TIME ),
  609. DEFINE_FIELD( m_iMineCount, FIELD_INTEGER ),
  610. DEFINE_FIELD( m_totalMinesToDrop, FIELD_INTEGER ),
  611. DEFINE_FIELD( m_soldiersToDrop, FIELD_INTEGER ),
  612. DEFINE_FIELD( m_iDropState, FIELD_INTEGER ),
  613. DEFINE_FIELD( m_bDropMines, FIELD_BOOLEAN ),
  614. DEFINE_FIELD( m_iLandState, FIELD_INTEGER ),
  615. DEFINE_FIELD( m_engineThrust, FIELD_FLOAT ),
  616. DEFINE_FIELD( m_bIsFiring, FIELD_BOOLEAN ),
  617. DEFINE_FIELD( m_iBurstRounds, FIELD_INTEGER ),
  618. DEFINE_FIELD( m_existPitch, FIELD_FLOAT ),
  619. DEFINE_FIELD( m_existRoll, FIELD_FLOAT ),
  620. DEFINE_FIELD( m_leaveCrate, FIELD_BOOLEAN ),
  621. DEFINE_KEYFIELD( m_iCrateType, FIELD_INTEGER, "CrateType" ),
  622. DEFINE_FIELD( m_flLandingSpeed, FIELD_FLOAT ),
  623. DEFINE_KEYFIELD( m_flGunRange, FIELD_FLOAT, "GunRange" ),
  624. DEFINE_FIELD( m_vecAngAcceleration,FIELD_VECTOR ),
  625. DEFINE_FIELD( m_hContainer, FIELD_EHANDLE ),
  626. DEFINE_FIELD( m_hPickupTarget, FIELD_EHANDLE ),
  627. DEFINE_FIELD( m_iContainerMoveType, FIELD_INTEGER ),
  628. DEFINE_FIELD( m_bWaitForDropoffInput, FIELD_BOOLEAN ),
  629. DEFINE_FIELD( m_hLandTarget, FIELD_EHANDLE ),
  630. DEFINE_FIELD( m_bHasDroppedOff, FIELD_BOOLEAN ),
  631. DEFINE_KEYFIELD( m_bInvulnerable, FIELD_BOOLEAN, "Invulnerable" ),
  632. DEFINE_KEYFIELD( m_iszLandTarget, FIELD_STRING, "LandTarget" ),
  633. DEFINE_SOUNDPATCH( m_pRotorOnGroundSound ),
  634. DEFINE_SOUNDPATCH( m_pDescendingWarningSound ),
  635. DEFINE_SOUNDPATCH( m_pNearRotorSound ),
  636. DEFINE_KEYFIELD( m_iszAPCVehicleName, FIELD_STRING, "APCVehicleName" ),
  637. DEFINE_KEYFIELD( m_sRollermineTemplate, FIELD_STRING, "RollermineTemplate" ),
  638. DEFINE_FIELD( m_sRollermineTemplateData, FIELD_STRING ),
  639. DEFINE_ARRAY( m_sNPCTemplateData, FIELD_STRING, DROPSHIP_MAX_SOLDIERS ),
  640. DEFINE_KEYFIELD( m_sNPCTemplate[0], FIELD_STRING, "NPCTemplate" ),
  641. DEFINE_KEYFIELD( m_sNPCTemplate[1], FIELD_STRING, "NPCTemplate2" ),
  642. DEFINE_KEYFIELD( m_sNPCTemplate[2], FIELD_STRING, "NPCTemplate3" ),
  643. DEFINE_KEYFIELD( m_sNPCTemplate[3], FIELD_STRING, "NPCTemplate4" ),
  644. DEFINE_KEYFIELD( m_sNPCTemplate[4], FIELD_STRING, "NPCTemplate5" ),
  645. DEFINE_KEYFIELD( m_sNPCTemplate[5], FIELD_STRING, "NPCTemplate6" ),
  646. // Here to shut classcheck up
  647. //DEFINE_ARRAY( m_sNPCTemplate, FIELD_STRING, DROPSHIP_MAX_SOLDIERS ),
  648. //DEFINE_ARRAY( m_sDustoffPoints, FIELD_STRING, DROPSHIP_MAX_SOLDIERS ),
  649. DEFINE_KEYFIELD( m_sDustoffPoints[0], FIELD_STRING, "Dustoff1" ),
  650. DEFINE_KEYFIELD( m_sDustoffPoints[1], FIELD_STRING, "Dustoff2" ),
  651. DEFINE_KEYFIELD( m_sDustoffPoints[2], FIELD_STRING, "Dustoff3" ),
  652. DEFINE_KEYFIELD( m_sDustoffPoints[3], FIELD_STRING, "Dustoff4" ),
  653. DEFINE_KEYFIELD( m_sDustoffPoints[4], FIELD_STRING, "Dustoff5" ),
  654. DEFINE_KEYFIELD( m_sDustoffPoints[5], FIELD_STRING, "Dustoff6" ),
  655. DEFINE_FIELD( m_iCurrentTroopExiting, FIELD_INTEGER ),
  656. DEFINE_FIELD( m_hLastTroopToLeave, FIELD_EHANDLE ),
  657. DEFINE_FIELD( m_iMuzzleAttachment, FIELD_INTEGER ),
  658. DEFINE_FIELD( m_iMachineGunBaseAttachment, FIELD_INTEGER ),
  659. DEFINE_FIELD( m_iMachineGunRefAttachment, FIELD_INTEGER ),
  660. DEFINE_FIELD( m_iAttachmentTroopDeploy, FIELD_INTEGER ),
  661. DEFINE_FIELD( m_iAttachmentDeployStart , FIELD_INTEGER ),
  662. DEFINE_SOUNDPATCH( m_pCannonSound ),
  663. DEFINE_INPUTFUNC( FIELD_INTEGER, "LandLeaveCrate", InputLandLeave ),
  664. DEFINE_INPUTFUNC( FIELD_INTEGER, "LandTakeCrate", InputLandTake ),
  665. DEFINE_INPUTFUNC( FIELD_STRING, "SetLandTarget", InputSetLandTarget ),
  666. DEFINE_INPUTFUNC( FIELD_INTEGER, "DropMines", InputDropMines ),
  667. DEFINE_INPUTFUNC( FIELD_VOID, "DropStrider", InputDropStrider ),
  668. DEFINE_INPUTFUNC( FIELD_VOID, "DropAPC", InputDropAPC ),
  669. DEFINE_INPUTFUNC( FIELD_STRING, "Pickup", InputPickup ),
  670. DEFINE_INPUTFUNC( FIELD_FLOAT, "SetGunRange", InputSetGunRange ),
  671. DEFINE_INPUTFUNC( FIELD_STRING, "NPCFinishDustoff", InputNPCFinishDustoff ),
  672. DEFINE_INPUTFUNC( FIELD_VOID, "StopWaitingForDropoff", InputStopWaitingForDropoff ),
  673. DEFINE_INPUTFUNC( FIELD_STRING, "Hover", InputHover ),
  674. DEFINE_INPUTFUNC( FIELD_STRING, "FlyToPathTrack", InputFlyToPathTrack ),
  675. DEFINE_OUTPUT( m_OnFinishedDropoff, "OnFinishedDropoff" ),
  676. DEFINE_OUTPUT( m_OnFinishedPickup, "OnFinishedPickup" ),
  677. DEFINE_OUTPUT( m_OnContainerShotDownBeforeDropoff, "OnCrateShotDownBeforeDropoff" ),
  678. DEFINE_OUTPUT( m_OnContainerShotDownAfterDropoff, "OnCrateShotDownAfterDropoff" ),
  679. END_DATADESC()
  680. //------------------------------------------------------------------------------
  681. // Purpose : Destructor
  682. //------------------------------------------------------------------------------
  683. CNPC_CombineDropship::~CNPC_CombineDropship(void)
  684. {
  685. if ( m_hContainer )
  686. {
  687. UTIL_Remove( m_hContainer ); // get rid of container
  688. }
  689. }
  690. //------------------------------------------------------------------------------
  691. // Purpose :
  692. // Input :
  693. // Output :
  694. //------------------------------------------------------------------------------
  695. void CNPC_CombineDropship::Spawn( void )
  696. {
  697. Precache( );
  698. SetModel( "models/combine_dropship.mdl" );
  699. #ifdef _XBOX
  700. AddEffects( EF_NOSHADOW );
  701. #endif //_XBOX
  702. InitPathingData( DROPSHIP_ARRIVE_DIST, DROPSHIP_MIN_CHASE_DIST_DIFF, DROPSHIP_AVOID_DIST );
  703. m_iContainerMoveType = MOVETYPE_NONE;
  704. m_iCurrentTroopExiting = 0;
  705. m_bHasDroppedOff = false;
  706. m_iMuzzleAttachment = -1;
  707. m_iMachineGunBaseAttachment = -1;
  708. m_iMachineGunRefAttachment = -1;
  709. m_iAttachmentTroopDeploy = -1;
  710. m_iAttachmentDeployStart = -1;
  711. // create the correct bin for the ship to carry
  712. switch ( m_iCrateType )
  713. {
  714. case CRATE_ROLLER_HOPPER:
  715. break;
  716. case CRATE_SOLDIER:
  717. m_hContainer = (CBaseAnimating*)CreateEntityByName( "prop_dropship_container" );
  718. if ( m_hContainer )
  719. {
  720. m_hContainer->SetName( AllocPooledString("dropship_container") );
  721. m_hContainer->SetAbsOrigin( GetAbsOrigin() );
  722. m_hContainer->SetAbsAngles( GetAbsAngles() );
  723. m_hContainer->SetParent(this, 0);
  724. m_hContainer->SetOwnerEntity(this);
  725. m_hContainer->Spawn();
  726. IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject();
  727. if ( pPhysicsObject )
  728. {
  729. pPhysicsObject->SetShadow( 1e4, 1e4, false, false );
  730. pPhysicsObject->UpdateShadow( m_hContainer->GetAbsOrigin(), m_hContainer->GetAbsAngles(), false, 0 );
  731. }
  732. m_hContainer->SetMoveType( MOVETYPE_PUSH );
  733. m_hContainer->SetGroundEntity( NULL );
  734. // Cache off container's attachment points
  735. m_iAttachmentTroopDeploy = m_hContainer->LookupAttachment( "deploy_landpoint" );
  736. m_iAttachmentDeployStart = m_hContainer->LookupAttachment( "Deploy_Start" );
  737. m_iMuzzleAttachment = m_hContainer->LookupAttachment( "muzzle" );
  738. m_iMachineGunBaseAttachment = m_hContainer->LookupAttachment( "gun_base" );
  739. // NOTE: gun_ref must have the same position as gun_base, but rotates with the gun
  740. m_iMachineGunRefAttachment = m_hContainer->LookupAttachment( "gun_ref" );
  741. }
  742. break;
  743. case CRATE_STRIDER:
  744. m_hContainer = (CBaseAnimating*)CreateEntityByName( "npc_strider" );
  745. m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector( 0, 0 , 100 ) );
  746. m_hContainer->SetAbsAngles( GetAbsAngles() );
  747. m_hContainer->SetParent(this, 0);
  748. m_hContainer->SetOwnerEntity(this);
  749. m_hContainer->Spawn();
  750. m_hContainer->SetAbsOrigin( GetAbsOrigin() - Vector( 0, 0 , 100 ) );
  751. break;
  752. case CRATE_APC:
  753. {
  754. m_soldiersToDrop = 0;
  755. m_hContainer = (CBaseAnimating*)gEntList.FindEntityByName( NULL, m_iszAPCVehicleName );
  756. if ( !m_hContainer )
  757. {
  758. Warning("Unable to find APC %s\n", STRING( m_iszAPCVehicleName ) );
  759. break;
  760. }
  761. Vector apcPosition = GetAbsOrigin() - Vector( 0, 0 , 25 );
  762. QAngle apcAngles = GetAbsAngles();
  763. VMatrix mat, rot, result;
  764. MatrixFromAngles( apcAngles, mat );
  765. MatrixBuildRotateZ( rot, -90 );
  766. MatrixMultiply( mat, rot, result );
  767. MatrixToAngles( result, apcAngles );
  768. m_hContainer->Teleport( &apcPosition, &apcAngles, NULL );
  769. m_iContainerMoveType = m_hContainer->GetMoveType();
  770. IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject();
  771. if ( pPhysicsObject )
  772. {
  773. pPhysicsObject->SetShadow( 1e4, 1e4, false, false );
  774. }
  775. m_hContainer->SetParent(this, 0);
  776. m_hContainer->SetOwnerEntity(this);
  777. m_hContainer->SetMoveType( MOVETYPE_PUSH );
  778. m_hContainer->SetGroundEntity( NULL );
  779. m_hContainer->UpdatePhysicsShadowToCurrentPosition(0);
  780. }
  781. break;
  782. case CRATE_JEEP:
  783. m_hContainer = (CBaseAnimating*)CreateEntityByName( "prop_dynamic_override" );
  784. if ( m_hContainer )
  785. {
  786. m_hContainer->SetModel( "models/buggy.mdl" );
  787. m_hContainer->SetName( AllocPooledString("dropship_jeep") );
  788. m_hContainer->SetAbsOrigin( GetAbsOrigin() );//- Vector( 0, 0 , 25 ) );
  789. QAngle angles = GetAbsAngles();
  790. VMatrix mat, rot, result;
  791. MatrixFromAngles( angles, mat );
  792. MatrixBuildRotateZ( rot, -90 );
  793. MatrixMultiply( mat, rot, result );
  794. MatrixToAngles( result, angles );
  795. m_hContainer->SetAbsAngles( angles );
  796. m_hContainer->SetParent(this, 0);
  797. m_hContainer->SetOwnerEntity(this);
  798. m_hContainer->SetSolid( SOLID_VPHYSICS );
  799. m_hContainer->Spawn();
  800. }
  801. break;
  802. case CRATE_NONE:
  803. default:
  804. break;
  805. }
  806. // Setup our bbox
  807. if ( m_hContainer )
  808. {
  809. UTIL_SetSize( this, DROPSHIP_BBOX_CRATE_MIN, DROPSHIP_BBOX_CRATE_MAX );
  810. SetIdealActivity( (Activity)ACT_DROPSHIP_FLY_IDLE_CARGO );
  811. }
  812. else
  813. {
  814. UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX );
  815. SetIdealActivity( (Activity)ACT_DROPSHIP_FLY_IDLE_EXAGG );
  816. }
  817. m_cullBoxMins = WorldAlignMins() - Vector(300,300,200);
  818. m_cullBoxMaxs = WorldAlignMaxs() + Vector(300,300,200);
  819. BaseClass::Spawn();
  820. // Dropship ignores all damage, but can deal it to its carried container
  821. m_takedamage = m_bInvulnerable ? DAMAGE_NO : DAMAGE_YES;
  822. if ( m_bInvulnerable && m_hContainer )
  823. {
  824. m_hContainer->m_takedamage = DAMAGE_NO;
  825. }
  826. m_iHealth = 100;
  827. m_flFieldOfView = 0.5; // 60 degrees
  828. m_iBurstRounds = 15;
  829. InitBoneControllers();
  830. InitCustomSchedules();
  831. m_flMaxSpeed = DROPSHIP_MAX_SPEED;
  832. m_flMaxSpeedFiring = BASECHOPPER_MAX_FIRING_SPEED;
  833. m_hPickupTarget = NULL;
  834. m_hLandTarget = NULL;
  835. //!!!HACKHACK
  836. // This tricks the AI code that constantly complains that the vehicle has no schedule.
  837. SetSchedule( SCHED_IDLE_STAND );
  838. SetLandingState( LANDING_NO );
  839. if ( HasSpawnFlags( SF_DROPSHIP_WAIT_FOR_DROPOFF_INPUT ) )
  840. {
  841. m_bWaitForDropoffInput = true;
  842. }
  843. else
  844. {
  845. m_bWaitForDropoffInput = false;
  846. }
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Purpose: Called after spawning on map load or on a load from save game.
  850. //-----------------------------------------------------------------------------
  851. void CNPC_CombineDropship::Activate( void )
  852. {
  853. BaseClass::Activate();
  854. if ( !m_sRollermineTemplateData )
  855. {
  856. m_sRollermineTemplateData = NULL_STRING;
  857. if ( m_sRollermineTemplate != NULL_STRING )
  858. {
  859. // This must be the first time we're activated, not a load from save game.
  860. // Look up the template in the template database.
  861. m_sRollermineTemplateData = Templates_FindByTargetName(STRING(m_sRollermineTemplate));
  862. if ( m_sRollermineTemplateData == NULL_STRING )
  863. {
  864. Warning( "npc_combinedropship %s: Rollermine Template %s not found!\n", STRING(GetEntityName()), STRING(m_sRollermineTemplate) );
  865. }
  866. }
  867. }
  868. }
  869. //------------------------------------------------------------------------------
  870. //------------------------------------------------------------------------------
  871. void CNPC_CombineDropship::Precache( void )
  872. {
  873. // Models
  874. PrecacheModel("models/combine_dropship.mdl");
  875. switch ( m_iCrateType )
  876. {
  877. case CRATE_SOLDIER:
  878. UTIL_PrecacheOther( "prop_dropship_container" );
  879. //
  880. // Precache the all templates that we are configured to spawn
  881. //
  882. for ( int i = 0; i < DROPSHIP_MAX_SOLDIERS; i++ )
  883. {
  884. if ( m_sNPCTemplate[i] != NULL_STRING )
  885. {
  886. if ( m_sNPCTemplateData[i] == NULL_STRING )
  887. {
  888. m_sNPCTemplateData[i] = Templates_FindByTargetName(STRING(m_sNPCTemplate[i]));
  889. }
  890. if ( m_sNPCTemplateData[i] != NULL_STRING )
  891. {
  892. CBaseEntity *pEntity = NULL;
  893. MapEntity_ParseEntity( pEntity, STRING(m_sNPCTemplateData[i]), NULL );
  894. if ( pEntity != NULL )
  895. {
  896. pEntity->Precache();
  897. UTIL_RemoveImmediate( pEntity );
  898. }
  899. }
  900. else
  901. {
  902. Warning( "npc_combinedropship %s: Template NPC %s not found!\n", STRING(GetEntityName()), STRING(m_sNPCTemplate[i]) );
  903. // Use the first template we've got
  904. m_sNPCTemplateData[i] = m_sNPCTemplateData[0];
  905. }
  906. // Make sure we've got a dustoff point for it
  907. if ( m_sDustoffPoints[i] == NULL_STRING )
  908. {
  909. Warning( "npc_combinedropship %s: Has no dustoff point for NPC %d!\n", STRING(GetEntityName()), i );
  910. }
  911. }
  912. else
  913. {
  914. m_sNPCTemplateData[i] = NULL_STRING;
  915. }
  916. }
  917. break;
  918. case CRATE_JEEP:
  919. PrecacheModel("models/buggy.mdl");
  920. break;
  921. default:
  922. break;
  923. }
  924. PrecacheScriptSound( "NPC_CombineDropship.RotorLoop" );
  925. PrecacheScriptSound( "NPC_CombineDropship.FireLoop" );
  926. PrecacheScriptSound( "NPC_CombineDropship.NearRotorLoop" );
  927. PrecacheScriptSound( "NPC_CombineDropship.OnGroundRotorLoop" );
  928. PrecacheScriptSound( "NPC_CombineDropship.DescendingWarningLoop" );
  929. PrecacheScriptSound( "NPC_CombineDropship.NearRotorLoop" );
  930. if ( m_sRollermineTemplate != NULL_STRING )
  931. {
  932. UTIL_PrecacheOther( "npc_rollermine" );
  933. }
  934. BaseClass::Precache();
  935. }
  936. //------------------------------------------------------------------------------
  937. // Purpose :
  938. // Input :
  939. // Output :
  940. //------------------------------------------------------------------------------
  941. void CNPC_CombineDropship::Flight( void )
  942. {
  943. // Only run the flight model in some flight states
  944. bool bRunFlight = ( GetLandingState() == LANDING_NO ||
  945. GetLandingState() == LANDING_LEVEL_OUT ||
  946. GetLandingState() == LANDING_LIFTOFF ||
  947. GetLandingState() == LANDING_SWOOPING ||
  948. GetLandingState() == LANDING_DESCEND ||
  949. GetLandingState() == LANDING_HOVER_LEVEL_OUT ||
  950. GetLandingState() == LANDING_HOVER_DESCEND );
  951. Vector forward, right, up;
  952. GetVectors( &forward, &right, &up );
  953. float finspeed = 0;
  954. float swayspeed = 0;
  955. Vector vecImpulse = vec3_origin;
  956. //Adrian: Slowly lerp the orientation and position of the cargo into place...
  957. //We assume CRATE_NONE means the dropship just picked up some random phys object.
  958. if ( m_hContainer != NULL && ( m_iCrateType == CRATE_SOLDIER || m_iCrateType == CRATE_NONE ) )
  959. {
  960. if ( m_hContainer->GetLocalOrigin() != vec3_origin )
  961. {
  962. Vector vCurrentLocalOrigin = m_hContainer->GetLocalOrigin();
  963. Vector vLocalOrigin;
  964. VectorLerp( vCurrentLocalOrigin, vec3_origin, 0.05f, vLocalOrigin );
  965. m_hContainer->SetLocalOrigin( vLocalOrigin );
  966. }
  967. if ( m_hContainer->GetLocalAngles() != vec3_angle )
  968. {
  969. QAngle vCurrentLocalAngles = m_hContainer->GetLocalAngles();
  970. QAngle vLocalAngles;
  971. vLocalAngles = Lerp( 0.05f, vCurrentLocalAngles, vec3_angle );
  972. m_hContainer->SetLocalAngles( vLocalAngles );
  973. }
  974. }
  975. if ( bRunFlight )
  976. {
  977. if( GetFlags() & FL_ONGROUND )
  978. {
  979. // This would be really bad.
  980. SetGroundEntity( NULL );
  981. }
  982. // calc desired acceleration
  983. float dt = 1.0f;
  984. Vector accel;
  985. float accelRate = DROPSHIP_ACCEL_RATE;
  986. float maxSpeed = GetMaxSpeed();
  987. if ( m_lifeState == LIFE_DYING )
  988. {
  989. accelRate *= 5.0;
  990. maxSpeed *= 5.0;
  991. }
  992. float flCurrentSpeed = GetAbsVelocity().Length();
  993. float flDist = MIN( flCurrentSpeed + accelRate, maxSpeed );
  994. Vector deltaPos;
  995. if ( GetLandingState() == LANDING_SWOOPING )
  996. {
  997. // Move directly to the target point
  998. deltaPos = GetDesiredPosition();
  999. }
  1000. else
  1001. {
  1002. ComputeActualTargetPosition( flDist, dt, 0.0f, &deltaPos );
  1003. }
  1004. deltaPos -= GetAbsOrigin();
  1005. //NDebugOverlay::Line( GetAbsOrigin(), GetAbsOrigin() + deltaPos, 0, 255, 0, true, 0.1f );
  1006. // calc goal linear accel to hit deltaPos in dt time.
  1007. accel.x = 2.0 * (deltaPos.x - GetAbsVelocity().x * dt) / (dt * dt);
  1008. accel.y = 2.0 * (deltaPos.y - GetAbsVelocity().y * dt) / (dt * dt);
  1009. accel.z = 2.0 * (deltaPos.z - GetAbsVelocity().z * dt + 0.5 * 384 * dt * dt) / (dt * dt);
  1010. float flDistFromPath = 0.0f;
  1011. Vector vecPoint, vecDelta;
  1012. if ( IsOnPathTrack() && GetLandingState() == LANDING_NO )
  1013. {
  1014. // Also, add in a little force to get us closer to our current line segment if we can
  1015. ClosestPointToCurrentPath( &vecPoint );
  1016. VectorSubtract( vecPoint, GetAbsOrigin(), vecDelta );
  1017. flDistFromPath = VectorNormalize( vecDelta );
  1018. if ( flDistFromPath > 200 )
  1019. {
  1020. // Strongly constrain to an n unit pipe around the current path
  1021. // by damping out all impulse forces that would push us further from the pipe
  1022. float flAmount = (flDistFromPath - 200) / 200.0f;
  1023. flAmount = clamp( flAmount, 0, 1 );
  1024. VectorMA( accel, flAmount * 200.0f, vecDelta, accel );
  1025. }
  1026. }
  1027. // don't fall faster than 0.2G or climb faster than 2G
  1028. accel.z = clamp( accel.z, 384 * 0.2, 384 * 2.0 );
  1029. Vector goalUp = accel;
  1030. VectorNormalize( goalUp );
  1031. // calc goal orientation to hit linear accel forces
  1032. float goalPitch = RAD2DEG( asin( DotProduct( forward, goalUp ) ) );
  1033. float goalYaw = UTIL_VecToYaw( m_vecDesiredFaceDir );
  1034. float goalRoll = RAD2DEG( asin( DotProduct( right, goalUp ) ) );
  1035. // clamp goal orientations
  1036. goalPitch = clamp( goalPitch, -45, 60 );
  1037. goalRoll = clamp( goalRoll, -45, 45 );
  1038. // calc angular accel needed to hit goal pitch in dt time.
  1039. dt = 0.6;
  1040. QAngle goalAngAccel;
  1041. goalAngAccel.x = 2.0 * (AngleDiff( goalPitch, AngleNormalize( GetLocalAngles().x ) ) - GetLocalAngularVelocity().x * dt) / (dt * dt);
  1042. goalAngAccel.y = 2.0 * (AngleDiff( goalYaw, AngleNormalize( GetLocalAngles().y ) ) - GetLocalAngularVelocity().y * dt) / (dt * dt);
  1043. goalAngAccel.z = 2.0 * (AngleDiff( goalRoll, AngleNormalize( GetLocalAngles().z ) ) - GetLocalAngularVelocity().z * dt) / (dt * dt);
  1044. goalAngAccel.x = clamp( goalAngAccel.x, -300, 300 );
  1045. //goalAngAccel.y = clamp( goalAngAccel.y, -60, 60 );
  1046. goalAngAccel.y = clamp( goalAngAccel.y, -120, 120 );
  1047. goalAngAccel.z = clamp( goalAngAccel.z, -300, 300 );
  1048. // limit angular accel changes to simulate mechanical response times
  1049. dt = 0.1;
  1050. QAngle angAccelAccel;
  1051. angAccelAccel.x = (goalAngAccel.x - m_vecAngAcceleration.x) / dt;
  1052. angAccelAccel.y = (goalAngAccel.y - m_vecAngAcceleration.y) / dt;
  1053. angAccelAccel.z = (goalAngAccel.z - m_vecAngAcceleration.z) / dt;
  1054. angAccelAccel.x = clamp( angAccelAccel.x, -1000, 1000 );
  1055. angAccelAccel.y = clamp( angAccelAccel.y, -1000, 1000 );
  1056. angAccelAccel.z = clamp( angAccelAccel.z, -1000, 1000 );
  1057. m_vecAngAcceleration += angAccelAccel * 0.1;
  1058. // DevMsg( "pitch %6.1f (%6.1f:%6.1f) ", goalPitch, GetLocalAngles().x, m_vecAngVelocity.x );
  1059. // DevMsg( "roll %6.1f (%6.1f:%6.1f) : ", goalRoll, GetLocalAngles().z, m_vecAngVelocity.z );
  1060. // DevMsg( "%6.1f %6.1f %6.1f : ", goalAngAccel.x, goalAngAccel.y, goalAngAccel.z );
  1061. // DevMsg( "%6.0f %6.0f %6.0f\n", angAccelAccel.x, angAccelAccel.y, angAccelAccel.z );
  1062. ApplySidewaysDrag( right );
  1063. ApplyGeneralDrag();
  1064. QAngle angVel = GetLocalAngularVelocity();
  1065. angVel += m_vecAngAcceleration * 0.1;
  1066. //angVel.y = clamp( angVel.y, -60, 60 );
  1067. //angVel.y = clamp( angVel.y, -120, 120 );
  1068. angVel.y = clamp( angVel.y, -120, 120 );
  1069. SetLocalAngularVelocity( angVel );
  1070. m_flForce = m_flForce * 0.8 + (accel.z + fabs( accel.x ) * 0.1 + fabs( accel.y ) * 0.1) * 0.1 * 0.2;
  1071. vecImpulse = m_flForce * up;
  1072. if ( m_lifeState == LIFE_DYING )
  1073. {
  1074. vecImpulse.z = -38.4; // 64ft/sec
  1075. }
  1076. else
  1077. {
  1078. vecImpulse.z -= 38.4; // 32ft/sec
  1079. }
  1080. // Find our current velocity
  1081. Vector vecVelDir = GetAbsVelocity();
  1082. VectorNormalize( vecVelDir );
  1083. if ( flDistFromPath > 100 )
  1084. {
  1085. // Strongly constrain to an n unit pipe around the current path
  1086. // by damping out all impulse forces that would push us further from the pipe
  1087. float flDot = DotProduct( vecImpulse, vecDelta );
  1088. if ( flDot < 0.0f )
  1089. {
  1090. VectorMA( vecImpulse, -flDot * 0.1f, vecDelta, vecImpulse );
  1091. }
  1092. // Also apply an extra impulse to compensate for the current velocity
  1093. flDot = DotProduct( vecVelDir, vecDelta );
  1094. if ( flDot < 0.0f )
  1095. {
  1096. VectorMA( vecImpulse, -flDot * 0.1f, vecDelta, vecImpulse );
  1097. }
  1098. }
  1099. // Find our acceleration direction
  1100. Vector vecAccelDir = vecImpulse;
  1101. VectorNormalize( vecAccelDir );
  1102. // Level out our plane of movement
  1103. vecAccelDir.z = 0.0f;
  1104. vecVelDir.z = 0.0f;
  1105. forward.z = 0.0f;
  1106. right.z = 0.0f;
  1107. // Find out how "fast" we're moving in relation to facing and acceleration
  1108. finspeed = m_flForce * DotProduct( vecVelDir, vecAccelDir );
  1109. swayspeed = m_flForce * DotProduct( vecVelDir, right );
  1110. }
  1111. // Use the correct pose params for the state of our container
  1112. int poseBodyAccel;
  1113. int poseBodySway;
  1114. if ( m_hContainer || GetLandingState() == LANDING_SWOOPING )
  1115. {
  1116. poseBodyAccel = m_poseCargo_Body_Accel;
  1117. poseBodySway = m_poseCargo_Body_Sway;
  1118. SetPoseParameter( m_poseBody_Accel, 0 );
  1119. SetPoseParameter( m_poseBody_Sway, 0 );
  1120. }
  1121. else
  1122. {
  1123. poseBodyAccel = m_poseBody_Accel;
  1124. poseBodySway = m_poseBody_Sway;
  1125. SetPoseParameter( m_poseCargo_Body_Accel, 0 );
  1126. SetPoseParameter( m_poseCargo_Body_Sway, 0 );
  1127. }
  1128. // If we're landing, deliberately tuck in the back end
  1129. if ( GetLandingState() == LANDING_DESCEND || GetLandingState() == LANDING_TOUCHDOWN ||
  1130. GetLandingState() == LANDING_UNLOADING || GetLandingState() == LANDING_UNLOADED || IsHovering() )
  1131. {
  1132. finspeed = -60;
  1133. }
  1134. // Apply the acceleration blend to the fins
  1135. float finAccelBlend = SimpleSplineRemapVal( finspeed, -60, 60, -1, 1 );
  1136. float curFinAccel = GetPoseParameter( poseBodyAccel );
  1137. curFinAccel = UTIL_Approach( finAccelBlend, curFinAccel, 0.1f );
  1138. SetPoseParameter( poseBodyAccel, EdgeLimitPoseParameter( poseBodyAccel, curFinAccel ) );
  1139. // Apply the spin sway to the fins
  1140. float finSwayBlend = SimpleSplineRemapVal( swayspeed, -60, 60, -1, 1 );
  1141. float curFinSway = GetPoseParameter( poseBodySway );
  1142. curFinSway = UTIL_Approach( finSwayBlend, curFinSway, 0.1f );
  1143. SetPoseParameter( poseBodySway, EdgeLimitPoseParameter( poseBodySway, curFinSway ) );
  1144. if ( bRunFlight )
  1145. {
  1146. // Add in our velocity pulse for this frame
  1147. ApplyAbsVelocityImpulse( vecImpulse );
  1148. }
  1149. //DevMsg("curFinAccel: %f, curFinSway: %f\n", curFinAccel, curFinSway );
  1150. }
  1151. //------------------------------------------------------------------------------
  1152. // Deals damage to what's behing carried
  1153. //------------------------------------------------------------------------------
  1154. int CNPC_CombineDropship::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  1155. {
  1156. // FIXME: To make this work for CRATE_STRIDER or CRATE_APC, we need to
  1157. // add code to the strider + apc to make them not take double-damage from rockets
  1158. // (owing to the blast hitting the crate + the dropship). See the dropship container
  1159. // code above to see how to do it.
  1160. if ( m_hContainer && !m_bInvulnerable )
  1161. {
  1162. if ( (inputInfo.GetDamageType() & DMG_AIRBOAT) || (m_iCrateType == CRATE_SOLDIER) )
  1163. {
  1164. m_hContainer->TakeDamage( inputInfo );
  1165. }
  1166. }
  1167. // don't die
  1168. return 0;
  1169. }
  1170. //------------------------------------------------------------------------------
  1171. // Updates the facing direction
  1172. //------------------------------------------------------------------------------
  1173. void CNPC_CombineDropship::UpdateFacingDirection( void )
  1174. {
  1175. if ( GetEnemy() )
  1176. {
  1177. if ( !IsCrashing() && m_flLastSeen + 5 > gpGlobals->curtime )
  1178. {
  1179. // If we've seen the target recently, face the target.
  1180. //Msg( "Facing Target \n" );
  1181. m_vecDesiredFaceDir = m_vecTargetPosition - GetAbsOrigin();
  1182. }
  1183. else
  1184. {
  1185. // Remain facing the way you were facing...
  1186. }
  1187. }
  1188. else
  1189. {
  1190. // Face our desired position.
  1191. if ( GetDesiredPosition().DistToSqr( GetAbsOrigin() ) > 1 )
  1192. {
  1193. m_vecDesiredFaceDir = GetDesiredPosition() - GetAbsOrigin();
  1194. }
  1195. else
  1196. {
  1197. GetVectors( &m_vecDesiredFaceDir, NULL, NULL );
  1198. }
  1199. }
  1200. VectorNormalize( m_vecDesiredFaceDir );
  1201. }
  1202. //------------------------------------------------------------------------------
  1203. // Purpose :
  1204. // Input :
  1205. // Output :
  1206. //------------------------------------------------------------------------------
  1207. void CNPC_CombineDropship::InitializeRotorSound( void )
  1208. {
  1209. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1210. CPASAttenuationFilter filter( this );
  1211. m_pRotorSound = controller.SoundCreate( filter, entindex(), "NPC_CombineDropship.RotorLoop" );
  1212. m_pNearRotorSound = controller.SoundCreate( filter, entindex(), "NPC_CombineDropship.NearRotorLoop" );
  1213. m_pRotorOnGroundSound = controller.SoundCreate( filter, entindex(), "NPC_CombineDropship.OnGroundRotorLoop" );
  1214. m_pDescendingWarningSound = controller.SoundCreate( filter, entindex(), "NPC_CombineDropship.DescendingWarningLoop" );
  1215. m_pCannonSound = controller.SoundCreate( filter, entindex(), "NPC_CombineDropship.FireLoop" );
  1216. // NOTE: m_pRotorSound is started up by the base class
  1217. if ( m_pCannonSound )
  1218. {
  1219. controller.Play( m_pCannonSound, 0.0, 100 );
  1220. }
  1221. if ( m_pDescendingWarningSound )
  1222. {
  1223. controller.Play( m_pDescendingWarningSound, 0.0, 100 );
  1224. }
  1225. if ( m_pRotorOnGroundSound )
  1226. {
  1227. controller.Play( m_pRotorOnGroundSound, 0.0, 100 );
  1228. }
  1229. if ( m_pNearRotorSound )
  1230. {
  1231. controller.Play( m_pNearRotorSound, 0.0, 100 );
  1232. }
  1233. m_engineThrust = 1.0f;
  1234. BaseClass::InitializeRotorSound();
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // Purpose:
  1238. //-----------------------------------------------------------------------------
  1239. void CNPC_CombineDropship::StopLoopingSounds()
  1240. {
  1241. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1242. if ( m_pCannonSound )
  1243. {
  1244. controller.SoundDestroy( m_pCannonSound );
  1245. m_pCannonSound = NULL;
  1246. }
  1247. if ( m_pRotorOnGroundSound )
  1248. {
  1249. controller.SoundDestroy( m_pRotorOnGroundSound );
  1250. m_pRotorOnGroundSound = NULL;
  1251. }
  1252. if ( m_pDescendingWarningSound )
  1253. {
  1254. controller.SoundDestroy( m_pDescendingWarningSound );
  1255. m_pDescendingWarningSound = NULL;
  1256. }
  1257. if ( m_pNearRotorSound )
  1258. {
  1259. controller.SoundDestroy( m_pNearRotorSound );
  1260. m_pNearRotorSound = NULL;
  1261. }
  1262. BaseClass::StopLoopingSounds();
  1263. }
  1264. //------------------------------------------------------------------------------
  1265. // Updates the rotor wash volume
  1266. //------------------------------------------------------------------------------
  1267. void CNPC_CombineDropship::UpdateRotorWashVolume( CSoundPatch *pRotorSound, float flVolume, float flDeltaTime )
  1268. {
  1269. if ( !pRotorSound )
  1270. return;
  1271. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1272. float flVolDelta = flVolume - controller.SoundGetVolume( pRotorSound );
  1273. if ( flVolDelta )
  1274. {
  1275. // We can change from 0 to 1 in 3 seconds.
  1276. // Figure out how many seconds flVolDelta will take.
  1277. float flRampTime = fabs( flVolDelta ) * flDeltaTime;
  1278. controller.SoundChangeVolume( pRotorSound, flVolume, flRampTime );
  1279. }
  1280. }
  1281. //------------------------------------------------------------------------------
  1282. // Updates the rotor wash volume
  1283. //------------------------------------------------------------------------------
  1284. void CNPC_CombineDropship::UpdateRotorWashVolume()
  1285. {
  1286. float flNearFactor = 0.0f;
  1287. CBaseEntity *pPlayer = UTIL_PlayerByIndex( 1 );
  1288. if (pPlayer)
  1289. {
  1290. float flDist = pPlayer->GetAbsOrigin().DistTo( GetAbsOrigin() );
  1291. flDist = clamp( flDist, DROPSHIP_NEAR_SOUND_MIN_DISTANCE, DROPSHIP_NEAR_SOUND_MAX_DISTANCE );
  1292. flNearFactor = RemapVal( flDist, DROPSHIP_NEAR_SOUND_MIN_DISTANCE, DROPSHIP_NEAR_SOUND_MAX_DISTANCE, 1.0f, 0.0f );
  1293. }
  1294. if ( m_pRotorSound )
  1295. {
  1296. UpdateRotorWashVolume( m_pRotorSound, m_engineThrust * GetRotorVolume() * (1.0f - flNearFactor), 3.0f );
  1297. }
  1298. if ( m_pNearRotorSound )
  1299. {
  1300. UpdateRotorWashVolume( m_pNearRotorSound, m_engineThrust * GetRotorVolume() * flNearFactor, 3.0f );
  1301. }
  1302. }
  1303. //------------------------------------------------------------------------------
  1304. // Purpose :
  1305. // Input :
  1306. // Output :
  1307. //------------------------------------------------------------------------------
  1308. void CNPC_CombineDropship::UpdateRotorSoundPitch( int iPitch )
  1309. {
  1310. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1311. float rotorPitch = 0.2 + m_engineThrust * 0.8;
  1312. if ( m_pRotorSound )
  1313. {
  1314. controller.SoundChangePitch( m_pRotorSound, iPitch + rotorPitch, 0.1 );
  1315. }
  1316. if ( m_pNearRotorSound )
  1317. {
  1318. controller.SoundChangePitch( m_pNearRotorSound, iPitch + rotorPitch, 0.1 );
  1319. }
  1320. if (m_pRotorOnGroundSound)
  1321. {
  1322. controller.SoundChangePitch( m_pRotorOnGroundSound, iPitch + rotorPitch, 0.1 );
  1323. }
  1324. UpdateRotorWashVolume();
  1325. }
  1326. //-----------------------------------------------------------------------------
  1327. // Purpose:
  1328. // Input : iSoldiers -
  1329. //-----------------------------------------------------------------------------
  1330. void CNPC_CombineDropship::CalculateSoldierCount( int iSoldiers )
  1331. {
  1332. if ( m_iCrateType >= 0 )
  1333. {
  1334. m_soldiersToDrop = clamp( iSoldiers, 0, DROPSHIP_MAX_SOLDIERS );
  1335. }
  1336. else
  1337. {
  1338. m_soldiersToDrop = 0;
  1339. }
  1340. }
  1341. //------------------------------------------------------------------------------
  1342. // Purpose : Leave crate being carried
  1343. // Input :
  1344. // Output :
  1345. //------------------------------------------------------------------------------
  1346. void CNPC_CombineDropship::InputLandLeave( inputdata_t &inputdata )
  1347. {
  1348. CalculateSoldierCount( inputdata.value.Int() );
  1349. m_leaveCrate = true;
  1350. LandCommon();
  1351. }
  1352. //------------------------------------------------------------------------------
  1353. // Purpose : Take crate being carried to next point
  1354. // Input :
  1355. // Output :
  1356. //------------------------------------------------------------------------------
  1357. void CNPC_CombineDropship::InputLandTake( inputdata_t &inputdata )
  1358. {
  1359. CalculateSoldierCount( inputdata.value.Int() );
  1360. m_leaveCrate = false;
  1361. LandCommon();
  1362. }
  1363. //------------------------------------------------------------------------------
  1364. // Purpose :
  1365. // Input : bHover - If true, means we're landing on a hover point, not the ground
  1366. // Output :
  1367. //------------------------------------------------------------------------------
  1368. void CNPC_CombineDropship::LandCommon( bool bHover )
  1369. {
  1370. // If we don't have a crate, we're not able to land
  1371. if ( !m_hContainer && !bHover )
  1372. return;
  1373. //DevMsg( "Landing\n" );
  1374. if( bHover )
  1375. {
  1376. SetLandingState( LANDING_HOVER_LEVEL_OUT );
  1377. }
  1378. else
  1379. {
  1380. SetLandingState( LANDING_LEVEL_OUT );
  1381. }
  1382. SetLocalAngularVelocity( vec3_angle );
  1383. // Do we have a land target?
  1384. if ( m_iszLandTarget != NULL_STRING )
  1385. {
  1386. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, m_iszLandTarget );
  1387. if ( !pTarget )
  1388. {
  1389. Warning("npc_combinedropship %s couldn't find land target named %s\n", STRING(GetEntityName()), STRING(m_iszLandTarget) );
  1390. return;
  1391. }
  1392. // Start heading to the point
  1393. m_hLandTarget = pTarget;
  1394. }
  1395. }
  1396. //-----------------------------------------------------------------------------
  1397. // Purpose:
  1398. //-----------------------------------------------------------------------------
  1399. void CNPC_CombineDropship::InputSetLandTarget( inputdata_t &inputdata )
  1400. {
  1401. m_iszLandTarget = inputdata.value.StringID();
  1402. }
  1403. //------------------------------------------------------------------------------
  1404. // Purpose : Drop mine inputs... done this way so generic path_corners can be used
  1405. // Input :
  1406. // Output :
  1407. //------------------------------------------------------------------------------
  1408. void CNPC_CombineDropship::InputDropMines( inputdata_t &inputdata )
  1409. {
  1410. m_totalMinesToDrop = inputdata.value.Int();
  1411. if ( m_totalMinesToDrop >= 1 ) // catch bogus values being passed in
  1412. {
  1413. m_bDropMines = true;
  1414. }
  1415. }
  1416. //-----------------------------------------------------------------------------
  1417. //-----------------------------------------------------------------------------
  1418. void CNPC_CombineDropship::InputDropStrider( inputdata_t &inputdata )
  1419. {
  1420. if ( !m_hContainer || !FClassnameIs( m_hContainer, "npc_strider" ) )
  1421. {
  1422. Warning("npc_combinedropship %s was told to drop Strider, but isn't carrying one!\n", STRING(GetEntityName()) );
  1423. return;
  1424. }
  1425. QAngle angles = GetAbsAngles();
  1426. angles.x = 0.0;
  1427. angles.z = 0.0;
  1428. m_hContainer->SetParent(NULL, 0);
  1429. m_hContainer->SetOwnerEntity(NULL);
  1430. m_hContainer->SetAbsAngles( angles );
  1431. m_hContainer->SetAbsVelocity( vec3_origin );
  1432. m_hContainer = NULL;
  1433. }
  1434. //-----------------------------------------------------------------------------
  1435. //-----------------------------------------------------------------------------
  1436. void CNPC_CombineDropship::InputDropAPC( inputdata_t &inputdata )
  1437. {
  1438. if ( !m_hContainer || !FClassnameIs( m_hContainer, "prop_vehicle_apc" ) )
  1439. {
  1440. Warning("npc_combinedropship %s was told to drop APC, but isn't carrying one!\n", STRING(GetEntityName()) );
  1441. return;
  1442. }
  1443. m_hContainer->SetParent(NULL, 0);
  1444. // m_hContainer->SetOwnerEntity(NULL);
  1445. Vector vecAbsVelocity = GetAbsVelocity();
  1446. if ( vecAbsVelocity.z > 0 )
  1447. {
  1448. vecAbsVelocity.z = 0.0f;
  1449. }
  1450. if ( m_hContainer->GetHealth() > 0 )
  1451. {
  1452. vecAbsVelocity = vec3_origin;
  1453. }
  1454. m_hContainer->SetAbsVelocity( vecAbsVelocity );
  1455. m_hContainer->SetMoveType( (MoveType_t)m_iContainerMoveType );
  1456. // If the container has a physics object, remove it's shadow
  1457. IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject();
  1458. if ( pPhysicsObject )
  1459. {
  1460. pPhysicsObject->RemoveShadowController();
  1461. }
  1462. UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX );
  1463. m_hContainer = NULL;
  1464. m_OnFinishedDropoff.FireOutput( this, this );
  1465. SetLandingState( LANDING_NO );
  1466. m_hLandTarget = NULL;
  1467. }
  1468. //-----------------------------------------------------------------------------
  1469. // Drop the soldier container
  1470. //-----------------------------------------------------------------------------
  1471. void CNPC_CombineDropship::DropSoldierContainer( )
  1472. {
  1473. m_hContainer->SetParent(NULL, 0);
  1474. // m_hContainer->SetOwnerEntity(NULL);
  1475. Vector vecAbsVelocity = GetAbsVelocity();
  1476. if ( vecAbsVelocity.z > 0 )
  1477. {
  1478. vecAbsVelocity.z = 0.0f;
  1479. }
  1480. m_hContainer->SetAbsVelocity( vecAbsVelocity );
  1481. m_hContainer->SetMoveType( MOVETYPE_VPHYSICS );
  1482. // If we have a troop in the process of exiting, kill him.
  1483. // We do this to avoid having to solve the AI problems resulting from it.
  1484. if ( m_hLastTroopToLeave )
  1485. {
  1486. CTakeDamageInfo dmgInfo( this, this, vec3_origin, m_hContainer->GetAbsOrigin(), m_hLastTroopToLeave->GetMaxHealth(), DMG_GENERIC );
  1487. m_hLastTroopToLeave->TakeDamage( dmgInfo );
  1488. }
  1489. // If the container has a physics object, remove it's shadow
  1490. IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject();
  1491. if ( pPhysicsObject )
  1492. {
  1493. pPhysicsObject->RemoveShadowController();
  1494. pPhysicsObject->SetVelocity( &vecAbsVelocity, &vec3_origin );
  1495. }
  1496. UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX );
  1497. m_hContainer = NULL;
  1498. SetLandingState( LANDING_NO );
  1499. m_hLandTarget = NULL;
  1500. if ( m_bHasDroppedOff )
  1501. {
  1502. m_OnContainerShotDownAfterDropoff.FireOutput( this, this );
  1503. }
  1504. else
  1505. {
  1506. int iTroopsNotUnloaded = (m_soldiersToDrop - m_iCurrentTroopExiting);
  1507. if ( g_debug_dropship.GetInt() )
  1508. {
  1509. Msg("Dropship died, troops not unloaded: %d\n", iTroopsNotUnloaded );
  1510. }
  1511. m_OnContainerShotDownBeforeDropoff.Set( iTroopsNotUnloaded, this, this );
  1512. }
  1513. }
  1514. //-----------------------------------------------------------------------------
  1515. // Purpose: Pick up a specified object
  1516. // Input : &inputdata -
  1517. //-----------------------------------------------------------------------------
  1518. void CNPC_CombineDropship::InputPickup( inputdata_t &inputdata )
  1519. {
  1520. // Can't pickup if we're already carrying something
  1521. if ( m_hContainer )
  1522. {
  1523. Warning("npc_combinedropship %s was told to pickup, but is already carrying something.\n", STRING(GetEntityName()) );
  1524. return;
  1525. }
  1526. string_t iszTargetName = inputdata.value.StringID();
  1527. if ( iszTargetName == NULL_STRING )
  1528. {
  1529. Warning("npc_combinedropship %s tried to pickup with no specified pickup target.\n", STRING(GetEntityName()) );
  1530. return;
  1531. }
  1532. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, iszTargetName );
  1533. if ( !pTarget )
  1534. {
  1535. Warning("npc_combinedropship %s couldn't find pickup target named %s\n", STRING(GetEntityName()), STRING(iszTargetName) );
  1536. return;
  1537. }
  1538. // Start heading to the point
  1539. m_hPickupTarget = pTarget;
  1540. m_bHasDroppedOff = false;
  1541. // Disable collisions to my target
  1542. m_hPickupTarget->SetOwnerEntity(this);
  1543. if ( m_NPCState == NPC_STATE_IDLE )
  1544. {
  1545. SetState( NPC_STATE_ALERT );
  1546. }
  1547. SetLandingState( LANDING_SWOOPING );
  1548. m_flLandingSpeed = GetAbsVelocity().Length();
  1549. UpdatePickupNavigation();
  1550. }
  1551. //-----------------------------------------------------------------------------
  1552. // Purpose: Set the range of the container's gun
  1553. // Input : &inputdata -
  1554. //-----------------------------------------------------------------------------
  1555. void CNPC_CombineDropship::InputSetGunRange( inputdata_t &inputdata )
  1556. {
  1557. m_flGunRange = inputdata.value.Float();
  1558. }
  1559. //------------------------------------------------------------------------------
  1560. // Set the landing state
  1561. //------------------------------------------------------------------------------
  1562. void CNPC_CombineDropship::SetLandingState( LandingState_t landingState )
  1563. {
  1564. if ( landingState == m_iLandState )
  1565. return;
  1566. if ( m_pDescendingWarningSound )
  1567. {
  1568. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1569. if ( ( landingState == LANDING_DESCEND ) || ( landingState == LANDING_TOUCHDOWN ) || ( landingState == LANDING_UNLOADING ) || ( landingState == LANDING_UNLOADED ) || ( landingState == LANDING_HOVER_DESCEND ) )
  1570. {
  1571. controller.SoundChangeVolume( m_pDescendingWarningSound, m_bSuppressSound ? 0.0f : 1.0f, 0.3f );
  1572. }
  1573. else
  1574. {
  1575. controller.SoundChangeVolume( m_pDescendingWarningSound, 0.0f, 0.0f );
  1576. }
  1577. }
  1578. m_iLandState = landingState;
  1579. }
  1580. //------------------------------------------------------------------------------
  1581. //------------------------------------------------------------------------------
  1582. bool CNPC_CombineDropship::IsHovering()
  1583. {
  1584. bool bIsHovering = false;
  1585. if( GetLandingState() > LANDING_START_HOVER && GetLandingState() < LANDING_END_HOVER )
  1586. {
  1587. bIsHovering = true;
  1588. }
  1589. return bIsHovering;
  1590. }
  1591. //------------------------------------------------------------------------------
  1592. // Update the ground rotorwash volume
  1593. //------------------------------------------------------------------------------
  1594. void CNPC_CombineDropship::UpdateGroundRotorWashSound( float flAltitude )
  1595. {
  1596. float flVolume = RemapValClamped( flAltitude, DROPSHIP_GROUND_WASH_MIN_ALTITUDE, DROPSHIP_GROUND_WASH_MAX_ALTITUDE, 1.0f, 0.0f );
  1597. UpdateRotorWashVolume( m_pRotorOnGroundSound, flVolume * GetRotorVolume(), 0.5f );
  1598. }
  1599. //------------------------------------------------------------------------------
  1600. // Purpose :
  1601. // Input :
  1602. // Output :
  1603. //------------------------------------------------------------------------------
  1604. void CNPC_CombineDropship::PrescheduleThink( void )
  1605. {
  1606. BaseClass::PrescheduleThink();
  1607. // "npc_kill" destroys our container
  1608. if (m_debugOverlays & OVERLAY_NPC_KILL_BIT)
  1609. {
  1610. if ( m_hContainer )
  1611. {
  1612. CTakeDamageInfo dmgInfo( this, this, vec3_origin, vec3_origin, 1000, DMG_BLAST );
  1613. m_hContainer->TakeDamage( dmgInfo );
  1614. }
  1615. }
  1616. // Update the ground rotorwash volume
  1617. float flAltitude = GetAltitude();
  1618. UpdateGroundRotorWashSound( flAltitude );
  1619. // keep track of think time deltas for burn calc below
  1620. float dt = gpGlobals->curtime - m_flLastTime;
  1621. m_flLastTime = gpGlobals->curtime;
  1622. switch( GetLandingState() )
  1623. {
  1624. case LANDING_NO:
  1625. {
  1626. if ( IsActivityFinished() && (GetActivity() != ACT_DROPSHIP_FLY_IDLE_EXAGG && GetActivity() != ACT_DROPSHIP_FLY_IDLE_CARGO) )
  1627. {
  1628. if ( m_hContainer )
  1629. {
  1630. SetIdealActivity( (Activity)ACT_DROPSHIP_FLY_IDLE_CARGO );
  1631. }
  1632. else
  1633. {
  1634. SetIdealActivity( (Activity)ACT_DROPSHIP_FLY_IDLE_EXAGG );
  1635. }
  1636. }
  1637. DoRotorWash();
  1638. }
  1639. break;
  1640. case LANDING_LEVEL_OUT:
  1641. case LANDING_HOVER_LEVEL_OUT:
  1642. {
  1643. // Approach the drop point
  1644. Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin());
  1645. float flDistance = vecToTarget.Length();
  1646. // Are we there yet?
  1647. float flSpeed = GetAbsVelocity().Length();
  1648. if ( flDistance < 70 && flSpeed < 100 )
  1649. {
  1650. m_flLandingSpeed = flSpeed;
  1651. if( IsHovering() )
  1652. {
  1653. SetLandingState( LANDING_HOVER_DESCEND );
  1654. }
  1655. else
  1656. {
  1657. SetLandingState( LANDING_DESCEND );
  1658. }
  1659. // save off current angles so we can work them out over time
  1660. QAngle angles = GetLocalAngles();
  1661. m_existPitch = angles.x;
  1662. m_existRoll = angles.z;
  1663. }
  1664. DoRotorWash();
  1665. }
  1666. break;
  1667. case LANDING_DESCEND:
  1668. case LANDING_HOVER_DESCEND:
  1669. {
  1670. /*
  1671. if ( IsActivityFinished() && GetActivity() != ACT_DROPSHIP_DESCEND_IDLE )
  1672. {
  1673. SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE );
  1674. }
  1675. */
  1676. if( IsHovering() && m_hLandTarget != NULL )
  1677. {
  1678. // We're trying to hover above an arbitrary point, not above the ground.
  1679. // Recompute flAltitude to indicate the vertical distance from the land
  1680. // target so that touchdown is correctly detected.
  1681. flAltitude = GetAbsOrigin().z - m_hLandTarget->GetAbsOrigin().z;
  1682. }
  1683. // Orient myself to the desired direction
  1684. bool bStillOrienting = false;
  1685. Vector targetDir;
  1686. if ( m_hLandTarget )
  1687. {
  1688. // We've got a land target, so match it's orientation
  1689. AngleVectors( m_hLandTarget->GetAbsAngles(), &targetDir );
  1690. }
  1691. else
  1692. {
  1693. // No land target.
  1694. targetDir = GetDesiredPosition() - GetAbsOrigin();
  1695. }
  1696. // Don't unload until we're facing the way the dropoff point specifies
  1697. float flTargetYaw = UTIL_VecToYaw( targetDir );
  1698. float flDeltaYaw = UTIL_AngleDiff( flTargetYaw, GetAbsAngles().y );
  1699. if ( fabs(flDeltaYaw) > 5 )
  1700. {
  1701. bStillOrienting = true;
  1702. }
  1703. // Ensure we land on the drop point. Stop dropping if we're still turning.
  1704. Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin());
  1705. float flDistance = vecToTarget.Length();
  1706. float flRampedSpeed = m_flLandingSpeed * (flDistance / 70);
  1707. Vector vecVelocity = (flRampedSpeed / flDistance) * vecToTarget;
  1708. #define MAX_LAND_VEL -300.0f
  1709. #define MIN_LAND_VEL -75.0f
  1710. #define ALTITUDE_CAP 512.0f
  1711. float flFactor = MIN( 1.0, flAltitude / ALTITUDE_CAP );
  1712. float flDescendVelocity = MIN( -75, MAX_LAND_VEL * flFactor );
  1713. vecVelocity.z = flDescendVelocity;
  1714. SetAbsVelocity( vecVelocity );
  1715. if ( flAltitude < 72 )
  1716. {
  1717. QAngle angles = GetLocalAngles();
  1718. // Level out quickly.
  1719. angles.x = UTIL_Approach( 0.0, angles.x, 0.2 );
  1720. angles.z = UTIL_Approach( 0.0, angles.z, 0.2 );
  1721. SetLocalAngles( angles );
  1722. }
  1723. else
  1724. {
  1725. // randomly move as if buffeted by ground effects
  1726. // gently flatten ship from starting pitch/yaw
  1727. m_existPitch = UTIL_Approach( 0.0, m_existPitch, 1 );
  1728. m_existRoll = UTIL_Approach( 0.0, m_existRoll, 1 );
  1729. QAngle angles = GetLocalAngles();
  1730. angles.x = m_existPitch + ( sin( gpGlobals->curtime * 3.5f ) * DROPSHIP_MAX_LAND_TILT );
  1731. angles.z = m_existRoll + ( sin( gpGlobals->curtime * 3.75f ) * DROPSHIP_MAX_LAND_TILT );
  1732. SetLocalAngles( angles );
  1733. }
  1734. DoRotorWash();
  1735. // place danger sounds 1 foot above ground to get troops to scatter if they are below dropship
  1736. Vector vecBottom = GetAbsOrigin();
  1737. vecBottom.z += WorldAlignMins().z;
  1738. Vector vecSpot = vecBottom + Vector(0, 0, -1) * (flAltitude - 12 );
  1739. CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.1, this, 0 );
  1740. CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, 400, 0.1, this, 1 );
  1741. // NDebugOverlay::Cross3D( vecSpot, -Vector(4,4,4), Vector(4,4,4), 255, 0, 255, false, 10.0f );
  1742. // now check to see if player is below us, if so, cause heat damage to them (i.e. get them to move)
  1743. trace_t tr;
  1744. Vector vecBBoxMin = CRATE_BBOX_MIN; // use flat box for check
  1745. vecBBoxMin.z = -5;
  1746. Vector vecBBoxMax = CRATE_BBOX_MAX;
  1747. vecBBoxMax.z = 5;
  1748. Vector pEndPoint = vecBottom + Vector(0, 0, -1) * ( flAltitude - 12 );
  1749. AI_TraceHull( vecBottom, pEndPoint, vecBBoxMin, vecBBoxMax, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1750. if ( tr.fraction < 1.0f )
  1751. {
  1752. // Damage anything that's blocking me
  1753. if ( tr.m_pEnt && tr.m_pEnt->m_takedamage != DAMAGE_NO )
  1754. {
  1755. CTakeDamageInfo info( this, this, 20 * dt, DMG_BURN );
  1756. tr.m_pEnt->TakeDamage( info );
  1757. }
  1758. }
  1759. if ( !bStillOrienting && ((flAltitude <= 0.5f) || (m_iCrateType == CRATE_APC)) )
  1760. {
  1761. if( IsHovering() )
  1762. {
  1763. SetAbsVelocity( vec3_origin );
  1764. SetLandingState( LANDING_HOVER_TOUCHDOWN );
  1765. }
  1766. else
  1767. {
  1768. SetLandingState( LANDING_TOUCHDOWN );
  1769. }
  1770. // upon landing, make sure ship is flat
  1771. QAngle angles = GetLocalAngles();
  1772. angles.x = 0;
  1773. angles.z = 0;
  1774. SetLocalAngles( angles );
  1775. // TODO: Release cargo anim
  1776. //SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE );
  1777. return;
  1778. }
  1779. }
  1780. break;
  1781. case LANDING_TOUCHDOWN:
  1782. case LANDING_HOVER_TOUCHDOWN:
  1783. {
  1784. /*
  1785. if ( IsActivityFinished() && ( GetActivity() != ACT_DROPSHIP_DESCEND_IDLE ) )
  1786. {
  1787. SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE );
  1788. }
  1789. */
  1790. // Wait here if we're supposed to wait for the dropoff input
  1791. if ( m_bWaitForDropoffInput )
  1792. return;
  1793. // Wait here till designer tells us to get moving again.
  1794. if ( IsHovering() )
  1795. return;
  1796. SetLandingState( LANDING_UNLOADING );
  1797. // If we're dropping off troops, we'll wait for them to be done.
  1798. // Otherwise, just pause on the ground for a few seconds and then leave.
  1799. if ( m_soldiersToDrop && m_hContainer)
  1800. {
  1801. m_flTimeTakeOff = 0;
  1802. m_flNextTroopSpawnAttempt = 0;
  1803. // Open our container
  1804. m_hContainer->SetSequence( m_hContainer->LookupSequence("open_idle") );
  1805. // Start unloading troops
  1806. m_iCurrentTroopExiting = 0;
  1807. SpawnTroop();
  1808. }
  1809. else
  1810. {
  1811. float flHoverTime = ( m_iCrateType >= 0 ) ? DROPSHIP_LANDING_HOVER_TIME : 0.5f;
  1812. m_flTimeTakeOff = gpGlobals->curtime + flHoverTime;
  1813. }
  1814. }
  1815. break;
  1816. case LANDING_UNLOADING:
  1817. {
  1818. // If we've got no specified takeoff time, we're still waiting for troops to exit. Idle.
  1819. if ( !m_flTimeTakeOff )
  1820. {
  1821. float idleVolume = 0.2f;
  1822. m_engineThrust = UTIL_Approach( idleVolume, m_engineThrust, 0.04f );
  1823. if ( m_engineThrust > idleVolume )
  1824. {
  1825. // Make sure we're kicking up dust/water as long as engine thrust is up
  1826. DoRotorWash();
  1827. }
  1828. // If we've lost the last troop who was leaving, he probably got killed during dustoff.
  1829. if ( !m_hLastTroopToLeave || !m_hLastTroopToLeave->IsAlive() )
  1830. {
  1831. // If we still have troops onboard, spawn the next one
  1832. if ( m_iCurrentTroopExiting < m_soldiersToDrop )
  1833. {
  1834. SpawnTroop();
  1835. }
  1836. else
  1837. {
  1838. // We're out of troops, time to leave
  1839. m_flTimeTakeOff = gpGlobals->curtime + 0.5;
  1840. }
  1841. }
  1842. }
  1843. else
  1844. {
  1845. if( gpGlobals->curtime > m_flTimeTakeOff )
  1846. {
  1847. SetLandingState( LANDING_LIFTOFF );
  1848. SetActivity( (Activity)ACT_DROPSHIP_LIFTOFF );
  1849. m_engineThrust = 1.0f; // ensure max volume once we're airborne
  1850. if ( m_bIsFiring )
  1851. {
  1852. StopCannon(); // kill cannon sounds if they are on
  1853. }
  1854. // detach container from ship
  1855. if ( m_hContainer && m_leaveCrate )
  1856. {
  1857. m_hContainer->SetParent(NULL);
  1858. m_hContainer->SetMoveType( (MoveType_t)m_iContainerMoveType );
  1859. Vector vecAbsVelocity( 0, 0, GetAbsVelocity().z );
  1860. if ( vecAbsVelocity.z > 0 )
  1861. {
  1862. vecAbsVelocity.z = 0.0f;
  1863. }
  1864. m_hContainer->SetAbsVelocity( vecAbsVelocity );
  1865. // If the container has a physics object, remove it's shadow
  1866. IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject();
  1867. if ( pPhysicsObject )
  1868. {
  1869. pPhysicsObject->RemoveShadowController();
  1870. pPhysicsObject->SetVelocity( &vecAbsVelocity, &vec3_origin );
  1871. }
  1872. m_hContainer = NULL;
  1873. UTIL_SetSize( this, DROPSHIP_BBOX_MIN, DROPSHIP_BBOX_MAX );
  1874. }
  1875. }
  1876. else if ( (m_flTimeTakeOff - gpGlobals->curtime) < 0.5f )
  1877. {
  1878. // Manage engine wash and volume
  1879. m_engineThrust = UTIL_Approach( 1.0f, m_engineThrust, 0.1f );
  1880. DoRotorWash();
  1881. }
  1882. }
  1883. }
  1884. break;
  1885. case LANDING_LIFTOFF:
  1886. {
  1887. // Once we're off the ground, start flying again
  1888. if ( flAltitude > 120 )
  1889. {
  1890. SetLandingState( LANDING_NO );
  1891. m_hLandTarget = NULL;
  1892. m_bHasDroppedOff = true;
  1893. m_OnFinishedDropoff.FireOutput( this, this );
  1894. }
  1895. if ( m_hContainer )
  1896. {
  1897. m_hContainer->SetSequence( m_hContainer->LookupSequence("close_idle") );
  1898. }
  1899. }
  1900. break;
  1901. case LANDING_SWOOPING:
  1902. {
  1903. // Did we lose our pickup target?
  1904. if ( !m_hPickupTarget )
  1905. {
  1906. SetLandingState( LANDING_NO );
  1907. }
  1908. else
  1909. {
  1910. // Decrease altitude and speed to hit the target point.
  1911. Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin());
  1912. float flDistance = vecToTarget.Length();
  1913. // Start cheating when we get near it
  1914. if ( flDistance < 50 )
  1915. {
  1916. /*
  1917. if ( flDistance > 10 )
  1918. {
  1919. // Cheat and ensure we touch the target
  1920. float flSpeed = GetAbsVelocity().Length();
  1921. Vector vecVelocity = vecToTarget;
  1922. VectorNormalize( vecVelocity );
  1923. SetAbsVelocity( vecVelocity * min(flSpeed,flDistance) );
  1924. }
  1925. else
  1926. */
  1927. {
  1928. // Grab the target
  1929. m_hContainer = m_hPickupTarget;
  1930. m_hPickupTarget = NULL;
  1931. m_iContainerMoveType = m_hContainer->GetMoveType();
  1932. if ( m_bInvulnerable && m_hContainer )
  1933. {
  1934. m_hContainer->m_takedamage = DAMAGE_NO;
  1935. }
  1936. // If the container has a physics object, move it to shadow
  1937. IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject();
  1938. if ( pPhysicsObject )
  1939. {
  1940. pPhysicsObject->EnableMotion( true );
  1941. pPhysicsObject->SetShadow( 1e4, 1e4, false, false );
  1942. pPhysicsObject->UpdateShadow( GetAbsOrigin(), GetAbsAngles(), false, 0 );
  1943. }
  1944. m_hContainer->SetParent(this, 0);
  1945. m_hContainer->SetMoveType( MOVETYPE_PUSH );
  1946. m_hContainer->SetGroundEntity( NULL );
  1947. m_OnFinishedPickup.FireOutput( this, this );
  1948. SetLandingState( LANDING_NO );
  1949. }
  1950. }
  1951. }
  1952. DoRotorWash();
  1953. }
  1954. break;
  1955. }
  1956. if ( !(CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) )
  1957. {
  1958. DoCombatStuff();
  1959. }
  1960. if ( GetActivity() != GetIdealActivity() )
  1961. {
  1962. //DevMsg( "setactivity" );
  1963. SetActivity( GetIdealActivity() );
  1964. }
  1965. }
  1966. //-----------------------------------------------------------------------------
  1967. // Purpose:
  1968. //-----------------------------------------------------------------------------
  1969. #define DROPSHIP_WASH_ALTITUDE 1024.0
  1970. void CNPC_CombineDropship::DoRotorWash( void )
  1971. {
  1972. Vector vecForward;
  1973. GetVectors( &vecForward, NULL, NULL );
  1974. Vector vecRotorHub = GetAbsOrigin() + vecForward * -64;
  1975. DrawRotorWash( DROPSHIP_WASH_ALTITUDE, vecRotorHub );
  1976. }
  1977. //------------------------------------------------------------------------------
  1978. // Purpose : Spawn the next NPC in our template list
  1979. //------------------------------------------------------------------------------
  1980. void CNPC_CombineDropship::SpawnTroop( void )
  1981. {
  1982. if ( !m_hContainer )
  1983. {
  1984. // We're done, take off.
  1985. m_flTimeTakeOff = gpGlobals->curtime + 0.5;
  1986. return;
  1987. }
  1988. // Are we fully unloaded? If so, take off. Otherwise, tell the next troop to exit.
  1989. if ( m_iCurrentTroopExiting >= m_soldiersToDrop || m_sNPCTemplateData[m_iCurrentTroopExiting] == NULL_STRING )
  1990. {
  1991. // We're done, take off.
  1992. m_flTimeTakeOff = gpGlobals->curtime + 0.5;
  1993. return;
  1994. }
  1995. m_hLastTroopToLeave = NULL;
  1996. // Not time to try again yet?
  1997. if ( m_flNextTroopSpawnAttempt > gpGlobals->curtime )
  1998. return;
  1999. // HACK: This is a nasty piece of work. We want to make sure the deploy end is clear, and has enough
  2000. // room with our deploying NPC, but we don't want to create the NPC unless it's clear, and we don't
  2001. // know how much room he needs without spawning him.
  2002. // So, because we know that we only ever spawn combine soldiers at the moment, we'll just use their hull.
  2003. // HACK: Add some bloat because the endpoint isn't perfectly aligned with NPC end origin
  2004. Vector vecNPCMins = NAI_Hull::Mins( HULL_HUMAN ) - Vector(4,4,4);
  2005. Vector vecNPCMaxs = NAI_Hull::Maxs( HULL_HUMAN ) + Vector(4,4,4);
  2006. // Scare NPCs away from our deploy endpoint to keep them away
  2007. Vector vecDeployEndPoint;
  2008. QAngle vecDeployEndAngles;
  2009. m_hContainer->GetAttachment( m_iAttachmentTroopDeploy, vecDeployEndPoint, vecDeployEndAngles );
  2010. vecDeployEndPoint = GetDropoffFinishPosition( vecDeployEndPoint, NULL, vecNPCMins, vecNPCMaxs );
  2011. CSoundEnt::InsertSound( SOUND_DANGER, vecDeployEndPoint, 120.0f, 2.0f, this );
  2012. // Make sure there are no NPCs on the spot
  2013. trace_t tr;
  2014. CTraceFilterOnlyNPCsAndPlayer filter( this, COLLISION_GROUP_NONE );
  2015. AI_TraceHull( vecDeployEndPoint, vecDeployEndPoint, vecNPCMins, vecNPCMaxs, MASK_SOLID, &filter, &tr );
  2016. if ( tr.m_pEnt )
  2017. {
  2018. if ( g_debug_dropship.GetInt() == 2 )
  2019. {
  2020. NDebugOverlay::Box( vecDeployEndPoint, vecNPCMins, vecNPCMaxs, 255,0,0, 64, 0.5 );
  2021. }
  2022. m_flNextTroopSpawnAttempt = gpGlobals->curtime + 1;
  2023. return;
  2024. }
  2025. if ( g_debug_dropship.GetInt() == 2 )
  2026. {
  2027. NDebugOverlay::Box( vecDeployEndPoint, vecNPCMins, vecNPCMaxs, 0,255,0, 64, 0.5 );
  2028. }
  2029. // Get the spawn point inside the container
  2030. Vector vecSpawnOrigin;
  2031. QAngle vecSpawnAngles;
  2032. m_hContainer->GetAttachment( m_iAttachmentDeployStart, vecSpawnOrigin, vecSpawnAngles );
  2033. // Spawn the templated NPC
  2034. CBaseEntity *pEntity = NULL;
  2035. MapEntity_ParseEntity( pEntity, STRING(m_sNPCTemplateData[m_iCurrentTroopExiting]), NULL );
  2036. // Increment troop count
  2037. m_iCurrentTroopExiting++;
  2038. if ( !pEntity )
  2039. {
  2040. Warning("Dropship could not create template NPC\n" );
  2041. return;
  2042. }
  2043. CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
  2044. Assert( pNPC );
  2045. // Spawn an entity blocker.
  2046. CBaseEntity *pBlocker = CEntityBlocker::Create( vecDeployEndPoint, vecNPCMins, vecNPCMaxs, pNPC, true );
  2047. g_EventQueue.AddEvent( pBlocker, "Kill", 2.5, this, this );
  2048. if ( g_debug_dropship.GetInt() == 2 )
  2049. {
  2050. NDebugOverlay::Box( vecDeployEndPoint, vecNPCMins, vecNPCMaxs, 255, 255, 255, 64, 2.5 );
  2051. }
  2052. // Ensure our NPCs are standing upright
  2053. vecSpawnAngles[PITCH] = vecSpawnAngles[ROLL] = 0;
  2054. // Move it to the container spawnpoint
  2055. pNPC->SetAbsOrigin( vecSpawnOrigin );
  2056. pNPC->SetAbsAngles( vecSpawnAngles );
  2057. DispatchSpawn( pNPC );
  2058. pNPC->m_NPCState = NPC_STATE_IDLE;
  2059. pNPC->Activate();
  2060. // Spawn a scripted sequence entity to make the NPC run out of the dropship
  2061. CAI_ScriptedSequence *pSequence = (CAI_ScriptedSequence*)CreateEntityByName( "scripted_sequence" );
  2062. pSequence->KeyValue( "m_iszEntity", STRING(pNPC->GetEntityName()) );
  2063. pSequence->KeyValue( "m_iszPlay", "Dropship_Deploy" );
  2064. pSequence->KeyValue( "m_fMoveTo", "4" ); // CINE_MOVETO_TELEPORT
  2065. pSequence->KeyValue( "OnEndSequence", UTIL_VarArgs("%s,NPCFinishDustoff,%s,0,-1", STRING(GetEntityName()), STRING(pNPC->GetEntityName())) );
  2066. pSequence->SetAbsOrigin( vecSpawnOrigin );
  2067. pSequence->SetAbsAngles( vecSpawnAngles );
  2068. pSequence->AddSpawnFlags( SF_SCRIPT_NOINTERRUPT | SF_SCRIPT_HIGH_PRIORITY | SF_SCRIPT_OVERRIDESTATE );
  2069. pSequence->Spawn();
  2070. pSequence->Activate();
  2071. variant_t emptyVariant;
  2072. pSequence->AcceptInput( "BeginSequence", this, this, emptyVariant, 0 );
  2073. m_hLastTroopToLeave = pNPC;
  2074. }
  2075. //-----------------------------------------------------------------------------
  2076. // Purpose: Returns a safe position above/below the specified origin for the NPC to finish it's dropoff on
  2077. // Input : vecOrigin -
  2078. //-----------------------------------------------------------------------------
  2079. Vector CNPC_CombineDropship::GetDropoffFinishPosition( Vector vecOrigin, CAI_BaseNPC *pNPC, Vector vecMins, Vector vecMaxs )
  2080. {
  2081. // Use the NPC's if they're specified
  2082. if ( pNPC )
  2083. {
  2084. vecMins = NAI_Hull::Mins( pNPC->GetHullType() );
  2085. vecMaxs = NAI_Hull::Maxs( pNPC->GetHullType() );
  2086. }
  2087. trace_t tr;
  2088. AI_TraceHull( vecOrigin + Vector(0,0,32), vecOrigin, vecMins, vecMaxs, MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
  2089. if ( tr.fraction < 1.0 )
  2090. {
  2091. if ( g_debug_dropship.GetInt() == 1 )
  2092. {
  2093. NDebugOverlay::Box( vecOrigin, vecMins, vecMaxs, 255,0,0, 8, 4.0 );
  2094. }
  2095. // Try and find the ground
  2096. AI_TraceHull( vecOrigin + Vector(0,0,32), vecOrigin, vecMins, vecMaxs, MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr );
  2097. if ( !tr.startsolid )
  2098. return (tr.endpos + Vector(0,0,1));
  2099. }
  2100. else if ( g_debug_dropship.GetInt() == 1 )
  2101. {
  2102. NDebugOverlay::Box( vecOrigin, vecMins, vecMaxs, 0,255,0, 8, 4.0 );
  2103. }
  2104. return vecOrigin;
  2105. }
  2106. //-----------------------------------------------------------------------------
  2107. // Purpose: A troop we dropped of has now finished the scripted sequence
  2108. // Input : &inputdata -
  2109. //-----------------------------------------------------------------------------
  2110. void CNPC_CombineDropship::InputNPCFinishDustoff( inputdata_t &inputdata )
  2111. {
  2112. CBaseEntity *pEnt = gEntList.FindEntityByName( NULL, inputdata.value.StringID(), NULL, inputdata.pActivator, inputdata.pCaller );
  2113. if ( !pEnt )
  2114. return;
  2115. CAI_BaseNPC *pNPC = pEnt->MyNPCPointer();
  2116. Assert( pNPC );
  2117. Vector vecOrigin = GetDropoffFinishPosition( pNPC->GetAbsOrigin(), pNPC, vec3_origin, vec3_origin );
  2118. pNPC->SetAbsOrigin( vecOrigin );
  2119. // Do we have a dustoff point?
  2120. CBaseEntity *pDustoff = NULL;
  2121. if ( m_sDustoffPoints[m_iCurrentTroopExiting-1] != NULL_STRING )
  2122. {
  2123. pDustoff = gEntList.FindEntityByName( NULL, m_sDustoffPoints[m_iCurrentTroopExiting-1] );
  2124. if ( !pDustoff )
  2125. {
  2126. Warning("npc_combinedropship %s couldn't find dustoff target named %s\n", STRING(GetEntityName()), STRING(m_sDustoffPoints[m_iCurrentTroopExiting-1]) );
  2127. }
  2128. }
  2129. if ( !pDustoff )
  2130. {
  2131. // Make a move away sound to scare the combine away from this point
  2132. CSoundEnt::InsertSound( SOUND_MOVE_AWAY | SOUND_CONTEXT_COMBINE_ONLY, pNPC->GetAbsOrigin(), 128, 0.1 );
  2133. }
  2134. else
  2135. {
  2136. if ( g_debug_dropship.GetInt() == 1 )
  2137. {
  2138. NDebugOverlay::Box( pDustoff->GetAbsOrigin(), -Vector(10,10,10), Vector(10,10,10), 0,255,0, 8, 5.0 );
  2139. }
  2140. // Tell the NPC to move to the dustoff position
  2141. pNPC->SetState( NPC_STATE_ALERT );
  2142. pNPC->ScheduledMoveToGoalEntity( SCHED_DROPSHIP_DUSTOFF, pDustoff, ACT_RUN );
  2143. pNPC->GetNavigator()->SetArrivalDirection( pDustoff->GetAbsAngles() );
  2144. // Make sure they ignore a bunch of conditions
  2145. static int g_Conditions[] =
  2146. {
  2147. COND_CAN_MELEE_ATTACK1,
  2148. COND_CAN_MELEE_ATTACK2,
  2149. COND_CAN_RANGE_ATTACK1,
  2150. COND_CAN_RANGE_ATTACK2,
  2151. COND_ENEMY_DEAD,
  2152. COND_HEAR_BULLET_IMPACT,
  2153. COND_HEAR_COMBAT,
  2154. COND_HEAR_DANGER,
  2155. COND_NEW_ENEMY,
  2156. COND_PROVOKED,
  2157. COND_SEE_ENEMY,
  2158. COND_SEE_FEAR,
  2159. COND_SMELL,
  2160. COND_LIGHT_DAMAGE,
  2161. COND_HEAVY_DAMAGE,
  2162. COND_PHYSICS_DAMAGE,
  2163. COND_REPEATED_DAMAGE,
  2164. };
  2165. pNPC->SetIgnoreConditions( g_Conditions, ARRAYSIZE(g_Conditions) );
  2166. }
  2167. // Unload the next troop
  2168. SpawnTroop();
  2169. }
  2170. //-----------------------------------------------------------------------------
  2171. // Purpose: Tells the dropship to stop waiting and dustoff
  2172. // Input : &inputdata -
  2173. //-----------------------------------------------------------------------------
  2174. void CNPC_CombineDropship::InputStopWaitingForDropoff( inputdata_t &inputdata )
  2175. {
  2176. m_bWaitForDropoffInput = false;
  2177. }
  2178. //------------------------------------------------------------------------------
  2179. //------------------------------------------------------------------------------
  2180. void CNPC_CombineDropship::InputHover( inputdata_t &inputdata )
  2181. {
  2182. m_iszLandTarget = inputdata.value.StringID();
  2183. LandCommon( true );
  2184. }
  2185. //------------------------------------------------------------------------------
  2186. //------------------------------------------------------------------------------
  2187. void CNPC_CombineDropship::InputFlyToPathTrack( inputdata_t &inputdata )
  2188. {
  2189. if( IsHovering() )
  2190. {
  2191. SetLandingState( LANDING_NO );
  2192. m_hLandTarget = NULL;
  2193. }
  2194. CAI_TrackPather::InputFlyToPathTrack( inputdata );
  2195. }
  2196. //------------------------------------------------------------------------------
  2197. // Purpose :
  2198. // Input :
  2199. // Output :
  2200. //------------------------------------------------------------------------------
  2201. float CNPC_CombineDropship::GetAltitude( void )
  2202. {
  2203. trace_t tr;
  2204. Vector vecBottom = GetAbsOrigin();
  2205. // Uneven terrain causes us problems, so trace our box down
  2206. AI_TraceEntity( this, vecBottom, vecBottom - Vector(0,0,4096), MASK_SOLID_BRUSHONLY, &tr );
  2207. float flAltitude = ( 4096 * tr.fraction );
  2208. //DevMsg(" Altitude: %.3f\n", flAltitude );
  2209. return flAltitude;
  2210. }
  2211. //-----------------------------------------------------------------------------
  2212. // Purpose: Drop rollermine from dropship
  2213. //-----------------------------------------------------------------------------
  2214. void CNPC_CombineDropship::DropMine( void )
  2215. {
  2216. NPC_Rollermine_DropFromPoint( GetAbsOrigin(), this, STRING(m_sRollermineTemplateData) );
  2217. }
  2218. //------------------------------------------------------------------------------
  2219. // Purpose : Fly towards our pickup target
  2220. //------------------------------------------------------------------------------
  2221. void CNPC_CombineDropship::UpdatePickupNavigation( void )
  2222. {
  2223. // Try and touch the top of the object
  2224. Vector vecPickup = m_hPickupTarget->WorldSpaceCenter();
  2225. vecPickup.z += (m_hPickupTarget->CollisionProp()->OBBSize().z * 0.5);
  2226. SetDesiredPosition( vecPickup );
  2227. //NDebugOverlay::Cross3D( GetDesiredPosition(), -Vector(32,32,32), Vector(32,32,32), 0, 255, 255, true, 0.1f );
  2228. }
  2229. //------------------------------------------------------------------------------
  2230. // Purpose : Fly towards our land target
  2231. //------------------------------------------------------------------------------
  2232. void CNPC_CombineDropship::UpdateLandTargetNavigation( void )
  2233. {
  2234. Vector vecPickup = m_hLandTarget->WorldSpaceCenter();
  2235. vecPickup.z += 256;
  2236. SetDesiredPosition( vecPickup );
  2237. //NDebugOverlay::Cross3D( GetDesiredPosition(), -Vector(32,32,32), Vector(32,32,32), 0, 255, 255, true, 0.1f );
  2238. }
  2239. //------------------------------------------------------------------------------
  2240. // Purpose :
  2241. // Input :
  2242. // Output :
  2243. //------------------------------------------------------------------------------
  2244. void CNPC_CombineDropship::Hunt( void )
  2245. {
  2246. // If we have a pickup target, fly to it
  2247. if ( m_hPickupTarget )
  2248. {
  2249. UpdatePickupNavigation();
  2250. }
  2251. else if ( m_hLandTarget )
  2252. {
  2253. UpdateLandTargetNavigation();
  2254. }
  2255. else if ( GetLandingState() == LANDING_NO )
  2256. {
  2257. UpdateTrackNavigation();
  2258. }
  2259. // don't face player ever, only face nav points
  2260. Vector desiredDir = GetDesiredPosition() - GetAbsOrigin();
  2261. VectorNormalize( desiredDir );
  2262. // Face our desired position.
  2263. m_vecDesiredFaceDir = desiredDir;
  2264. if ( GetLandingState() == LANDING_DESCEND || GetLandingState() == LANDING_LEVEL_OUT || IsHovering() )
  2265. {
  2266. if ( m_hLandTarget )
  2267. {
  2268. // We've got a land target, so match it's orientation
  2269. AngleVectors( m_hLandTarget->GetAbsAngles(), &m_vecDesiredFaceDir );
  2270. }
  2271. else
  2272. {
  2273. // No land target.
  2274. m_vecDesiredFaceDir = GetDesiredPosition() - GetAbsOrigin();
  2275. }
  2276. }
  2277. UpdateEnemy();
  2278. Flight();
  2279. UpdatePlayerDopplerShift( );
  2280. }
  2281. //-----------------------------------------------------------------------------
  2282. // Purpose:
  2283. //-----------------------------------------------------------------------------
  2284. void CNPC_CombineDropship::GatherEnemyConditions( CBaseEntity *pEnemy )
  2285. {
  2286. BaseClass::GatherEnemyConditions(pEnemy);
  2287. // If we can't see the enemy for a few seconds, consider him unreachable
  2288. if ( !HasCondition(COND_SEE_ENEMY) )
  2289. {
  2290. if ( gpGlobals->curtime - GetEnemyLastTimeSeen() >= 3.0f )
  2291. {
  2292. MarkEnemyAsEluded();
  2293. }
  2294. }
  2295. }
  2296. //-----------------------------------------------------------------------------
  2297. // Purpose: do all of the stuff related to having an enemy, attacking, etc.
  2298. //-----------------------------------------------------------------------------
  2299. void CNPC_CombineDropship::DoCombatStuff( void )
  2300. {
  2301. // Handle mines
  2302. if ( m_bDropMines )
  2303. {
  2304. switch( m_iDropState )
  2305. {
  2306. case DROP_IDLE:
  2307. {
  2308. m_iMineCount = m_totalMinesToDrop - 1;
  2309. DropMine();
  2310. // setup next individual drop time
  2311. m_flDropDelay = gpGlobals->curtime + DROPSHIP_TIME_BETWEEN_MINES;
  2312. // get ready to drop next mine, unless we're only supposed to drop 1
  2313. if ( m_iMineCount )
  2314. {
  2315. m_iDropState = DROP_NEXT;
  2316. }
  2317. else
  2318. {
  2319. m_bDropMines = false; // no more...
  2320. }
  2321. break;
  2322. }
  2323. case DROP_NEXT:
  2324. {
  2325. if ( gpGlobals->curtime > m_flDropDelay ) // time to drop next mine?
  2326. {
  2327. DropMine();
  2328. m_flDropDelay = gpGlobals->curtime + DROPSHIP_TIME_BETWEEN_MINES;
  2329. m_iMineCount--;
  2330. if ( !m_iMineCount )
  2331. {
  2332. m_iDropState = DROP_IDLE;
  2333. m_bDropMines = false; // reset flag
  2334. }
  2335. }
  2336. break;
  2337. }
  2338. }
  2339. }
  2340. // Handle guns
  2341. bool bStopGun = true;
  2342. if ( GetEnemy() )
  2343. {
  2344. bStopGun = !FireCannonRound();
  2345. }
  2346. if ( bStopGun && m_bIsFiring )
  2347. {
  2348. StopCannon();
  2349. }
  2350. }
  2351. //-----------------------------------------------------------------------------
  2352. // Purpose: Update the container's gun to face the enemy.
  2353. // Input : &vecMuzzle - The gun's muzzle/firing point
  2354. // &vecAimDir - The gun's current aim direction
  2355. //-----------------------------------------------------------------------------
  2356. void CNPC_CombineDropship::UpdateContainerGunFacing( Vector &vecMuzzle, Vector &vecToTarget, Vector &vecAimDir, float *flTargetRange )
  2357. {
  2358. Assert( m_hContainer );
  2359. // Get the desired aim vector
  2360. vecToTarget = GetEnemy()->WorldSpaceCenter( );
  2361. Vector vecBarrelPos, vecWorldBarrelPos;
  2362. QAngle worldBarrelAngle, vecAngles;
  2363. matrix3x4_t matRefToWorld;
  2364. m_hContainer->GetAttachment( m_iMuzzleAttachment, vecMuzzle, vecAngles );
  2365. vecWorldBarrelPos = vecMuzzle;
  2366. worldBarrelAngle = vecAngles;
  2367. m_hContainer->GetAttachment( m_iMachineGunRefAttachment, matRefToWorld );
  2368. VectorITransform( vecWorldBarrelPos, matRefToWorld, vecBarrelPos );
  2369. EntityMatrix parentMatrix;
  2370. parentMatrix.InitFromEntity( m_hContainer, m_iMachineGunBaseAttachment );
  2371. Vector target = parentMatrix.WorldToLocal( vecToTarget );
  2372. float quadTarget = target.LengthSqr();
  2373. float quadTargetXY = target.x*target.x + target.y*target.y;
  2374. // Target is too close! Can't aim at it
  2375. if ( quadTarget > vecBarrelPos.LengthSqr() )
  2376. {
  2377. // We're trying to aim the offset barrel at an arbitrary point.
  2378. // To calculate this, I think of the target as being on a sphere with
  2379. // it's center at the origin of the gun.
  2380. // The rotation we need is the opposite of the rotation that moves the target
  2381. // along the surface of that sphere to intersect with the gun's shooting direction
  2382. // To calculate that rotation, we simply calculate the intersection of the ray
  2383. // coming out of the barrel with the target sphere (that's the new target position)
  2384. // and use atan2() to get angles
  2385. // angles from target pos to center
  2386. float targetToCenterYaw = atan2( target.y, target.x );
  2387. float centerToGunYaw = atan2( vecBarrelPos.y, sqrt( quadTarget - (vecBarrelPos.y*vecBarrelPos.y) ) );
  2388. float targetToCenterPitch = atan2( target.z, sqrt( quadTargetXY ) );
  2389. float centerToGunPitch = atan2( -vecBarrelPos.z, sqrt( quadTarget - (vecBarrelPos.z*vecBarrelPos.z) ) );
  2390. QAngle angles;
  2391. angles.Init( RAD2DEG(targetToCenterPitch+centerToGunPitch), RAD2DEG( targetToCenterYaw + centerToGunYaw ), 0 );
  2392. float flNewAngle = AngleNormalize( UTIL_ApproachAngle( angles.x, m_hContainer->GetPoseParameter(m_poseWeapon_Pitch), DROPSHIP_GUN_SPEED));
  2393. m_hContainer->SetPoseParameter( m_poseWeapon_Pitch, flNewAngle );
  2394. flNewAngle = AngleNormalize( UTIL_ApproachAngle( angles.y, m_hContainer->GetPoseParameter(m_poseWeapon_Yaw), DROPSHIP_GUN_SPEED));
  2395. m_hContainer->SetPoseParameter( m_poseWeapon_Yaw, flNewAngle );
  2396. m_hContainer->StudioFrameAdvance();
  2397. }
  2398. vecToTarget -= vecMuzzle;
  2399. *flTargetRange = VectorNormalize( vecToTarget );
  2400. AngleVectors( vecAngles, &vecAimDir );
  2401. }
  2402. //------------------------------------------------------------------------------
  2403. // Purpose: Fire a round from the cannon
  2404. // Notes: Only call this if you have an enemy.
  2405. // Returns true if the cannon round was actually fired
  2406. //------------------------------------------------------------------------------
  2407. bool CNPC_CombineDropship::FireCannonRound( void )
  2408. {
  2409. // Try and aim my cannon at the enemy, if I have a container
  2410. if ( !m_hContainer || (m_iCrateType < 0) )
  2411. return false;
  2412. // Update the container gun, and get the vector to the enemy, and the gun's current aim direction
  2413. float flRange;
  2414. Vector vecMuzzle, vecAimDir, vecToEnemy;
  2415. UpdateContainerGunFacing( vecMuzzle, vecToEnemy, vecAimDir, &flRange );
  2416. // Out of range?
  2417. if ( flRange > m_flGunRange )
  2418. return false;
  2419. // Only fire if the target's close enough to our aim direction
  2420. float flCosAngle = DotProduct( vecToEnemy, vecAimDir );
  2421. if ( flCosAngle < DOT_15DEGREE )
  2422. {
  2423. m_flTimeNextAttack = gpGlobals->curtime + 0.1;
  2424. return false;
  2425. }
  2426. // If we're out of rounds, reload
  2427. if ( m_iBurstRounds <= 0 )
  2428. {
  2429. m_iBurstRounds = RandomInt( 10, 20 );
  2430. m_flTimeNextAttack = gpGlobals->curtime + (m_iBurstRounds * 0.1);
  2431. return false;
  2432. }
  2433. // HACK: Return true so the fire sound isn't stopped
  2434. if ( m_flTimeNextAttack > gpGlobals->curtime )
  2435. return true;
  2436. m_iBurstRounds--;
  2437. // If we're not currently firing, start it up
  2438. if ( !m_bIsFiring )
  2439. {
  2440. StartCannon();
  2441. }
  2442. // Add a muzzle flash
  2443. QAngle vecAimAngles;
  2444. VectorAngles( vecAimDir, vecAimAngles );
  2445. g_pEffects->MuzzleFlash( vecMuzzle, vecAimAngles, random->RandomFloat( 5.0f, 7.0f ), MUZZLEFLASH_TYPE_GUNSHIP );
  2446. m_flTimeNextAttack = gpGlobals->curtime + 0.05;
  2447. // Clamp to account for inaccuracy in aiming w/ pose parameters
  2448. vecAimDir = vecToEnemy;
  2449. // Fire the bullet
  2450. int ammoType = GetAmmoDef()->Index("CombineCannon");
  2451. FireBullets( 1, vecMuzzle, vecAimDir, VECTOR_CONE_2DEGREES, 8192, ammoType, 1, -1, -1, sk_npc_dmg_dropship.GetInt() );
  2452. return true;
  2453. }
  2454. //------------------------------------------------------------------------------
  2455. // Scare AIs in the area where bullets are impacting
  2456. //------------------------------------------------------------------------------
  2457. void CNPC_CombineDropship::DoImpactEffect( trace_t &tr, int nDamageType )
  2458. {
  2459. CSoundEnt::InsertSound( SOUND_DANGER | SOUND_CONTEXT_REACT_TO_SOURCE, tr.endpos, 120.0f, 0.3f, this );
  2460. BaseClass::DoImpactEffect( tr, nDamageType );
  2461. }
  2462. //------------------------------------------------------------------------------
  2463. // Purpose : The proper way to begin the gunship cannon firing at the enemy.
  2464. // Input :
  2465. // :
  2466. // Output :
  2467. //------------------------------------------------------------------------------
  2468. void CNPC_CombineDropship::StartCannon( void )
  2469. {
  2470. m_bIsFiring = true;
  2471. // Start up the cannon sound.
  2472. if ( m_pCannonSound )
  2473. {
  2474. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  2475. controller.SoundChangeVolume(m_pCannonSound, 1.0, 0.0);
  2476. }
  2477. }
  2478. //------------------------------------------------------------------------------
  2479. // Purpose : The proper way to cease the gunship cannon firing.
  2480. // Input :
  2481. // :
  2482. // Output :
  2483. //------------------------------------------------------------------------------
  2484. void CNPC_CombineDropship::StopCannon( void )
  2485. {
  2486. m_bIsFiring = false;
  2487. // Stop the cannon sound.
  2488. if ( m_pCannonSound )
  2489. {
  2490. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  2491. controller.SoundChangeVolume(m_pCannonSound, 0.0, 0.1);
  2492. }
  2493. }
  2494. //-----------------------------------------------------------------------------
  2495. // Purpose: Used the gunship's tracer for now
  2496. //-----------------------------------------------------------------------------
  2497. void CNPC_CombineDropship::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType )
  2498. {
  2499. switch ( iTracerType )
  2500. {
  2501. case TRACER_LINE:
  2502. {
  2503. float flTracerDist;
  2504. Vector vecDir;
  2505. Vector vecEndPos;
  2506. vecDir = tr.endpos - vecTracerSrc;
  2507. flTracerDist = VectorNormalize( vecDir );
  2508. UTIL_Tracer( vecTracerSrc, tr.endpos, 0, TRACER_DONT_USE_ATTACHMENT, 16000, true, "GunshipTracer" );
  2509. }
  2510. break;
  2511. default:
  2512. BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType );
  2513. break;
  2514. }
  2515. }
  2516. AI_BEGIN_CUSTOM_NPC( npc_combinedropship, CNPC_CombineDropship )
  2517. DECLARE_ACTIVITY( ACT_DROPSHIP_FLY_IDLE );
  2518. DECLARE_ACTIVITY( ACT_DROPSHIP_FLY_IDLE_EXAGG );
  2519. DECLARE_ACTIVITY( ACT_DROPSHIP_DESCEND_IDLE );
  2520. DECLARE_ACTIVITY( ACT_DROPSHIP_DEPLOY_IDLE );
  2521. DECLARE_ACTIVITY( ACT_DROPSHIP_LIFTOFF );
  2522. DECLARE_ACTIVITY( ACT_DROPSHIP_FLY_IDLE_CARGO );
  2523. AI_END_CUSTOM_NPC()