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.

1001 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A slow-moving, once-human headcrab victim with only melee attacks.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "doors.h"
  8. #include "simtimer.h"
  9. #include "npc_BaseZombie.h"
  10. #include "ai_hull.h"
  11. #include "ai_navigator.h"
  12. #include "ai_memory.h"
  13. #include "gib.h"
  14. #include "soundenvelope.h"
  15. #include "engine/IEngineSound.h"
  16. #include "ammodef.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. // ACT_FLINCH_PHYSICS
  20. ConVar sk_zombie_health( "sk_zombie_health","0");
  21. envelopePoint_t envZombieMoanVolumeFast[] =
  22. {
  23. { 7.0f, 7.0f,
  24. 0.1f, 0.1f,
  25. },
  26. { 0.0f, 0.0f,
  27. 0.2f, 0.3f,
  28. },
  29. };
  30. envelopePoint_t envZombieMoanVolume[] =
  31. {
  32. { 1.0f, 1.0f,
  33. 0.1f, 0.1f,
  34. },
  35. { 1.0f, 1.0f,
  36. 0.2f, 0.2f,
  37. },
  38. { 0.0f, 0.0f,
  39. 0.3f, 0.4f,
  40. },
  41. };
  42. envelopePoint_t envZombieMoanVolumeLong[] =
  43. {
  44. { 1.0f, 1.0f,
  45. 0.3f, 0.5f,
  46. },
  47. { 1.0f, 1.0f,
  48. 0.6f, 1.0f,
  49. },
  50. { 0.0f, 0.0f,
  51. 0.3f, 0.4f,
  52. },
  53. };
  54. envelopePoint_t envZombieMoanIgnited[] =
  55. {
  56. { 1.0f, 1.0f,
  57. 0.5f, 1.0f,
  58. },
  59. { 1.0f, 1.0f,
  60. 30.0f, 30.0f,
  61. },
  62. { 0.0f, 0.0f,
  63. 0.5f, 1.0f,
  64. },
  65. };
  66. //=============================================================================
  67. //=============================================================================
  68. class CZombie : public CAI_BlendingHost<CNPC_BaseZombie>
  69. {
  70. DECLARE_DATADESC();
  71. DECLARE_CLASS( CZombie, CAI_BlendingHost<CNPC_BaseZombie> );
  72. public:
  73. CZombie()
  74. : m_DurationDoorBash( 2, 6),
  75. m_NextTimeToStartDoorBash( 3.0 )
  76. {
  77. }
  78. void Spawn( void );
  79. void Precache( void );
  80. void SetZombieModel( void );
  81. void MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize );
  82. bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold );
  83. bool CanBecomeLiveTorso() { return !m_fIsHeadless; }
  84. void GatherConditions( void );
  85. int SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode );
  86. int TranslateSchedule( int scheduleType );
  87. #ifndef HL2_EPISODIC
  88. void CheckFlinches() {} // Zombie has custom flinch code
  89. #endif // HL2_EPISODIC
  90. Activity NPC_TranslateActivity( Activity newActivity );
  91. void OnStateChange( NPC_STATE OldState, NPC_STATE NewState );
  92. void StartTask( const Task_t *pTask );
  93. void RunTask( const Task_t *pTask );
  94. virtual const char *GetLegsModel( void );
  95. virtual const char *GetTorsoModel( void );
  96. virtual const char *GetHeadcrabClassname( void );
  97. virtual const char *GetHeadcrabModel( void );
  98. virtual bool OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal,
  99. CBaseDoor *pDoor,
  100. float distClear,
  101. AIMoveResult_t *pResult );
  102. Activity SelectDoorBash();
  103. void Ignite( float flFlameLifetime, bool bNPCOnly = true, float flSize = 0.0f, bool bCalledByLevelDesigner = false );
  104. void Extinguish();
  105. int OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo );
  106. bool IsHeavyDamage( const CTakeDamageInfo &info );
  107. bool IsSquashed( const CTakeDamageInfo &info );
  108. void BuildScheduleTestBits( void );
  109. void PrescheduleThink( void );
  110. int SelectSchedule ( void );
  111. void PainSound( const CTakeDamageInfo &info );
  112. void DeathSound( const CTakeDamageInfo &info );
  113. void AlertSound( void );
  114. void IdleSound( void );
  115. void AttackSound( void );
  116. void AttackHitSound( void );
  117. void AttackMissSound( void );
  118. void FootstepSound( bool fRightFoot );
  119. void FootscuffSound( bool fRightFoot );
  120. const char *GetMoanSound( int nSound );
  121. public:
  122. DEFINE_CUSTOM_AI;
  123. protected:
  124. static const char *pMoanSounds[];
  125. private:
  126. CHandle< CBaseDoor > m_hBlockingDoor;
  127. float m_flDoorBashYaw;
  128. CRandSimTimer m_DurationDoorBash;
  129. CSimTimer m_NextTimeToStartDoorBash;
  130. Vector m_vPositionCharged;
  131. };
  132. LINK_ENTITY_TO_CLASS( npc_zombie, CZombie );
  133. LINK_ENTITY_TO_CLASS( npc_zombie_torso, CZombie );
  134. //---------------------------------------------------------
  135. //---------------------------------------------------------
  136. const char *CZombie::pMoanSounds[] =
  137. {
  138. "NPC_BaseZombie.Moan1",
  139. "NPC_BaseZombie.Moan2",
  140. "NPC_BaseZombie.Moan3",
  141. "NPC_BaseZombie.Moan4",
  142. };
  143. //=========================================================
  144. // Conditions
  145. //=========================================================
  146. enum
  147. {
  148. COND_BLOCKED_BY_DOOR = LAST_BASE_ZOMBIE_CONDITION,
  149. COND_DOOR_OPENED,
  150. COND_ZOMBIE_CHARGE_TARGET_MOVED,
  151. };
  152. //=========================================================
  153. // Schedules
  154. //=========================================================
  155. enum
  156. {
  157. SCHED_ZOMBIE_BASH_DOOR = LAST_BASE_ZOMBIE_SCHEDULE,
  158. SCHED_ZOMBIE_WANDER_ANGRILY,
  159. SCHED_ZOMBIE_CHARGE_ENEMY,
  160. SCHED_ZOMBIE_FAIL,
  161. };
  162. //=========================================================
  163. // Tasks
  164. //=========================================================
  165. enum
  166. {
  167. TASK_ZOMBIE_EXPRESS_ANGER = LAST_BASE_ZOMBIE_TASK,
  168. TASK_ZOMBIE_YAW_TO_DOOR,
  169. TASK_ZOMBIE_ATTACK_DOOR,
  170. TASK_ZOMBIE_CHARGE_ENEMY,
  171. };
  172. //-----------------------------------------------------------------------------
  173. int ACT_ZOMBIE_TANTRUM;
  174. int ACT_ZOMBIE_WALLPOUND;
  175. BEGIN_DATADESC( CZombie )
  176. DEFINE_FIELD( m_hBlockingDoor, FIELD_EHANDLE ),
  177. DEFINE_FIELD( m_flDoorBashYaw, FIELD_FLOAT ),
  178. DEFINE_EMBEDDED( m_DurationDoorBash ),
  179. DEFINE_EMBEDDED( m_NextTimeToStartDoorBash ),
  180. DEFINE_FIELD( m_vPositionCharged, FIELD_POSITION_VECTOR ),
  181. END_DATADESC()
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. //-----------------------------------------------------------------------------
  185. void CZombie::Precache( void )
  186. {
  187. BaseClass::Precache();
  188. PrecacheModel( "models/zombie/classic.mdl" );
  189. PrecacheModel( "models/zombie/classic_torso.mdl" );
  190. PrecacheModel( "models/zombie/classic_legs.mdl" );
  191. PrecacheScriptSound( "Zombie.FootstepRight" );
  192. PrecacheScriptSound( "Zombie.FootstepLeft" );
  193. PrecacheScriptSound( "Zombie.FootstepLeft" );
  194. PrecacheScriptSound( "Zombie.ScuffRight" );
  195. PrecacheScriptSound( "Zombie.ScuffLeft" );
  196. PrecacheScriptSound( "Zombie.AttackHit" );
  197. PrecacheScriptSound( "Zombie.AttackMiss" );
  198. PrecacheScriptSound( "Zombie.Pain" );
  199. PrecacheScriptSound( "Zombie.Die" );
  200. PrecacheScriptSound( "Zombie.Alert" );
  201. PrecacheScriptSound( "Zombie.Idle" );
  202. PrecacheScriptSound( "Zombie.Attack" );
  203. PrecacheScriptSound( "NPC_BaseZombie.Moan1" );
  204. PrecacheScriptSound( "NPC_BaseZombie.Moan2" );
  205. PrecacheScriptSound( "NPC_BaseZombie.Moan3" );
  206. PrecacheScriptSound( "NPC_BaseZombie.Moan4" );
  207. }
  208. //-----------------------------------------------------------------------------
  209. //-----------------------------------------------------------------------------
  210. void CZombie::Spawn( void )
  211. {
  212. Precache();
  213. if( FClassnameIs( this, "npc_zombie" ) )
  214. {
  215. m_fIsTorso = false;
  216. }
  217. else
  218. {
  219. // This was placed as an npc_zombie_torso
  220. m_fIsTorso = true;
  221. }
  222. m_fIsHeadless = false;
  223. #ifdef HL2_EPISODIC
  224. SetBloodColor( BLOOD_COLOR_ZOMBIE );
  225. #else
  226. SetBloodColor( BLOOD_COLOR_GREEN );
  227. #endif // HL2_EPISODIC
  228. m_iHealth = sk_zombie_health.GetFloat();
  229. m_flFieldOfView = 0.2;
  230. CapabilitiesClear();
  231. //GetNavigator()->SetRememberStaleNodes( false );
  232. BaseClass::Spawn();
  233. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 4.0 );
  234. }
  235. //-----------------------------------------------------------------------------
  236. //-----------------------------------------------------------------------------
  237. void CZombie::PrescheduleThink( void )
  238. {
  239. if( gpGlobals->curtime > m_flNextMoanSound )
  240. {
  241. if( CanPlayMoanSound() )
  242. {
  243. // Classic guy idles instead of moans.
  244. IdleSound();
  245. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 2.0, 5.0 );
  246. }
  247. else
  248. {
  249. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 2.0 );
  250. }
  251. }
  252. BaseClass::PrescheduleThink();
  253. }
  254. //-----------------------------------------------------------------------------
  255. //-----------------------------------------------------------------------------
  256. int CZombie::SelectSchedule ( void )
  257. {
  258. if( HasCondition( COND_PHYSICS_DAMAGE ) && !m_ActBusyBehavior.IsActive() )
  259. {
  260. return SCHED_FLINCH_PHYSICS;
  261. }
  262. return BaseClass::SelectSchedule();
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: Sound of a footstep
  266. //-----------------------------------------------------------------------------
  267. void CZombie::FootstepSound( bool fRightFoot )
  268. {
  269. if( fRightFoot )
  270. {
  271. EmitSound( "Zombie.FootstepRight" );
  272. }
  273. else
  274. {
  275. EmitSound( "Zombie.FootstepLeft" );
  276. }
  277. }
  278. //-----------------------------------------------------------------------------
  279. // Purpose: Sound of a foot sliding/scraping
  280. //-----------------------------------------------------------------------------
  281. void CZombie::FootscuffSound( bool fRightFoot )
  282. {
  283. if( fRightFoot )
  284. {
  285. EmitSound( "Zombie.ScuffRight" );
  286. }
  287. else
  288. {
  289. EmitSound( "Zombie.ScuffLeft" );
  290. }
  291. }
  292. //-----------------------------------------------------------------------------
  293. // Purpose: Play a random attack hit sound
  294. //-----------------------------------------------------------------------------
  295. void CZombie::AttackHitSound( void )
  296. {
  297. EmitSound( "Zombie.AttackHit" );
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Purpose: Play a random attack miss sound
  301. //-----------------------------------------------------------------------------
  302. void CZombie::AttackMissSound( void )
  303. {
  304. // Play a random attack miss sound
  305. EmitSound( "Zombie.AttackMiss" );
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose:
  309. //-----------------------------------------------------------------------------
  310. void CZombie::PainSound( const CTakeDamageInfo &info )
  311. {
  312. // We're constantly taking damage when we are on fire. Don't make all those noises!
  313. if ( IsOnFire() )
  314. {
  315. return;
  316. }
  317. EmitSound( "Zombie.Pain" );
  318. }
  319. //-----------------------------------------------------------------------------
  320. //-----------------------------------------------------------------------------
  321. void CZombie::DeathSound( const CTakeDamageInfo &info )
  322. {
  323. EmitSound( "Zombie.Die" );
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. //-----------------------------------------------------------------------------
  328. void CZombie::AlertSound( void )
  329. {
  330. EmitSound( "Zombie.Alert" );
  331. // Don't let a moan sound cut off the alert sound.
  332. m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 );
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: Returns a moan sound for this class of zombie.
  336. //-----------------------------------------------------------------------------
  337. const char *CZombie::GetMoanSound( int nSound )
  338. {
  339. return pMoanSounds[ nSound % ARRAYSIZE( pMoanSounds ) ];
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose: Play a random idle sound.
  343. //-----------------------------------------------------------------------------
  344. void CZombie::IdleSound( void )
  345. {
  346. if( GetState() == NPC_STATE_IDLE && random->RandomFloat( 0, 1 ) == 0 )
  347. {
  348. // Moan infrequently in IDLE state.
  349. return;
  350. }
  351. if( IsSlumped() )
  352. {
  353. // Sleeping zombies are quiet.
  354. return;
  355. }
  356. EmitSound( "Zombie.Idle" );
  357. MakeAISpookySound( 360.0f );
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose: Play a random attack sound.
  361. //-----------------------------------------------------------------------------
  362. void CZombie::AttackSound( void )
  363. {
  364. EmitSound( "Zombie.Attack" );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: Returns the classname (ie "npc_headcrab") to spawn when our headcrab bails.
  368. //-----------------------------------------------------------------------------
  369. const char *CZombie::GetHeadcrabClassname( void )
  370. {
  371. return "npc_headcrab";
  372. }
  373. //-----------------------------------------------------------------------------
  374. //-----------------------------------------------------------------------------
  375. const char *CZombie::GetHeadcrabModel( void )
  376. {
  377. return "models/headcrabclassic.mdl";
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose:
  381. //-----------------------------------------------------------------------------
  382. const char *CZombie::GetLegsModel( void )
  383. {
  384. return "models/zombie/classic_legs.mdl";
  385. }
  386. //-----------------------------------------------------------------------------
  387. //-----------------------------------------------------------------------------
  388. const char *CZombie::GetTorsoModel( void )
  389. {
  390. return "models/zombie/classic_torso.mdl";
  391. }
  392. //---------------------------------------------------------
  393. //---------------------------------------------------------
  394. void CZombie::SetZombieModel( void )
  395. {
  396. Hull_t lastHull = GetHullType();
  397. if ( m_fIsTorso )
  398. {
  399. SetModel( "models/zombie/classic_torso.mdl" );
  400. SetHullType( HULL_TINY );
  401. }
  402. else
  403. {
  404. SetModel( "models/zombie/classic.mdl" );
  405. SetHullType( HULL_HUMAN );
  406. }
  407. SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );
  408. SetHullSizeNormal( true );
  409. SetDefaultEyeOffset();
  410. SetActivity( ACT_IDLE );
  411. // hull changed size, notify vphysics
  412. // UNDONE: Solve this generally, systematically so other
  413. // NPCs can change size
  414. if ( lastHull != GetHullType() )
  415. {
  416. if ( VPhysicsGetObject() )
  417. {
  418. SetupVPhysicsHull();
  419. }
  420. }
  421. }
  422. //---------------------------------------------------------
  423. // Classic zombie only uses moan sound if on fire.
  424. //---------------------------------------------------------
  425. void CZombie::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize )
  426. {
  427. if( IsOnFire() )
  428. {
  429. BaseClass::MoanSound( pEnvelope, iEnvelopeSize );
  430. }
  431. }
  432. //---------------------------------------------------------
  433. //---------------------------------------------------------
  434. bool CZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold )
  435. {
  436. if( IsSlumped() )
  437. {
  438. // Never break apart a slouched zombie. This is because the most fun
  439. // slouched zombies to kill are ones sleeping leaning against explosive
  440. // barrels. If you break them in half in the blast, the force of being
  441. // so close to the explosion makes the body pieces fly at ridiculous
  442. // velocities because the pieces weigh less than the whole.
  443. return false;
  444. }
  445. return BaseClass::ShouldBecomeTorso( info, flDamageThreshold );
  446. }
  447. //---------------------------------------------------------
  448. //---------------------------------------------------------
  449. void CZombie::GatherConditions( void )
  450. {
  451. BaseClass::GatherConditions();
  452. static int conditionsToClear[] =
  453. {
  454. COND_BLOCKED_BY_DOOR,
  455. COND_DOOR_OPENED,
  456. COND_ZOMBIE_CHARGE_TARGET_MOVED,
  457. };
  458. ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) );
  459. if ( m_hBlockingDoor == NULL ||
  460. ( m_hBlockingDoor->m_toggle_state == TS_AT_TOP ||
  461. m_hBlockingDoor->m_toggle_state == TS_GOING_UP ) )
  462. {
  463. ClearCondition( COND_BLOCKED_BY_DOOR );
  464. if ( m_hBlockingDoor != NULL )
  465. {
  466. SetCondition( COND_DOOR_OPENED );
  467. m_hBlockingDoor = NULL;
  468. }
  469. }
  470. else
  471. SetCondition( COND_BLOCKED_BY_DOOR );
  472. if ( ConditionInterruptsCurSchedule( COND_ZOMBIE_CHARGE_TARGET_MOVED ) )
  473. {
  474. if ( GetNavigator()->IsGoalActive() )
  475. {
  476. const float CHARGE_RESET_TOLERANCE = 60.0;
  477. if ( !GetEnemy() ||
  478. ( m_vPositionCharged - GetEnemyLKP() ).Length() > CHARGE_RESET_TOLERANCE )
  479. {
  480. SetCondition( COND_ZOMBIE_CHARGE_TARGET_MOVED );
  481. }
  482. }
  483. }
  484. }
  485. //---------------------------------------------------------
  486. //---------------------------------------------------------
  487. int CZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  488. {
  489. if ( HasCondition( COND_BLOCKED_BY_DOOR ) && m_hBlockingDoor != NULL )
  490. {
  491. ClearCondition( COND_BLOCKED_BY_DOOR );
  492. if ( m_NextTimeToStartDoorBash.Expired() && failedSchedule != SCHED_ZOMBIE_BASH_DOOR )
  493. return SCHED_ZOMBIE_BASH_DOOR;
  494. m_hBlockingDoor = NULL;
  495. }
  496. if ( failedSchedule != SCHED_ZOMBIE_CHARGE_ENEMY &&
  497. IsPathTaskFailure( taskFailCode ) &&
  498. random->RandomInt( 1, 100 ) < 50 )
  499. {
  500. return SCHED_ZOMBIE_CHARGE_ENEMY;
  501. }
  502. if ( failedSchedule != SCHED_ZOMBIE_WANDER_ANGRILY &&
  503. ( failedSchedule == SCHED_TAKE_COVER_FROM_ENEMY ||
  504. failedSchedule == SCHED_CHASE_ENEMY_FAILED ) )
  505. {
  506. return SCHED_ZOMBIE_WANDER_ANGRILY;
  507. }
  508. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  509. }
  510. //---------------------------------------------------------
  511. //---------------------------------------------------------
  512. int CZombie::TranslateSchedule( int scheduleType )
  513. {
  514. if ( scheduleType == SCHED_COMBAT_FACE && IsUnreachable( GetEnemy() ) )
  515. return SCHED_TAKE_COVER_FROM_ENEMY;
  516. if ( !m_fIsTorso && scheduleType == SCHED_FAIL )
  517. return SCHED_ZOMBIE_FAIL;
  518. return BaseClass::TranslateSchedule( scheduleType );
  519. }
  520. //---------------------------------------------------------
  521. Activity CZombie::NPC_TranslateActivity( Activity newActivity )
  522. {
  523. newActivity = BaseClass::NPC_TranslateActivity( newActivity );
  524. if ( newActivity == ACT_RUN )
  525. return ACT_WALK;
  526. if ( m_fIsTorso && ( newActivity == ACT_ZOMBIE_TANTRUM ) )
  527. return ACT_IDLE;
  528. return newActivity;
  529. }
  530. //---------------------------------------------------------
  531. //---------------------------------------------------------
  532. void CZombie::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  533. {
  534. BaseClass::OnStateChange( OldState, NewState );
  535. }
  536. //---------------------------------------------------------
  537. //---------------------------------------------------------
  538. void CZombie::StartTask( const Task_t *pTask )
  539. {
  540. switch( pTask->iTask )
  541. {
  542. case TASK_ZOMBIE_EXPRESS_ANGER:
  543. {
  544. if ( random->RandomInt( 1, 4 ) == 2 )
  545. {
  546. SetIdealActivity( (Activity)ACT_ZOMBIE_TANTRUM );
  547. }
  548. else
  549. {
  550. TaskComplete();
  551. }
  552. break;
  553. }
  554. case TASK_ZOMBIE_YAW_TO_DOOR:
  555. {
  556. AssertMsg( m_hBlockingDoor != NULL, "Expected condition handling to break schedule before landing here" );
  557. if ( m_hBlockingDoor != NULL )
  558. {
  559. GetMotor()->SetIdealYaw( m_flDoorBashYaw );
  560. }
  561. TaskComplete();
  562. break;
  563. }
  564. case TASK_ZOMBIE_ATTACK_DOOR:
  565. {
  566. m_DurationDoorBash.Reset();
  567. SetIdealActivity( SelectDoorBash() );
  568. break;
  569. }
  570. case TASK_ZOMBIE_CHARGE_ENEMY:
  571. {
  572. if ( !GetEnemy() )
  573. TaskFail( FAIL_NO_ENEMY );
  574. else if ( GetNavigator()->SetVectorGoalFromTarget( GetEnemy()->GetLocalOrigin() ) )
  575. {
  576. m_vPositionCharged = GetEnemy()->GetLocalOrigin();
  577. TaskComplete();
  578. }
  579. else
  580. TaskFail( FAIL_NO_ROUTE );
  581. break;
  582. }
  583. default:
  584. BaseClass::StartTask( pTask );
  585. break;
  586. }
  587. }
  588. //---------------------------------------------------------
  589. //---------------------------------------------------------
  590. void CZombie::RunTask( const Task_t *pTask )
  591. {
  592. switch( pTask->iTask )
  593. {
  594. case TASK_ZOMBIE_ATTACK_DOOR:
  595. {
  596. if ( IsActivityFinished() )
  597. {
  598. if ( m_DurationDoorBash.Expired() )
  599. {
  600. TaskComplete();
  601. m_NextTimeToStartDoorBash.Reset();
  602. }
  603. else
  604. ResetIdealActivity( SelectDoorBash() );
  605. }
  606. break;
  607. }
  608. case TASK_ZOMBIE_CHARGE_ENEMY:
  609. {
  610. break;
  611. }
  612. case TASK_ZOMBIE_EXPRESS_ANGER:
  613. {
  614. if ( IsActivityFinished() )
  615. {
  616. TaskComplete();
  617. }
  618. break;
  619. }
  620. default:
  621. BaseClass::RunTask( pTask );
  622. break;
  623. }
  624. }
  625. //---------------------------------------------------------
  626. //---------------------------------------------------------
  627. bool CZombie::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor,
  628. float distClear, AIMoveResult_t *pResult )
  629. {
  630. if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) )
  631. {
  632. if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin )
  633. {
  634. m_hBlockingDoor = pDoor;
  635. m_flDoorBashYaw = UTIL_VecToYaw( pMoveGoal->directTrace.vHitNormal * -1 );
  636. }
  637. return true;
  638. }
  639. return false;
  640. }
  641. //---------------------------------------------------------
  642. //---------------------------------------------------------
  643. Activity CZombie::SelectDoorBash()
  644. {
  645. if ( random->RandomInt( 1, 3 ) == 1 )
  646. return ACT_MELEE_ATTACK1;
  647. return (Activity)ACT_ZOMBIE_WALLPOUND;
  648. }
  649. //---------------------------------------------------------
  650. // Zombies should scream continuously while burning, so long
  651. // as they are alive... but NOT IN GERMANY!
  652. //---------------------------------------------------------
  653. void CZombie::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner )
  654. {
  655. if( !IsOnFire() && IsAlive() )
  656. {
  657. BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
  658. if ( !UTIL_IsLowViolence() )
  659. {
  660. RemoveSpawnFlags( SF_NPC_GAG );
  661. MoanSound( envZombieMoanIgnited, ARRAYSIZE( envZombieMoanIgnited ) );
  662. if ( m_pMoanSound )
  663. {
  664. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, 120, 1.0 );
  665. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pMoanSound, 1, 1.0 );
  666. }
  667. }
  668. }
  669. }
  670. //---------------------------------------------------------
  671. // If a zombie stops burning and hasn't died, quiet him down
  672. //---------------------------------------------------------
  673. void CZombie::Extinguish()
  674. {
  675. if( m_pMoanSound )
  676. {
  677. ENVELOPE_CONTROLLER.SoundChangeVolume( m_pMoanSound, 0, 2.0 );
  678. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, 100, 2.0 );
  679. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 2.0, 4.0 );
  680. }
  681. BaseClass::Extinguish();
  682. }
  683. //---------------------------------------------------------
  684. //---------------------------------------------------------
  685. int CZombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  686. {
  687. #ifndef HL2_EPISODIC
  688. if ( inputInfo.GetDamageType() & DMG_BUCKSHOT )
  689. {
  690. if( !m_fIsTorso && inputInfo.GetDamage() > (m_iMaxHealth/3) )
  691. {
  692. // Always flinch if damaged a lot by buckshot, even if not shot in the head.
  693. // The reason for making sure we did at least 1/3rd of the zombie's max health
  694. // is so the zombie doesn't flinch every time the odd shotgun pellet hits them,
  695. // and so the maximum number of times you'll see a zombie flinch like this is 2.(sjb)
  696. AddGesture( ACT_GESTURE_FLINCH_HEAD );
  697. }
  698. }
  699. #endif // HL2_EPISODIC
  700. return BaseClass::OnTakeDamage_Alive( inputInfo );
  701. }
  702. //-----------------------------------------------------------------------------
  703. //-----------------------------------------------------------------------------
  704. bool CZombie::IsHeavyDamage( const CTakeDamageInfo &info )
  705. {
  706. #ifdef HL2_EPISODIC
  707. if ( info.GetDamageType() & DMG_BUCKSHOT )
  708. {
  709. if ( !m_fIsTorso && info.GetDamage() > (m_iMaxHealth/3) )
  710. return true;
  711. }
  712. // Randomly treat all damage as heavy
  713. if ( info.GetDamageType() & (DMG_BULLET | DMG_BUCKSHOT) )
  714. {
  715. // Don't randomly flinch if I'm melee attacking
  716. if ( !HasCondition(COND_CAN_MELEE_ATTACK1) && (RandomFloat() > 0.5) )
  717. {
  718. // Randomly forget I've flinched, so that I'll be forced to play a big flinch
  719. // If this doesn't happen, it means I may not fully flinch if I recently flinched
  720. if ( RandomFloat() > 0.75 )
  721. {
  722. Forget(bits_MEMORY_FLINCHED);
  723. }
  724. return true;
  725. }
  726. }
  727. #endif // HL2_EPISODIC
  728. return BaseClass::IsHeavyDamage(info);
  729. }
  730. //---------------------------------------------------------
  731. //---------------------------------------------------------
  732. #define ZOMBIE_SQUASH_MASS 300.0f // Anything this heavy or heavier squashes a zombie good. (show special fx)
  733. bool CZombie::IsSquashed( const CTakeDamageInfo &info )
  734. {
  735. if( GetHealth() > 0 )
  736. {
  737. return false;
  738. }
  739. if( info.GetDamageType() & DMG_CRUSH )
  740. {
  741. IPhysicsObject *pCrusher = info.GetInflictor()->VPhysicsGetObject();
  742. if( pCrusher && pCrusher->GetMass() >= ZOMBIE_SQUASH_MASS && info.GetInflictor()->WorldSpaceCenter().z > EyePosition().z )
  743. {
  744. // This heuristic detects when a zombie has been squashed from above by a heavy
  745. // item. Done specifically so we can add gore effects to Ravenholm cartraps.
  746. // The zombie must take physics damage from a 300+kg object that is centered above its eyes (comes from above)
  747. return true;
  748. }
  749. }
  750. return false;
  751. }
  752. //---------------------------------------------------------
  753. //---------------------------------------------------------
  754. void CZombie::BuildScheduleTestBits( void )
  755. {
  756. BaseClass::BuildScheduleTestBits();
  757. if( !m_fIsTorso && !IsCurSchedule( SCHED_FLINCH_PHYSICS ) && !m_ActBusyBehavior.IsActive() )
  758. {
  759. SetCustomInterruptCondition( COND_PHYSICS_DAMAGE );
  760. }
  761. }
  762. //=============================================================================
  763. AI_BEGIN_CUSTOM_NPC( npc_zombie, CZombie )
  764. DECLARE_CONDITION( COND_BLOCKED_BY_DOOR )
  765. DECLARE_CONDITION( COND_DOOR_OPENED )
  766. DECLARE_CONDITION( COND_ZOMBIE_CHARGE_TARGET_MOVED )
  767. DECLARE_TASK( TASK_ZOMBIE_EXPRESS_ANGER )
  768. DECLARE_TASK( TASK_ZOMBIE_YAW_TO_DOOR )
  769. DECLARE_TASK( TASK_ZOMBIE_ATTACK_DOOR )
  770. DECLARE_TASK( TASK_ZOMBIE_CHARGE_ENEMY )
  771. DECLARE_ACTIVITY( ACT_ZOMBIE_TANTRUM );
  772. DECLARE_ACTIVITY( ACT_ZOMBIE_WALLPOUND );
  773. DEFINE_SCHEDULE
  774. (
  775. SCHED_ZOMBIE_BASH_DOOR,
  776. " Tasks"
  777. " TASK_SET_ACTIVITY ACTIVITY:ACT_ZOMBIE_TANTRUM"
  778. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_TAKE_COVER_FROM_ENEMY"
  779. " TASK_ZOMBIE_YAW_TO_DOOR 0"
  780. " TASK_FACE_IDEAL 0"
  781. " TASK_ZOMBIE_ATTACK_DOOR 0"
  782. ""
  783. " Interrupts"
  784. " COND_ZOMBIE_RELEASECRAB"
  785. " COND_ENEMY_DEAD"
  786. " COND_NEW_ENEMY"
  787. " COND_DOOR_OPENED"
  788. )
  789. DEFINE_SCHEDULE
  790. (
  791. SCHED_ZOMBIE_WANDER_ANGRILY,
  792. " Tasks"
  793. " TASK_WANDER 480240" // 48 units to 240 units.
  794. " TASK_WALK_PATH 0"
  795. " TASK_WAIT_FOR_MOVEMENT 4"
  796. ""
  797. " Interrupts"
  798. " COND_ZOMBIE_RELEASECRAB"
  799. " COND_ENEMY_DEAD"
  800. " COND_NEW_ENEMY"
  801. " COND_DOOR_OPENED"
  802. )
  803. DEFINE_SCHEDULE
  804. (
  805. SCHED_ZOMBIE_CHARGE_ENEMY,
  806. " Tasks"
  807. " TASK_ZOMBIE_CHARGE_ENEMY 0"
  808. " TASK_WALK_PATH 0"
  809. " TASK_WAIT_FOR_MOVEMENT 0"
  810. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ZOMBIE_TANTRUM" /* placeholder until frustration/rage/fence shake animation available */
  811. ""
  812. " Interrupts"
  813. " COND_ZOMBIE_RELEASECRAB"
  814. " COND_ENEMY_DEAD"
  815. " COND_NEW_ENEMY"
  816. " COND_DOOR_OPENED"
  817. " COND_ZOMBIE_CHARGE_TARGET_MOVED"
  818. )
  819. DEFINE_SCHEDULE
  820. (
  821. SCHED_ZOMBIE_FAIL,
  822. " Tasks"
  823. " TASK_STOP_MOVING 0"
  824. " TASK_SET_ACTIVITY ACTIVITY:ACT_ZOMBIE_TANTRUM"
  825. " TASK_WAIT 1"
  826. " TASK_WAIT_PVS 0"
  827. ""
  828. " Interrupts"
  829. " COND_CAN_RANGE_ATTACK1 "
  830. " COND_CAN_RANGE_ATTACK2 "
  831. " COND_CAN_MELEE_ATTACK1 "
  832. " COND_CAN_MELEE_ATTACK2"
  833. " COND_GIVE_WAY"
  834. " COND_DOOR_OPENED"
  835. )
  836. AI_END_CUSTOM_NPC()
  837. //=============================================================================