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.

2156 lines
57 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "ai_basenpc.h"
  9. #include "ai_default.h"
  10. #include "ai_schedule.h"
  11. #include "ai_hull.h"
  12. #include "ai_motor.h"
  13. #include "ai_memory.h"
  14. #include "ai_route.h"
  15. #include "soundent.h"
  16. #include "game.h"
  17. #include "npcevent.h"
  18. #include "entitylist.h"
  19. #include "ai_task.h"
  20. #include "activitylist.h"
  21. #include "engine/IEngineSound.h"
  22. #include "npc_BaseZombie.h"
  23. #include "movevars_shared.h"
  24. #include "IEffects.h"
  25. #include "props.h"
  26. #include "physics_npc_solver.h"
  27. #include "physics_prop_ragdoll.h"
  28. #ifdef HL2_EPISODIC
  29. #include "episodic/ai_behavior_passenger_zombie.h"
  30. #endif // HL2_EPISODIC
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. #define FASTZOMBIE_IDLE_PITCH 35
  34. #define FASTZOMBIE_MIN_PITCH 70
  35. #define FASTZOMBIE_MAX_PITCH 130
  36. #define FASTZOMBIE_SOUND_UPDATE_FREQ 0.5
  37. #define FASTZOMBIE_MAXLEAP_Z 128
  38. #define FASTZOMBIE_EXCITE_DIST 480.0
  39. #define FASTZOMBIE_BASE_FREQ 1.5
  40. // If flying at an enemy, and this close or closer, start playing the maul animation!!
  41. #define FASTZOMBIE_MAUL_RANGE 300
  42. #ifdef HL2_EPISODIC
  43. int AE_PASSENGER_PHYSICS_PUSH;
  44. int AE_FASTZOMBIE_VEHICLE_LEAP;
  45. int AE_FASTZOMBIE_VEHICLE_SS_DIE; // Killed while doing scripted sequence on vehicle
  46. #endif // HL2_EPISODIC
  47. enum
  48. {
  49. COND_FASTZOMBIE_CLIMB_TOUCH = LAST_BASE_ZOMBIE_CONDITION,
  50. };
  51. envelopePoint_t envFastZombieVolumeJump[] =
  52. {
  53. { 1.0f, 1.0f,
  54. 0.1f, 0.1f,
  55. },
  56. { 0.0f, 0.0f,
  57. 1.0f, 1.2f,
  58. },
  59. };
  60. envelopePoint_t envFastZombieVolumePain[] =
  61. {
  62. { 1.0f, 1.0f,
  63. 0.1f, 0.1f,
  64. },
  65. { 0.0f, 0.0f,
  66. 1.0f, 1.0f,
  67. },
  68. };
  69. envelopePoint_t envFastZombieInverseVolumePain[] =
  70. {
  71. { 0.0f, 0.0f,
  72. 0.1f, 0.1f,
  73. },
  74. { 1.0f, 1.0f,
  75. 1.0f, 1.0f,
  76. },
  77. };
  78. envelopePoint_t envFastZombieVolumeJumpPostApex[] =
  79. {
  80. { 1.0f, 1.0f,
  81. 0.1f, 0.1f,
  82. },
  83. { 0.0f, 0.0f,
  84. 1.0f, 1.2f,
  85. },
  86. };
  87. envelopePoint_t envFastZombieVolumeClimb[] =
  88. {
  89. { 1.0f, 1.0f,
  90. 0.1f, 0.1f,
  91. },
  92. { 0.0f, 0.0f,
  93. 0.2f, 0.2f,
  94. },
  95. };
  96. envelopePoint_t envFastZombieMoanVolumeFast[] =
  97. {
  98. { 1.0f, 1.0f,
  99. 0.1f, 0.1f,
  100. },
  101. { 0.0f, 0.0f,
  102. 0.2f, 0.3f,
  103. },
  104. };
  105. envelopePoint_t envFastZombieMoanVolume[] =
  106. {
  107. { 1.0f, 1.0f,
  108. 0.1f, 0.1f,
  109. },
  110. { 1.0f, 1.0f,
  111. 0.2f, 0.2f,
  112. },
  113. { 0.0f, 0.0f,
  114. 1.0f, 0.4f,
  115. },
  116. };
  117. envelopePoint_t envFastZombieFootstepVolume[] =
  118. {
  119. { 1.0f, 1.0f,
  120. 0.1f, 0.1f,
  121. },
  122. { 0.7f, 0.7f,
  123. 0.2f, 0.2f,
  124. },
  125. };
  126. envelopePoint_t envFastZombieVolumeFrenzy[] =
  127. {
  128. { 1.0f, 1.0f,
  129. 0.1f, 0.1f,
  130. },
  131. { 0.0f, 0.0f,
  132. 2.0f, 2.0f,
  133. },
  134. };
  135. //=========================================================
  136. // animation events
  137. //=========================================================
  138. int AE_FASTZOMBIE_LEAP;
  139. int AE_FASTZOMBIE_GALLOP_LEFT;
  140. int AE_FASTZOMBIE_GALLOP_RIGHT;
  141. int AE_FASTZOMBIE_CLIMB_LEFT;
  142. int AE_FASTZOMBIE_CLIMB_RIGHT;
  143. //=========================================================
  144. // tasks
  145. //=========================================================
  146. enum
  147. {
  148. TASK_FASTZOMBIE_DO_ATTACK = LAST_SHARED_TASK + 100, // again, my !!!HACKHACK
  149. TASK_FASTZOMBIE_LAND_RECOVER,
  150. TASK_FASTZOMBIE_UNSTICK_JUMP,
  151. TASK_FASTZOMBIE_JUMP_BACK,
  152. TASK_FASTZOMBIE_VERIFY_ATTACK,
  153. };
  154. //=========================================================
  155. // activities
  156. //=========================================================
  157. int ACT_FASTZOMBIE_LEAP_SOAR;
  158. int ACT_FASTZOMBIE_LEAP_STRIKE;
  159. int ACT_FASTZOMBIE_LAND_RIGHT;
  160. int ACT_FASTZOMBIE_LAND_LEFT;
  161. int ACT_FASTZOMBIE_FRENZY;
  162. int ACT_FASTZOMBIE_BIG_SLASH;
  163. //=========================================================
  164. // schedules
  165. //=========================================================
  166. enum
  167. {
  168. SCHED_FASTZOMBIE_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE + 100, // hack to get past the base zombie's schedules
  169. SCHED_FASTZOMBIE_UNSTICK_JUMP,
  170. SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP,
  171. SCHED_FASTZOMBIE_MELEE_ATTACK1,
  172. SCHED_FASTZOMBIE_TORSO_MELEE_ATTACK1,
  173. };
  174. //=========================================================
  175. //=========================================================
  176. class CFastZombie : public CNPC_BaseZombie
  177. {
  178. DECLARE_CLASS( CFastZombie, CNPC_BaseZombie );
  179. public:
  180. void Spawn( void );
  181. void Precache( void );
  182. void SetZombieModel( void );
  183. bool CanSwatPhysicsObjects( void ) { return false; }
  184. int TranslateSchedule( int scheduleType );
  185. Activity NPC_TranslateActivity( Activity baseAct );
  186. void LeapAttackTouch( CBaseEntity *pOther );
  187. void ClimbTouch( CBaseEntity *pOther );
  188. void StartTask( const Task_t *pTask );
  189. void RunTask( const Task_t *pTask );
  190. int SelectSchedule( void );
  191. void OnScheduleChange( void );
  192. void PrescheduleThink( void );
  193. float InnateRange1MaxRange( void );
  194. int RangeAttack1Conditions( float flDot, float flDist );
  195. int MeleeAttack1Conditions( float flDot, float flDist );
  196. virtual float GetClawAttackRange() const { return 50; }
  197. bool ShouldPlayFootstepMoan( void ) { return false; }
  198. void HandleAnimEvent( animevent_t *pEvent );
  199. void PostNPCInit( void );
  200. void LeapAttack( void );
  201. void LeapAttackSound( void );
  202. void BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce );
  203. bool IsJumpLegal(const Vector &startPos, const Vector &apex, const Vector &endPos) const;
  204. bool MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost );
  205. bool ShouldFailNav( bool bMovementFailed );
  206. int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
  207. const char *GetMoanSound( int nSound );
  208. void OnChangeActivity( Activity NewActivity );
  209. void OnStateChange( NPC_STATE OldState, NPC_STATE NewState );
  210. void Event_Killed( const CTakeDamageInfo &info );
  211. bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold );
  212. virtual Vector GetAutoAimCenter() { return WorldSpaceCenter() - Vector( 0, 0, 12.0f ); }
  213. void PainSound( const CTakeDamageInfo &info );
  214. void DeathSound( const CTakeDamageInfo &info );
  215. void AlertSound( void );
  216. void IdleSound( void );
  217. void AttackSound( void );
  218. void AttackHitSound( void );
  219. void AttackMissSound( void );
  220. void FootstepSound( bool fRightFoot );
  221. void FootscuffSound( bool fRightFoot ) {}; // fast guy doesn't scuff
  222. void StopLoopingSounds( void );
  223. void SoundInit( void );
  224. void SetIdleSoundState( void );
  225. void SetAngrySoundState( void );
  226. void BuildScheduleTestBits( void );
  227. void BeginNavJump( void );
  228. void EndNavJump( void );
  229. bool IsNavJumping( void ) { return m_fIsNavJumping; }
  230. void OnNavJumpHitApex( void );
  231. void BeginAttackJump( void );
  232. void EndAttackJump( void );
  233. float MaxYawSpeed( void );
  234. virtual const char *GetHeadcrabClassname( void );
  235. virtual const char *GetHeadcrabModel( void );
  236. virtual const char *GetLegsModel( void );
  237. virtual const char *GetTorsoModel( void );
  238. //=============================================================================
  239. #ifdef HL2_EPISODIC
  240. public:
  241. virtual bool CreateBehaviors( void );
  242. virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
  243. virtual void UpdateEfficiency( bool bInPVS );
  244. virtual bool IsInAVehicle( void );
  245. void InputAttachToVehicle( inputdata_t &inputdata );
  246. void VehicleLeapAttackTouch( CBaseEntity *pOther );
  247. private:
  248. void VehicleLeapAttack( void );
  249. bool CanEnterVehicle( CPropJeepEpisodic *pVehicle );
  250. CAI_PassengerBehaviorZombie m_PassengerBehavior;
  251. #endif // HL2_EPISODIC
  252. //=============================================================================
  253. protected:
  254. static const char *pMoanSounds[];
  255. // Sound stuff
  256. float m_flDistFactor;
  257. unsigned char m_iClimbCount; // counts rungs climbed (for sound)
  258. bool m_fIsNavJumping;
  259. bool m_fIsAttackJumping;
  260. bool m_fHitApex;
  261. mutable float m_flJumpDist;
  262. bool m_fHasScreamed;
  263. private:
  264. float m_flNextMeleeAttack;
  265. bool m_fJustJumped;
  266. float m_flJumpStartAltitude;
  267. float m_flTimeUpdateSound;
  268. CSoundPatch *m_pLayer2; // used for climbing ladders, and when jumping (pre apex)
  269. public:
  270. DEFINE_CUSTOM_AI;
  271. DECLARE_DATADESC();
  272. };
  273. LINK_ENTITY_TO_CLASS( npc_fastzombie, CFastZombie );
  274. LINK_ENTITY_TO_CLASS( npc_fastzombie_torso, CFastZombie );
  275. BEGIN_DATADESC( CFastZombie )
  276. DEFINE_FIELD( m_flDistFactor, FIELD_FLOAT ),
  277. DEFINE_FIELD( m_iClimbCount, FIELD_CHARACTER ),
  278. DEFINE_FIELD( m_fIsNavJumping, FIELD_BOOLEAN ),
  279. DEFINE_FIELD( m_fIsAttackJumping, FIELD_BOOLEAN ),
  280. DEFINE_FIELD( m_fHitApex, FIELD_BOOLEAN ),
  281. DEFINE_FIELD( m_flJumpDist, FIELD_FLOAT ),
  282. DEFINE_FIELD( m_fHasScreamed, FIELD_BOOLEAN ),
  283. DEFINE_FIELD( m_flNextMeleeAttack, FIELD_TIME ),
  284. DEFINE_FIELD( m_fJustJumped, FIELD_BOOLEAN ),
  285. DEFINE_FIELD( m_flJumpStartAltitude, FIELD_FLOAT ),
  286. DEFINE_FIELD( m_flTimeUpdateSound, FIELD_TIME ),
  287. // Function Pointers
  288. DEFINE_ENTITYFUNC( LeapAttackTouch ),
  289. DEFINE_ENTITYFUNC( ClimbTouch ),
  290. DEFINE_SOUNDPATCH( m_pLayer2 ),
  291. #ifdef HL2_EPISODIC
  292. DEFINE_ENTITYFUNC( VehicleLeapAttackTouch ),
  293. DEFINE_INPUTFUNC( FIELD_STRING, "AttachToVehicle", InputAttachToVehicle ),
  294. #endif // HL2_EPISODIC
  295. END_DATADESC()
  296. const char *CFastZombie::pMoanSounds[] =
  297. {
  298. "NPC_FastZombie.Moan1",
  299. };
  300. //-----------------------------------------------------------------------------
  301. // The model we use for our legs when we get blowed up.
  302. //-----------------------------------------------------------------------------
  303. static const char *s_pLegsModel = "models/gibs/fast_zombie_legs.mdl";
  304. //-----------------------------------------------------------------------------
  305. // Purpose:
  306. //
  307. //
  308. //-----------------------------------------------------------------------------
  309. void CFastZombie::Precache( void )
  310. {
  311. PrecacheModel("models/zombie/fast.mdl");
  312. #ifdef HL2_EPISODIC
  313. PrecacheModel("models/zombie/Fast_torso.mdl");
  314. PrecacheScriptSound( "NPC_FastZombie.CarEnter1" );
  315. PrecacheScriptSound( "NPC_FastZombie.CarEnter2" );
  316. PrecacheScriptSound( "NPC_FastZombie.CarEnter3" );
  317. PrecacheScriptSound( "NPC_FastZombie.CarEnter4" );
  318. PrecacheScriptSound( "NPC_FastZombie.CarScream" );
  319. #endif
  320. PrecacheModel( "models/gibs/fast_zombie_torso.mdl" );
  321. PrecacheModel( "models/gibs/fast_zombie_legs.mdl" );
  322. PrecacheScriptSound( "NPC_FastZombie.LeapAttack" );
  323. PrecacheScriptSound( "NPC_FastZombie.FootstepRight" );
  324. PrecacheScriptSound( "NPC_FastZombie.FootstepLeft" );
  325. PrecacheScriptSound( "NPC_FastZombie.AttackHit" );
  326. PrecacheScriptSound( "NPC_FastZombie.AttackMiss" );
  327. PrecacheScriptSound( "NPC_FastZombie.LeapAttack" );
  328. PrecacheScriptSound( "NPC_FastZombie.Attack" );
  329. PrecacheScriptSound( "NPC_FastZombie.Idle" );
  330. PrecacheScriptSound( "NPC_FastZombie.AlertFar" );
  331. PrecacheScriptSound( "NPC_FastZombie.AlertNear" );
  332. PrecacheScriptSound( "NPC_FastZombie.GallopLeft" );
  333. PrecacheScriptSound( "NPC_FastZombie.GallopRight" );
  334. PrecacheScriptSound( "NPC_FastZombie.Scream" );
  335. PrecacheScriptSound( "NPC_FastZombie.RangeAttack" );
  336. PrecacheScriptSound( "NPC_FastZombie.Frenzy" );
  337. PrecacheScriptSound( "NPC_FastZombie.NoSound" );
  338. PrecacheScriptSound( "NPC_FastZombie.Die" );
  339. PrecacheScriptSound( "NPC_FastZombie.Gurgle" );
  340. PrecacheScriptSound( "NPC_FastZombie.Moan1" );
  341. BaseClass::Precache();
  342. }
  343. //---------------------------------------------------------
  344. //---------------------------------------------------------
  345. void CFastZombie::OnScheduleChange( void )
  346. {
  347. if ( m_flNextMeleeAttack > gpGlobals->curtime + 1 )
  348. {
  349. // Allow melee attacks again.
  350. m_flNextMeleeAttack = gpGlobals->curtime + 0.5;
  351. }
  352. BaseClass::OnScheduleChange();
  353. }
  354. //---------------------------------------------------------
  355. //---------------------------------------------------------
  356. int CFastZombie::SelectSchedule ( void )
  357. {
  358. // ========================================================
  359. #ifdef HL2_EPISODIC
  360. // Defer all decisions to the behavior if it's running
  361. if ( m_PassengerBehavior.CanSelectSchedule() )
  362. {
  363. DeferSchedulingToBehavior( &m_PassengerBehavior );
  364. return BaseClass::SelectSchedule();
  365. }
  366. #endif //HL2_EPISODIC
  367. // ========================================================
  368. if ( HasCondition( COND_ZOMBIE_RELEASECRAB ) )
  369. {
  370. // Death waits for no man. Or zombie. Or something.
  371. return SCHED_ZOMBIE_RELEASECRAB;
  372. }
  373. if ( HasCondition( COND_FASTZOMBIE_CLIMB_TOUCH ) )
  374. {
  375. return SCHED_FASTZOMBIE_UNSTICK_JUMP;
  376. }
  377. switch ( m_NPCState )
  378. {
  379. case NPC_STATE_COMBAT:
  380. if ( HasCondition( COND_LOST_ENEMY ) || ( HasCondition( COND_ENEMY_UNREACHABLE ) && MustCloseToAttack() ) )
  381. {
  382. // Set state to alert and recurse!
  383. SetState( NPC_STATE_ALERT );
  384. return SelectSchedule();
  385. }
  386. break;
  387. case NPC_STATE_ALERT:
  388. if ( HasCondition( COND_LOST_ENEMY ) || ( HasCondition( COND_ENEMY_UNREACHABLE ) && MustCloseToAttack() ) )
  389. {
  390. ClearCondition( COND_LOST_ENEMY );
  391. ClearCondition( COND_ENEMY_UNREACHABLE );
  392. SetEnemy( NULL );
  393. #ifdef DEBUG_ZOMBIES
  394. DevMsg("Wandering\n");
  395. #endif
  396. // Just lost track of our enemy.
  397. // Wander around a bit so we don't look like a dingus.
  398. return SCHED_ZOMBIE_WANDER_MEDIUM;
  399. }
  400. break;
  401. }
  402. return BaseClass::SelectSchedule();
  403. }
  404. //-----------------------------------------------------------------------------
  405. //-----------------------------------------------------------------------------
  406. void CFastZombie::PrescheduleThink( void )
  407. {
  408. BaseClass::PrescheduleThink();
  409. if( GetGroundEntity() && GetGroundEntity()->Classify() == CLASS_HEADCRAB )
  410. {
  411. // Kill!
  412. CTakeDamageInfo info;
  413. info.SetDamage( GetGroundEntity()->GetHealth() );
  414. info.SetAttacker( this );
  415. info.SetInflictor( this );
  416. info.SetDamageType( DMG_GENERIC );
  417. GetGroundEntity()->TakeDamage( info );
  418. }
  419. if( m_pMoanSound && gpGlobals->curtime > m_flTimeUpdateSound )
  420. {
  421. // Manage the snorting sound, pitch up for closer.
  422. float flDistNoBBox;
  423. if( GetEnemy() && m_NPCState == NPC_STATE_COMBAT )
  424. {
  425. flDistNoBBox = ( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() ).Length();
  426. flDistNoBBox -= WorldAlignSize().x;
  427. }
  428. else
  429. {
  430. // Calm down!
  431. flDistNoBBox = FASTZOMBIE_EXCITE_DIST;
  432. m_flTimeUpdateSound += 1.0;
  433. }
  434. if( flDistNoBBox >= FASTZOMBIE_EXCITE_DIST && m_flDistFactor != 1.0 )
  435. {
  436. // Go back to normal pitch.
  437. m_flDistFactor = 1.0;
  438. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, FASTZOMBIE_IDLE_PITCH, FASTZOMBIE_SOUND_UPDATE_FREQ );
  439. }
  440. else if( flDistNoBBox < FASTZOMBIE_EXCITE_DIST )
  441. {
  442. // Zombie is close! Recalculate pitch.
  443. int iPitch;
  444. m_flDistFactor = MIN( 1.0, 1 - flDistNoBBox / FASTZOMBIE_EXCITE_DIST );
  445. iPitch = FASTZOMBIE_MIN_PITCH + ( ( FASTZOMBIE_MAX_PITCH - FASTZOMBIE_MIN_PITCH ) * m_flDistFactor);
  446. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, iPitch, FASTZOMBIE_SOUND_UPDATE_FREQ );
  447. }
  448. m_flTimeUpdateSound = gpGlobals->curtime + FASTZOMBIE_SOUND_UPDATE_FREQ;
  449. }
  450. // Crudely detect the apex of our jump
  451. if( IsNavJumping() && !m_fHitApex && GetAbsVelocity().z <= 0.0 )
  452. {
  453. OnNavJumpHitApex();
  454. }
  455. if( IsCurSchedule(SCHED_FASTZOMBIE_RANGE_ATTACK1, false) )
  456. {
  457. // Think more frequently when flying quickly through the
  458. // air, to update the server's location more often.
  459. SetNextThink(gpGlobals->curtime);
  460. }
  461. }
  462. //-----------------------------------------------------------------------------
  463. // Purpose: Startup all of the sound patches that the fast zombie uses.
  464. //
  465. //
  466. //-----------------------------------------------------------------------------
  467. void CFastZombie::SoundInit( void )
  468. {
  469. if( !m_pMoanSound )
  470. {
  471. // !!!HACKHACK - kickstart the moan sound. (sjb)
  472. MoanSound( envFastZombieMoanVolume, ARRAYSIZE( envFastZombieMoanVolume ) );
  473. // Clear the commands that the base class gave the moaning sound channel.
  474. ENVELOPE_CONTROLLER.CommandClear( m_pMoanSound );
  475. }
  476. CPASAttenuationFilter filter( this );
  477. if( !m_pLayer2 )
  478. {
  479. // Set up layer2
  480. m_pLayer2 = ENVELOPE_CONTROLLER.SoundCreate( filter, entindex(), CHAN_VOICE, "NPC_FastZombie.Gurgle", ATTN_NORM );
  481. // Start silent.
  482. ENVELOPE_CONTROLLER.Play( m_pLayer2, 0.0, 100 );
  483. }
  484. SetIdleSoundState();
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose: Make the zombie sound calm.
  488. //-----------------------------------------------------------------------------
  489. void CFastZombie::SetIdleSoundState( void )
  490. {
  491. // Main looping sound
  492. if ( m_pMoanSound )
  493. {
  494. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, FASTZOMBIE_IDLE_PITCH, 1.0 );
  495. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pMoanSound, 0.75, 1.0 );
  496. }
  497. // Second Layer
  498. if ( m_pLayer2 )
  499. {
  500. ENVELOPE_CONTROLLER.SoundChangePitch( m_pLayer2, 100, 1.0 );
  501. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pLayer2, 0.0, 1.0 );
  502. }
  503. }
  504. //-----------------------------------------------------------------------------
  505. // Purpose: Make the zombie sound pizzled
  506. //-----------------------------------------------------------------------------
  507. void CFastZombie::SetAngrySoundState( void )
  508. {
  509. if (( !m_pMoanSound ) || ( !m_pLayer2 ))
  510. {
  511. return;
  512. }
  513. EmitSound( "NPC_FastZombie.LeapAttack" );
  514. // Main looping sound
  515. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, FASTZOMBIE_MIN_PITCH, 0.5 );
  516. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pMoanSound, 1.0, 0.5 );
  517. // Second Layer
  518. ENVELOPE_CONTROLLER.SoundChangePitch( m_pLayer2, 100, 1.0 );
  519. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pLayer2, 0.0, 1.0 );
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose:
  523. //
  524. //
  525. //-----------------------------------------------------------------------------
  526. void CFastZombie::Spawn( void )
  527. {
  528. Precache();
  529. m_fJustJumped = false;
  530. m_fIsTorso = m_fIsHeadless = false;
  531. if( FClassnameIs( this, "npc_fastzombie" ) )
  532. {
  533. m_fIsTorso = false;
  534. }
  535. else
  536. {
  537. // This was placed as an npc_fastzombie_torso
  538. m_fIsTorso = true;
  539. }
  540. #ifdef HL2_EPISODIC
  541. SetBloodColor( BLOOD_COLOR_ZOMBIE );
  542. #else
  543. SetBloodColor( BLOOD_COLOR_YELLOW );
  544. #endif // HL2_EPISODIC
  545. m_iHealth = 50;
  546. m_flFieldOfView = 0.2;
  547. CapabilitiesClear();
  548. CapabilitiesAdd( bits_CAP_MOVE_CLIMB | bits_CAP_MOVE_JUMP | bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 /* | bits_CAP_INNATE_MELEE_ATTACK1 */);
  549. if ( m_fIsTorso == true )
  550. {
  551. CapabilitiesRemove( bits_CAP_MOVE_JUMP | bits_CAP_INNATE_RANGE_ATTACK1 );
  552. }
  553. m_flNextAttack = gpGlobals->curtime;
  554. m_pLayer2 = NULL;
  555. m_iClimbCount = 0;
  556. EndNavJump();
  557. m_flDistFactor = 1.0;
  558. BaseClass::Spawn();
  559. }
  560. //-----------------------------------------------------------------------------
  561. //-----------------------------------------------------------------------------
  562. void CFastZombie::PostNPCInit( void )
  563. {
  564. SoundInit();
  565. m_flTimeUpdateSound = gpGlobals->curtime;
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose: Returns the classname (ie "npc_headcrab") to spawn when our headcrab bails.
  569. //-----------------------------------------------------------------------------
  570. const char *CFastZombie::GetHeadcrabClassname( void )
  571. {
  572. return "npc_headcrab_fast";
  573. }
  574. const char *CFastZombie::GetHeadcrabModel( void )
  575. {
  576. return "models/headcrab.mdl";
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Purpose:
  580. //-----------------------------------------------------------------------------
  581. float CFastZombie::MaxYawSpeed( void )
  582. {
  583. switch( GetActivity() )
  584. {
  585. case ACT_TURN_LEFT:
  586. case ACT_TURN_RIGHT:
  587. return 120;
  588. break;
  589. case ACT_RUN:
  590. return 160;
  591. break;
  592. case ACT_WALK:
  593. case ACT_IDLE:
  594. return 25;
  595. break;
  596. default:
  597. return 20;
  598. break;
  599. }
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose:
  603. //
  604. //
  605. //-----------------------------------------------------------------------------
  606. void CFastZombie::SetZombieModel( void )
  607. {
  608. Hull_t lastHull = GetHullType();
  609. if ( m_fIsTorso )
  610. {
  611. SetModel( "models/zombie/fast_torso.mdl" );
  612. SetHullType(HULL_TINY);
  613. }
  614. else
  615. {
  616. SetModel( "models/zombie/fast.mdl" );
  617. SetHullType(HULL_HUMAN);
  618. }
  619. SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );
  620. SetHullSizeNormal( true );
  621. SetDefaultEyeOffset();
  622. SetActivity( ACT_IDLE );
  623. // hull changed size, notify vphysics
  624. // UNDONE: Solve this generally, systematically so other
  625. // NPCs can change size
  626. if ( lastHull != GetHullType() )
  627. {
  628. if ( VPhysicsGetObject() )
  629. {
  630. SetupVPhysicsHull();
  631. }
  632. }
  633. }
  634. //-----------------------------------------------------------------------------
  635. // Purpose: Returns the model to use for our legs ragdoll when we are blown in twain.
  636. //-----------------------------------------------------------------------------
  637. const char *CFastZombie::GetLegsModel( void )
  638. {
  639. return s_pLegsModel;
  640. }
  641. const char *CFastZombie::GetTorsoModel( void )
  642. {
  643. return "models/gibs/fast_zombie_torso.mdl";
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Purpose: See if I can swat the player
  647. //
  648. //
  649. //-----------------------------------------------------------------------------
  650. int CFastZombie::MeleeAttack1Conditions( float flDot, float flDist )
  651. {
  652. if ( !GetEnemy() )
  653. {
  654. return COND_NONE;
  655. }
  656. if( !(GetFlags() & FL_ONGROUND) )
  657. {
  658. // Have to be on the ground!
  659. return COND_NONE;
  660. }
  661. if( gpGlobals->curtime < m_flNextMeleeAttack )
  662. {
  663. return COND_NONE;
  664. }
  665. int baseResult = BaseClass::MeleeAttack1Conditions( flDot, flDist );
  666. // @TODO (toml 07-21-04): follow up with Steve to find out why fz was explicitly not using these conditions
  667. if ( baseResult == COND_TOO_FAR_TO_ATTACK || baseResult == COND_NOT_FACING_ATTACK )
  668. {
  669. return COND_NONE;
  670. }
  671. return baseResult;
  672. }
  673. //-----------------------------------------------------------------------------
  674. // Purpose: Returns a moan sound for this class of zombie.
  675. //-----------------------------------------------------------------------------
  676. const char *CFastZombie::GetMoanSound( int nSound )
  677. {
  678. return pMoanSounds[ nSound % ARRAYSIZE( pMoanSounds ) ];
  679. }
  680. //-----------------------------------------------------------------------------
  681. // Purpose: Sound of a footstep
  682. //-----------------------------------------------------------------------------
  683. void CFastZombie::FootstepSound( bool fRightFoot )
  684. {
  685. if( fRightFoot )
  686. {
  687. EmitSound( "NPC_FastZombie.FootstepRight" );
  688. }
  689. else
  690. {
  691. EmitSound( "NPC_FastZombie.FootstepLeft" );
  692. }
  693. }
  694. //-----------------------------------------------------------------------------
  695. // Purpose: Play a random attack hit sound
  696. //-----------------------------------------------------------------------------
  697. void CFastZombie::AttackHitSound( void )
  698. {
  699. EmitSound( "NPC_FastZombie.AttackHit" );
  700. }
  701. //-----------------------------------------------------------------------------
  702. // Purpose: Play a random attack miss sound
  703. //-----------------------------------------------------------------------------
  704. void CFastZombie::AttackMissSound( void )
  705. {
  706. // Play a random attack miss sound
  707. EmitSound( "NPC_FastZombie.AttackMiss" );
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose: Play a random attack sound.
  711. //-----------------------------------------------------------------------------
  712. void CFastZombie::LeapAttackSound( void )
  713. {
  714. EmitSound( "NPC_FastZombie.LeapAttack" );
  715. }
  716. //-----------------------------------------------------------------------------
  717. // Purpose: Play a random attack sound.
  718. //-----------------------------------------------------------------------------
  719. void CFastZombie::AttackSound( void )
  720. {
  721. EmitSound( "NPC_FastZombie.Attack" );
  722. }
  723. //-----------------------------------------------------------------------------
  724. // Purpose: Play a random idle sound.
  725. //-----------------------------------------------------------------------------
  726. void CFastZombie::IdleSound( void )
  727. {
  728. EmitSound( "NPC_FastZombie.Idle" );
  729. MakeAISpookySound( 360.0f );
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Purpose: Play a random pain sound.
  733. //-----------------------------------------------------------------------------
  734. void CFastZombie::PainSound( const CTakeDamageInfo &info )
  735. {
  736. if ( m_pLayer2 )
  737. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pLayer2, SOUNDCTRL_CHANGE_VOLUME, envFastZombieVolumePain, ARRAYSIZE(envFastZombieVolumePain) );
  738. if ( m_pMoanSound )
  739. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pMoanSound, SOUNDCTRL_CHANGE_VOLUME, envFastZombieInverseVolumePain, ARRAYSIZE(envFastZombieInverseVolumePain) );
  740. }
  741. //-----------------------------------------------------------------------------
  742. //-----------------------------------------------------------------------------
  743. void CFastZombie::DeathSound( const CTakeDamageInfo &info )
  744. {
  745. EmitSound( "NPC_FastZombie.Die" );
  746. }
  747. //-----------------------------------------------------------------------------
  748. // Purpose: Play a random alert sound.
  749. //-----------------------------------------------------------------------------
  750. void CFastZombie::AlertSound( void )
  751. {
  752. CBaseEntity *pPlayer = AI_GetSinglePlayer();
  753. if( pPlayer )
  754. {
  755. // Measure how far the player is, and play the appropriate type of alert sound.
  756. // Doesn't matter if I'm getting mad at a different character, the player is the
  757. // one that hears the sound.
  758. float flDist;
  759. flDist = ( GetAbsOrigin() - pPlayer->GetAbsOrigin() ).Length();
  760. if( flDist > 512 )
  761. {
  762. EmitSound( "NPC_FastZombie.AlertFar" );
  763. }
  764. else
  765. {
  766. EmitSound( "NPC_FastZombie.AlertNear" );
  767. }
  768. }
  769. }
  770. //-----------------------------------------------------------------------------
  771. //-----------------------------------------------------------------------------
  772. #define FASTZOMBIE_MINLEAP 200
  773. #define FASTZOMBIE_MAXLEAP 300
  774. float CFastZombie::InnateRange1MaxRange( void )
  775. {
  776. return FASTZOMBIE_MAXLEAP;
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose: See if I can make my leaping attack!!
  780. //
  781. //
  782. //-----------------------------------------------------------------------------
  783. int CFastZombie::RangeAttack1Conditions( float flDot, float flDist )
  784. {
  785. if (GetEnemy() == NULL)
  786. {
  787. return( COND_NONE );
  788. }
  789. if( !(GetFlags() & FL_ONGROUND) )
  790. {
  791. return COND_NONE;
  792. }
  793. if( gpGlobals->curtime < m_flNextAttack )
  794. {
  795. return( COND_NONE );
  796. }
  797. // make sure the enemy isn't on a roof and I'm in the streets (Ravenholm)
  798. float flZDist;
  799. flZDist = fabs( GetEnemy()->GetLocalOrigin().z - GetLocalOrigin().z );
  800. if( flZDist > FASTZOMBIE_MAXLEAP_Z )
  801. {
  802. return COND_TOO_FAR_TO_ATTACK;
  803. }
  804. if( flDist > InnateRange1MaxRange() )
  805. {
  806. return COND_TOO_FAR_TO_ATTACK;
  807. }
  808. if( flDist < FASTZOMBIE_MINLEAP )
  809. {
  810. return COND_NONE;
  811. }
  812. if (flDot < 0.8)
  813. {
  814. return COND_NONE;
  815. }
  816. if ( !IsMoving() )
  817. {
  818. // I Have to be running!!!
  819. return COND_NONE;
  820. }
  821. // Don't jump at the player unless he's facing me.
  822. // This allows the player to get away if he turns and sprints
  823. CBasePlayer *pPlayer = static_cast<CBasePlayer*>( GetEnemy() );
  824. if( pPlayer )
  825. {
  826. // If the enemy is a player, don't attack from behind!
  827. if( !pPlayer->FInViewCone( this ) )
  828. {
  829. return COND_NONE;
  830. }
  831. }
  832. // Drumroll please!
  833. // The final check! Is the path from my position to halfway between me
  834. // and the player clear?
  835. trace_t tr;
  836. Vector vecDirToEnemy;
  837. vecDirToEnemy = GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter();
  838. Vector vecHullMin( -16, -16, -16 );
  839. Vector vecHullMax( 16, 16, 16 );
  840. // only check half the distance. (the first part of the jump)
  841. vecDirToEnemy = vecDirToEnemy * 0.5;
  842. AI_TraceHull( WorldSpaceCenter(), WorldSpaceCenter() + vecDirToEnemy, vecHullMin, vecHullMax, MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr );
  843. if( tr.fraction != 1.0 )
  844. {
  845. // There's some sort of obstacle pretty much right in front of me.
  846. return COND_NONE;
  847. }
  848. return COND_CAN_RANGE_ATTACK1;
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Purpose:
  852. //
  853. //
  854. //-----------------------------------------------------------------------------
  855. void CFastZombie::HandleAnimEvent( animevent_t *pEvent )
  856. {
  857. if ( pEvent->event == AE_FASTZOMBIE_CLIMB_LEFT || pEvent->event == AE_FASTZOMBIE_CLIMB_RIGHT )
  858. {
  859. if( ++m_iClimbCount % 3 == 0 )
  860. {
  861. ENVELOPE_CONTROLLER.SoundChangePitch( m_pLayer2, random->RandomFloat( 100, 150 ), 0.0 );
  862. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pLayer2, SOUNDCTRL_CHANGE_VOLUME, envFastZombieVolumeClimb, ARRAYSIZE(envFastZombieVolumeClimb) );
  863. }
  864. return;
  865. }
  866. if ( pEvent->event == AE_FASTZOMBIE_LEAP )
  867. {
  868. LeapAttack();
  869. return;
  870. }
  871. if ( pEvent->event == AE_FASTZOMBIE_GALLOP_LEFT )
  872. {
  873. EmitSound( "NPC_FastZombie.GallopLeft" );
  874. return;
  875. }
  876. if ( pEvent->event == AE_FASTZOMBIE_GALLOP_RIGHT )
  877. {
  878. EmitSound( "NPC_FastZombie.GallopRight" );
  879. return;
  880. }
  881. if ( pEvent->event == AE_ZOMBIE_ATTACK_RIGHT )
  882. {
  883. Vector right;
  884. AngleVectors( GetLocalAngles(), NULL, &right, NULL );
  885. right = right * -50;
  886. QAngle angle( -3, -5, -3 );
  887. ClawAttack( GetClawAttackRange(), 3, angle, right, ZOMBIE_BLOOD_RIGHT_HAND );
  888. return;
  889. }
  890. if ( pEvent->event == AE_ZOMBIE_ATTACK_LEFT )
  891. {
  892. Vector right;
  893. AngleVectors( GetLocalAngles(), NULL, &right, NULL );
  894. right = right * 50;
  895. QAngle angle( -3, 5, -3 );
  896. ClawAttack( GetClawAttackRange(), 3, angle, right, ZOMBIE_BLOOD_LEFT_HAND );
  897. return;
  898. }
  899. //=============================================================================
  900. #ifdef HL2_EPISODIC
  901. // Do the leap attack
  902. if ( pEvent->event == AE_FASTZOMBIE_VEHICLE_LEAP )
  903. {
  904. VehicleLeapAttack();
  905. return;
  906. }
  907. // Die while doing an SS in a vehicle
  908. if ( pEvent->event == AE_FASTZOMBIE_VEHICLE_SS_DIE )
  909. {
  910. if ( IsInAVehicle() )
  911. {
  912. // Get the vehicle's present speed as a baseline
  913. Vector vecVelocity = vec3_origin;
  914. CBaseEntity *pVehicle = m_PassengerBehavior.GetTargetVehicle();
  915. if ( pVehicle )
  916. {
  917. pVehicle->GetVelocity( &vecVelocity, NULL );
  918. }
  919. // TODO: We need to make this content driven -- jdw
  920. Vector vecForward, vecRight, vecUp;
  921. GetVectors( &vecForward, &vecRight, &vecUp );
  922. vecVelocity += ( vecForward * -2500.0f ) + ( vecRight * 200.0f ) + ( vecUp * 300 );
  923. // Always kill
  924. float flDamage = GetMaxHealth() + 10;
  925. // Take the damage and die
  926. CTakeDamageInfo info( this, this, vecVelocity * 25.0f, WorldSpaceCenter(), flDamage, (DMG_CRUSH|DMG_VEHICLE) );
  927. TakeDamage( info );
  928. }
  929. return;
  930. }
  931. #endif // HL2_EPISODIC
  932. //=============================================================================
  933. BaseClass::HandleAnimEvent( pEvent );
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Purpose: Jump at the enemy!! (stole this from the headcrab)
  937. //
  938. //
  939. //-----------------------------------------------------------------------------
  940. void CFastZombie::LeapAttack( void )
  941. {
  942. SetGroundEntity( NULL );
  943. BeginAttackJump();
  944. LeapAttackSound();
  945. //
  946. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  947. //
  948. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  949. Vector vecJumpDir;
  950. CBaseEntity *pEnemy = GetEnemy();
  951. if ( pEnemy )
  952. {
  953. Vector vecEnemyPos = pEnemy->WorldSpaceCenter();
  954. float gravity = GetCurrentGravity();
  955. if ( gravity <= 1 )
  956. {
  957. gravity = 1;
  958. }
  959. //
  960. // How fast does the zombie need to travel to reach my enemy's eyes given gravity?
  961. //
  962. float height = ( vecEnemyPos.z - GetAbsOrigin().z );
  963. if ( height < 16 )
  964. {
  965. height = 16;
  966. }
  967. else if ( height > 120 )
  968. {
  969. height = 120;
  970. }
  971. float speed = sqrt( 2 * gravity * height );
  972. float time = speed / gravity;
  973. //
  974. // Scale the sideways velocity to get there at the right time
  975. //
  976. vecJumpDir = vecEnemyPos - GetAbsOrigin();
  977. vecJumpDir = vecJumpDir / time;
  978. //
  979. // Speed to offset gravity at the desired height.
  980. //
  981. vecJumpDir.z = speed;
  982. //
  983. // Don't jump too far/fast.
  984. //
  985. #define CLAMP 1000.0
  986. float distance = vecJumpDir.Length();
  987. if ( distance > CLAMP )
  988. {
  989. vecJumpDir = vecJumpDir * ( CLAMP / distance );
  990. }
  991. // try speeding up a bit.
  992. SetAbsVelocity( vecJumpDir );
  993. m_flNextAttack = gpGlobals->curtime + 2;
  994. }
  995. }
  996. //-----------------------------------------------------------------------------
  997. // Purpose:
  998. //-----------------------------------------------------------------------------
  999. void CFastZombie::StartTask( const Task_t *pTask )
  1000. {
  1001. switch( pTask->iTask )
  1002. {
  1003. case TASK_FASTZOMBIE_VERIFY_ATTACK:
  1004. // Simply ensure that the zombie still has a valid melee attack
  1005. if( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  1006. {
  1007. TaskComplete();
  1008. }
  1009. else
  1010. {
  1011. TaskFail("");
  1012. }
  1013. break;
  1014. case TASK_FASTZOMBIE_JUMP_BACK:
  1015. {
  1016. SetActivity( ACT_IDLE );
  1017. SetGroundEntity( NULL );
  1018. BeginAttackJump();
  1019. Vector forward;
  1020. AngleVectors( GetLocalAngles(), &forward );
  1021. //
  1022. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  1023. //
  1024. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  1025. ApplyAbsVelocityImpulse( forward * -200 + Vector( 0, 0, 200 ) );
  1026. }
  1027. break;
  1028. case TASK_FASTZOMBIE_UNSTICK_JUMP:
  1029. {
  1030. SetGroundEntity( NULL );
  1031. // Call begin attack jump. A little bit later if we fail to pathfind, we check
  1032. // this value to see if we just jumped. If so, we assume we've jumped
  1033. // to someplace that's not pathing friendly, and so must jump again to get out.
  1034. BeginAttackJump();
  1035. //
  1036. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  1037. //
  1038. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  1039. CBaseEntity *pEnemy = GetEnemy();
  1040. Vector vecJumpDir;
  1041. if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN )
  1042. {
  1043. // Jump off the pipe backwards!
  1044. Vector forward;
  1045. GetVectors( &forward, NULL, NULL );
  1046. ApplyAbsVelocityImpulse( forward * -200 );
  1047. }
  1048. else if( pEnemy )
  1049. {
  1050. vecJumpDir = pEnemy->GetLocalOrigin() - GetLocalOrigin();
  1051. VectorNormalize( vecJumpDir );
  1052. vecJumpDir.z = 0;
  1053. ApplyAbsVelocityImpulse( vecJumpDir * 300 + Vector( 0, 0, 200 ) );
  1054. }
  1055. else
  1056. {
  1057. DevMsg("UNHANDLED CASE! Stuck Fast Zombie with no enemy!\n");
  1058. }
  1059. }
  1060. break;
  1061. case TASK_WAIT_FOR_MOVEMENT:
  1062. // If we're waiting for movement, that means that pathfinding succeeded, and
  1063. // we're about to be moving. So we aren't stuck. So clear this flag.
  1064. m_fJustJumped = false;
  1065. BaseClass::StartTask( pTask );
  1066. break;
  1067. case TASK_FACE_ENEMY:
  1068. {
  1069. // We don't use the base class implementation of this, because GetTurnActivity
  1070. // stomps our landing scrabble animations (sjb)
  1071. Vector flEnemyLKP = GetEnemyLKP();
  1072. GetMotor()->SetIdealYawToTarget( flEnemyLKP );
  1073. }
  1074. break;
  1075. case TASK_FASTZOMBIE_LAND_RECOVER:
  1076. {
  1077. // Set the ideal yaw
  1078. Vector flEnemyLKP = GetEnemyLKP();
  1079. GetMotor()->SetIdealYawToTarget( flEnemyLKP );
  1080. // figure out which way to turn.
  1081. float flDeltaYaw = GetMotor()->DeltaIdealYaw();
  1082. if( flDeltaYaw < 0 )
  1083. {
  1084. SetIdealActivity( (Activity)ACT_FASTZOMBIE_LAND_RIGHT );
  1085. }
  1086. else
  1087. {
  1088. SetIdealActivity( (Activity)ACT_FASTZOMBIE_LAND_LEFT );
  1089. }
  1090. TaskComplete();
  1091. }
  1092. break;
  1093. case TASK_RANGE_ATTACK1:
  1094. // Make melee attacks impossible until we land!
  1095. m_flNextMeleeAttack = gpGlobals->curtime + 60;
  1096. SetTouch( &CFastZombie::LeapAttackTouch );
  1097. break;
  1098. case TASK_FASTZOMBIE_DO_ATTACK:
  1099. SetActivity( (Activity)ACT_FASTZOMBIE_LEAP_SOAR );
  1100. break;
  1101. default:
  1102. BaseClass::StartTask( pTask );
  1103. break;
  1104. }
  1105. }
  1106. //-----------------------------------------------------------------------------
  1107. // Purpose:
  1108. //-----------------------------------------------------------------------------
  1109. void CFastZombie::RunTask( const Task_t *pTask )
  1110. {
  1111. switch( pTask->iTask )
  1112. {
  1113. case TASK_FASTZOMBIE_JUMP_BACK:
  1114. case TASK_FASTZOMBIE_UNSTICK_JUMP:
  1115. if( GetFlags() & FL_ONGROUND )
  1116. {
  1117. TaskComplete();
  1118. }
  1119. break;
  1120. case TASK_RANGE_ATTACK1:
  1121. if( ( GetFlags() & FL_ONGROUND ) || ( m_pfnTouch == NULL ) )
  1122. {
  1123. // All done when you touch the ground, or if our touch function has somehow cleared.
  1124. TaskComplete();
  1125. // Allow melee attacks again.
  1126. m_flNextMeleeAttack = gpGlobals->curtime + 0.5;
  1127. return;
  1128. }
  1129. break;
  1130. default:
  1131. BaseClass::RunTask( pTask );
  1132. break;
  1133. }
  1134. }
  1135. //---------------------------------------------------------
  1136. //---------------------------------------------------------
  1137. int CFastZombie::TranslateSchedule( int scheduleType )
  1138. {
  1139. switch( scheduleType )
  1140. {
  1141. case SCHED_RANGE_ATTACK1:
  1142. {
  1143. // Scream right now, cause in half a second, we're gonna jump!!
  1144. if( !m_fHasScreamed )
  1145. {
  1146. // Only play that over-the-top attack scream once per combat state.
  1147. EmitSound( "NPC_FastZombie.Scream" );
  1148. m_fHasScreamed = true;
  1149. }
  1150. else
  1151. {
  1152. EmitSound( "NPC_FastZombie.RangeAttack" );
  1153. }
  1154. return SCHED_FASTZOMBIE_RANGE_ATTACK1;
  1155. }
  1156. break;
  1157. case SCHED_MELEE_ATTACK1:
  1158. if ( m_fIsTorso == true )
  1159. {
  1160. return SCHED_FASTZOMBIE_TORSO_MELEE_ATTACK1;
  1161. }
  1162. else
  1163. {
  1164. return SCHED_FASTZOMBIE_MELEE_ATTACK1;
  1165. }
  1166. break;
  1167. case SCHED_FASTZOMBIE_UNSTICK_JUMP:
  1168. if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT )
  1169. {
  1170. return SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP;
  1171. }
  1172. else
  1173. {
  1174. return SCHED_FASTZOMBIE_UNSTICK_JUMP;
  1175. }
  1176. break;
  1177. case SCHED_MOVE_TO_WEAPON_RANGE:
  1178. {
  1179. float flZDist = fabs( GetEnemy()->GetLocalOrigin().z - GetLocalOrigin().z );
  1180. if ( flZDist > FASTZOMBIE_MAXLEAP_Z )
  1181. return SCHED_CHASE_ENEMY;
  1182. else // fall through to default
  1183. return BaseClass::TranslateSchedule( scheduleType );
  1184. break;
  1185. }
  1186. default:
  1187. return BaseClass::TranslateSchedule( scheduleType );
  1188. }
  1189. }
  1190. //---------------------------------------------------------
  1191. //---------------------------------------------------------
  1192. Activity CFastZombie::NPC_TranslateActivity( Activity baseAct )
  1193. {
  1194. if ( baseAct == ACT_CLIMB_DOWN )
  1195. return ACT_CLIMB_UP;
  1196. return BaseClass::NPC_TranslateActivity( baseAct );
  1197. }
  1198. //---------------------------------------------------------
  1199. //---------------------------------------------------------
  1200. void CFastZombie::LeapAttackTouch( CBaseEntity *pOther )
  1201. {
  1202. if ( !pOther->IsSolid() )
  1203. {
  1204. // Touching a trigger or something.
  1205. return;
  1206. }
  1207. // Stop the zombie and knock the player back
  1208. Vector vecNewVelocity( 0, 0, GetAbsVelocity().z );
  1209. SetAbsVelocity( vecNewVelocity );
  1210. Vector forward;
  1211. AngleVectors( GetLocalAngles(), &forward );
  1212. forward *= 500;
  1213. QAngle qaPunch( 15, random->RandomInt(-5,5), random->RandomInt(-5,5) );
  1214. ClawAttack( GetClawAttackRange(), 5, qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS );
  1215. SetTouch( NULL );
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Purpose: Lets us know if we touch the player while we're climbing.
  1219. //-----------------------------------------------------------------------------
  1220. void CFastZombie::ClimbTouch( CBaseEntity *pOther )
  1221. {
  1222. if ( pOther->IsPlayer() )
  1223. {
  1224. // If I hit the player, shove him aside.
  1225. Vector vecDir = pOther->WorldSpaceCenter() - WorldSpaceCenter();
  1226. vecDir.z = 0.0; // planar
  1227. VectorNormalize( vecDir );
  1228. if( IsXbox() )
  1229. {
  1230. vecDir *= 400.0f;
  1231. }
  1232. else
  1233. {
  1234. vecDir *= 200.0f;
  1235. }
  1236. pOther->VelocityPunch( vecDir );
  1237. if ( GetActivity() != ACT_CLIMB_DISMOUNT ||
  1238. ( pOther->GetGroundEntity() == NULL &&
  1239. GetNavigator()->IsGoalActive() &&
  1240. pOther->GetAbsOrigin().z - GetNavigator()->GetCurWaypointPos().z < -1.0 ) )
  1241. {
  1242. SetCondition( COND_FASTZOMBIE_CLIMB_TOUCH );
  1243. }
  1244. SetTouch( NULL );
  1245. }
  1246. else if ( dynamic_cast<CPhysicsProp *>(pOther) )
  1247. {
  1248. NPCPhysics_CreateSolver( this, pOther, true, 5.0 );
  1249. }
  1250. }
  1251. //-----------------------------------------------------------------------------
  1252. // Purpose: Shuts down our looping sounds.
  1253. //-----------------------------------------------------------------------------
  1254. void CFastZombie::StopLoopingSounds( void )
  1255. {
  1256. if ( m_pMoanSound )
  1257. {
  1258. ENVELOPE_CONTROLLER.SoundDestroy( m_pMoanSound );
  1259. m_pMoanSound = NULL;
  1260. }
  1261. if ( m_pLayer2 )
  1262. {
  1263. ENVELOPE_CONTROLLER.SoundDestroy( m_pLayer2 );
  1264. m_pLayer2 = NULL;
  1265. }
  1266. BaseClass::StopLoopingSounds();
  1267. }
  1268. //-----------------------------------------------------------------------------
  1269. // Purpose: Fast zombie cannot range attack when he's a torso!
  1270. //-----------------------------------------------------------------------------
  1271. void CFastZombie::BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce )
  1272. {
  1273. CapabilitiesRemove( bits_CAP_INNATE_RANGE_ATTACK1 );
  1274. CapabilitiesRemove( bits_CAP_MOVE_JUMP );
  1275. CapabilitiesRemove( bits_CAP_MOVE_CLIMB );
  1276. ReleaseHeadcrab( EyePosition(), vecLegsForce * 0.5, true, true, true );
  1277. BaseClass::BecomeTorso( vecTorsoForce, vecLegsForce );
  1278. }
  1279. //-----------------------------------------------------------------------------
  1280. // Purpose: Returns true if a reasonable jumping distance
  1281. // Input :
  1282. // Output :
  1283. //-----------------------------------------------------------------------------
  1284. bool CFastZombie::IsJumpLegal(const Vector &startPos, const Vector &apex, const Vector &endPos) const
  1285. {
  1286. const float MAX_JUMP_RISE = 220.0f;
  1287. const float MAX_JUMP_DISTANCE = 512.0f;
  1288. const float MAX_JUMP_DROP = 384.0f;
  1289. if ( BaseClass::IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DROP, MAX_JUMP_DISTANCE ) )
  1290. {
  1291. // Hang onto the jump distance. The AI is going to want it.
  1292. m_flJumpDist = (startPos - endPos).Length();
  1293. return true;
  1294. }
  1295. return false;
  1296. }
  1297. //-----------------------------------------------------------------------------
  1298. //-----------------------------------------------------------------------------
  1299. bool CFastZombie::MovementCost( int moveType, const Vector &vecStart, const Vector &vecEnd, float *pCost )
  1300. {
  1301. float delta = vecEnd.z - vecStart.z;
  1302. float multiplier = 1;
  1303. if ( moveType == bits_CAP_MOVE_JUMP )
  1304. {
  1305. multiplier = ( delta < 0 ) ? 0.5 : 1.5;
  1306. }
  1307. else if ( moveType == bits_CAP_MOVE_CLIMB )
  1308. {
  1309. multiplier = ( delta > 0 ) ? 0.5 : 4.0;
  1310. }
  1311. *pCost *= multiplier;
  1312. return ( multiplier != 1 );
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. //-----------------------------------------------------------------------------
  1316. bool CFastZombie::ShouldFailNav( bool bMovementFailed )
  1317. {
  1318. if ( !BaseClass::ShouldFailNav( bMovementFailed ) )
  1319. {
  1320. DevMsg( 2, "Fast zombie in scripted sequence probably hit bad node configuration at %s\n", VecToString( GetAbsOrigin() ) );
  1321. if ( GetNavigator()->GetPath()->CurWaypointNavType() == NAV_JUMP && GetNavigator()->RefindPathToGoal( false ) )
  1322. {
  1323. return false;
  1324. }
  1325. DevMsg( 2, "Fast zombie failed to get to scripted sequence\n" );
  1326. }
  1327. return true;
  1328. }
  1329. //---------------------------------------------------------
  1330. // Purpose: Notifier that lets us know when the fast
  1331. // zombie has hit the apex of a navigational jump.
  1332. //---------------------------------------------------------
  1333. void CFastZombie::OnNavJumpHitApex( void )
  1334. {
  1335. m_fHitApex = true; // stop subsequent notifications
  1336. }
  1337. //---------------------------------------------------------
  1338. // Purpose: Overridden to detect when the zombie goes into
  1339. // and out of his climb state and his navigation
  1340. // jump state.
  1341. //---------------------------------------------------------
  1342. void CFastZombie::OnChangeActivity( Activity NewActivity )
  1343. {
  1344. if ( NewActivity == ACT_FASTZOMBIE_FRENZY )
  1345. {
  1346. // Scream!!!!
  1347. EmitSound( "NPC_FastZombie.Frenzy" );
  1348. SetPlaybackRate( random->RandomFloat( .9, 1.1 ) );
  1349. }
  1350. if( NewActivity == ACT_JUMP )
  1351. {
  1352. BeginNavJump();
  1353. }
  1354. else if( GetActivity() == ACT_JUMP )
  1355. {
  1356. EndNavJump();
  1357. }
  1358. if ( NewActivity == ACT_LAND )
  1359. {
  1360. m_flNextAttack = gpGlobals->curtime + 1.0;
  1361. }
  1362. if ( NewActivity == ACT_GLIDE )
  1363. {
  1364. // Started a jump.
  1365. BeginNavJump();
  1366. }
  1367. else if ( GetActivity() == ACT_GLIDE )
  1368. {
  1369. // Landed a jump
  1370. EndNavJump();
  1371. if ( m_pMoanSound )
  1372. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, FASTZOMBIE_MIN_PITCH, 0.3 );
  1373. }
  1374. if ( NewActivity == ACT_CLIMB_UP )
  1375. {
  1376. // Started a climb!
  1377. if ( m_pMoanSound )
  1378. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pMoanSound, 0.0, 0.2 );
  1379. SetTouch( &CFastZombie::ClimbTouch );
  1380. }
  1381. else if ( GetActivity() == ACT_CLIMB_DISMOUNT || ( GetActivity() == ACT_CLIMB_UP && NewActivity != ACT_CLIMB_DISMOUNT ) )
  1382. {
  1383. // Ended a climb
  1384. if ( m_pMoanSound )
  1385. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pMoanSound, 1.0, 0.2 );
  1386. SetTouch( NULL );
  1387. }
  1388. BaseClass::OnChangeActivity( NewActivity );
  1389. }
  1390. //=========================================================
  1391. //
  1392. //=========================================================
  1393. int CFastZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1394. {
  1395. if ( m_fJustJumped )
  1396. {
  1397. // Assume we failed cause we jumped to a bad place.
  1398. m_fJustJumped = false;
  1399. return SCHED_FASTZOMBIE_UNSTICK_JUMP;
  1400. }
  1401. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  1402. }
  1403. //=========================================================
  1404. // Purpose: Do some record keeping for jumps made for
  1405. // navigational purposes (i.e., not attack jumps)
  1406. //=========================================================
  1407. void CFastZombie::BeginNavJump( void )
  1408. {
  1409. m_fIsNavJumping = true;
  1410. m_fHitApex = false;
  1411. ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pLayer2, SOUNDCTRL_CHANGE_VOLUME, envFastZombieVolumeJump, ARRAYSIZE(envFastZombieVolumeJump) );
  1412. }
  1413. //=========================================================
  1414. //
  1415. //=========================================================
  1416. void CFastZombie::EndNavJump( void )
  1417. {
  1418. m_fIsNavJumping = false;
  1419. m_fHitApex = false;
  1420. }
  1421. //=========================================================
  1422. //
  1423. //=========================================================
  1424. void CFastZombie::BeginAttackJump( void )
  1425. {
  1426. // Set this to true. A little bit later if we fail to pathfind, we check
  1427. // this value to see if we just jumped. If so, we assume we've jumped
  1428. // to someplace that's not pathing friendly, and so must jump again to get out.
  1429. m_fJustJumped = true;
  1430. m_flJumpStartAltitude = GetLocalOrigin().z;
  1431. }
  1432. //=========================================================
  1433. //
  1434. //=========================================================
  1435. void CFastZombie::EndAttackJump( void )
  1436. {
  1437. }
  1438. //-----------------------------------------------------------------------------
  1439. // Purpose:
  1440. //-----------------------------------------------------------------------------
  1441. void CFastZombie::BuildScheduleTestBits( void )
  1442. {
  1443. // FIXME: This is probably the desired call to make, but it opts into an untested base class path, we'll need to
  1444. // revisit this and figure out if we want that. -- jdw
  1445. // BaseClass::BuildScheduleTestBits();
  1446. //
  1447. // For now, make sure our active behavior gets a chance to add its own bits
  1448. if ( GetRunningBehavior() )
  1449. GetRunningBehavior()->BridgeBuildScheduleTestBits();
  1450. #ifdef HL2_EPISODIC
  1451. SetCustomInterruptCondition( COND_PROVOKED );
  1452. #endif // HL2_EPISODIC
  1453. // Any schedule that makes us climb should break if we touch player
  1454. if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT)
  1455. {
  1456. SetCustomInterruptCondition( COND_FASTZOMBIE_CLIMB_TOUCH );
  1457. }
  1458. else
  1459. {
  1460. ClearCustomInterruptCondition( COND_FASTZOMBIE_CLIMB_TOUCH );
  1461. }
  1462. }
  1463. //=========================================================
  1464. //
  1465. //=========================================================
  1466. void CFastZombie::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  1467. {
  1468. if( NewState == NPC_STATE_COMBAT )
  1469. {
  1470. SetAngrySoundState();
  1471. }
  1472. else if( (m_pMoanSound) && ( NewState == NPC_STATE_IDLE || NewState == NPC_STATE_ALERT ) ) ///!!!HACKHACK - sjb
  1473. {
  1474. // Don't make this sound while we're slumped
  1475. if ( IsSlumped() == false )
  1476. {
  1477. // Set it up so that if the zombie goes into combat state sometime down the road
  1478. // that he'll be able to scream.
  1479. m_fHasScreamed = false;
  1480. SetIdleSoundState();
  1481. }
  1482. }
  1483. }
  1484. //-----------------------------------------------------------------------------
  1485. //-----------------------------------------------------------------------------
  1486. void CFastZombie::Event_Killed( const CTakeDamageInfo &info )
  1487. {
  1488. // Shut up my screaming sounds.
  1489. CPASAttenuationFilter filter( this );
  1490. EmitSound( filter, entindex(), "NPC_FastZombie.NoSound" );
  1491. CTakeDamageInfo dInfo = info;
  1492. #if 0
  1493. // Become a server-side ragdoll and create a constraint at the hand
  1494. if ( m_PassengerBehavior.GetPassengerState() == PASSENGER_STATE_INSIDE )
  1495. {
  1496. IPhysicsObject *pVehiclePhys = m_PassengerBehavior.GetTargetVehicle()->GetServerVehicle()->GetVehicleEnt()->VPhysicsGetObject();
  1497. CBaseAnimating *pVehicleAnimating = m_PassengerBehavior.GetTargetVehicle()->GetServerVehicle()->GetVehicleEnt()->GetBaseAnimating();
  1498. int nRightHandBone = 31;//GetBaseAnimating()->LookupBone( "ValveBiped.Bip01_R_Finger2" );
  1499. Vector vecRightHandPos;
  1500. QAngle vecRightHandAngle;
  1501. GetAttachment( LookupAttachment( "Blood_Right" ), vecRightHandPos, vecRightHandAngle );
  1502. //CTakeDamageInfo dInfo( GetEnemy(), GetEnemy(), RandomVector( -200, 200 ), WorldSpaceCenter(), 50.0f, DMG_CRUSH );
  1503. dInfo.SetDamageType( info.GetDamageType() | DMG_REMOVENORAGDOLL );
  1504. dInfo.ScaleDamageForce( 10.0f );
  1505. CBaseEntity *pRagdoll = CreateServerRagdoll( GetBaseAnimating(), 0, info, COLLISION_GROUP_DEBRIS );
  1506. /*
  1507. GetBaseAnimating()->GetBonePosition( nRightHandBone, vecRightHandPos, vecRightHandAngle );
  1508. CBaseEntity *pRagdoll = CreateServerRagdollAttached( GetBaseAnimating(),
  1509. vec3_origin,
  1510. -1,
  1511. COLLISION_GROUP_DEBRIS,
  1512. pVehiclePhys,
  1513. pVehicleAnimating,
  1514. 0,
  1515. vecRightHandPos,
  1516. nRightHandBone,
  1517. vec3_origin );*/
  1518. }
  1519. #endif
  1520. BaseClass::Event_Killed( dInfo );
  1521. }
  1522. //-----------------------------------------------------------------------------
  1523. //-----------------------------------------------------------------------------
  1524. bool CFastZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold )
  1525. {
  1526. if( m_fIsTorso )
  1527. {
  1528. // Already split.
  1529. return false;
  1530. }
  1531. // Break in half IF:
  1532. //
  1533. // Take half or more of max health in DMG_BLAST
  1534. if( (info.GetDamageType() & DMG_BLAST) && m_iHealth <= 0 )
  1535. {
  1536. return true;
  1537. }
  1538. return false;
  1539. }
  1540. //=============================================================================
  1541. #ifdef HL2_EPISODIC
  1542. //-----------------------------------------------------------------------------
  1543. // Purpose: Add the passenger behavior to our repertoire
  1544. //-----------------------------------------------------------------------------
  1545. bool CFastZombie::CreateBehaviors( void )
  1546. {
  1547. AddBehavior( &m_PassengerBehavior );
  1548. return BaseClass::CreateBehaviors();
  1549. }
  1550. //-----------------------------------------------------------------------------
  1551. // Purpose: Get on the vehicle!
  1552. //-----------------------------------------------------------------------------
  1553. void CFastZombie::InputAttachToVehicle( inputdata_t &inputdata )
  1554. {
  1555. // Interrupt us
  1556. SetCondition( COND_PROVOKED );
  1557. // Find the target vehicle
  1558. CBaseEntity *pEntity = FindNamedEntity( inputdata.value.String() );
  1559. CPropJeepEpisodic *pVehicle = dynamic_cast<CPropJeepEpisodic *>(pEntity);
  1560. // Get in the car if it's valid
  1561. if ( pVehicle && CanEnterVehicle( pVehicle ) )
  1562. {
  1563. // Set her into a "passenger" behavior
  1564. m_PassengerBehavior.Enable( pVehicle );
  1565. m_PassengerBehavior.AttachToVehicle();
  1566. }
  1567. RemoveSpawnFlags( SF_NPC_GAG );
  1568. }
  1569. //-----------------------------------------------------------------------------
  1570. // Purpose: Passed along from the vehicle's callback list
  1571. //-----------------------------------------------------------------------------
  1572. void CFastZombie::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
  1573. {
  1574. // Only do the override while riding on a vehicle
  1575. if ( m_PassengerBehavior.CanSelectSchedule() && m_PassengerBehavior.GetPassengerState() != PASSENGER_STATE_OUTSIDE )
  1576. {
  1577. int damageType = 0;
  1578. float flDamage = CalculatePhysicsImpactDamage( index, pEvent, gZombiePassengerImpactDamageTable, 1.0, true, damageType );
  1579. if ( flDamage > 0 )
  1580. {
  1581. Vector damagePos;
  1582. pEvent->pInternalData->GetContactPoint( damagePos );
  1583. Vector damageForce = pEvent->postVelocity[index] * pEvent->pObjects[index]->GetMass();
  1584. CTakeDamageInfo info( this, this, damageForce, damagePos, flDamage, (damageType|DMG_VEHICLE) );
  1585. TakeDamage( info );
  1586. }
  1587. return;
  1588. }
  1589. BaseClass::VPhysicsCollision( index, pEvent );
  1590. }
  1591. //-----------------------------------------------------------------------------
  1592. // Purpose: FIXME: Fold this into LeapAttack using different jump targets!
  1593. //-----------------------------------------------------------------------------
  1594. void CFastZombie::VehicleLeapAttack( void )
  1595. {
  1596. CBaseEntity *pEnemy = GetEnemy();
  1597. if ( pEnemy == NULL )
  1598. return;
  1599. Vector vecEnemyPos;
  1600. UTIL_PredictedPosition( pEnemy, 1.0f, &vecEnemyPos );
  1601. // Move
  1602. SetGroundEntity( NULL );
  1603. BeginAttackJump();
  1604. LeapAttackSound();
  1605. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  1606. UTIL_SetOrigin( this, GetLocalOrigin() + Vector( 0 , 0 , 1 ));
  1607. // FIXME: This should be the exact position we'll enter at, but this approximates it generally
  1608. //vecEnemyPos[2] += 16;
  1609. Vector vecMins = GetHullMins();
  1610. Vector vecMaxs = GetHullMaxs();
  1611. Vector vecJumpDir = VecCheckToss( this, GetAbsOrigin(), vecEnemyPos, 0.1f, 1.0f, false, &vecMins, &vecMaxs );
  1612. SetAbsVelocity( vecJumpDir );
  1613. m_flNextAttack = gpGlobals->curtime + 2.0f;
  1614. SetTouch( &CFastZombie::VehicleLeapAttackTouch );
  1615. }
  1616. //-----------------------------------------------------------------------------
  1617. // Purpose:
  1618. // Output : Returns true on success, false on failure.
  1619. //-----------------------------------------------------------------------------
  1620. bool CFastZombie::CanEnterVehicle( CPropJeepEpisodic *pVehicle )
  1621. {
  1622. if ( pVehicle == NULL )
  1623. return false;
  1624. return pVehicle->NPC_CanEnterVehicle( this, false );
  1625. }
  1626. //-----------------------------------------------------------------------------
  1627. // Purpose: FIXME: Move into behavior?
  1628. // Input : *pOther -
  1629. //-----------------------------------------------------------------------------
  1630. void CFastZombie::VehicleLeapAttackTouch( CBaseEntity *pOther )
  1631. {
  1632. if ( pOther->GetServerVehicle() )
  1633. {
  1634. m_PassengerBehavior.AttachToVehicle();
  1635. // HACK: Stop us cold
  1636. SetLocalVelocity( vec3_origin );
  1637. }
  1638. }
  1639. //-----------------------------------------------------------------------------
  1640. // Purpose: Determine whether we're in a vehicle or not
  1641. // Output : Returns true on success, false on failure.
  1642. //-----------------------------------------------------------------------------
  1643. bool CFastZombie::IsInAVehicle( void )
  1644. {
  1645. // Must be active and getting in/out of vehicle
  1646. if ( m_PassengerBehavior.IsEnabled() && m_PassengerBehavior.GetPassengerState() != PASSENGER_STATE_OUTSIDE )
  1647. return true;
  1648. return false;
  1649. }
  1650. //-----------------------------------------------------------------------------
  1651. // Purpose: Override our efficiency so that we don't jitter when we're in the middle
  1652. // of our enter/exit animations.
  1653. // Input : bInPVS - Whether we're in the PVS or not
  1654. //-----------------------------------------------------------------------------
  1655. void CFastZombie::UpdateEfficiency( bool bInPVS )
  1656. {
  1657. // If we're transitioning and in the PVS, we override our efficiency
  1658. if ( IsInAVehicle() && bInPVS )
  1659. {
  1660. PassengerState_e nState = m_PassengerBehavior.GetPassengerState();
  1661. if ( nState == PASSENGER_STATE_ENTERING || nState == PASSENGER_STATE_EXITING )
  1662. {
  1663. SetEfficiency( AIE_NORMAL );
  1664. return;
  1665. }
  1666. }
  1667. // Do the default behavior
  1668. BaseClass::UpdateEfficiency( bInPVS );
  1669. }
  1670. #endif // HL2_EPISODIC
  1671. //=============================================================================
  1672. //-----------------------------------------------------------------------------
  1673. AI_BEGIN_CUSTOM_NPC( npc_fastzombie, CFastZombie )
  1674. DECLARE_ACTIVITY( ACT_FASTZOMBIE_LEAP_SOAR )
  1675. DECLARE_ACTIVITY( ACT_FASTZOMBIE_LEAP_STRIKE )
  1676. DECLARE_ACTIVITY( ACT_FASTZOMBIE_LAND_RIGHT )
  1677. DECLARE_ACTIVITY( ACT_FASTZOMBIE_LAND_LEFT )
  1678. DECLARE_ACTIVITY( ACT_FASTZOMBIE_FRENZY )
  1679. DECLARE_ACTIVITY( ACT_FASTZOMBIE_BIG_SLASH )
  1680. DECLARE_TASK( TASK_FASTZOMBIE_DO_ATTACK )
  1681. DECLARE_TASK( TASK_FASTZOMBIE_LAND_RECOVER )
  1682. DECLARE_TASK( TASK_FASTZOMBIE_UNSTICK_JUMP )
  1683. DECLARE_TASK( TASK_FASTZOMBIE_JUMP_BACK )
  1684. DECLARE_TASK( TASK_FASTZOMBIE_VERIFY_ATTACK )
  1685. DECLARE_CONDITION( COND_FASTZOMBIE_CLIMB_TOUCH )
  1686. //Adrian: events go here
  1687. DECLARE_ANIMEVENT( AE_FASTZOMBIE_LEAP )
  1688. DECLARE_ANIMEVENT( AE_FASTZOMBIE_GALLOP_LEFT )
  1689. DECLARE_ANIMEVENT( AE_FASTZOMBIE_GALLOP_RIGHT )
  1690. DECLARE_ANIMEVENT( AE_FASTZOMBIE_CLIMB_LEFT )
  1691. DECLARE_ANIMEVENT( AE_FASTZOMBIE_CLIMB_RIGHT )
  1692. #ifdef HL2_EPISODIC
  1693. // FIXME: Move!
  1694. DECLARE_ANIMEVENT( AE_PASSENGER_PHYSICS_PUSH )
  1695. DECLARE_ANIMEVENT( AE_FASTZOMBIE_VEHICLE_LEAP )
  1696. DECLARE_ANIMEVENT( AE_FASTZOMBIE_VEHICLE_SS_DIE )
  1697. #endif // HL2_EPISODIC
  1698. //=========================================================
  1699. //
  1700. //=========================================================
  1701. DEFINE_SCHEDULE
  1702. (
  1703. SCHED_FASTZOMBIE_RANGE_ATTACK1,
  1704. " Tasks"
  1705. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_RANGE_ATTACK1"
  1706. " TASK_SET_ACTIVITY ACTIVITY:ACT_FASTZOMBIE_LEAP_STRIKE"
  1707. " TASK_RANGE_ATTACK1 0"
  1708. " TASK_WAIT 0.1"
  1709. " TASK_FASTZOMBIE_LAND_RECOVER 0" // essentially just figure out which way to turn.
  1710. " TASK_FACE_ENEMY 0"
  1711. " "
  1712. " Interrupts"
  1713. )
  1714. //=========================================================
  1715. // I have landed somewhere that's pathfinding-unfriendly
  1716. // just try to jump out.
  1717. //=========================================================
  1718. DEFINE_SCHEDULE
  1719. (
  1720. SCHED_FASTZOMBIE_UNSTICK_JUMP,
  1721. " Tasks"
  1722. " TASK_FASTZOMBIE_UNSTICK_JUMP 0"
  1723. " "
  1724. " Interrupts"
  1725. )
  1726. //=========================================================
  1727. //=========================================================
  1728. DEFINE_SCHEDULE
  1729. (
  1730. SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP,
  1731. " Tasks"
  1732. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  1733. " TASK_FASTZOMBIE_UNSTICK_JUMP 0"
  1734. " "
  1735. " Interrupts"
  1736. )
  1737. //=========================================================
  1738. // > Melee_Attack1
  1739. //=========================================================
  1740. DEFINE_SCHEDULE
  1741. (
  1742. SCHED_FASTZOMBIE_MELEE_ATTACK1,
  1743. " Tasks"
  1744. " TASK_STOP_MOVING 0"
  1745. " TASK_FACE_ENEMY 0"
  1746. " TASK_MELEE_ATTACK1 0"
  1747. " TASK_MELEE_ATTACK1 0"
  1748. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_FASTZOMBIE_FRENZY"
  1749. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  1750. " TASK_FASTZOMBIE_VERIFY_ATTACK 0"
  1751. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_FASTZOMBIE_BIG_SLASH"
  1752. ""
  1753. " Interrupts"
  1754. " COND_NEW_ENEMY"
  1755. " COND_ENEMY_DEAD"
  1756. " COND_ENEMY_OCCLUDED"
  1757. );
  1758. //=========================================================
  1759. // > Melee_Attack1
  1760. //=========================================================
  1761. DEFINE_SCHEDULE
  1762. (
  1763. SCHED_FASTZOMBIE_TORSO_MELEE_ATTACK1,
  1764. " Tasks"
  1765. " TASK_STOP_MOVING 0"
  1766. " TASK_FACE_ENEMY 0"
  1767. " TASK_MELEE_ATTACK1 0"
  1768. " TASK_MELEE_ATTACK1 0"
  1769. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  1770. " TASK_FASTZOMBIE_VERIFY_ATTACK 0"
  1771. ""
  1772. " Interrupts"
  1773. " COND_NEW_ENEMY"
  1774. " COND_ENEMY_DEAD"
  1775. " COND_ENEMY_OCCLUDED"
  1776. );
  1777. AI_END_CUSTOM_NPC()