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.

858 lines
22 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
  4. // events
  5. //
  6. // $Workfile: $
  7. // $Date: $
  8. //
  9. //-----------------------------------------------------------------------------
  10. // $Log: $
  11. //
  12. // $NoKeywords: $
  13. //=============================================================================//
  14. #include "cbase.h"
  15. #include "ai_default.h"
  16. #include "ai_task.h"
  17. #include "ai_schedule.h"
  18. #include "ai_node.h"
  19. #include "ai_hull.h"
  20. #include "ai_hint.h"
  21. #include "ai_route.h"
  22. #include "soundent.h"
  23. #include "game.h"
  24. #include "npcevent.h"
  25. #include "entitylist.h"
  26. #include "activitylist.h"
  27. #include "animation.h"
  28. #include "basecombatweapon.h"
  29. #include "IEffects.h"
  30. #include "vstdlib/random.h"
  31. #include "engine/IEngineSound.h"
  32. #include "ammodef.h"
  33. #include "hl1_ai_basenpc.h"
  34. #define AFLOCK_MAX_RECRUIT_RADIUS 1024
  35. #define AFLOCK_FLY_SPEED 125
  36. #define AFLOCK_TURN_RATE 75
  37. #define AFLOCK_ACCELERATE 10
  38. #define AFLOCK_CHECK_DIST 192
  39. #define AFLOCK_TOO_CLOSE 100
  40. #define AFLOCK_TOO_FAR 256
  41. //=========================================================
  42. //=========================================================
  43. class CNPC_FlockingFlyerFlock : public CHL1BaseNPC
  44. {
  45. DECLARE_CLASS( CNPC_FlockingFlyerFlock, CHL1BaseNPC );
  46. public:
  47. void Spawn( void );
  48. void Precache( void );
  49. bool KeyValue( const char *szKeyName, const char *szValue );
  50. void SpawnFlock( void );
  51. // Sounds are shared by the flock
  52. static void PrecacheFlockSounds( void );
  53. DECLARE_DATADESC();
  54. int m_cFlockSize;
  55. float m_flFlockRadius;
  56. };
  57. BEGIN_DATADESC( CNPC_FlockingFlyerFlock )
  58. DEFINE_FIELD( m_cFlockSize, FIELD_INTEGER ),
  59. DEFINE_FIELD( m_flFlockRadius, FIELD_FLOAT ),
  60. END_DATADESC()
  61. class CNPC_FlockingFlyer : public CHL1BaseNPC
  62. {
  63. DECLARE_CLASS( CNPC_FlockingFlyer, CHL1BaseNPC );
  64. public:
  65. void Spawn( void );
  66. void Precache( void );
  67. void SpawnCommonCode( void );
  68. void IdleThink( void );
  69. void BoidAdvanceFrame( void );
  70. void Start( void );
  71. bool FPathBlocked( void );
  72. void FlockLeaderThink( void );
  73. void SpreadFlock( void );
  74. void SpreadFlock2( void );
  75. void MakeSound( void );
  76. void FlockFollowerThink( void );
  77. void Event_Killed( const CTakeDamageInfo &info );
  78. void FallHack( void );
  79. //void Poop ( void ); Adrian - wtf?!
  80. int IsLeader( void ) { return m_pSquadLeader == this; }
  81. int InSquad( void ) { return m_pSquadLeader != NULL; }
  82. int SquadCount( void );
  83. void SquadRemove( CNPC_FlockingFlyer *pRemove );
  84. void SquadUnlink( void );
  85. void SquadAdd( CNPC_FlockingFlyer *pAdd );
  86. void SquadDisband( void );
  87. CNPC_FlockingFlyer *m_pSquadLeader;
  88. CNPC_FlockingFlyer *m_pSquadNext;
  89. bool m_fTurning;// is this boid turning?
  90. bool m_fCourseAdjust;// followers set this flag TRUE to override flocking while they avoid something
  91. bool m_fPathBlocked;// TRUE if there is an obstacle ahead
  92. Vector m_vecReferencePoint;// last place we saw leader
  93. Vector m_vecAdjustedVelocity;// adjusted velocity (used when fCourseAdjust is TRUE)
  94. float m_flGoalSpeed;
  95. float m_flLastBlockedTime;
  96. float m_flFakeBlockedTime;
  97. float m_flAlertTime;
  98. float m_flFlockNextSoundTime;
  99. float m_flTempVar;
  100. DECLARE_DATADESC();
  101. };
  102. BEGIN_DATADESC( CNPC_FlockingFlyer )
  103. DEFINE_FIELD( m_pSquadLeader, FIELD_CLASSPTR ),
  104. DEFINE_FIELD( m_pSquadNext, FIELD_CLASSPTR ),
  105. DEFINE_FIELD( m_fTurning, FIELD_BOOLEAN ),
  106. DEFINE_FIELD( m_fCourseAdjust, FIELD_BOOLEAN ),
  107. DEFINE_FIELD( m_fPathBlocked, FIELD_BOOLEAN ),
  108. DEFINE_FIELD( m_vecReferencePoint, FIELD_POSITION_VECTOR ),
  109. DEFINE_FIELD( m_vecAdjustedVelocity, FIELD_VECTOR ),
  110. DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ),
  111. DEFINE_FIELD( m_flLastBlockedTime, FIELD_TIME ),
  112. DEFINE_FIELD( m_flFakeBlockedTime, FIELD_TIME ),
  113. DEFINE_FIELD( m_flAlertTime, FIELD_TIME ),
  114. DEFINE_THINKFUNC( IdleThink ),
  115. DEFINE_THINKFUNC( Start ),
  116. DEFINE_THINKFUNC( FlockLeaderThink ),
  117. DEFINE_THINKFUNC( FlockFollowerThink ),
  118. DEFINE_THINKFUNC( FallHack ),
  119. DEFINE_FIELD( m_flFlockNextSoundTime, FIELD_TIME ),
  120. DEFINE_FIELD( m_flTempVar, FIELD_FLOAT ),
  121. END_DATADESC()
  122. LINK_ENTITY_TO_CLASS( monster_flyer, CNPC_FlockingFlyer );
  123. LINK_ENTITY_TO_CLASS( monster_flyer_flock, CNPC_FlockingFlyerFlock );
  124. bool CNPC_FlockingFlyerFlock::KeyValue( const char *szKeyName, const char *szValue )
  125. {
  126. if ( FStrEq( szKeyName, "iFlockSize" ) )
  127. {
  128. m_cFlockSize = atoi( szValue );
  129. return true;
  130. }
  131. else if ( FStrEq( szKeyName, "flFlockRadius" ) )
  132. {
  133. m_flFlockRadius = atof( szValue );
  134. return true;
  135. }
  136. else
  137. BaseClass::KeyValue( szKeyName, szValue );
  138. return false;
  139. }
  140. //=========================================================
  141. //=========================================================
  142. void CNPC_FlockingFlyerFlock::Spawn( void )
  143. {
  144. Precache( );
  145. SetRenderColor( 255, 255, 255, 255 );
  146. SpawnFlock();
  147. SetThink( &CBaseEntity::SUB_Remove );
  148. SetNextThink( gpGlobals->curtime + 0.1f );
  149. }
  150. //=========================================================
  151. //=========================================================
  152. void CNPC_FlockingFlyerFlock::Precache( void )
  153. {
  154. //PRECACHE_MODEL("models/aflock.mdl");
  155. PrecacheModel("models/boid.mdl");
  156. PrecacheFlockSounds();
  157. }
  158. void CNPC_FlockingFlyerFlock::SpawnFlock( void )
  159. {
  160. float R = m_flFlockRadius;
  161. int iCount;
  162. Vector vecSpot;
  163. CNPC_FlockingFlyer *pBoid, *pLeader;
  164. pLeader = pBoid = NULL;
  165. for ( iCount = 0 ; iCount < m_cFlockSize ; iCount++ )
  166. {
  167. pBoid = CREATE_ENTITY( CNPC_FlockingFlyer, "monster_flyer" );
  168. if ( !pLeader )
  169. {
  170. // make this guy the leader.
  171. pLeader = pBoid;
  172. pLeader->m_pSquadLeader = pLeader;
  173. pLeader->m_pSquadNext = NULL;
  174. }
  175. vecSpot.x = random->RandomFloat( -R, R );
  176. vecSpot.y = random->RandomFloat( -R, R );
  177. vecSpot.z = random->RandomFloat( 0, 16 );
  178. vecSpot = GetAbsOrigin() + vecSpot;
  179. UTIL_SetOrigin( pBoid, vecSpot);
  180. pBoid->SetMoveType( MOVETYPE_FLY );
  181. pBoid->SpawnCommonCode();
  182. pBoid->SetGroundEntity( NULL );
  183. pBoid->SetAbsVelocity( Vector ( 0, 0, 0 ) );
  184. pBoid->SetAbsAngles( GetAbsAngles() );
  185. pBoid->SetCycle( 0 );
  186. pBoid->SetThink( &CNPC_FlockingFlyer::IdleThink );
  187. pBoid->SetNextThink( gpGlobals->curtime + 0.2 );
  188. if ( pBoid != pLeader )
  189. {
  190. pLeader->SquadAdd( pBoid );
  191. }
  192. }
  193. }
  194. void CNPC_FlockingFlyerFlock::PrecacheFlockSounds( void )
  195. {
  196. }
  197. //=========================================================
  198. //=========================================================
  199. void CNPC_FlockingFlyer::Spawn( )
  200. {
  201. Precache( );
  202. SpawnCommonCode();
  203. SetCycle( 0 );
  204. SetNextThink( gpGlobals->curtime + 0.1f );
  205. SetThink( &CNPC_FlockingFlyer::IdleThink );
  206. }
  207. //=========================================================
  208. //=========================================================
  209. void CNPC_FlockingFlyer::SpawnCommonCode( )
  210. {
  211. m_lifeState = LIFE_ALIVE;
  212. SetClassname( "monster_flyer" );
  213. SetSolid( SOLID_BBOX );
  214. AddSolidFlags( FSOLID_NOT_STANDABLE );
  215. SetMoveType( MOVETYPE_FLY );
  216. m_takedamage = DAMAGE_NO;
  217. m_iHealth = 1;
  218. m_fPathBlocked = FALSE;// obstacles will be detected
  219. m_flFieldOfView = 0.2;
  220. m_flTempVar = 0;
  221. //SET_MODEL(ENT(pev), "models/aflock.mdl");
  222. SetModel( "models/boid.mdl" );
  223. // UTIL_SetSize(this, Vector(0,0,0), Vector(0,0,0));
  224. UTIL_SetSize(this, Vector(-5,-5,0), Vector(5,5,2));
  225. }
  226. //=========================================================
  227. //=========================================================
  228. void CNPC_FlockingFlyer::Precache( )
  229. {
  230. //PRECACHE_MODEL("models/aflock.mdl");
  231. PrecacheModel("models/boid.mdl");
  232. CNPC_FlockingFlyerFlock::PrecacheFlockSounds();
  233. PrecacheScriptSound( "FlockingFlyer.Alert" );
  234. PrecacheScriptSound( "FlockingFlyer.Idle" );
  235. }
  236. //=========================================================
  237. //=========================================================
  238. void CNPC_FlockingFlyer::IdleThink( void )
  239. {
  240. SetNextThink( gpGlobals->curtime + 0.2 );
  241. // see if there's a client in the same pvs as the monster
  242. if ( !FNullEnt( UTIL_FindClientInPVS( edict() ) ) )
  243. {
  244. SetThink( &CNPC_FlockingFlyer::Start );
  245. SetNextThink( gpGlobals->curtime + 0.1f );
  246. }
  247. }
  248. //=========================================================
  249. //
  250. // SquadUnlink(), Unlink the squad pointers.
  251. //
  252. //=========================================================
  253. void CNPC_FlockingFlyer::SquadUnlink( void )
  254. {
  255. m_pSquadLeader = NULL;
  256. m_pSquadNext = NULL;
  257. }
  258. //=========================================================
  259. //
  260. // SquadAdd(), add pAdd to my squad
  261. //
  262. //=========================================================
  263. void CNPC_FlockingFlyer::SquadAdd( CNPC_FlockingFlyer *pAdd )
  264. {
  265. ASSERT( pAdd!=NULL );
  266. ASSERT( !pAdd->InSquad() );
  267. ASSERT( this->IsLeader() );
  268. pAdd->m_pSquadNext = m_pSquadNext;
  269. m_pSquadNext = pAdd;
  270. pAdd->m_pSquadLeader = this;
  271. }
  272. //=========================================================
  273. //
  274. // SquadRemove(), remove pRemove from my squad.
  275. // If I am pRemove, promote m_pSquadNext to leader
  276. //
  277. //=========================================================
  278. void CNPC_FlockingFlyer::SquadRemove( CNPC_FlockingFlyer *pRemove )
  279. {
  280. ASSERT( pRemove!=NULL );
  281. ASSERT( this->IsLeader() );
  282. ASSERT( pRemove->m_pSquadLeader == this );
  283. if ( SquadCount() > 2 )
  284. {
  285. // Removing the leader, promote m_pSquadNext to leader
  286. if ( pRemove == this )
  287. {
  288. CNPC_FlockingFlyer *pLeader = m_pSquadNext;
  289. // copy the enemy LKP to the new leader
  290. // if ( GetEnemy() )
  291. // pLeader->m_vecEnemyLKP = m_vecEnemyLKP;
  292. if ( pLeader )
  293. {
  294. CNPC_FlockingFlyer *pList = pLeader;
  295. while ( pList )
  296. {
  297. pList->m_pSquadLeader = pLeader;
  298. pList = pList->m_pSquadNext;
  299. }
  300. }
  301. SquadUnlink();
  302. }
  303. else // removing a node
  304. {
  305. CNPC_FlockingFlyer *pList = this;
  306. // Find the node before pRemove
  307. while ( pList->m_pSquadNext != pRemove )
  308. {
  309. // assert to test valid list construction
  310. ASSERT( pList->m_pSquadNext != NULL );
  311. pList = pList->m_pSquadNext;
  312. }
  313. // List validity
  314. ASSERT( pList->m_pSquadNext == pRemove );
  315. // Relink without pRemove
  316. pList->m_pSquadNext = pRemove->m_pSquadNext;
  317. // Unlink pRemove
  318. pRemove->SquadUnlink();
  319. }
  320. }
  321. else
  322. SquadDisband();
  323. }
  324. //=========================================================
  325. //
  326. // SquadCount(), return the number of members of this squad
  327. // callable from leaders & followers
  328. //
  329. //=========================================================
  330. int CNPC_FlockingFlyer::SquadCount( void )
  331. {
  332. CNPC_FlockingFlyer *pList = m_pSquadLeader;
  333. int squadCount = 0;
  334. while ( pList )
  335. {
  336. squadCount++;
  337. pList = pList->m_pSquadNext;
  338. }
  339. return squadCount;
  340. }
  341. //=========================================================
  342. //
  343. // SquadDisband(), Unlink all squad members
  344. //
  345. //=========================================================
  346. void CNPC_FlockingFlyer::SquadDisband( void )
  347. {
  348. CNPC_FlockingFlyer *pList = m_pSquadLeader;
  349. CNPC_FlockingFlyer *pNext;
  350. while ( pList )
  351. {
  352. pNext = pList->m_pSquadNext;
  353. pList->SquadUnlink();
  354. pList = pNext;
  355. }
  356. }
  357. //=========================================================
  358. // Start - player enters the pvs, so get things going.
  359. //=========================================================
  360. void CNPC_FlockingFlyer::Start( void )
  361. {
  362. SetNextThink( gpGlobals->curtime + 0.1f );
  363. if ( IsLeader() )
  364. {
  365. SetThink( &CNPC_FlockingFlyer::FlockLeaderThink );
  366. }
  367. else
  368. {
  369. SetThink( &CNPC_FlockingFlyer::FlockFollowerThink );
  370. }
  371. SetActivity ( ACT_FLY );
  372. ResetSequenceInfo( );
  373. BoidAdvanceFrame( );
  374. m_flSpeed = AFLOCK_FLY_SPEED;// no delay!
  375. }
  376. //=========================================================
  377. //=========================================================
  378. void CNPC_FlockingFlyer::BoidAdvanceFrame ( void )
  379. {
  380. float flapspeed = ( m_flSpeed - m_flTempVar ) / AFLOCK_ACCELERATE;
  381. m_flTempVar = m_flTempVar * .8 + m_flSpeed * .2;
  382. if (flapspeed < 0) flapspeed = -flapspeed;
  383. if (flapspeed < 0.25) flapspeed = 0.25;
  384. if (flapspeed > 1.9) flapspeed = 1.9;
  385. m_flPlaybackRate = flapspeed;
  386. QAngle angVel = GetLocalAngularVelocity();
  387. // lean
  388. angVel.x = - GetAbsAngles().x + flapspeed * 5;
  389. // bank
  390. angVel.z = - GetAbsAngles().z + angVel.y;
  391. SetLocalAngularVelocity( angVel );
  392. // pev->framerate = flapspeed;
  393. StudioFrameAdvance();
  394. }
  395. //=========================================================
  396. // Leader boids use this think every tenth
  397. //=========================================================
  398. void CNPC_FlockingFlyer::FlockLeaderThink( void )
  399. {
  400. trace_t tr;
  401. Vector vecDist;// used for general measurements
  402. Vector vecDir;// used for general measurements
  403. float flLeftSide;
  404. float flRightSide;
  405. Vector vForward, vRight, vUp;
  406. SetNextThink( gpGlobals->curtime + 0.1f );
  407. AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
  408. // is the way ahead clear?
  409. if ( !FPathBlocked () )
  410. {
  411. // if the boid is turning, stop the trend.
  412. if ( m_fTurning )
  413. {
  414. m_fTurning = FALSE;
  415. QAngle angVel = GetLocalAngularVelocity();
  416. angVel.y = 0;
  417. SetLocalAngularVelocity( angVel );
  418. }
  419. m_fPathBlocked = FALSE;
  420. if ( m_flSpeed <= AFLOCK_FLY_SPEED )
  421. m_flSpeed += 5;
  422. SetAbsVelocity( vForward * m_flSpeed );
  423. BoidAdvanceFrame( );
  424. return;
  425. }
  426. // IF we get this far in the function, the leader's path is blocked!
  427. m_fPathBlocked = TRUE;
  428. if ( !m_fTurning)// something in the way and boid is not already turning to avoid
  429. {
  430. // measure clearance on left and right to pick the best dir to turn
  431. UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vRight * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  432. vecDist = (tr.endpos - GetAbsOrigin());
  433. flRightSide = vecDist.Length();
  434. UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() - vRight * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  435. vecDist = (tr.endpos - GetAbsOrigin());
  436. flLeftSide = vecDist.Length();
  437. // turn right if more clearance on right side
  438. if ( flRightSide > flLeftSide )
  439. {
  440. QAngle angVel = GetLocalAngularVelocity();
  441. angVel.y = -AFLOCK_TURN_RATE;
  442. SetLocalAngularVelocity( angVel );
  443. m_fTurning = TRUE;
  444. }
  445. // default to left turn :)
  446. else if ( flLeftSide > flRightSide )
  447. {
  448. QAngle angVel = GetLocalAngularVelocity();
  449. angVel.y = AFLOCK_TURN_RATE;
  450. SetLocalAngularVelocity( angVel );
  451. m_fTurning = TRUE;
  452. }
  453. else
  454. {
  455. // equidistant. Pick randomly between left and right.
  456. m_fTurning = TRUE;
  457. QAngle angVel = GetLocalAngularVelocity();
  458. if ( random->RandomInt( 0, 1 ) == 0 )
  459. {
  460. angVel.y = AFLOCK_TURN_RATE;
  461. }
  462. else
  463. {
  464. angVel.y = -AFLOCK_TURN_RATE;
  465. }
  466. SetLocalAngularVelocity( angVel );
  467. }
  468. }
  469. SpreadFlock( );
  470. SetAbsVelocity( vForward * m_flSpeed );
  471. // check and make sure we aren't about to plow into the ground, don't let it happen
  472. UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() - vUp * 16, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  473. if (tr.fraction != 1.0 && GetAbsVelocity().z < 0 )
  474. {
  475. Vector vecVel = GetAbsVelocity();
  476. vecVel.z = 0;
  477. SetAbsVelocity( vecVel );
  478. }
  479. // maybe it did, though.
  480. if ( GetFlags() & FL_ONGROUND )
  481. {
  482. UTIL_SetOrigin( this, GetAbsOrigin() + Vector ( 0 , 0 , 1 ) );
  483. Vector vecVel = GetAbsVelocity();
  484. vecVel.z = 0;
  485. SetAbsVelocity( vecVel );
  486. }
  487. if ( m_flFlockNextSoundTime < gpGlobals->curtime )
  488. {
  489. // MakeSound();
  490. m_flFlockNextSoundTime = gpGlobals->curtime + random->RandomFloat( 1, 3 );
  491. }
  492. BoidAdvanceFrame( );
  493. return;
  494. }
  495. //=========================================================
  496. // FBoidPathBlocked - returns TRUE if there is an obstacle ahead
  497. //=========================================================
  498. bool CNPC_FlockingFlyer::FPathBlocked( void )
  499. {
  500. trace_t tr;
  501. Vector vecDist;// used for general measurements
  502. Vector vecDir;// used for general measurements
  503. bool fBlocked;
  504. Vector vForward, vRight, vUp;
  505. if ( m_flFakeBlockedTime > gpGlobals->curtime )
  506. {
  507. m_flLastBlockedTime = gpGlobals->curtime;
  508. return TRUE;
  509. }
  510. // use VELOCITY, not angles, not all boids point the direction they are flying
  511. //vecDir = UTIL_VecToAngles( pevBoid->velocity );
  512. AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
  513. fBlocked = FALSE;// assume the way ahead is clear
  514. // check for obstacle ahead
  515. UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  516. if (tr.fraction != 1.0)
  517. {
  518. m_flLastBlockedTime = gpGlobals->curtime;
  519. fBlocked = TRUE;
  520. }
  521. // extra wide checks
  522. UTIL_TraceLine(GetAbsOrigin() + vRight * 12, GetAbsOrigin() + vRight * 12 + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  523. if (tr.fraction != 1.0)
  524. {
  525. m_flLastBlockedTime = gpGlobals->curtime;
  526. fBlocked = TRUE;
  527. }
  528. UTIL_TraceLine(GetAbsOrigin() - vRight * 12, GetAbsOrigin() - vRight * 12 + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  529. if (tr.fraction != 1.0)
  530. {
  531. m_flLastBlockedTime = gpGlobals->curtime;
  532. fBlocked = TRUE;
  533. }
  534. if ( !fBlocked && gpGlobals->curtime - m_flLastBlockedTime > 6 )
  535. {
  536. // not blocked, and it's been a few seconds since we've actually been blocked.
  537. m_flFakeBlockedTime = gpGlobals->curtime + random->RandomInt(1, 3);
  538. }
  539. return fBlocked;
  540. }
  541. //=========================================================
  542. // Searches for boids that are too close and pushes them away
  543. //=========================================================
  544. void CNPC_FlockingFlyer::SpreadFlock( )
  545. {
  546. Vector vecDir;
  547. float flSpeed;// holds vector magnitude while we fiddle with the direction
  548. CNPC_FlockingFlyer *pList = m_pSquadLeader;
  549. while ( pList )
  550. {
  551. if ( pList != this && ( GetAbsOrigin() - pList->GetAbsOrigin() ).Length() <= AFLOCK_TOO_CLOSE )
  552. {
  553. // push the other away
  554. vecDir = ( pList->GetAbsOrigin() - GetAbsOrigin() );
  555. VectorNormalize( vecDir );
  556. // store the magnitude of the other boid's velocity, and normalize it so we
  557. // can average in a course that points away from the leader.
  558. flSpeed = pList->GetAbsVelocity().Length();
  559. Vector vecVel = pList->GetAbsVelocity();
  560. VectorNormalize( vecVel );
  561. pList->SetAbsVelocity( ( vecVel + vecDir ) * 0.5 * flSpeed );
  562. }
  563. pList = pList->m_pSquadNext;
  564. }
  565. }
  566. //=========================================================
  567. // Alters the caller's course if he's too close to others
  568. //
  569. // This function should **ONLY** be called when Caller's velocity is normalized!!
  570. //=========================================================
  571. void CNPC_FlockingFlyer::SpreadFlock2 ( )
  572. {
  573. Vector vecDir;
  574. CNPC_FlockingFlyer *pList = m_pSquadLeader;
  575. while ( pList )
  576. {
  577. if ( pList != this && ( GetAbsOrigin() - pList->GetAbsOrigin() ).Length() <= AFLOCK_TOO_CLOSE )
  578. {
  579. vecDir = ( GetAbsOrigin() - pList->GetAbsOrigin() );
  580. VectorNormalize( vecDir );
  581. SetAbsVelocity( ( GetAbsVelocity() + vecDir ) );
  582. }
  583. pList = pList->m_pSquadNext;
  584. }
  585. }
  586. //=========================================================
  587. //=========================================================
  588. void CNPC_FlockingFlyer::MakeSound( void )
  589. {
  590. if ( m_flAlertTime > gpGlobals->curtime )
  591. {
  592. CPASAttenuationFilter filter1( this );
  593. // make agitated sounds
  594. EmitSound( filter1, entindex(), "FlockingFlyer.Alert" );
  595. return;
  596. }
  597. // make normal sound
  598. CPASAttenuationFilter filter2( this );
  599. EmitSound( filter2, entindex(), "FlockingFlyer.Idle" );
  600. }
  601. //=========================================================
  602. // follower boids execute this code when flocking
  603. //=========================================================
  604. void CNPC_FlockingFlyer::FlockFollowerThink( void )
  605. {
  606. Vector vecDist;
  607. Vector vecDir;
  608. Vector vecDirToLeader;
  609. float flDistToLeader;
  610. SetNextThink( gpGlobals->curtime + 0.1f );
  611. if ( IsLeader() || !InSquad() )
  612. {
  613. // the leader has been killed and this flyer suddenly finds himself the leader.
  614. SetThink ( &CNPC_FlockingFlyer::FlockLeaderThink );
  615. return;
  616. }
  617. vecDirToLeader = ( m_pSquadLeader->GetAbsOrigin() - GetAbsOrigin() );
  618. flDistToLeader = vecDirToLeader.Length();
  619. // match heading with leader
  620. SetAbsAngles( m_pSquadLeader->GetAbsAngles() );
  621. //
  622. // We can see the leader, so try to catch up to it
  623. //
  624. if ( FInViewCone ( m_pSquadLeader ) )
  625. {
  626. // if we're too far away, speed up
  627. if ( flDistToLeader > AFLOCK_TOO_FAR )
  628. {
  629. m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 1.5;
  630. }
  631. // if we're too close, slow down
  632. else if ( flDistToLeader < AFLOCK_TOO_CLOSE )
  633. {
  634. m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 0.5;
  635. }
  636. }
  637. else
  638. {
  639. // wait up! the leader isn't out in front, so we slow down to let him pass
  640. m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 0.5;
  641. }
  642. SpreadFlock2();
  643. Vector vecVel = GetAbsVelocity();
  644. m_flSpeed = vecVel.Length();
  645. VectorNormalize( vecVel );
  646. // if we are too far from leader, average a vector towards it into our current velocity
  647. if ( flDistToLeader > AFLOCK_TOO_FAR )
  648. {
  649. VectorNormalize( vecDirToLeader );
  650. vecVel = (vecVel + vecDirToLeader) * 0.5;
  651. }
  652. // clamp speeds and handle acceleration
  653. if ( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2 )
  654. {
  655. m_flGoalSpeed = AFLOCK_FLY_SPEED * 2;
  656. }
  657. if ( m_flSpeed < m_flGoalSpeed )
  658. {
  659. m_flSpeed += AFLOCK_ACCELERATE;
  660. }
  661. else if ( m_flSpeed > m_flGoalSpeed )
  662. {
  663. m_flSpeed -= AFLOCK_ACCELERATE;
  664. }
  665. SetAbsVelocity( vecVel * m_flSpeed );
  666. BoidAdvanceFrame( );
  667. }
  668. //=========================================================
  669. //=========================================================
  670. void CNPC_FlockingFlyer::Event_Killed( const CTakeDamageInfo &info )
  671. {
  672. CNPC_FlockingFlyer *pSquad;
  673. pSquad = (CNPC_FlockingFlyer *)m_pSquadLeader;
  674. while ( pSquad )
  675. {
  676. pSquad->m_flAlertTime = gpGlobals->curtime + 15;
  677. pSquad = (CNPC_FlockingFlyer *)pSquad->m_pSquadNext;
  678. }
  679. if ( m_pSquadLeader )
  680. {
  681. m_pSquadLeader->SquadRemove( this );
  682. }
  683. m_lifeState = LIFE_DEAD;
  684. m_flPlaybackRate = 0;
  685. IncrementInterpolationFrame();
  686. UTIL_SetSize( this, Vector(0,0,0), Vector(0,0,0) );
  687. SetMoveType( MOVETYPE_FLYGRAVITY );
  688. SetThink ( &CNPC_FlockingFlyer::FallHack );
  689. SetNextThink( gpGlobals->curtime + 0.1f );
  690. }
  691. void CNPC_FlockingFlyer::FallHack( void )
  692. {
  693. if ( GetFlags() & FL_ONGROUND )
  694. {
  695. CBaseEntity *groundentity = GetContainingEntity( GetGroundEntity()->edict() );
  696. if ( !FClassnameIs ( groundentity, "worldspawn" ) )
  697. {
  698. SetGroundEntity( NULL );
  699. SetNextThink( gpGlobals->curtime + 0.1f );
  700. }
  701. else
  702. {
  703. SetAbsVelocity( Vector( 0, 0, 0 ) );
  704. SetThink( NULL );
  705. }
  706. }
  707. }