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.

1589 lines
39 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "beam_shared.h"
  10. #include "ai_default.h"
  11. #include "ai_task.h"
  12. #include "ai_schedule.h"
  13. #include "ai_node.h"
  14. #include "ai_hull.h"
  15. #include "ai_hint.h"
  16. #include "ai_memory.h"
  17. #include "ai_route.h"
  18. #include "ai_motor.h"
  19. #include "ai_senses.h"
  20. #include "hl1_npc_hgrunt.h"
  21. #include "soundent.h"
  22. #include "game.h"
  23. #include "npcevent.h"
  24. #include "entitylist.h"
  25. #include "activitylist.h"
  26. #include "animation.h"
  27. #include "engine/IEngineSound.h"
  28. #include "ammodef.h"
  29. #include "basecombatweapon.h"
  30. #include "hl1_basegrenade.h"
  31. #include "soundenvelope.h"
  32. #include "hl1_CBaseHelicopter.h"
  33. #include "IEffects.h"
  34. #include "smoke_trail.h"
  35. extern short g_sModelIndexFireball;
  36. typedef struct
  37. {
  38. int isValid;
  39. EHANDLE hGrunt;
  40. Vector vecOrigin;
  41. Vector vecAngles;
  42. } t_ospreygrunt;
  43. #define LOADED_WITH_GRUNTS 0 //WORST NAME EVER!
  44. #define UNLOADING_GRUNTS 1
  45. #define GRUNTS_DEPLOYED 2 //Waiting for them to finish repelin
  46. #define SF_WAITFORTRIGGER 0x40
  47. #define MAX_CARRY 24
  48. #define DEFAULT_SPEED 250
  49. #define HELICOPTER_THINK_INTERVAL 0.1
  50. class CNPC_Osprey : public CBaseHelicopter
  51. {
  52. DECLARE_CLASS( CNPC_Osprey, CBaseHelicopter );
  53. public:
  54. int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
  55. void Spawn( void );
  56. void Precache( void );
  57. Class_T Classify( void ) { return CLASS_NONE; };
  58. int BloodColor( void ) { return DONT_BLEED; }
  59. void FindAllThink( void );
  60. void DeployThink( void );
  61. bool HasDead( void );
  62. void Flight( void );
  63. void HoverThink( void );
  64. CAI_BaseNPC *MakeGrunt( Vector vecSrc );
  65. void InitializeRotorSound( void );
  66. void PrescheduleThink( void );
  67. void DyingThink( void );
  68. void CrashTouch( CBaseEntity *pOther );
  69. /*
  70. void CrashTouch( CBaseEntity *pOther );
  71. void DyingThink( void );
  72. void CommandUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
  73. */
  74. void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  75. float m_startTime;
  76. float m_flIdealtilt;
  77. float m_flRotortilt;
  78. float m_flRightHealth;
  79. float m_flLeftHealth;
  80. int m_iUnits;
  81. EHANDLE m_hGrunt[MAX_CARRY];
  82. Vector m_vecOrigin[MAX_CARRY];
  83. EHANDLE m_hRepel[4];
  84. int m_iSoundState;
  85. int m_iSpriteTexture;
  86. int m_iPitch;
  87. int m_iExplode;
  88. int m_iTailGibs;
  89. int m_iBodyGibs;
  90. int m_iEngineGibs;
  91. int m_iDoLeftSmokePuff;
  92. int m_iDoRightSmokePuff;
  93. int m_iRepelState;
  94. float m_flPrevGoalVel;
  95. int m_iRotorAngle;
  96. int m_nDebrisModel;
  97. CHandle<SmokeTrail> m_hLeftSmoke;
  98. CHandle<SmokeTrail> m_hRightSmoke;
  99. DECLARE_DATADESC();
  100. int m_iNextCrashModel; //which gib to explode with next
  101. };
  102. LINK_ENTITY_TO_CLASS( monster_osprey, CNPC_Osprey );
  103. BEGIN_DATADESC( CNPC_Osprey )
  104. DEFINE_FIELD( m_startTime, FIELD_TIME ),
  105. DEFINE_FIELD( m_flIdealtilt, FIELD_FLOAT ),
  106. DEFINE_FIELD( m_flRotortilt, FIELD_FLOAT ),
  107. DEFINE_FIELD( m_flRightHealth, FIELD_FLOAT ),
  108. DEFINE_FIELD( m_flLeftHealth, FIELD_FLOAT ),
  109. DEFINE_FIELD( m_iRepelState, FIELD_INTEGER ),
  110. DEFINE_FIELD( m_iUnits, FIELD_INTEGER ),
  111. DEFINE_ARRAY( m_hGrunt, FIELD_EHANDLE, MAX_CARRY ),
  112. DEFINE_ARRAY( m_vecOrigin, FIELD_POSITION_VECTOR, MAX_CARRY ),
  113. DEFINE_ARRAY( m_hRepel, FIELD_EHANDLE, 4 ),
  114. // DEFINE_FIELD( m_iTailGibs, FIELD_INTEGER ),
  115. // DEFINE_FIELD( m_iBodyGibs, FIELD_INTEGER ),
  116. // DEFINE_FIELD( m_iEngineGibs, FIELD_INTEGER ),
  117. // DEFINE_FIELD( m_nDebrisModel, FIELD_INTEGER ),
  118. // DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ),
  119. // DEFINE_FIELD( m_iSpriteTexture, FIELD_INTEGER ),
  120. // DEFINE_FIELD( m_iPitch, FIELD_INTEGER ),
  121. DEFINE_FIELD( m_flPrevGoalVel, FIELD_FLOAT ),
  122. DEFINE_FIELD( m_iRotorAngle, FIELD_INTEGER ),
  123. DEFINE_FIELD( m_hLeftSmoke, FIELD_EHANDLE ),
  124. DEFINE_FIELD( m_hRightSmoke, FIELD_EHANDLE ),
  125. DEFINE_FIELD( m_iNextCrashModel, FIELD_INTEGER ),
  126. DEFINE_FIELD( m_iDoLeftSmokePuff, FIELD_INTEGER ),
  127. DEFINE_FIELD( m_iDoRightSmokePuff, FIELD_INTEGER ),
  128. //DEFINE_FIELD( m_iExplode, FIELD_INTEGER ),
  129. DEFINE_THINKFUNC( FindAllThink ),
  130. DEFINE_THINKFUNC( DeployThink ),
  131. DEFINE_ENTITYFUNC( CrashTouch ),
  132. /* DEFINE_FUNCTION ( HoverThink ),
  133. DEFINE_FUNCTION ( DyingThink ),
  134. DEFINE_FUNCTION ( CommandUse ),*/
  135. END_DATADESC()
  136. void CNPC_Osprey::Spawn( void )
  137. {
  138. Precache( );
  139. // motor
  140. SetModel( "models/osprey.mdl" );
  141. BaseClass::Spawn();
  142. Vector mins, maxs;
  143. ExtractBbox( 0, mins, maxs );
  144. UTIL_SetSize( this, mins, maxs );
  145. UTIL_SetOrigin( this, GetAbsOrigin() );
  146. AddFlag( FL_NPC );
  147. m_takedamage = DAMAGE_YES;
  148. m_flRightHealth = 200;
  149. m_flLeftHealth = 200;
  150. m_iHealth = 400;
  151. m_flFieldOfView = 0; // 180 degrees
  152. SetSequence( 0 );
  153. ResetSequenceInfo( );
  154. SetCycle( random->RandomInt( 0,0xFF ) );
  155. // InitBoneControllers();
  156. m_startTime = gpGlobals->curtime + 1;
  157. //FindAllThink();
  158. // SetUse( CommandUse );
  159. /* if (!( m_spawnflags & SF_WAITFORTRIGGER))
  160. {
  161. SetThink( gpGlobals->curtime + 1.0 );
  162. }*/
  163. m_flMaxSpeed = (float)BASECHOPPER_MAX_SPEED / 2;
  164. m_iRepelState = LOADED_WITH_GRUNTS;
  165. m_flPrevGoalVel = 9999;
  166. m_iRotorAngle = -1;
  167. SetBoneController( 0, m_iRotorAngle );
  168. m_hLeftSmoke = NULL;
  169. m_hRightSmoke = NULL;
  170. }
  171. void CNPC_Osprey::Precache( void )
  172. {
  173. UTIL_PrecacheOther( "monster_human_grunt" );
  174. PrecacheModel("models/osprey.mdl");
  175. PrecacheModel("models/HVR.mdl");
  176. m_iSpriteTexture = PrecacheModel( "sprites/rope.vmt" );
  177. m_iExplode = PrecacheModel( "sprites/fexplo.vmt" );
  178. m_iTailGibs = PrecacheModel( "models/osprey_tailgibs.mdl" );
  179. m_iBodyGibs = PrecacheModel( "models/osprey_bodygibs.mdl" );
  180. m_iEngineGibs = PrecacheModel( "models/osprey_enginegibs.mdl" );
  181. m_nDebrisModel = PrecacheModel( "models/mechgibs.mdl" );
  182. PrecacheScriptSound( "Apache.RotorSpinup" );
  183. BaseClass::Precache();
  184. }
  185. void CNPC_Osprey::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  186. {
  187. float flDamage = info.GetDamage();
  188. // Hit right engine
  189. if (ptr->hitgroup == 3 )
  190. {
  191. if( m_flRightHealth <= 0 )
  192. return;
  193. else
  194. m_flRightHealth -= flDamage;
  195. if( m_flRightHealth <= 0 )
  196. {
  197. Assert( m_hRightSmoke == NULL );
  198. if ( (m_hRightSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
  199. {
  200. m_hRightSmoke->m_Opacity = 1.0f;
  201. m_hRightSmoke->m_SpawnRate = 60;
  202. m_hRightSmoke->m_ParticleLifetime = 1.3f;
  203. m_hRightSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
  204. m_hRightSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
  205. m_hRightSmoke->m_StartSize = 12;
  206. m_hRightSmoke->m_EndSize = 80;
  207. m_hRightSmoke->m_SpawnRadius = 8;
  208. m_hRightSmoke->m_MinSpeed = 2;
  209. m_hRightSmoke->m_MaxSpeed = 24;
  210. m_hRightSmoke->SetLifetime( 1e6 );
  211. m_hRightSmoke->FollowEntity( this, "right" );
  212. }
  213. }
  214. }
  215. // Hit left engine
  216. if (ptr->hitgroup == 2 )
  217. {
  218. if( m_flLeftHealth <= 0 )
  219. return;
  220. else
  221. m_flLeftHealth -= flDamage;
  222. if( m_flLeftHealth <= 0 )
  223. {
  224. //create smoke trail
  225. Assert( m_hLeftSmoke == NULL );
  226. if ( (m_hLeftSmoke = SmokeTrail::CreateSmokeTrail()) != NULL )
  227. {
  228. m_hLeftSmoke->m_Opacity = 1.0f;
  229. m_hLeftSmoke->m_SpawnRate = 60;
  230. m_hLeftSmoke->m_ParticleLifetime = 1.3f;
  231. m_hLeftSmoke->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
  232. m_hLeftSmoke->m_EndColor.Init( 0.65f, 0.65f, 0.65f );
  233. m_hLeftSmoke->m_StartSize = 12;
  234. m_hLeftSmoke->m_EndSize = 64;
  235. m_hLeftSmoke->m_SpawnRadius = 8;
  236. m_hLeftSmoke->m_MinSpeed = 2;
  237. m_hLeftSmoke->m_MaxSpeed = 24;
  238. m_hLeftSmoke->SetLifetime( 1e6 );
  239. m_hLeftSmoke->FollowEntity( this, "left" );
  240. }
  241. }
  242. }
  243. // hit hard, hits cockpit, hits engines
  244. if (flDamage > 50 || ptr->hitgroup == 1 || ptr->hitgroup == 2 || ptr->hitgroup == 3)
  245. {
  246. AddMultiDamage( info, this );
  247. }
  248. else
  249. {
  250. g_pEffects->Sparks( ptr->endpos );
  251. }
  252. }
  253. //------------------------------------------------------------------------------
  254. // Purpose :
  255. // Input :
  256. // Output :
  257. //------------------------------------------------------------------------------
  258. void CNPC_Osprey::InitializeRotorSound( void )
  259. {
  260. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  261. CPASAttenuationFilter filter( this );
  262. m_pRotorSound = controller.SoundCreate( filter, entindex(), CHAN_STATIC, "Apache.RotorSpinup", 0.2 );
  263. BaseClass::InitializeRotorSound();
  264. }
  265. void CNPC_Osprey::FindAllThink( void )
  266. {
  267. CBaseEntity *pEntity = NULL;
  268. m_iUnits = 0;
  269. while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" ) ) != NULL)
  270. {
  271. if ( m_iUnits > MAX_CARRY )
  272. break;
  273. if (pEntity->IsAlive())
  274. {
  275. m_hGrunt[m_iUnits] = pEntity;
  276. m_vecOrigin[m_iUnits] = pEntity->GetAbsOrigin();
  277. m_iUnits++;
  278. }
  279. }
  280. if (m_iUnits == 0)
  281. {
  282. Msg( "osprey error: no grunts to resupply\n");
  283. UTIL_Remove( this );
  284. return;
  285. }
  286. m_startTime = 0.0f;
  287. }
  288. void CNPC_Osprey::DeployThink( void )
  289. {
  290. Vector vecForward;
  291. Vector vecRight;
  292. Vector vecUp;
  293. Vector vecSrc;
  294. SetLocalAngularVelocity( QAngle ( 0, 0, 0 ) );
  295. SetAbsVelocity( Vector ( 0, 0, 0 ) );
  296. AngleVectors( GetAbsAngles(), &vecForward, &vecRight, &vecUp );
  297. trace_t tr;
  298. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, -4096.0), MASK_SOLID_BRUSHONLY, this,COLLISION_GROUP_NONE, &tr);
  299. CSoundEnt::InsertSound ( SOUND_DANGER, tr.endpos, 400, 0.3 );
  300. vecSrc = GetAbsOrigin() + vecForward * 32 + vecRight * 100 + vecUp * -96;
  301. m_hRepel[0] = MakeGrunt( vecSrc );
  302. vecSrc = GetAbsOrigin() + vecForward * -64 + vecRight * 100 + vecUp * -96;
  303. m_hRepel[1] = MakeGrunt( vecSrc );
  304. vecSrc = GetAbsOrigin() + vecForward * 32 + vecRight * -100 + vecUp * -96;
  305. m_hRepel[2] = MakeGrunt( vecSrc );
  306. vecSrc = GetAbsOrigin() + vecForward * -64 + vecRight * -100 + vecUp * -96;
  307. m_hRepel[3] = MakeGrunt( vecSrc );
  308. m_iRepelState = GRUNTS_DEPLOYED;
  309. HoverThink();
  310. }
  311. bool CNPC_Osprey::HasDead( )
  312. {
  313. for (int i = 0; i < m_iUnits; i++)
  314. {
  315. if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
  316. {
  317. return TRUE;
  318. }
  319. else
  320. {
  321. m_vecOrigin[i] = m_hGrunt[i]->GetAbsOrigin(); // send them to where they died
  322. }
  323. }
  324. return FALSE;
  325. }
  326. void CNPC_Osprey::HoverThink( void )
  327. {
  328. int i = 0;
  329. for (i = 0; i < 4; i++)
  330. {
  331. CBaseEntity *pRepel = (CBaseEntity*)m_hRepel[i];
  332. if ( pRepel != NULL && pRepel->m_iHealth > 0 && pRepel->GetMoveType() == MOVETYPE_FLYGRAVITY )
  333. {
  334. break;
  335. }
  336. }
  337. if ( i == 4 )
  338. m_iRepelState = LOADED_WITH_GRUNTS;
  339. if( m_iRepelState != LOADED_WITH_GRUNTS )
  340. {
  341. // angle of engines should approach vertical
  342. m_iRotorAngle = UTIL_Approach(0, m_iRotorAngle, 5);
  343. }
  344. }
  345. CAI_BaseNPC *CNPC_Osprey::MakeGrunt( Vector vecSrc )
  346. {
  347. CBaseEntity *pEntity;
  348. CAI_BaseNPC *pGrunt;
  349. trace_t tr;
  350. UTIL_TraceLine( vecSrc, vecSrc + Vector( 0, 0, -4096.0), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr);
  351. if ( tr.m_pEnt && tr.m_pEnt->GetSolid() != SOLID_BSP)
  352. return NULL;
  353. for (int i = 0; i < m_iUnits; i++)
  354. {
  355. if (m_hGrunt[i] == NULL || !m_hGrunt[i]->IsAlive())
  356. {
  357. if (m_hGrunt[i] != NULL && m_hGrunt[i]->m_nRenderMode == kRenderNormal)
  358. {
  359. m_hGrunt[i]->SUB_StartFadeOut( );
  360. }
  361. pEntity = Create( "monster_human_grunt", vecSrc, GetAbsAngles() );
  362. pGrunt = pEntity->MyNPCPointer( );
  363. pGrunt->SetMoveType( MOVETYPE_FLYGRAVITY );
  364. pGrunt->SetGravity( 0.0001 );
  365. Vector spd = Vector( 0, 0, random->RandomFloat( -196, -128 ) );
  366. pGrunt->SetLocalVelocity( spd );
  367. pGrunt->SetActivity( ACT_GLIDE );
  368. pGrunt->SetGroundEntity( NULL );
  369. pGrunt->SetOwnerEntity(this);
  370. CBeam *pBeam = CBeam::BeamCreate( "sprites/rope.vmt", 1.0 );
  371. pBeam->PointEntInit( vecSrc + Vector(0,0,112), pGrunt );
  372. pBeam->SetBeamFlags( FBEAM_SOLID );
  373. pBeam->SetColor( 255, 255, 255 );
  374. pBeam->SetThink( &CBaseEntity::SUB_Remove );
  375. pBeam->SetNextThink( gpGlobals->curtime + -4096.0 * tr.fraction / pGrunt->GetAbsVelocity().z + 0.5 );
  376. // ALERT( at_console, "%d at %.0f %.0f %.0f\n", i, m_vecOrigin[i].x, m_vecOrigin[i].y, m_vecOrigin[i].z );
  377. pGrunt->m_vecLastPosition = m_vecOrigin[i];
  378. m_hGrunt[i] = pGrunt;
  379. return pGrunt;
  380. }
  381. }
  382. // ALERT( at_console, "none dead\n");
  383. return NULL;
  384. }
  385. void CNPC_Osprey::Flight( void )
  386. {
  387. if ( m_iRepelState == LOADED_WITH_GRUNTS )
  388. {
  389. BaseClass::Flight();
  390. // adjust angle of osprey rotors
  391. if ( m_angleVelocity > 0 )
  392. {
  393. m_iRotorAngle = UTIL_Approach(-45, m_iRotorAngle, 5 * (m_angleVelocity / 10));
  394. }
  395. else
  396. {
  397. m_iRotorAngle = UTIL_Approach(-1, m_iRotorAngle, 5);
  398. }
  399. SetBoneController( 0, m_iRotorAngle );
  400. }
  401. }
  402. void CNPC_Osprey::PrescheduleThink( void )
  403. {
  404. BaseClass::PrescheduleThink();
  405. StudioFrameAdvance( );
  406. if ( m_startTime != 0.0 && m_startTime <= gpGlobals->curtime )
  407. FindAllThink();
  408. if ( GetGoalEnt() )
  409. {
  410. if ( m_flPrevGoalVel != GetGoalEnt()->m_flSpeed )
  411. {
  412. if ( m_flPrevGoalVel == 0 && GetGoalEnt()->m_flSpeed != 0 )
  413. {
  414. if ( HasDead() && m_iRepelState == LOADED_WITH_GRUNTS )
  415. m_iRepelState = UNLOADING_GRUNTS;
  416. }
  417. m_flPrevGoalVel = GetGoalEnt()->m_flSpeed;
  418. }
  419. }
  420. if ( m_iRepelState == UNLOADING_GRUNTS )
  421. DeployThink();
  422. else if ( m_iRepelState == GRUNTS_DEPLOYED )
  423. HoverThink();
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose: Lame, temporary death
  427. //-----------------------------------------------------------------------------
  428. void CNPC_Osprey::DyingThink( void )
  429. {
  430. StudioFrameAdvance( );
  431. SetNextThink( gpGlobals->curtime + 0.1f );
  432. if( gpGlobals->curtime > m_flNextCrashExplosion )
  433. {
  434. CPASFilter filter( GetAbsOrigin() );
  435. Vector pos;
  436. QAngle dummy;
  437. int rand = RandomInt(0,10);
  438. if( rand < 4 )
  439. {
  440. int iAttach = LookupAttachment( rand % 2 ? "left" : "right" );
  441. GetAttachment( iAttach, pos, dummy );
  442. }
  443. else
  444. {
  445. pos = GetAbsOrigin();
  446. pos.x += random->RandomFloat( -150, 150 );
  447. pos.y += random->RandomFloat( -150, 150 );
  448. pos.z += random->RandomFloat( -150, -50 );
  449. }
  450. te->Explosion( filter, 0.0, &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
  451. m_flNextCrashExplosion = gpGlobals->curtime + random->RandomFloat( 0.4, 0.7 );
  452. Vector vecSize = Vector( 500, 500, 60 );
  453. switch( m_iNextCrashModel )
  454. {
  455. case 0:
  456. {
  457. te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
  458. vecSize, vec3_origin, m_iTailGibs, 100, 0, 2.5, BREAK_METAL );
  459. break;
  460. }
  461. case 1:
  462. {
  463. te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
  464. vecSize, vec3_origin, m_iBodyGibs, 100, 0, 2.5, BREAK_METAL );
  465. break;
  466. }
  467. case 2:
  468. {
  469. te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
  470. vecSize, vec3_origin, m_iEngineGibs, 100, 0, 2.5, BREAK_METAL );
  471. break;
  472. }
  473. case 3:
  474. {
  475. te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
  476. vecSize, vec3_origin, m_nDebrisModel, 100, 0, 2.5, BREAK_METAL );
  477. break;
  478. }
  479. }
  480. m_iNextCrashModel++;
  481. if( m_iNextCrashModel > 3 ) m_iNextCrashModel = 0;
  482. }
  483. QAngle angVel = GetLocalAngularVelocity();
  484. if( angVel.y < 400 )
  485. {
  486. angVel.y *= 1.1;
  487. SetLocalAngularVelocity( angVel );
  488. }
  489. Vector vecImpulse( 0, 0, 0 );
  490. // add gravity
  491. vecImpulse.z -= 38.4; // 32ft/sec
  492. ApplyAbsVelocityImpulse( vecImpulse );
  493. }
  494. void CNPC_Osprey::CrashTouch( CBaseEntity *pOther )
  495. {
  496. Vector vecSize = Vector( 120, 120, 30 );
  497. CPVSFilter filter( GetAbsOrigin() );
  498. te->BreakModel( filter, 0.0, GetAbsOrigin(), vec3_angle,
  499. vecSize, vec3_origin, m_iTailGibs, 100, 0, 2.5, BREAK_METAL );
  500. if( m_hLeftSmoke )
  501. {
  502. m_hLeftSmoke->SetLifetime(0.1f);
  503. m_hLeftSmoke = NULL;
  504. }
  505. if( m_hRightSmoke )
  506. {
  507. m_hRightSmoke->SetLifetime(0.1f);
  508. m_hRightSmoke = NULL;
  509. }
  510. BaseClass::CrashTouch( pOther );
  511. }
  512. //------------------------------------------------------------------------------
  513. // Purpose :
  514. // Input :
  515. // Output :
  516. //------------------------------------------------------------------------------
  517. BEGIN_DATADESC( CBaseHelicopter )
  518. DEFINE_THINKFUNC( HelicopterThink ),
  519. DEFINE_THINKFUNC( CallDyingThink ),
  520. DEFINE_ENTITYFUNC( CrashTouch ),
  521. DEFINE_ENTITYFUNC( FlyTouch ),
  522. DEFINE_SOUNDPATCH( m_pRotorSound ),
  523. DEFINE_FIELD( m_flForce, FIELD_FLOAT ),
  524. DEFINE_FIELD( m_fHelicopterFlags, FIELD_INTEGER),
  525. DEFINE_FIELD( m_vecDesiredFaceDir, FIELD_VECTOR ),
  526. DEFINE_FIELD( m_vecDesiredPosition,FIELD_POSITION_VECTOR ),
  527. DEFINE_FIELD( m_vecGoalOrientation,FIELD_VECTOR ),
  528. DEFINE_FIELD( m_flLastSeen, FIELD_TIME ),
  529. DEFINE_FIELD( m_flPrevSeen, FIELD_TIME ),
  530. // DEFINE_FIELD( m_iSoundState, FIELD_INTEGER ), // Don't save, precached
  531. DEFINE_FIELD( m_vecTarget, FIELD_VECTOR ),
  532. DEFINE_FIELD( m_vecTargetPosition, FIELD_POSITION_VECTOR ),
  533. DEFINE_FIELD( m_angleVelocity, FIELD_FLOAT ),
  534. DEFINE_FIELD( m_flNextCrashExplosion, FIELD_TIME ),
  535. DEFINE_FIELD( m_flMaxSpeed, FIELD_FLOAT ),
  536. DEFINE_FIELD( m_flMaxSpeedFiring, FIELD_FLOAT ),
  537. DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
  538. DEFINE_KEYFIELD( m_flInitialSpeed, FIELD_FLOAT, "InitialSpeed" ),
  539. // Inputs
  540. DEFINE_INPUTFUNC( FIELD_STRING, "ChangePathCorner", InputChangePathCorner),
  541. DEFINE_INPUTFUNC( FIELD_VOID, "Activate", InputActivate),
  542. // Outputs
  543. DEFINE_OUTPUT(m_AtTarget, "AtPathCorner" ),
  544. DEFINE_OUTPUT(m_LeaveTarget, "LeavePathCorner" ),//<<TEMP>> Undone
  545. END_DATADESC()
  546. IMPLEMENT_SERVERCLASS_ST( CBaseHelicopter, DT_BaseHelicopter )
  547. END_SEND_TABLE()
  548. //------------------------------------------------------------------------------
  549. // Purpose :
  550. // Input :
  551. // Output :
  552. // Notes : Have your derived Helicopter's Spawn() function call this one FIRST
  553. //------------------------------------------------------------------------------
  554. void CBaseHelicopter::Precache( void )
  555. {
  556. }
  557. //------------------------------------------------------------------------------
  558. // Purpose :
  559. // Input :
  560. // Output :
  561. // Notes : Have your derived Helicopter's Spawn() function call this one FIRST
  562. //------------------------------------------------------------------------------
  563. void CBaseHelicopter::Spawn( void )
  564. {
  565. Precache( );
  566. SetSolid( SOLID_BBOX );
  567. SetMoveType( MOVETYPE_STEP );
  568. AddFlag( FL_FLY );
  569. m_lifeState = LIFE_ALIVE;
  570. // This base class assumes the helicopter has no guns or missiles.
  571. // Set the appropriate flags in your derived class' Spawn() function.
  572. m_fHelicopterFlags &= ~BITS_HELICOPTER_MISSILE_ON;
  573. m_fHelicopterFlags &= ~BITS_HELICOPTER_GUN_ON;
  574. m_pRotorSound = NULL;
  575. SetCycle( 0 );
  576. ResetSequenceInfo();
  577. AddFlag( FL_NPC );
  578. m_flMaxSpeed = BASECHOPPER_MAX_SPEED;
  579. m_flMaxSpeedFiring = BASECHOPPER_MAX_FIRING_SPEED;
  580. m_takedamage = DAMAGE_AIM;
  581. // Don't start up if the level designer has asked the
  582. // helicopter to start disabled.
  583. if ( !(m_spawnflags & SF_AWAITINPUT) )
  584. {
  585. Startup();
  586. SetNextThink( gpGlobals->curtime + 1.0f );
  587. }
  588. }
  589. //------------------------------------------------------------------------------
  590. // Purpose :
  591. // Input :
  592. // Output :
  593. //------------------------------------------------------------------------------
  594. bool CBaseHelicopter::FireGun( void )
  595. {
  596. return true;
  597. }
  598. //------------------------------------------------------------------------------
  599. // Purpose : The main think function for the helicopters
  600. // Input :
  601. // Output :
  602. //------------------------------------------------------------------------------
  603. void CBaseHelicopter::HelicopterThink( void )
  604. {
  605. SetNextThink( gpGlobals->curtime + HELICOPTER_THINK_INTERVAL );
  606. // Don't keep this around for more than one frame.
  607. ClearCondition( COND_ENEMY_DEAD );
  608. // Animate and dispatch animation events.
  609. DispatchAnimEvents( this );
  610. PrescheduleThink();
  611. ShowDamage( );
  612. // -----------------------------------------------
  613. // If AI is disabled, kill any motion and return
  614. // -----------------------------------------------
  615. if (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI)
  616. {
  617. SetAbsVelocity( vec3_origin );
  618. SetLocalAngularVelocity( vec3_angle );
  619. SetNextThink( gpGlobals->curtime + HELICOPTER_THINK_INTERVAL );
  620. return;
  621. }
  622. Hunt();
  623. HelicopterPostThink();
  624. }
  625. //------------------------------------------------------------------------------
  626. // Purpose :
  627. // Input :
  628. // Output :
  629. //------------------------------------------------------------------------------
  630. void CBaseHelicopter::Hunt( void )
  631. {
  632. FlyPathCorners( );
  633. if( HasCondition( COND_ENEMY_DEAD ) )
  634. {
  635. SetEnemy( NULL );
  636. }
  637. // Look for my best enemy. If I change enemies,
  638. // be sure and change my prevseen/lastseen timers.
  639. if( m_lifeState == LIFE_ALIVE )
  640. {
  641. GetSenses()->Look( 4092 );
  642. ChooseEnemy();
  643. if( HasEnemy() )
  644. {
  645. CheckEnemy( GetEnemy() );
  646. if (FVisible( GetEnemy() ))
  647. {
  648. if (m_flLastSeen < gpGlobals->curtime - 2)
  649. {
  650. m_flPrevSeen = gpGlobals->curtime;
  651. }
  652. m_flLastSeen = gpGlobals->curtime;
  653. m_vecTargetPosition = GetEnemy()->WorldSpaceCenter();
  654. }
  655. }
  656. else
  657. {
  658. // look at where we're going instead
  659. m_vecTargetPosition = m_vecDesiredPosition;
  660. }
  661. }
  662. else
  663. {
  664. // If we're dead or dying, forget our enemy and don't look for new ones(sjb)
  665. SetEnemy( NULL );
  666. }
  667. if ( 1 )
  668. {
  669. Vector targetDir = m_vecTargetPosition - GetAbsOrigin();
  670. Vector desiredDir = m_vecDesiredPosition - GetAbsOrigin();
  671. VectorNormalize( targetDir );
  672. VectorNormalize( desiredDir );
  673. if ( !IsCrashing() && m_flLastSeen + 5 > gpGlobals->curtime ) //&& DotProduct( targetDir, desiredDir) > 0.25)
  674. {
  675. // If we've seen the target recently, face the target.
  676. //Msg( "Facing Target \n" );
  677. m_vecDesiredFaceDir = targetDir;
  678. }
  679. else
  680. {
  681. // Face our desired position.
  682. // Msg( "Facing Position\n" );
  683. m_vecDesiredFaceDir = desiredDir;
  684. }
  685. }
  686. else
  687. {
  688. // Face the way the path corner tells us to.
  689. //Msg( "Facing my path corner\n" );
  690. m_vecDesiredFaceDir = m_vecGoalOrientation;
  691. }
  692. Flight();
  693. UpdatePlayerDopplerShift( );
  694. // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->curtime, m_flLastSeen, m_flPrevSeen );
  695. if (m_fHelicopterFlags & BITS_HELICOPTER_GUN_ON)
  696. {
  697. if ( (m_flLastSeen + 1 > gpGlobals->curtime) && (m_flPrevSeen + 2 < gpGlobals->curtime) )
  698. {
  699. if (FireGun( ))
  700. {
  701. // slow down if we're firing
  702. if (m_flGoalSpeed > m_flMaxSpeedFiring )
  703. {
  704. m_flGoalSpeed = m_flMaxSpeedFiring;
  705. }
  706. }
  707. }
  708. }
  709. if (m_fHelicopterFlags & BITS_HELICOPTER_MISSILE_ON)
  710. {
  711. AimRocketGun();
  712. }
  713. // Finally, forget dead enemies.
  714. if( GetEnemy() != NULL && !GetEnemy()->IsAlive() )
  715. {
  716. SetEnemy( NULL );
  717. }
  718. }
  719. //------------------------------------------------------------------------------
  720. // Purpose :
  721. // Input :
  722. // Output :
  723. //------------------------------------------------------------------------------
  724. void CBaseHelicopter::FlyPathCorners( void )
  725. {
  726. if ( GetGoalEnt() == NULL && m_target != NULL_STRING )// this monster has a target
  727. {
  728. SetGoalEnt( gEntList.FindEntityByName( NULL, m_target ) );
  729. if (GetGoalEnt())
  730. {
  731. m_vecDesiredPosition = GetGoalEnt()->GetLocalOrigin();
  732. // FIXME: orienation removed from path_corners!
  733. AngleVectors( GetGoalEnt()->GetLocalAngles(), &m_vecGoalOrientation );
  734. }
  735. }
  736. // walk route
  737. if (GetGoalEnt())
  738. {
  739. // ALERT( at_console, "%.0f\n", flLength );
  740. if ( HasReachedTarget( ) )
  741. {
  742. // If we get this close to the desired position, it's assumed that we've reached
  743. // the desired position, so move on.
  744. // Fire target that I've reached my goal
  745. m_AtTarget.FireOutput( GetGoalEnt(), this );
  746. OnReachedTarget( GetGoalEnt() );
  747. SetGoalEnt( gEntList.FindEntityByName( NULL, GetGoalEnt()->m_target ) );
  748. if (GetGoalEnt())
  749. {
  750. m_vecDesiredPosition = GetGoalEnt()->GetAbsOrigin();
  751. // FIXME: orienation removed from path_corners!
  752. AngleVectors( GetGoalEnt()->GetLocalAngles(), &m_vecGoalOrientation );
  753. // NDebugOverlay::Box( m_vecDesiredPosition, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0,255,0, false, 30.0);
  754. }
  755. }
  756. }
  757. else
  758. {
  759. // If we can't find a new target, just stay where we are.
  760. m_vecDesiredPosition = GetAbsOrigin();
  761. }
  762. }
  763. //------------------------------------------------------------------------------
  764. // Purpose :
  765. // Input :
  766. // Output :
  767. //------------------------------------------------------------------------------
  768. void CBaseHelicopter::UpdatePlayerDopplerShift( )
  769. {
  770. // -----------------------------
  771. // make rotor, engine sounds
  772. // -----------------------------
  773. if (m_iSoundState == 0)
  774. {
  775. // Sound startup.
  776. InitializeRotorSound();
  777. }
  778. else
  779. {
  780. CBaseEntity *pPlayer = NULL;
  781. // UNDONE: this needs to send different sounds to every player for multiplayer.
  782. // FIXME: this isn't the correct way to find a player!!!
  783. pPlayer = gEntList.FindEntityByName( NULL, "!player" );
  784. if (pPlayer)
  785. {
  786. Vector dir = pPlayer->GetLocalOrigin() - GetLocalOrigin();
  787. VectorNormalize(dir);
  788. float velReceiver = -DotProduct( pPlayer->GetAbsVelocity(), dir );
  789. float velTransmitter = -DotProduct( GetAbsVelocity(), dir );
  790. // speed of sound == 13049in/s
  791. int iPitch = 100 * ((1 - velReceiver / 13049) / (1 + velTransmitter / 13049));
  792. // clamp pitch shifts
  793. if (iPitch > 250)
  794. iPitch = 250;
  795. if (iPitch < 50)
  796. iPitch = 50;
  797. // Msg( "Pitch:%d\n", iPitch );
  798. UpdateRotorSoundPitch( iPitch );
  799. //Msg( "%.0f\n", pitch );
  800. //Msg( "%.0f\n", flVol );
  801. }
  802. else
  803. {
  804. Msg( "Chopper didn't find a player!\n" );
  805. }
  806. }
  807. }
  808. //------------------------------------------------------------------------------
  809. //------------------------------------------------------------------------------
  810. void CBaseHelicopter::Flight( void )
  811. {
  812. if( GetFlags() & FL_ONGROUND )
  813. {
  814. //This would be really bad.
  815. SetGroundEntity( NULL );
  816. }
  817. // Generic speed up
  818. if (m_flGoalSpeed < m_flMaxSpeed)
  819. {
  820. m_flGoalSpeed += GetAcceleration();
  821. }
  822. // NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0.1);
  823. // estimate where I'll be facing in one seconds
  824. Vector forward, right, up;
  825. AngleVectors( GetLocalAngles() + GetLocalAngularVelocity() * 2, &forward, &right, &up );
  826. QAngle angVel = GetLocalAngularVelocity();
  827. float flSide = DotProduct( m_vecDesiredFaceDir, right );
  828. if (flSide < 0)
  829. {
  830. if ( angVel.y < 8 )
  831. {
  832. angVel.y += 2;
  833. }
  834. else if (angVel.y < 60)
  835. {
  836. angVel.y += 8;
  837. }
  838. }
  839. else
  840. {
  841. if ( angVel.y > -8 )
  842. {
  843. angVel.y -= 2;
  844. }
  845. else if (angVel.y > -60)
  846. {
  847. angVel.y -= 8;
  848. }
  849. }
  850. angVel.y *= ( 0.98 ); // why?! (sjb)
  851. // estimate where I'll be in two seconds
  852. AngleVectors( GetLocalAngles() + angVel, NULL, NULL, &up );
  853. Vector vecEst = GetAbsOrigin() + GetAbsVelocity() * 2.0 + up * m_flForce * 20 - Vector( 0, 0, 384 * 2 );
  854. // add immediate force
  855. AngleVectors( GetLocalAngles(), &forward, &right, &up );
  856. Vector vecImpulse( 0, 0, 0 );
  857. vecImpulse.x += up.x * m_flForce;
  858. vecImpulse.y += up.y * m_flForce;
  859. vecImpulse.z += up.z * m_flForce;
  860. // add gravity
  861. vecImpulse.z -= 38.4; // 32ft/sec
  862. ApplyAbsVelocityImpulse( vecImpulse );
  863. float flSpeed = GetAbsVelocity().Length();
  864. float flDir = DotProduct( Vector( forward.x, forward.y, 0 ), Vector( GetAbsVelocity().x, GetAbsVelocity().y, 0 ) );
  865. if (flDir < 0)
  866. {
  867. flSpeed = -flSpeed;
  868. }
  869. float flDist = DotProduct( m_vecDesiredPosition - vecEst, forward );
  870. // float flDist = (m_vecDesiredPosition - vecEst).Length();
  871. // float flSlip = DotProduct( GetAbsVelocity(), right );
  872. float flSlip = -DotProduct( m_vecDesiredPosition - vecEst, right );
  873. // fly sideways
  874. if (flSlip > 0)
  875. {
  876. if (GetLocalAngles().z > -30 && angVel.z > -15)
  877. angVel.z -= 4;
  878. else
  879. angVel.z += 2;
  880. }
  881. else
  882. {
  883. if (GetLocalAngles().z < 30 && angVel.z < 15)
  884. angVel.z += 4;
  885. else
  886. angVel.z -= 2;
  887. }
  888. // These functions contain code Ken wrote that used to be right here as part of the flight model,
  889. // but we want different helicopter vehicles to have different drag characteristics, so I made
  890. // them virtual functions (sjb)
  891. ApplySidewaysDrag( right );
  892. ApplyGeneralDrag();
  893. // apply power to stay correct height
  894. // FIXME: these need to be per class variables
  895. #define MAX_FORCE 80
  896. #define FORCE_POSDELTA 12
  897. #define FORCE_NEGDELTA 8
  898. if (m_flForce < MAX_FORCE && vecEst.z < m_vecDesiredPosition.z)
  899. {
  900. m_flForce += FORCE_POSDELTA;
  901. }
  902. else if (m_flForce > 30)
  903. {
  904. if (vecEst.z > m_vecDesiredPosition.z)
  905. m_flForce -= FORCE_NEGDELTA;
  906. }
  907. // pitch forward or back to get to target
  908. //-----------------------------------------
  909. // Pitch is reversed since Half-Life! (sjb)
  910. //-----------------------------------------
  911. // when we're way out, lean forward up to 40 degrees to accelerate to target
  912. // not exceeding our goal speed.
  913. #if 0
  914. float nodeSpeed = GetGoalEnt()->m_flSpeed;
  915. if ( flDist > 300 && flSpeed < m_flGoalSpeed && GetLocalAngles().x + angVel.x < 25 )
  916. {
  917. angVel.x += 8.0; //lean forward
  918. }
  919. else if ( flDist < 500 && GetLocalAngles().x + angVel.x > -30 && nodeSpeed == 0)
  920. {
  921. angVel.x -= 8.0; // lean backwards as we approach the target
  922. }
  923. else if (GetLocalAngles().x + angVel.x < -20 )
  924. {
  925. // ALERT( at_console, "f " );
  926. angVel.x += 2.0;
  927. }
  928. else if (GetLocalAngles().x + angVel.x > 20 )
  929. {
  930. // ALERT( at_console, "b " );
  931. angVel.x -= 2.0;
  932. }
  933. #else
  934. m_angleVelocity = GetLocalAngles().x + angVel.x;
  935. float angleX = angVel.x;
  936. // Msg("AngVel %f, %f\n", m_angleVelocity, flDist);
  937. if (flDist > 128 && flSpeed < m_flGoalSpeed && m_angleVelocity < 30)
  938. {
  939. // ALERT( at_console, "F " );
  940. // lean forward
  941. angleX += 6;
  942. }
  943. else if (flDist < -128 && flSpeed > -50 && m_angleVelocity > -20)
  944. {
  945. // ALERT( at_console, "B " );
  946. // lean backward
  947. angleX -= 12.0;
  948. }
  949. else if ( (m_angleVelocity < -20) || (m_angleVelocity < 0 && flDist < 128) )
  950. {
  951. // ALERT( at_console, "f " );
  952. if ( abs(m_angleVelocity) < 5 )
  953. {
  954. angleX += 1.0;
  955. }
  956. else
  957. {
  958. angleX += 4.0;
  959. }
  960. }
  961. else if ( (m_angleVelocity > 20) || (m_angleVelocity > 0 && flDist < 128) )
  962. {
  963. // ALERT( at_console, "b " );
  964. if ( abs(m_angleVelocity) < 5 )
  965. {
  966. angleX -= 1.0;
  967. }
  968. else
  969. {
  970. angleX -= 4.0;
  971. }
  972. }
  973. angVel.x = angleX;
  974. //Msg("AngVel.x %f %f\n", angVel.x, angleX );
  975. #endif
  976. SetLocalAngularVelocity( angVel );
  977. // ALERT( at_console, "%.0f %.0f : %.0f %.0f : %.0f %.0f : %.0f\n", GetAbsOrigin().x, GetAbsVelocity().x, flDist, flSpeed, GetLocalAngles().x, m_vecAngVelocity.x, m_flForce );
  978. // ALERT( at_console, "%.0f %.0f : %.0f %0.f : %.0f\n", GetAbsOrigin().z, GetAbsVelocity().z, vecEst.z, m_vecDesiredPosition.z, m_flForce );
  979. }
  980. //------------------------------------------------------------------------------
  981. // Purpose :
  982. // Input :
  983. // Output :
  984. //------------------------------------------------------------------------------
  985. void CBaseHelicopter::InitializeRotorSound( void )
  986. {
  987. if (m_pRotorSound)
  988. {
  989. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  990. // Get the rotor sound started up.
  991. controller.Play( m_pRotorSound, 0.0, 100 );
  992. controller.SoundChangeVolume(m_pRotorSound, GetRotorVolume(), 2.0);
  993. }
  994. m_iSoundState = SND_CHANGE_PITCH; // hack for going through level transitions
  995. }
  996. //------------------------------------------------------------------------------
  997. // Purpose :
  998. // Input :
  999. // Output :
  1000. //------------------------------------------------------------------------------
  1001. void CBaseHelicopter::UpdateRotorSoundPitch( int iPitch )
  1002. {
  1003. if (m_pRotorSound)
  1004. {
  1005. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1006. controller.SoundChangePitch( m_pRotorSound, iPitch, 0.1 );
  1007. controller.SoundChangeVolume( m_pRotorSound, GetRotorVolume(), 0.1 );
  1008. }
  1009. }
  1010. //------------------------------------------------------------------------------
  1011. // Purpose :
  1012. // Input :
  1013. // Output :
  1014. //------------------------------------------------------------------------------
  1015. void CBaseHelicopter::FlyTouch( CBaseEntity *pOther )
  1016. {
  1017. // bounce if we hit something solid
  1018. if ( pOther->GetSolid() == SOLID_BSP)
  1019. {
  1020. const trace_t &tr = CBaseEntity::GetTouchTrace( );
  1021. // UNDONE, do a real bounce
  1022. ApplyAbsVelocityImpulse( tr.plane.normal * (GetAbsVelocity().Length() + 200) );
  1023. }
  1024. }
  1025. //------------------------------------------------------------------------------
  1026. // Purpose :
  1027. // Input :
  1028. // Output :
  1029. //------------------------------------------------------------------------------
  1030. void CBaseHelicopter::CrashTouch( CBaseEntity *pOther )
  1031. {
  1032. // only crash if we hit something solid
  1033. if ( pOther->GetSolid() == SOLID_BSP)
  1034. {
  1035. SetTouch( NULL );
  1036. SetNextThink( gpGlobals->curtime );
  1037. CPASFilter filter( GetAbsOrigin() );
  1038. for (int i = 0; i < 5; i++)
  1039. {
  1040. Vector pos = GetAbsOrigin();
  1041. pos.x += random->RandomFloat( -150, 150 );
  1042. pos.y += random->RandomFloat( -150, 150 );
  1043. pos.z += random->RandomFloat( -150, -50 );
  1044. te->Explosion( filter, MIN( 0.99, i * 0.2 ), &pos, g_sModelIndexFireball, 10, 15, TE_EXPLFLAG_NONE, 100, 0 );
  1045. }
  1046. UTIL_Remove( this );
  1047. }
  1048. }
  1049. //------------------------------------------------------------------------------
  1050. // Purpose :
  1051. // Input :
  1052. // Output :
  1053. //------------------------------------------------------------------------------
  1054. void CBaseHelicopter::DyingThink( void )
  1055. {
  1056. StudioFrameAdvance( );
  1057. SetNextThink( gpGlobals->curtime + 0.1f );
  1058. SetLocalAngularVelocity( GetLocalAngularVelocity() * 1.02 );
  1059. }
  1060. //-----------------------------------------------------------------------------
  1061. // Purpose:
  1062. // Input :
  1063. // Output :
  1064. //-----------------------------------------------------------------------------
  1065. int CBaseHelicopter::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  1066. {
  1067. #if 0
  1068. // This code didn't port easily. WTF does it do? (sjb)
  1069. if (pevInflictor->m_owner == pev)
  1070. return 0;
  1071. #endif
  1072. /*
  1073. if ( (bitsDamageType & DMG_BULLET) && flDamage > 50)
  1074. {
  1075. // clip bullet damage at 50
  1076. flDamage = 50;
  1077. }
  1078. */
  1079. return BaseClass::OnTakeDamage_Alive( info );
  1080. }
  1081. //-----------------------------------------------------------------------------
  1082. // Purpose: Override base class to add display of fly direction
  1083. // Input :
  1084. // Output :
  1085. //-----------------------------------------------------------------------------
  1086. void CBaseHelicopter::DrawDebugGeometryOverlays(void)
  1087. {
  1088. if (m_pfnThink!= NULL)
  1089. {
  1090. // ------------------------------
  1091. // Draw route if requested
  1092. // ------------------------------
  1093. if (m_debugOverlays & OVERLAY_NPC_ROUTE_BIT)
  1094. {
  1095. NDebugOverlay::Line(GetAbsOrigin(), m_vecDesiredPosition, 0,0,255, true, 0);
  1096. }
  1097. }
  1098. BaseClass::DrawDebugGeometryOverlays();
  1099. }
  1100. //-----------------------------------------------------------------------------
  1101. // Purpose:
  1102. // Input :
  1103. // Output :
  1104. //-----------------------------------------------------------------------------
  1105. void CBaseHelicopter::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  1106. {
  1107. CTakeDamageInfo dmgInfo = info;
  1108. // ALERT( at_console, "%d %.0f\n", ptr->iHitgroup, flDamage );
  1109. // HITGROUPS don't work currently.
  1110. // ignore blades
  1111. // if (ptr->hitgroup == 6 && (info.GetDamageType() & (DMG_ENERGYBEAM|DMG_BULLET|DMG_CLUB)))
  1112. // return;
  1113. // hit hard, hits cockpit
  1114. if (info.GetDamage() > 50 || ptr->hitgroup == 11 )
  1115. {
  1116. // ALERT( at_console, "%map .0f\n", flDamage );
  1117. AddMultiDamage( dmgInfo, this );
  1118. // m_iDoSmokePuff = 3 + (info.GetDamage() / 5.0);
  1119. }
  1120. else
  1121. {
  1122. // do half damage in the body
  1123. dmgInfo.ScaleDamage(0.5);
  1124. AddMultiDamage( dmgInfo, this );
  1125. g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
  1126. }
  1127. }
  1128. //------------------------------------------------------------------------------
  1129. // Purpose :
  1130. // Input :
  1131. // Output :
  1132. //------------------------------------------------------------------------------
  1133. void CBaseHelicopter::NullThink( void )
  1134. {
  1135. StudioFrameAdvance( );
  1136. SetNextThink( gpGlobals->curtime + 0.5f );
  1137. }
  1138. void CBaseHelicopter::Startup( void )
  1139. {
  1140. m_flGoalSpeed = m_flInitialSpeed;
  1141. SetThink( &CBaseHelicopter::HelicopterThink );
  1142. SetTouch( &CBaseHelicopter::FlyTouch );
  1143. SetNextThink( gpGlobals->curtime + 0.1f );
  1144. }
  1145. void CBaseHelicopter::Event_Killed( const CTakeDamageInfo &info )
  1146. {
  1147. m_lifeState = LIFE_DYING;
  1148. SetMoveType( MOVETYPE_FLYGRAVITY );
  1149. SetGravity( UTIL_ScaleForGravity( 240 ) ); // use a lower gravity
  1150. // Kill the rotor sound.
  1151. UTIL_SetSize( this, Vector( -32, -32, -64), Vector( 32, 32, 0) );
  1152. SetThink( &CBaseHelicopter::CallDyingThink );
  1153. SetTouch( &CBaseHelicopter::CrashTouch );
  1154. m_flNextCrashExplosion = gpGlobals->curtime + 0.0f;
  1155. SetNextThink( gpGlobals->curtime + 0.1f );
  1156. m_iHealth = 0;
  1157. m_takedamage = DAMAGE_NO;
  1158. /*
  1159. if (m_spawnflags & SF_NOWRECKAGE)
  1160. {
  1161. m_flNextRocket = gpGlobals->curtime + 4.0;
  1162. }
  1163. else
  1164. {
  1165. m_flNextRocket = gpGlobals->curtime + 15.0;
  1166. }
  1167. */
  1168. m_OnDeath.FireOutput( info.GetAttacker(), this );
  1169. }
  1170. void CBaseHelicopter::StopLoopingSounds()
  1171. {
  1172. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1173. controller.SoundDestroy( m_pRotorSound );
  1174. m_pRotorSound = NULL;
  1175. BaseClass::StopLoopingSounds();
  1176. }
  1177. void CBaseHelicopter::GibMonster( void )
  1178. {
  1179. }
  1180. void CBaseHelicopter::ChangePathCorner( const char *pszName )
  1181. {
  1182. if( m_lifeState != LIFE_ALIVE )
  1183. {
  1184. // Disregard this if dead or dying.
  1185. return;
  1186. }
  1187. if (GetGoalEnt())
  1188. {
  1189. SetGoalEnt( gEntList.FindEntityByName( NULL, pszName ) );
  1190. // I don't think we need to do this. The FLIGHT() code will do it for us (sjb)
  1191. if (GetGoalEnt())
  1192. {
  1193. m_vecDesiredPosition = GetGoalEnt()->GetLocalOrigin();
  1194. AngleVectors( GetGoalEnt()->GetLocalAngles(), &m_vecGoalOrientation );
  1195. }
  1196. }
  1197. }
  1198. //-----------------------------------------------------------------------------
  1199. // Purpose: Slams the chopper's current path corner and sends it to a new one.
  1200. // This code does NOT check that the path from the current position
  1201. // to the wished path corner is clear!
  1202. //-----------------------------------------------------------------------------
  1203. void CBaseHelicopter::InputChangePathCorner( inputdata_t &inputdata )
  1204. {
  1205. ChangePathCorner( inputdata.value.String() );
  1206. }
  1207. //-----------------------------------------------------------------------------
  1208. // Purpose: Call Startup for a helicopter that's been flagged to start disabled
  1209. //-----------------------------------------------------------------------------
  1210. void CBaseHelicopter::InputActivate( inputdata_t &inputdata )
  1211. {
  1212. if( m_spawnflags & SF_AWAITINPUT )
  1213. {
  1214. Startup();
  1215. // Now clear the spawnflag to protect from
  1216. // subsequent calls.
  1217. m_spawnflags &= ~SF_AWAITINPUT;
  1218. }
  1219. }
  1220. //-----------------------------------------------------------------------------
  1221. // Purpose:
  1222. // Input :
  1223. // Output :
  1224. //-----------------------------------------------------------------------------
  1225. void CBaseHelicopter::ApplySidewaysDrag( const Vector &vecRight )
  1226. {
  1227. Vector vecNewVelocity = GetAbsVelocity();
  1228. vecNewVelocity.x *= 1.0 - fabs( vecRight.x ) * 0.05;
  1229. vecNewVelocity.y *= 1.0 - fabs( vecRight.y ) * 0.05;
  1230. vecNewVelocity.z *= 1.0 - fabs( vecRight.z ) * 0.05;
  1231. SetAbsVelocity( vecNewVelocity );
  1232. }
  1233. //-----------------------------------------------------------------------------
  1234. // Purpose:
  1235. // Input :
  1236. // Output :
  1237. //-----------------------------------------------------------------------------
  1238. void CBaseHelicopter::ApplyGeneralDrag( void )
  1239. {
  1240. Vector vecNewVelocity = GetAbsVelocity();
  1241. vecNewVelocity *= 0.995;
  1242. SetAbsVelocity( vecNewVelocity );
  1243. }
  1244. //-----------------------------------------------------------------------------
  1245. // Purpose:
  1246. // Input :
  1247. // Output :
  1248. //-----------------------------------------------------------------------------
  1249. bool CBaseHelicopter::ChooseEnemy( void )
  1250. {
  1251. // See if there's a new enemy.
  1252. CBaseEntity *pNewEnemy;
  1253. pNewEnemy = BestEnemy();
  1254. if( ( pNewEnemy != GetEnemy() ) && pNewEnemy != NULL )
  1255. {
  1256. //New enemy! Clear the timers and set conditions.
  1257. SetEnemy( pNewEnemy );
  1258. m_flLastSeen = m_flPrevSeen = gpGlobals->curtime;
  1259. return true;
  1260. }
  1261. else
  1262. {
  1263. ClearCondition( COND_NEW_ENEMY );
  1264. return false;
  1265. }
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. // Purpose:
  1269. // Input :
  1270. // Output :
  1271. //-----------------------------------------------------------------------------
  1272. void CBaseHelicopter::CheckEnemy( CBaseEntity *pEnemy )
  1273. {
  1274. // -------------------
  1275. // If enemy is dead
  1276. // -------------------
  1277. if ( !pEnemy->IsAlive() )
  1278. {
  1279. SetCondition( COND_ENEMY_DEAD );
  1280. ClearCondition( COND_SEE_ENEMY );
  1281. ClearCondition( COND_ENEMY_OCCLUDED );
  1282. return;
  1283. }
  1284. }
  1285. bool CBaseHelicopter::HasReachedTarget( void )
  1286. {
  1287. float flDist = (WorldSpaceCenter() - m_vecDesiredPosition).Length();
  1288. if( GetGoalEnt()->m_flSpeed <= 0 )
  1289. return ( flDist < 145 );
  1290. else
  1291. return( flDist < 512 );
  1292. }