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.

1032 lines
25 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Combine Zombie... Zombie Combine... its like a... Zombine... get it?
  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 "ai_squad.h"
  16. #include "soundent.h"
  17. #include "game.h"
  18. #include "npcevent.h"
  19. #include "entitylist.h"
  20. #include "ai_task.h"
  21. #include "activitylist.h"
  22. #include "engine/IEngineSound.h"
  23. #include "npc_BaseZombie.h"
  24. #include "movevars_shared.h"
  25. #include "IEffects.h"
  26. #include "props.h"
  27. #include "physics_npc_solver.h"
  28. #include "hl2_player.h"
  29. #include "hl2_gamerules.h"
  30. #include "basecombatweapon.h"
  31. #include "basegrenade_shared.h"
  32. #include "grenade_frag.h"
  33. #include "ai_interactions.h"
  34. // memdbgon must be the last include file in a .cpp file!!!
  35. #include "tier0/memdbgon.h"
  36. enum
  37. {
  38. SQUAD_SLOT_ZOMBINE_SPRINT1 = LAST_SHARED_SQUADSLOT,
  39. SQUAD_SLOT_ZOMBINE_SPRINT2,
  40. };
  41. #define MIN_SPRINT_TIME 3.5f
  42. #define MAX_SPRINT_TIME 5.5f
  43. #define MIN_SPRINT_DISTANCE 64.0f
  44. #define MAX_SPRINT_DISTANCE 1024.0f
  45. #define SPRINT_CHANCE_VALUE 10
  46. #define SPRINT_CHANCE_VALUE_DARKNESS 50
  47. #define GRENADE_PULL_MAX_DISTANCE 256.0f
  48. #define ZOMBINE_MAX_GRENADES 1
  49. int ACT_ZOMBINE_GRENADE_PULL;
  50. int ACT_ZOMBINE_GRENADE_WALK;
  51. int ACT_ZOMBINE_GRENADE_RUN;
  52. int ACT_ZOMBINE_GRENADE_IDLE;
  53. int ACT_ZOMBINE_ATTACK_FAST;
  54. int ACT_ZOMBINE_GRENADE_FLINCH_BACK;
  55. int ACT_ZOMBINE_GRENADE_FLINCH_FRONT;
  56. int ACT_ZOMBINE_GRENADE_FLINCH_WEST;
  57. int ACT_ZOMBINE_GRENADE_FLINCH_EAST;
  58. int AE_ZOMBINE_PULLPIN;
  59. extern bool IsAlyxInDarknessMode();
  60. ConVar sk_zombie_soldier_health( "sk_zombie_soldier_health","0");
  61. float g_flZombineGrenadeTimes = 0;
  62. class CNPC_Zombine : public CAI_BlendingHost<CNPC_BaseZombie>, public CDefaultPlayerPickupVPhysics
  63. {
  64. DECLARE_DATADESC();
  65. DECLARE_CLASS( CNPC_Zombine, CAI_BlendingHost<CNPC_BaseZombie> );
  66. public:
  67. void Spawn( void );
  68. void Precache( void );
  69. void SetZombieModel( void );
  70. virtual void PrescheduleThink( void );
  71. virtual int SelectSchedule( void );
  72. virtual void BuildScheduleTestBits( void );
  73. virtual void HandleAnimEvent( animevent_t *pEvent );
  74. virtual const char *GetLegsModel( void );
  75. virtual const char *GetTorsoModel( void );
  76. virtual const char *GetHeadcrabClassname( void );
  77. virtual const char *GetHeadcrabModel( void );
  78. virtual void PainSound( const CTakeDamageInfo &info );
  79. virtual void DeathSound( const CTakeDamageInfo &info );
  80. virtual void AlertSound( void );
  81. virtual void IdleSound( void );
  82. virtual void AttackSound( void );
  83. virtual void AttackHitSound( void );
  84. virtual void AttackMissSound( void );
  85. virtual void FootstepSound( bool fRightFoot );
  86. virtual void FootscuffSound( bool fRightFoot );
  87. virtual void MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize );
  88. virtual void Event_Killed( const CTakeDamageInfo &info );
  89. virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
  90. virtual void RunTask( const Task_t *pTask );
  91. virtual int MeleeAttack1Conditions ( float flDot, float flDist );
  92. virtual bool ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold );
  93. virtual void OnScheduleChange ( void );
  94. virtual bool CanRunAScriptedNPCInteraction( bool bForced );
  95. void GatherGrenadeConditions( void );
  96. virtual Activity NPC_TranslateActivity( Activity baseAct );
  97. const char *GetMoanSound( int nSound );
  98. bool AllowedToSprint( void );
  99. void Sprint( bool bMadSprint = false );
  100. void StopSprint( void );
  101. void DropGrenade( Vector vDir );
  102. bool IsSprinting( void ) { return m_flSprintTime > gpGlobals->curtime; }
  103. bool HasGrenade( void ) { return m_hGrenade != NULL; }
  104. int TranslateSchedule( int scheduleType );
  105. void InputStartSprint ( inputdata_t &inputdata );
  106. void InputPullGrenade ( inputdata_t &inputdata );
  107. virtual CBaseEntity *OnFailedPhysGunPickup ( Vector vPhysgunPos );
  108. //Called when we want to let go of a grenade and let the physcannon pick it up.
  109. void ReleaseGrenade( Vector vPhysgunPos );
  110. virtual bool HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt );
  111. enum
  112. {
  113. COND_ZOMBINE_GRENADE = LAST_BASE_ZOMBIE_CONDITION,
  114. };
  115. enum
  116. {
  117. SCHED_ZOMBINE_PULL_GRENADE = LAST_BASE_ZOMBIE_SCHEDULE,
  118. };
  119. public:
  120. DEFINE_CUSTOM_AI;
  121. private:
  122. float m_flSprintTime;
  123. float m_flSprintRestTime;
  124. float m_flSuperFastAttackTime;
  125. float m_flGrenadePullTime;
  126. int m_iGrenadeCount;
  127. EHANDLE m_hGrenade;
  128. protected:
  129. static const char *pMoanSounds[];
  130. };
  131. LINK_ENTITY_TO_CLASS( npc_zombine, CNPC_Zombine );
  132. BEGIN_DATADESC( CNPC_Zombine )
  133. DEFINE_FIELD( m_flSprintTime, FIELD_TIME ),
  134. DEFINE_FIELD( m_flSprintRestTime, FIELD_TIME ),
  135. DEFINE_FIELD( m_flSuperFastAttackTime, FIELD_TIME ),
  136. DEFINE_FIELD( m_hGrenade, FIELD_EHANDLE ),
  137. DEFINE_FIELD( m_flGrenadePullTime, FIELD_TIME ),
  138. DEFINE_FIELD( m_iGrenadeCount, FIELD_INTEGER ),
  139. DEFINE_INPUTFUNC( FIELD_VOID, "StartSprint", InputStartSprint ),
  140. DEFINE_INPUTFUNC( FIELD_VOID, "PullGrenade", InputPullGrenade ),
  141. END_DATADESC()
  142. //---------------------------------------------------------
  143. //---------------------------------------------------------
  144. const char *CNPC_Zombine::pMoanSounds[] =
  145. {
  146. "ATV_engine_null",
  147. };
  148. void CNPC_Zombine::Spawn( void )
  149. {
  150. Precache();
  151. m_fIsTorso = false;
  152. m_fIsHeadless = false;
  153. #ifdef HL2_EPISODIC
  154. SetBloodColor( BLOOD_COLOR_ZOMBIE );
  155. #else
  156. SetBloodColor( BLOOD_COLOR_GREEN );
  157. #endif // HL2_EPISODIC
  158. m_iHealth = sk_zombie_soldier_health.GetFloat();
  159. SetMaxHealth( m_iHealth );
  160. m_flFieldOfView = 0.2;
  161. CapabilitiesClear();
  162. BaseClass::Spawn();
  163. m_flSprintTime = 0.0f;
  164. m_flSprintRestTime = 0.0f;
  165. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 1.0, 4.0 );
  166. g_flZombineGrenadeTimes = gpGlobals->curtime;
  167. m_flGrenadePullTime = gpGlobals->curtime;
  168. m_iGrenadeCount = ZOMBINE_MAX_GRENADES;
  169. }
  170. void CNPC_Zombine::Precache( void )
  171. {
  172. BaseClass::Precache();
  173. PrecacheModel( "models/zombie/zombie_soldier.mdl" );
  174. PrecacheScriptSound( "Zombie.FootstepRight" );
  175. PrecacheScriptSound( "Zombie.FootstepLeft" );
  176. PrecacheScriptSound( "Zombine.ScuffRight" );
  177. PrecacheScriptSound( "Zombine.ScuffLeft" );
  178. PrecacheScriptSound( "Zombie.AttackHit" );
  179. PrecacheScriptSound( "Zombie.AttackMiss" );
  180. PrecacheScriptSound( "Zombine.Pain" );
  181. PrecacheScriptSound( "Zombine.Die" );
  182. PrecacheScriptSound( "Zombine.Alert" );
  183. PrecacheScriptSound( "Zombine.Idle" );
  184. PrecacheScriptSound( "Zombine.ReadyGrenade" );
  185. PrecacheScriptSound( "ATV_engine_null" );
  186. PrecacheScriptSound( "Zombine.Charge" );
  187. PrecacheScriptSound( "Zombie.Attack" );
  188. }
  189. void CNPC_Zombine::SetZombieModel( void )
  190. {
  191. SetModel( "models/zombie/zombie_soldier.mdl" );
  192. SetHullType( HULL_HUMAN );
  193. SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );
  194. SetHullSizeNormal( true );
  195. SetDefaultEyeOffset();
  196. SetActivity( ACT_IDLE );
  197. }
  198. void CNPC_Zombine::PrescheduleThink( void )
  199. {
  200. GatherGrenadeConditions();
  201. if( gpGlobals->curtime > m_flNextMoanSound )
  202. {
  203. if( CanPlayMoanSound() )
  204. {
  205. // Classic guy idles instead of moans.
  206. IdleSound();
  207. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 10.0, 15.0 );
  208. }
  209. else
  210. {
  211. m_flNextMoanSound = gpGlobals->curtime + random->RandomFloat( 2.5, 5.0 );
  212. }
  213. }
  214. if ( HasGrenade () )
  215. {
  216. CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetSmoothedVelocity() * 0.5f , 256, 0.1, this, SOUNDENT_CHANNEL_ZOMBINE_GRENADE );
  217. if( IsSprinting() && GetEnemy() && GetEnemy()->Classify() == CLASS_PLAYER_ALLY_VITAL && HasCondition( COND_SEE_ENEMY ) )
  218. {
  219. if( GetAbsOrigin().DistToSqr(GetEnemy()->GetAbsOrigin()) < Square( 144 ) )
  220. {
  221. StopSprint();
  222. }
  223. }
  224. }
  225. BaseClass::PrescheduleThink();
  226. }
  227. void CNPC_Zombine::OnScheduleChange( void )
  228. {
  229. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) && IsSprinting() == true )
  230. {
  231. m_flSuperFastAttackTime = gpGlobals->curtime + 1.0f;
  232. }
  233. BaseClass::OnScheduleChange();
  234. }
  235. bool CNPC_Zombine::CanRunAScriptedNPCInteraction( bool bForced )
  236. {
  237. if ( HasGrenade() == true )
  238. return false;
  239. return BaseClass::CanRunAScriptedNPCInteraction( bForced );
  240. }
  241. int CNPC_Zombine::SelectSchedule( void )
  242. {
  243. if ( GetHealth() <= 0 )
  244. return BaseClass::SelectSchedule();
  245. if ( HasCondition( COND_ZOMBINE_GRENADE ) )
  246. {
  247. ClearCondition( COND_ZOMBINE_GRENADE );
  248. return SCHED_ZOMBINE_PULL_GRENADE;
  249. }
  250. return BaseClass::SelectSchedule();
  251. }
  252. void CNPC_Zombine::BuildScheduleTestBits( void )
  253. {
  254. BaseClass::BuildScheduleTestBits();
  255. SetCustomInterruptCondition( COND_ZOMBINE_GRENADE );
  256. }
  257. Activity CNPC_Zombine::NPC_TranslateActivity( Activity baseAct )
  258. {
  259. if ( baseAct == ACT_MELEE_ATTACK1 )
  260. {
  261. if ( m_flSuperFastAttackTime > gpGlobals->curtime || HasGrenade() )
  262. {
  263. return (Activity)ACT_ZOMBINE_ATTACK_FAST;
  264. }
  265. }
  266. if ( baseAct == ACT_IDLE )
  267. {
  268. if ( HasGrenade() )
  269. {
  270. return (Activity)ACT_ZOMBINE_GRENADE_IDLE;
  271. }
  272. }
  273. return BaseClass::NPC_TranslateActivity( baseAct );
  274. }
  275. int CNPC_Zombine::MeleeAttack1Conditions ( float flDot, float flDist )
  276. {
  277. int iBase = BaseClass::MeleeAttack1Conditions( flDot, flDist );
  278. if( HasGrenade() )
  279. {
  280. //Adrian: stop spriting if we get close enough to melee and we have a grenade
  281. //this gives NPCs time to move away from you (before it was almost impossible cause of the high sprint speed)
  282. if ( iBase == COND_CAN_MELEE_ATTACK1 )
  283. {
  284. StopSprint();
  285. }
  286. }
  287. return iBase;
  288. }
  289. void CNPC_Zombine::GatherGrenadeConditions( void )
  290. {
  291. if ( m_iGrenadeCount <= 0 )
  292. return;
  293. if ( g_flZombineGrenadeTimes > gpGlobals->curtime )
  294. return;
  295. if ( m_flGrenadePullTime > gpGlobals->curtime )
  296. return;
  297. if ( m_flSuperFastAttackTime >= gpGlobals->curtime )
  298. return;
  299. if ( HasGrenade() )
  300. return;
  301. if ( GetEnemy() == NULL )
  302. return;
  303. if ( FVisible( GetEnemy() ) == false )
  304. return;
  305. if ( IsSprinting() )
  306. return;
  307. if ( IsOnFire() )
  308. return;
  309. if ( IsRunningDynamicInteraction() == true )
  310. return;
  311. if ( m_ActBusyBehavior.IsActive() )
  312. return;
  313. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  314. if ( pPlayer && pPlayer->FVisible( this ) )
  315. {
  316. float flLengthToPlayer = (pPlayer->GetAbsOrigin() - GetAbsOrigin()).Length();
  317. float flLengthToEnemy = flLengthToPlayer;
  318. if ( pPlayer != GetEnemy() )
  319. {
  320. flLengthToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin()).Length();
  321. }
  322. if ( flLengthToPlayer <= GRENADE_PULL_MAX_DISTANCE && flLengthToEnemy <= GRENADE_PULL_MAX_DISTANCE )
  323. {
  324. float flPullChance = 1.0f - ( flLengthToEnemy / GRENADE_PULL_MAX_DISTANCE );
  325. m_flGrenadePullTime = gpGlobals->curtime + 0.5f;
  326. if ( flPullChance >= random->RandomFloat( 0.0f, 1.0f ) )
  327. {
  328. g_flZombineGrenadeTimes = gpGlobals->curtime + 10.0f;
  329. SetCondition( COND_ZOMBINE_GRENADE );
  330. }
  331. }
  332. }
  333. }
  334. int CNPC_Zombine::TranslateSchedule( int scheduleType )
  335. {
  336. return BaseClass::TranslateSchedule( scheduleType );
  337. }
  338. void CNPC_Zombine::DropGrenade( Vector vDir )
  339. {
  340. if ( m_hGrenade == NULL )
  341. return;
  342. m_hGrenade->SetParent( NULL );
  343. m_hGrenade->SetOwnerEntity( NULL );
  344. Vector vGunPos;
  345. QAngle angles;
  346. GetAttachment( "grenade_attachment", vGunPos, angles );
  347. IPhysicsObject *pPhysObj = m_hGrenade->VPhysicsGetObject();
  348. if ( pPhysObj == NULL )
  349. {
  350. m_hGrenade->SetMoveType( MOVETYPE_VPHYSICS );
  351. m_hGrenade->SetSolid( SOLID_VPHYSICS );
  352. m_hGrenade->SetCollisionGroup( COLLISION_GROUP_WEAPON );
  353. m_hGrenade->CreateVPhysics();
  354. }
  355. if ( pPhysObj )
  356. {
  357. pPhysObj->Wake();
  358. pPhysObj->SetPosition( vGunPos, angles, true );
  359. pPhysObj->ApplyForceCenter( vDir * 0.2f );
  360. pPhysObj->RecheckCollisionFilter();
  361. }
  362. m_hGrenade = NULL;
  363. }
  364. void CNPC_Zombine::Event_Killed( const CTakeDamageInfo &info )
  365. {
  366. BaseClass::Event_Killed( info );
  367. if ( HasGrenade() )
  368. {
  369. DropGrenade( vec3_origin );
  370. }
  371. }
  372. //-----------------------------------------------------------------------------
  373. // Purpose: This is a generic function (to be implemented by sub-classes) to
  374. // handle specific interactions between different types of characters
  375. // (For example the barnacle grabbing an NPC)
  376. // Input : Constant for the type of interaction
  377. // Output : true - if sub-class has a response for the interaction
  378. // false - if sub-class has no response
  379. //-----------------------------------------------------------------------------
  380. bool CNPC_Zombine::HandleInteraction( int interactionType, void *data, CBaseCombatCharacter *sourceEnt )
  381. {
  382. if ( interactionType == g_interactionBarnacleVictimGrab )
  383. {
  384. if ( HasGrenade() )
  385. {
  386. DropGrenade( vec3_origin );
  387. }
  388. }
  389. return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
  390. }
  391. void CNPC_Zombine::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  392. {
  393. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  394. //Only knock grenades off their hands if it's a player doing the damage.
  395. if ( info.GetAttacker() && info.GetAttacker()->IsNPC() )
  396. return;
  397. if ( info.GetDamageType() & ( DMG_BULLET | DMG_CLUB ) )
  398. {
  399. if ( ptr->hitgroup == HITGROUP_LEFTARM )
  400. {
  401. if ( HasGrenade() )
  402. {
  403. DropGrenade( info.GetDamageForce() );
  404. StopSprint();
  405. }
  406. }
  407. }
  408. }
  409. void CNPC_Zombine::HandleAnimEvent( animevent_t *pEvent )
  410. {
  411. if ( pEvent->event == AE_ZOMBINE_PULLPIN )
  412. {
  413. Vector vecStart;
  414. QAngle angles;
  415. GetAttachment( "grenade_attachment", vecStart, angles );
  416. CBaseGrenade *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vec3_origin, AngularImpulse( 0, 0, 0 ), this, 3.5f, true );
  417. if ( pGrenade )
  418. {
  419. // Move physobject to shadow
  420. IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
  421. if ( pPhysicsObject )
  422. {
  423. pGrenade->VPhysicsDestroyObject();
  424. int iAttachment = LookupAttachment( "grenade_attachment");
  425. pGrenade->SetMoveType( MOVETYPE_NONE );
  426. pGrenade->SetSolid( SOLID_NONE );
  427. pGrenade->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  428. pGrenade->SetAbsOrigin( vecStart );
  429. pGrenade->SetAbsAngles( angles );
  430. pGrenade->SetParent( this, iAttachment );
  431. pGrenade->SetDamage( 200.0f );
  432. m_hGrenade = pGrenade;
  433. EmitSound( "Zombine.ReadyGrenade" );
  434. // Tell player allies nearby to regard me!
  435. CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs();
  436. CAI_BaseNPC *pNPC;
  437. for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ )
  438. {
  439. pNPC = ppAIs[i];
  440. if( pNPC->Classify() == CLASS_PLAYER_ALLY || ( pNPC->Classify() == CLASS_PLAYER_ALLY_VITAL && pNPC->FVisible(this) ) )
  441. {
  442. int priority;
  443. Disposition_t disposition;
  444. priority = pNPC->IRelationPriority(this);
  445. disposition = pNPC->IRelationType(this);
  446. pNPC->AddEntityRelationship( this, disposition, priority + 1 );
  447. }
  448. }
  449. }
  450. m_iGrenadeCount--;
  451. }
  452. return;
  453. }
  454. if ( pEvent->event == AE_NPC_ATTACK_BROADCAST )
  455. {
  456. if ( HasGrenade() )
  457. return;
  458. }
  459. BaseClass::HandleAnimEvent( pEvent );
  460. }
  461. bool CNPC_Zombine::AllowedToSprint( void )
  462. {
  463. if ( IsOnFire() )
  464. return false;
  465. //If you're sprinting then there's no reason to sprint again.
  466. if ( IsSprinting() )
  467. return false;
  468. int iChance = SPRINT_CHANCE_VALUE;
  469. CHL2_Player *pPlayer = dynamic_cast <CHL2_Player*> ( AI_GetSinglePlayer() );
  470. if ( pPlayer )
  471. {
  472. if ( HL2GameRules()->IsAlyxInDarknessMode() && pPlayer->FlashlightIsOn() == false )
  473. {
  474. iChance = SPRINT_CHANCE_VALUE_DARKNESS;
  475. }
  476. //Bigger chance of this happening if the player is not looking at the zombie
  477. if ( pPlayer->FInViewCone( this ) == false )
  478. {
  479. iChance *= 2;
  480. }
  481. }
  482. if ( HasGrenade() )
  483. {
  484. iChance *= 4;
  485. }
  486. //Below 25% health they'll always sprint
  487. if ( ( GetHealth() > GetMaxHealth() * 0.5f ) )
  488. {
  489. if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 ) == true )
  490. return false;
  491. if ( random->RandomInt( 0, 100 ) > iChance )
  492. return false;
  493. if ( m_flSprintRestTime > gpGlobals->curtime )
  494. return false;
  495. }
  496. float flLength = ( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() ).Length();
  497. if ( flLength > MAX_SPRINT_DISTANCE )
  498. return false;
  499. return true;
  500. }
  501. void CNPC_Zombine::StopSprint( void )
  502. {
  503. GetNavigator()->SetMovementActivity( ACT_WALK );
  504. m_flSprintTime = gpGlobals->curtime;
  505. m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f );
  506. }
  507. void CNPC_Zombine::Sprint( bool bMadSprint )
  508. {
  509. if ( IsSprinting() )
  510. return;
  511. OccupyStrategySlotRange( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 );
  512. GetNavigator()->SetMovementActivity( ACT_RUN );
  513. float flSprintTime = random->RandomFloat( MIN_SPRINT_TIME, MAX_SPRINT_TIME );
  514. //If holding a grenade then sprint until it blows up.
  515. if ( HasGrenade() || bMadSprint == true )
  516. {
  517. flSprintTime = 9999;
  518. }
  519. m_flSprintTime = gpGlobals->curtime + flSprintTime;
  520. //Don't sprint for this long after I'm done with this sprint run.
  521. m_flSprintRestTime = m_flSprintTime + random->RandomFloat( 2.5f, 5.0f );
  522. EmitSound( "Zombine.Charge" );
  523. }
  524. void CNPC_Zombine::RunTask( const Task_t *pTask )
  525. {
  526. switch ( pTask->iTask )
  527. {
  528. case TASK_WAIT_FOR_MOVEMENT_STEP:
  529. case TASK_WAIT_FOR_MOVEMENT:
  530. {
  531. BaseClass::RunTask( pTask );
  532. if ( IsOnFire() && IsSprinting() )
  533. {
  534. StopSprint();
  535. }
  536. //Only do this if I have an enemy
  537. if ( GetEnemy() )
  538. {
  539. if ( AllowedToSprint() == true )
  540. {
  541. Sprint( ( GetHealth() <= GetMaxHealth() * 0.5f ) );
  542. return;
  543. }
  544. if ( HasGrenade() )
  545. {
  546. if ( IsSprinting() )
  547. {
  548. GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_RUN );
  549. }
  550. else
  551. {
  552. GetNavigator()->SetMovementActivity( (Activity)ACT_ZOMBINE_GRENADE_WALK );
  553. }
  554. return;
  555. }
  556. if ( GetNavigator()->GetMovementActivity() != ACT_WALK )
  557. {
  558. if ( IsSprinting() == false )
  559. {
  560. GetNavigator()->SetMovementActivity( ACT_WALK );
  561. }
  562. }
  563. }
  564. else
  565. {
  566. GetNavigator()->SetMovementActivity( ACT_WALK );
  567. }
  568. break;
  569. }
  570. default:
  571. {
  572. BaseClass::RunTask( pTask );
  573. break;
  574. }
  575. }
  576. }
  577. void CNPC_Zombine::InputStartSprint ( inputdata_t &inputdata )
  578. {
  579. Sprint();
  580. }
  581. void CNPC_Zombine::InputPullGrenade ( inputdata_t &inputdata )
  582. {
  583. g_flZombineGrenadeTimes = gpGlobals->curtime + 5.0f;
  584. SetCondition( COND_ZOMBINE_GRENADE );
  585. }
  586. //-----------------------------------------------------------------------------
  587. // Purpose: Returns a moan sound for this class of zombie.
  588. //-----------------------------------------------------------------------------
  589. const char *CNPC_Zombine::GetMoanSound( int nSound )
  590. {
  591. return pMoanSounds[ nSound % ARRAYSIZE( pMoanSounds ) ];
  592. }
  593. //-----------------------------------------------------------------------------
  594. // Purpose: Sound of a footstep
  595. //-----------------------------------------------------------------------------
  596. void CNPC_Zombine::FootstepSound( bool fRightFoot )
  597. {
  598. if( fRightFoot )
  599. {
  600. EmitSound( "Zombie.FootstepRight" );
  601. }
  602. else
  603. {
  604. EmitSound( "Zombie.FootstepLeft" );
  605. }
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose: Overloaded so that explosions don't split the zombine in twain.
  609. //-----------------------------------------------------------------------------
  610. bool CNPC_Zombine::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold )
  611. {
  612. return false;
  613. }
  614. //-----------------------------------------------------------------------------
  615. // Purpose: Sound of a foot sliding/scraping
  616. //-----------------------------------------------------------------------------
  617. void CNPC_Zombine::FootscuffSound( bool fRightFoot )
  618. {
  619. if( fRightFoot )
  620. {
  621. EmitSound( "Zombine.ScuffRight" );
  622. }
  623. else
  624. {
  625. EmitSound( "Zombine.ScuffLeft" );
  626. }
  627. }
  628. //-----------------------------------------------------------------------------
  629. // Purpose: Play a random attack hit sound
  630. //-----------------------------------------------------------------------------
  631. void CNPC_Zombine::AttackHitSound( void )
  632. {
  633. EmitSound( "Zombie.AttackHit" );
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose: Play a random attack miss sound
  637. //-----------------------------------------------------------------------------
  638. void CNPC_Zombine::AttackMissSound( void )
  639. {
  640. // Play a random attack miss sound
  641. EmitSound( "Zombie.AttackMiss" );
  642. }
  643. //-----------------------------------------------------------------------------
  644. // Purpose:
  645. //-----------------------------------------------------------------------------
  646. void CNPC_Zombine::PainSound( const CTakeDamageInfo &info )
  647. {
  648. // We're constantly taking damage when we are on fire. Don't make all those noises!
  649. if ( IsOnFire() )
  650. {
  651. return;
  652. }
  653. EmitSound( "Zombine.Pain" );
  654. }
  655. //-----------------------------------------------------------------------------
  656. //-----------------------------------------------------------------------------
  657. void CNPC_Zombine::DeathSound( const CTakeDamageInfo &info )
  658. {
  659. EmitSound( "Zombine.Die" );
  660. }
  661. //-----------------------------------------------------------------------------
  662. // Purpose:
  663. //-----------------------------------------------------------------------------
  664. void CNPC_Zombine::AlertSound( void )
  665. {
  666. EmitSound( "Zombine.Alert" );
  667. // Don't let a moan sound cut off the alert sound.
  668. m_flNextMoanSound += random->RandomFloat( 2.0, 4.0 );
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose: Play a random idle sound.
  672. //-----------------------------------------------------------------------------
  673. void CNPC_Zombine::IdleSound( void )
  674. {
  675. if( GetState() == NPC_STATE_IDLE && random->RandomFloat( 0, 1 ) == 0 )
  676. {
  677. // Moan infrequently in IDLE state.
  678. return;
  679. }
  680. if( IsSlumped() )
  681. {
  682. // Sleeping zombies are quiet.
  683. return;
  684. }
  685. EmitSound( "Zombine.Idle" );
  686. MakeAISpookySound( 360.0f );
  687. }
  688. //-----------------------------------------------------------------------------
  689. // Purpose: Play a random attack sound.
  690. //-----------------------------------------------------------------------------
  691. void CNPC_Zombine::AttackSound( void )
  692. {
  693. }
  694. //-----------------------------------------------------------------------------
  695. //-----------------------------------------------------------------------------
  696. const char *CNPC_Zombine::GetHeadcrabModel( void )
  697. {
  698. return "models/headcrabclassic.mdl";
  699. }
  700. //-----------------------------------------------------------------------------
  701. // Purpose:
  702. //-----------------------------------------------------------------------------
  703. const char *CNPC_Zombine::GetLegsModel( void )
  704. {
  705. return "models/zombie/zombie_soldier_legs.mdl";
  706. }
  707. //-----------------------------------------------------------------------------
  708. //-----------------------------------------------------------------------------
  709. const char *CNPC_Zombine::GetTorsoModel( void )
  710. {
  711. return "models/zombie/zombie_soldier_torso.mdl";
  712. }
  713. //---------------------------------------------------------
  714. // Classic zombie only uses moan sound if on fire.
  715. //---------------------------------------------------------
  716. void CNPC_Zombine::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize )
  717. {
  718. if( IsOnFire() )
  719. {
  720. BaseClass::MoanSound( pEnvelope, iEnvelopeSize );
  721. }
  722. }
  723. //-----------------------------------------------------------------------------
  724. // Purpose: Returns the classname (ie "npc_headcrab") to spawn when our headcrab bails.
  725. //-----------------------------------------------------------------------------
  726. const char *CNPC_Zombine::GetHeadcrabClassname( void )
  727. {
  728. return "npc_headcrab";
  729. }
  730. void CNPC_Zombine::ReleaseGrenade( Vector vPhysgunPos )
  731. {
  732. if ( HasGrenade() == false )
  733. return;
  734. Vector vDir = vPhysgunPos - m_hGrenade->GetAbsOrigin();
  735. VectorNormalize( vDir );
  736. Activity aActivity;
  737. Vector vForward, vRight;
  738. GetVectors( &vForward, &vRight, NULL );
  739. float flDotForward = DotProduct( vForward, vDir );
  740. float flDotRight = DotProduct( vRight, vDir );
  741. bool bNegativeForward = false;
  742. bool bNegativeRight = false;
  743. if ( flDotForward < 0.0f )
  744. {
  745. bNegativeForward = true;
  746. flDotForward = flDotForward * -1;
  747. }
  748. if ( flDotRight < 0.0f )
  749. {
  750. bNegativeRight = true;
  751. flDotRight = flDotRight * -1;
  752. }
  753. if ( flDotRight > flDotForward )
  754. {
  755. if ( bNegativeRight == true )
  756. aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_WEST;
  757. else
  758. aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_EAST;
  759. }
  760. else
  761. {
  762. if ( bNegativeForward == true )
  763. aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_BACK;
  764. else
  765. aActivity = (Activity)ACT_ZOMBINE_GRENADE_FLINCH_FRONT;
  766. }
  767. AddGesture( aActivity );
  768. DropGrenade( vec3_origin );
  769. if ( IsSprinting() )
  770. {
  771. StopSprint();
  772. }
  773. else
  774. {
  775. Sprint();
  776. }
  777. }
  778. CBaseEntity *CNPC_Zombine::OnFailedPhysGunPickup( Vector vPhysgunPos )
  779. {
  780. CBaseEntity *pGrenade = m_hGrenade;
  781. ReleaseGrenade( vPhysgunPos );
  782. return pGrenade;
  783. }
  784. //-----------------------------------------------------------------------------
  785. //
  786. // Schedules
  787. //
  788. //-----------------------------------------------------------------------------
  789. AI_BEGIN_CUSTOM_NPC( npc_zombine, CNPC_Zombine )
  790. //Squad slots
  791. DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT1 )
  792. DECLARE_SQUADSLOT( SQUAD_SLOT_ZOMBINE_SPRINT2 )
  793. DECLARE_CONDITION( COND_ZOMBINE_GRENADE )
  794. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_PULL )
  795. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_WALK )
  796. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_RUN )
  797. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_IDLE )
  798. DECLARE_ACTIVITY( ACT_ZOMBINE_ATTACK_FAST )
  799. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_BACK )
  800. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_FRONT )
  801. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_WEST)
  802. DECLARE_ACTIVITY( ACT_ZOMBINE_GRENADE_FLINCH_EAST )
  803. DECLARE_ANIMEVENT( AE_ZOMBINE_PULLPIN )
  804. DEFINE_SCHEDULE
  805. (
  806. SCHED_ZOMBINE_PULL_GRENADE,
  807. " Tasks"
  808. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_ZOMBINE_GRENADE_PULL"
  809. " Interrupts"
  810. )
  811. AI_END_CUSTOM_NPC()