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.

3023 lines
81 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the zombie, a horrific once-human headcrab victim.
  4. //
  5. // The zombie has two main states: Full and Torso.
  6. //
  7. // In Full state, the zombie is whole and walks upright as he did in Half-Life.
  8. // He will try to claw the player and swat physics items at him.
  9. //
  10. // In Torso state, the zombie has been blasted or cut in half, and the Torso will
  11. // drag itself along the ground with its arms. It will try to claw the player.
  12. //
  13. // In either state, a severely injured Zombie will release its headcrab, which
  14. // will immediately go after the player. The Zombie will then die (ragdoll).
  15. //
  16. //=============================================================================//
  17. #include "cbase.h"
  18. #include "npc_BaseZombie.h"
  19. #include "player.h"
  20. #include "game.h"
  21. #include "ai_network.h"
  22. #include "ai_navigator.h"
  23. #include "ai_motor.h"
  24. #include "ai_default.h"
  25. #include "ai_schedule.h"
  26. #include "ai_hull.h"
  27. #include "ai_node.h"
  28. #include "ai_memory.h"
  29. #include "ai_senses.h"
  30. #include "bitstring.h"
  31. #include "EntityFlame.h"
  32. #include "hl2_shareddefs.h"
  33. #include "npcevent.h"
  34. #include "activitylist.h"
  35. #include "entitylist.h"
  36. #include "gib.h"
  37. #include "soundenvelope.h"
  38. #include "ndebugoverlay.h"
  39. #include "rope.h"
  40. #include "rope_shared.h"
  41. #include "igamesystem.h"
  42. #include "vstdlib/random.h"
  43. #include "engine/IEngineSound.h"
  44. #include "props.h"
  45. #include "hl2_gamerules.h"
  46. #include "weapon_physcannon.h"
  47. #include "ammodef.h"
  48. #include "vehicle_base.h"
  49. // memdbgon must be the last include file in a .cpp file!!!
  50. #include "tier0/memdbgon.h"
  51. extern ConVar sk_npc_head;
  52. #define ZOMBIE_BULLET_DAMAGE_SCALE 0.5f
  53. int g_interactionZombieMeleeWarning;
  54. envelopePoint_t envDefaultZombieMoanVolumeFast[] =
  55. {
  56. { 1.0f, 1.0f,
  57. 0.1f, 0.1f,
  58. },
  59. { 0.0f, 0.0f,
  60. 0.2f, 0.3f,
  61. },
  62. };
  63. envelopePoint_t envDefaultZombieMoanVolume[] =
  64. {
  65. { 1.0f, 0.1f,
  66. 0.1f, 0.1f,
  67. },
  68. { 1.0f, 1.0f,
  69. 0.2f, 0.2f,
  70. },
  71. { 0.0f, 0.0f,
  72. 0.3f, 0.4f,
  73. },
  74. };
  75. // if the zombie doesn't find anything closer than this, it doesn't swat.
  76. #define ZOMBIE_FARTHEST_PHYSICS_OBJECT 40.0*12.0
  77. #define ZOMBIE_PHYSICS_SEARCH_DEPTH 100
  78. // Don't swat objects unless player is closer than this.
  79. #define ZOMBIE_PLAYER_MAX_SWAT_DIST 1000
  80. //
  81. // How much health a Zombie torso gets when a whole zombie is broken
  82. // It's whole zombie's MAX Health * this value
  83. #define ZOMBIE_TORSO_HEALTH_FACTOR 0.5
  84. //
  85. // When the zombie has health < m_iMaxHealth * this value, it will
  86. // try to release its headcrab.
  87. #define ZOMBIE_RELEASE_HEALTH_FACTOR 0.5
  88. //
  89. // The heaviest physics object that a zombie should try to swat. (kg)
  90. #define ZOMBIE_MAX_PHYSOBJ_MASS 60
  91. //
  92. // Zombie tries to get this close to a physics object's origin to swat it
  93. #define ZOMBIE_PHYSOBJ_SWATDIST 80
  94. //
  95. // Because movement code sometimes doesn't get us QUITE where we
  96. // want to go, the zombie tries to get this close to a physics object
  97. // Zombie will end up somewhere between PHYSOBJ_MOVE_TO_DIST & PHYSOBJ_SWATDIST
  98. #define ZOMBIE_PHYSOBJ_MOVE_TO_DIST 48
  99. //
  100. // How long between physics swat attacks (in seconds).
  101. #define ZOMBIE_SWAT_DELAY 5
  102. //
  103. // After taking damage, ignore further damage for n seconds. This keeps the zombie
  104. // from being interrupted while.
  105. //
  106. #define ZOMBIE_FLINCH_DELAY 3
  107. #define ZOMBIE_BURN_TIME 10 // If ignited, burn for this many seconds
  108. #define ZOMBIE_BURN_TIME_NOISE 2 // Give or take this many seconds.
  109. //=========================================================
  110. // private activities
  111. //=========================================================
  112. int CNPC_BaseZombie::ACT_ZOM_SWATLEFTMID;
  113. int CNPC_BaseZombie::ACT_ZOM_SWATRIGHTMID;
  114. int CNPC_BaseZombie::ACT_ZOM_SWATLEFTLOW;
  115. int CNPC_BaseZombie::ACT_ZOM_SWATRIGHTLOW;
  116. int CNPC_BaseZombie::ACT_ZOM_RELEASECRAB;
  117. int CNPC_BaseZombie::ACT_ZOM_FALL;
  118. ConVar sk_zombie_dmg_one_slash( "sk_zombie_dmg_one_slash","0");
  119. ConVar sk_zombie_dmg_both_slash( "sk_zombie_dmg_both_slash","0");
  120. // When a zombie spawns, he will select a 'base' pitch value
  121. // that's somewhere between basepitchmin & basepitchmax
  122. ConVar zombie_basemin( "zombie_basemin", "100" );
  123. ConVar zombie_basemax( "zombie_basemax", "100" );
  124. ConVar zombie_changemin( "zombie_changemin", "0" );
  125. ConVar zombie_changemax( "zombie_changemax", "0" );
  126. // play a sound once in every zombie_stepfreq steps
  127. ConVar zombie_stepfreq( "zombie_stepfreq", "4" );
  128. ConVar zombie_moanfreq( "zombie_moanfreq", "1" );
  129. ConVar zombie_decaymin( "zombie_decaymin", "0.1" );
  130. ConVar zombie_decaymax( "zombie_decaymax", "0.4" );
  131. ConVar zombie_ambushdist( "zombie_ambushdist", "16000" );
  132. //=========================================================
  133. // For a couple of reasons, we keep a running count of how
  134. // many zombies in the world are angry at any given time.
  135. //=========================================================
  136. static int s_iAngryZombies = 0;
  137. //=========================================================
  138. //=========================================================
  139. class CAngryZombieCounter : public CAutoGameSystem
  140. {
  141. public:
  142. CAngryZombieCounter( char const *name ) : CAutoGameSystem( name )
  143. {
  144. }
  145. // Level init, shutdown
  146. virtual void LevelInitPreEntity()
  147. {
  148. s_iAngryZombies = 0;
  149. }
  150. };
  151. CAngryZombieCounter AngryZombieCounter( "CAngryZombieCounter" );
  152. int AE_ZOMBIE_ATTACK_RIGHT;
  153. int AE_ZOMBIE_ATTACK_LEFT;
  154. int AE_ZOMBIE_ATTACK_BOTH;
  155. int AE_ZOMBIE_SWATITEM;
  156. int AE_ZOMBIE_STARTSWAT;
  157. int AE_ZOMBIE_STEP_LEFT;
  158. int AE_ZOMBIE_STEP_RIGHT;
  159. int AE_ZOMBIE_SCUFF_LEFT;
  160. int AE_ZOMBIE_SCUFF_RIGHT;
  161. int AE_ZOMBIE_ATTACK_SCREAM;
  162. int AE_ZOMBIE_GET_UP;
  163. int AE_ZOMBIE_POUND;
  164. int AE_ZOMBIE_ALERTSOUND;
  165. int AE_ZOMBIE_POPHEADCRAB;
  166. //=========================================================
  167. //=========================================================
  168. BEGIN_DATADESC( CNPC_BaseZombie )
  169. DEFINE_SOUNDPATCH( m_pMoanSound ),
  170. DEFINE_FIELD( m_fIsTorso, FIELD_BOOLEAN ),
  171. DEFINE_FIELD( m_fIsHeadless, FIELD_BOOLEAN ),
  172. DEFINE_FIELD( m_flNextFlinch, FIELD_TIME ),
  173. DEFINE_FIELD( m_bHeadShot, FIELD_BOOLEAN ),
  174. DEFINE_FIELD( m_flBurnDamage, FIELD_FLOAT ),
  175. DEFINE_FIELD( m_flBurnDamageResetTime, FIELD_TIME ),
  176. DEFINE_FIELD( m_hPhysicsEnt, FIELD_EHANDLE ),
  177. DEFINE_FIELD( m_flNextMoanSound, FIELD_TIME ),
  178. DEFINE_FIELD( m_flNextSwat, FIELD_TIME ),
  179. DEFINE_FIELD( m_flNextSwatScan, FIELD_TIME ),
  180. DEFINE_FIELD( m_crabHealth, FIELD_FLOAT ),
  181. DEFINE_FIELD( m_flMoanPitch, FIELD_FLOAT ),
  182. DEFINE_FIELD( m_iMoanSound, FIELD_INTEGER ),
  183. DEFINE_FIELD( m_hObstructor, FIELD_EHANDLE ),
  184. DEFINE_FIELD( m_bIsSlumped, FIELD_BOOLEAN ),
  185. END_DATADESC()
  186. //LINK_ENTITY_TO_CLASS( base_zombie, CNPC_BaseZombie );
  187. //---------------------------------------------------------
  188. //---------------------------------------------------------
  189. int CNPC_BaseZombie::g_numZombies = 0;
  190. //---------------------------------------------------------
  191. //---------------------------------------------------------
  192. CNPC_BaseZombie::CNPC_BaseZombie()
  193. {
  194. // Gotta select which sound we're going to play, right here!
  195. // Because everyone's constructed before they spawn.
  196. //
  197. // Assign moan sounds in order, over and over.
  198. // This means if 3 or so zombies spawn near each
  199. // other, they will definitely not pick the same
  200. // moan loop.
  201. m_iMoanSound = g_numZombies;
  202. g_numZombies++;
  203. }
  204. //---------------------------------------------------------
  205. //---------------------------------------------------------
  206. CNPC_BaseZombie::~CNPC_BaseZombie()
  207. {
  208. g_numZombies--;
  209. }
  210. //---------------------------------------------------------
  211. // The closest physics object is chosen that is:
  212. // <= MaxMass in Mass
  213. // Between the zombie and the enemy
  214. // not too far from a direct line to the enemy.
  215. //---------------------------------------------------------
  216. bool CNPC_BaseZombie::FindNearestPhysicsObject( int iMaxMass )
  217. {
  218. CBaseEntity *pList[ ZOMBIE_PHYSICS_SEARCH_DEPTH ];
  219. CBaseEntity *pNearest = NULL;
  220. float flDist;
  221. IPhysicsObject *pPhysObj;
  222. int i;
  223. Vector vecDirToEnemy;
  224. Vector vecDirToObject;
  225. if ( !CanSwatPhysicsObjects() || !GetEnemy() )
  226. {
  227. // Can't swat, or no enemy, so no swat.
  228. m_hPhysicsEnt = NULL;
  229. return false;
  230. }
  231. vecDirToEnemy = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
  232. float dist = VectorNormalize(vecDirToEnemy);
  233. vecDirToEnemy.z = 0;
  234. if( dist > ZOMBIE_PLAYER_MAX_SWAT_DIST )
  235. {
  236. // Player is too far away. Don't bother
  237. // trying to swat anything at them until
  238. // they are closer.
  239. return false;
  240. }
  241. float flNearestDist = MIN( dist, ZOMBIE_FARTHEST_PHYSICS_OBJECT * 0.5 );
  242. Vector vecDelta( flNearestDist, flNearestDist, GetHullHeight() * 2.0 );
  243. class CZombieSwatEntitiesEnum : public CFlaggedEntitiesEnum
  244. {
  245. public:
  246. CZombieSwatEntitiesEnum( CBaseEntity **pList, int listMax, int iMaxMass )
  247. : CFlaggedEntitiesEnum( pList, listMax, 0 ),
  248. m_iMaxMass( iMaxMass )
  249. {
  250. }
  251. virtual IterationRetval_t EnumElement( IHandleEntity *pHandleEntity )
  252. {
  253. CBaseEntity *pEntity = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
  254. if ( pEntity &&
  255. pEntity->VPhysicsGetObject() &&
  256. pEntity->VPhysicsGetObject()->GetMass() <= m_iMaxMass &&
  257. pEntity->VPhysicsGetObject()->IsAsleep() &&
  258. pEntity->VPhysicsGetObject()->IsMoveable() )
  259. {
  260. return CFlaggedEntitiesEnum::EnumElement( pHandleEntity );
  261. }
  262. return ITERATION_CONTINUE;
  263. }
  264. int m_iMaxMass;
  265. };
  266. CZombieSwatEntitiesEnum swatEnum( pList, ZOMBIE_PHYSICS_SEARCH_DEPTH, iMaxMass );
  267. int count = UTIL_EntitiesInBox( GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, &swatEnum );
  268. // magically know where they are
  269. Vector vecZombieKnees;
  270. CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.25f ), &vecZombieKnees );
  271. for( i = 0 ; i < count ; i++ )
  272. {
  273. pPhysObj = pList[ i ]->VPhysicsGetObject();
  274. Assert( !( !pPhysObj || pPhysObj->GetMass() > iMaxMass || !pPhysObj->IsAsleep() ) );
  275. Vector center = pList[ i ]->WorldSpaceCenter();
  276. flDist = UTIL_DistApprox2D( GetAbsOrigin(), center );
  277. if( flDist >= flNearestDist )
  278. continue;
  279. // This object is closer... but is it between the player and the zombie?
  280. vecDirToObject = pList[ i ]->WorldSpaceCenter() - GetAbsOrigin();
  281. VectorNormalize(vecDirToObject);
  282. vecDirToObject.z = 0;
  283. if( DotProduct( vecDirToEnemy, vecDirToObject ) < 0.8 )
  284. continue;
  285. if( flDist >= UTIL_DistApprox2D( center, GetEnemy()->GetAbsOrigin() ) )
  286. continue;
  287. // don't swat things where the highest point is under my knees
  288. // NOTE: This is a rough test; a more exact test is going to occur below
  289. if ( (center.z + pList[i]->BoundingRadius()) < vecZombieKnees.z )
  290. continue;
  291. // don't swat things that are over my head.
  292. if( center.z > EyePosition().z )
  293. continue;
  294. vcollide_t *pCollide = modelinfo->GetVCollide( pList[i]->GetModelIndex() );
  295. Vector objMins, objMaxs;
  296. physcollision->CollideGetAABB( &objMins, &objMaxs, pCollide->solids[0], pList[i]->GetAbsOrigin(), pList[i]->GetAbsAngles() );
  297. if ( objMaxs.z < vecZombieKnees.z )
  298. continue;
  299. if ( !FVisible( pList[i] ) )
  300. continue;
  301. if ( hl2_episodic.GetBool() )
  302. {
  303. // Skip things that the enemy can't see. Do we want this as a general thing?
  304. // The case for this feature is that zombies who are pursuing the player will
  305. // stop along the way to swat objects at the player who is around the corner or
  306. // otherwise not in a place that the object has a hope of hitting. This diversion
  307. // makes the zombies very late (in a random fashion) getting where they are going. (sjb 1/2/06)
  308. if( !GetEnemy()->FVisible( pList[i] ) )
  309. continue;
  310. }
  311. // Make this the last check, since it makes a string.
  312. // Don't swat server ragdolls!
  313. if ( FClassnameIs( pList[ i ], "physics_prop_ragdoll" ) )
  314. continue;
  315. if ( FClassnameIs( pList[ i ], "prop_ragdoll" ) )
  316. continue;
  317. // The object must also be closer to the zombie than it is to the enemy
  318. pNearest = pList[ i ];
  319. flNearestDist = flDist;
  320. }
  321. m_hPhysicsEnt = pNearest;
  322. if( m_hPhysicsEnt == NULL )
  323. {
  324. return false;
  325. }
  326. else
  327. {
  328. return true;
  329. }
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Returns this monster's place in the relationship table.
  333. //-----------------------------------------------------------------------------
  334. Class_T CNPC_BaseZombie::Classify( void )
  335. {
  336. if ( IsSlumped() )
  337. return CLASS_NONE;
  338. return( CLASS_ZOMBIE );
  339. }
  340. //-----------------------------------------------------------------------------
  341. //-----------------------------------------------------------------------------
  342. Disposition_t CNPC_BaseZombie::IRelationType( CBaseEntity *pTarget )
  343. {
  344. // Slumping should not affect Zombie's opinion of others
  345. if ( IsSlumped() )
  346. {
  347. m_bIsSlumped = false;
  348. Disposition_t result = BaseClass::IRelationType( pTarget );
  349. m_bIsSlumped = true;
  350. return result;
  351. }
  352. return BaseClass::IRelationType( pTarget );
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose: Returns the maximum yaw speed based on the monster's current activity.
  356. //-----------------------------------------------------------------------------
  357. float CNPC_BaseZombie::MaxYawSpeed( void )
  358. {
  359. if( m_fIsTorso )
  360. {
  361. return( 60 );
  362. }
  363. else if (IsMoving() && HasPoseParameter( GetSequence(), m_poseMove_Yaw ))
  364. {
  365. return( 15 );
  366. }
  367. else
  368. {
  369. switch( GetActivity() )
  370. {
  371. case ACT_TURN_LEFT:
  372. case ACT_TURN_RIGHT:
  373. return 100;
  374. break;
  375. case ACT_RUN:
  376. return 15;
  377. break;
  378. case ACT_WALK:
  379. case ACT_IDLE:
  380. return 25;
  381. break;
  382. case ACT_RANGE_ATTACK1:
  383. case ACT_RANGE_ATTACK2:
  384. case ACT_MELEE_ATTACK1:
  385. case ACT_MELEE_ATTACK2:
  386. return 120;
  387. default:
  388. return 90;
  389. break;
  390. }
  391. }
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Purpose: turn in the direction of movement
  395. // Output :
  396. //-----------------------------------------------------------------------------
  397. bool CNPC_BaseZombie::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
  398. {
  399. if (!HasPoseParameter( GetSequence(), m_poseMove_Yaw ))
  400. {
  401. return BaseClass::OverrideMoveFacing( move, flInterval );
  402. }
  403. // required movement direction
  404. float flMoveYaw = UTIL_VecToYaw( move.dir );
  405. float idealYaw = UTIL_AngleMod( flMoveYaw );
  406. if (GetEnemy())
  407. {
  408. float flEDist = UTIL_DistApprox2D( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter() );
  409. if (flEDist < 256.0)
  410. {
  411. float flEYaw = UTIL_VecToYaw( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() );
  412. if (flEDist < 128.0)
  413. {
  414. idealYaw = flEYaw;
  415. }
  416. else
  417. {
  418. idealYaw = flMoveYaw + UTIL_AngleDiff( flEYaw, flMoveYaw ) * (2 - flEDist / 128.0);
  419. }
  420. //DevMsg("was %.0f now %.0f\n", flMoveYaw, idealYaw );
  421. }
  422. }
  423. GetMotor()->SetIdealYawAndUpdate( idealYaw );
  424. // find movement direction to compensate for not being turned far enough
  425. float fSequenceMoveYaw = GetSequenceMoveYaw( GetSequence() );
  426. float flDiff = UTIL_AngleDiff( flMoveYaw, GetLocalAngles().y + fSequenceMoveYaw );
  427. SetPoseParameter( m_poseMove_Yaw, GetPoseParameter( m_poseMove_Yaw ) + flDiff );
  428. return true;
  429. }
  430. //-----------------------------------------------------------------------------
  431. // Purpose: For innate melee attack
  432. // Input :
  433. // Output :
  434. //-----------------------------------------------------------------------------
  435. int CNPC_BaseZombie::MeleeAttack1Conditions ( float flDot, float flDist )
  436. {
  437. float range = GetClawAttackRange();
  438. if (flDist > range )
  439. {
  440. // Translate a hit vehicle into its passenger if found
  441. if ( GetEnemy() != NULL )
  442. {
  443. #if defined(HL2_DLL) && !defined(HL2MP)
  444. // If the player is holding an object, knock it down.
  445. if( GetEnemy()->IsPlayer() )
  446. {
  447. CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() );
  448. Assert( pPlayer != NULL );
  449. // Is the player carrying something?
  450. CBaseEntity *pObject = GetPlayerHeldEntity(pPlayer);
  451. if( !pObject )
  452. {
  453. pObject = PhysCannonGetHeldEntity( pPlayer->GetActiveWeapon() );
  454. }
  455. if( pObject )
  456. {
  457. float flDist = pObject->WorldSpaceCenter().DistTo( WorldSpaceCenter() );
  458. if( flDist <= GetClawAttackRange() )
  459. return COND_CAN_MELEE_ATTACK1;
  460. }
  461. }
  462. #endif
  463. }
  464. return COND_TOO_FAR_TO_ATTACK;
  465. }
  466. if (flDot < 0.7)
  467. {
  468. return COND_NOT_FACING_ATTACK;
  469. }
  470. // Build a cube-shaped hull, the same hull that ClawAttack() is going to use.
  471. Vector vecMins = GetHullMins();
  472. Vector vecMaxs = GetHullMaxs();
  473. vecMins.z = vecMins.x;
  474. vecMaxs.z = vecMaxs.x;
  475. Vector forward;
  476. GetVectors( &forward, NULL, NULL );
  477. trace_t tr;
  478. CTraceFilterNav traceFilter( this, false, this, COLLISION_GROUP_NONE );
  479. AI_TraceHull( WorldSpaceCenter(), WorldSpaceCenter() + forward * GetClawAttackRange(), vecMins, vecMaxs, MASK_NPCSOLID, &traceFilter, &tr );
  480. if( tr.fraction == 1.0 || !tr.m_pEnt )
  481. {
  482. #ifdef HL2_EPISODIC
  483. // If our trace was unobstructed but we were shooting
  484. if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE )
  485. return COND_CAN_MELEE_ATTACK1;
  486. #endif // HL2_EPISODIC
  487. // This attack would miss completely. Trick the zombie into moving around some more.
  488. return COND_TOO_FAR_TO_ATTACK;
  489. }
  490. if( tr.m_pEnt == GetEnemy() ||
  491. tr.m_pEnt->IsNPC() ||
  492. ( tr.m_pEnt->m_takedamage == DAMAGE_YES && (dynamic_cast<CBreakableProp*>(tr.m_pEnt) ) ) )
  493. {
  494. // -Let the zombie swipe at his enemy if he's going to hit them.
  495. // -Also let him swipe at NPC's that happen to be between the zombie and the enemy.
  496. // This makes mobs of zombies seem more rowdy since it doesn't leave guys in the back row standing around.
  497. // -Also let him swipe at things that takedamage, under the assumptions that they can be broken.
  498. return COND_CAN_MELEE_ATTACK1;
  499. }
  500. Vector vecTrace = tr.endpos - tr.startpos;
  501. float lenTraceSq = vecTrace.Length2DSqr();
  502. if ( GetEnemy() && GetEnemy()->MyCombatCharacterPointer() && tr.m_pEnt == static_cast<CBaseCombatCharacter *>(GetEnemy())->GetVehicleEntity() )
  503. {
  504. if ( lenTraceSq < Square( GetClawAttackRange() * 0.75f ) )
  505. {
  506. return COND_CAN_MELEE_ATTACK1;
  507. }
  508. }
  509. if( tr.m_pEnt->IsBSPModel() )
  510. {
  511. // The trace hit something solid, but it's not the enemy. If this item is closer to the zombie than
  512. // the enemy is, treat this as an obstruction.
  513. Vector vecToEnemy = GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter();
  514. if( lenTraceSq < vecToEnemy.Length2DSqr() )
  515. {
  516. return COND_ZOMBIE_LOCAL_MELEE_OBSTRUCTION;
  517. }
  518. }
  519. #ifdef HL2_EPISODIC
  520. if ( !tr.m_pEnt->IsWorld() && GetEnemy() && GetEnemy()->GetGroundEntity() == tr.m_pEnt )
  521. {
  522. //Try to swat whatever the player is standing on instead of acting like a dill.
  523. return COND_CAN_MELEE_ATTACK1;
  524. }
  525. // Bullseyes are given some grace on if they can be hit
  526. if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE )
  527. return COND_CAN_MELEE_ATTACK1;
  528. #endif // HL2_EPISODIC
  529. // Move around some more
  530. return COND_TOO_FAR_TO_ATTACK;
  531. }
  532. //-----------------------------------------------------------------------------
  533. //-----------------------------------------------------------------------------
  534. #define ZOMBIE_BUCKSHOT_TRIPLE_DAMAGE_DIST 96.0f // Triple damage from buckshot at 8 feet (headshot only)
  535. float CNPC_BaseZombie::GetHitgroupDamageMultiplier( int iHitGroup, const CTakeDamageInfo &info )
  536. {
  537. switch( iHitGroup )
  538. {
  539. case HITGROUP_HEAD:
  540. {
  541. if( info.GetDamageType() & DMG_BUCKSHOT )
  542. {
  543. float flDist = FLT_MAX;
  544. if( info.GetAttacker() )
  545. {
  546. flDist = ( GetAbsOrigin() - info.GetAttacker()->GetAbsOrigin() ).Length();
  547. }
  548. if( flDist <= ZOMBIE_BUCKSHOT_TRIPLE_DAMAGE_DIST )
  549. {
  550. return 3.0f;
  551. }
  552. }
  553. else
  554. {
  555. return 2.0f;
  556. }
  557. }
  558. }
  559. return BaseClass::GetHitgroupDamageMultiplier( iHitGroup, info );
  560. }
  561. //-----------------------------------------------------------------------------
  562. // Purpose:
  563. //-----------------------------------------------------------------------------
  564. void CNPC_BaseZombie::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  565. {
  566. CTakeDamageInfo infoCopy = info;
  567. // Keep track of headshots so we can determine whether to pop off our headcrab.
  568. if (ptr->hitgroup == HITGROUP_HEAD)
  569. {
  570. m_bHeadShot = true;
  571. }
  572. if( infoCopy.GetDamageType() & DMG_BUCKSHOT )
  573. {
  574. // Zombie gets across-the-board damage reduction for buckshot. This compensates for the recent changes which
  575. // make the shotgun much more powerful, and returns the zombies to a level that has been playtested extensively.(sjb)
  576. // This normalizes the buckshot damage to what it used to be on normal (5 dmg per pellet. Now it's 8 dmg per pellet).
  577. infoCopy.ScaleDamage( 0.625 );
  578. }
  579. BaseClass::TraceAttack( infoCopy, vecDir, ptr, pAccumulator );
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose: A zombie has taken damage. Determine whether he should split in half
  583. // Input :
  584. // Output : bool, true if yes.
  585. //-----------------------------------------------------------------------------
  586. bool CNPC_BaseZombie::ShouldBecomeTorso( const CTakeDamageInfo &info, float flDamageThreshold )
  587. {
  588. if ( info.GetDamageType() & DMG_REMOVENORAGDOLL )
  589. return false;
  590. if ( m_fIsTorso )
  591. {
  592. // Already split.
  593. return false;
  594. }
  595. // Not if we're in a dss
  596. if ( IsRunningDynamicInteraction() )
  597. return false;
  598. // Break in half IF:
  599. //
  600. // Take half or more of max health in DMG_BLAST
  601. if( (info.GetDamageType() & DMG_BLAST) && flDamageThreshold >= 0.5 )
  602. {
  603. return true;
  604. }
  605. if ( hl2_episodic.GetBool() )
  606. {
  607. // Always split after a cannon hit
  608. if ( info.GetAmmoType() == GetAmmoDef()->Index("CombineHeavyCannon") )
  609. return true;
  610. }
  611. #if 0
  612. if( info.GetDamageType() & DMG_BUCKSHOT )
  613. {
  614. if( m_iHealth <= 0 || flDamageThreshold >= 0.5 )
  615. {
  616. return true;
  617. }
  618. }
  619. #endif
  620. return false;
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Purpose: A zombie has taken damage. Determine whether he release his headcrab.
  624. // Output : YES, IMMEDIATE, or SCHEDULED (see HeadcrabRelease_t)
  625. //-----------------------------------------------------------------------------
  626. HeadcrabRelease_t CNPC_BaseZombie::ShouldReleaseHeadcrab( const CTakeDamageInfo &info, float flDamageThreshold )
  627. {
  628. if ( m_iHealth <= 0 )
  629. {
  630. if ( info.GetDamageType() & DMG_REMOVENORAGDOLL )
  631. return RELEASE_NO;
  632. if ( info.GetDamageType() & DMG_SNIPER )
  633. return RELEASE_RAGDOLL;
  634. // If I was killed by a bullet...
  635. if ( info.GetDamageType() & DMG_BULLET )
  636. {
  637. if( m_bHeadShot )
  638. {
  639. if( flDamageThreshold > 0.25 )
  640. {
  641. // Enough force to kill the crab.
  642. return RELEASE_RAGDOLL;
  643. }
  644. }
  645. else
  646. {
  647. // Killed by a shot to body or something. Crab is ok!
  648. return RELEASE_IMMEDIATE;
  649. }
  650. }
  651. // If I was killed by an explosion, release the crab.
  652. if ( info.GetDamageType() & DMG_BLAST )
  653. {
  654. return RELEASE_RAGDOLL;
  655. }
  656. if ( m_fIsTorso && IsChopped( info ) )
  657. {
  658. return RELEASE_RAGDOLL_SLICED_OFF;
  659. }
  660. }
  661. return RELEASE_NO;
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose:
  665. // Input : pInflictor -
  666. // pAttacker -
  667. // flDamage -
  668. // bitsDamageType -
  669. // Output : int
  670. //-----------------------------------------------------------------------------
  671. #define ZOMBIE_SCORCH_RATE 8
  672. #define ZOMBIE_MIN_RENDERCOLOR 50
  673. int CNPC_BaseZombie::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  674. {
  675. CTakeDamageInfo info = inputInfo;
  676. if( inputInfo.GetDamageType() & DMG_BURN )
  677. {
  678. // If a zombie is on fire it only takes damage from the fire that's attached to it. (DMG_DIRECT)
  679. // This is to stop zombies from burning to death 10x faster when they're standing around
  680. // 10 fire entities.
  681. if( IsOnFire() && !(inputInfo.GetDamageType() & DMG_DIRECT) )
  682. {
  683. return 0;
  684. }
  685. Scorch( ZOMBIE_SCORCH_RATE, ZOMBIE_MIN_RENDERCOLOR );
  686. }
  687. // Take some percentage of damage from bullets (unless hit in the crab). Always take full buckshot & sniper damage
  688. if ( !m_bHeadShot && (info.GetDamageType() & DMG_BULLET) && !(info.GetDamageType() & (DMG_BUCKSHOT|DMG_SNIPER)) )
  689. {
  690. info.ScaleDamage( ZOMBIE_BULLET_DAMAGE_SCALE );
  691. }
  692. if ( ShouldIgnite( info ) )
  693. {
  694. Ignite( 100.0f );
  695. }
  696. int tookDamage = BaseClass::OnTakeDamage_Alive( info );
  697. // flDamageThreshold is what percentage of the creature's max health
  698. // this amount of damage represents. (clips at 1.0)
  699. float flDamageThreshold = MIN( 1, info.GetDamage() / m_iMaxHealth );
  700. // Being chopped up by a sharp physics object is a pretty special case
  701. // so we handle it with some special code. Mainly for
  702. // Ravenholm's helicopter traps right now (sjb).
  703. bool bChopped = IsChopped(info);
  704. bool bSquashed = IsSquashed(info);
  705. bool bKilledByVehicle = ( ( info.GetDamageType() & DMG_VEHICLE ) != 0 );
  706. if( !m_fIsTorso && (bChopped || bSquashed) && !bKilledByVehicle && !(info.GetDamageType() & DMG_REMOVENORAGDOLL) )
  707. {
  708. if( bChopped )
  709. {
  710. EmitSound( "E3_Phystown.Slicer" );
  711. }
  712. DieChopped( info );
  713. }
  714. else
  715. {
  716. HeadcrabRelease_t release = ShouldReleaseHeadcrab( info, flDamageThreshold );
  717. switch( release )
  718. {
  719. case RELEASE_IMMEDIATE:
  720. ReleaseHeadcrab( EyePosition(), vec3_origin, true, true );
  721. break;
  722. case RELEASE_RAGDOLL:
  723. // Go a little easy on headcrab ragdoll force. They're light!
  724. ReleaseHeadcrab( EyePosition(), inputInfo.GetDamageForce() * 0.25, true, false, true );
  725. break;
  726. case RELEASE_RAGDOLL_SLICED_OFF:
  727. {
  728. EmitSound( "E3_Phystown.Slicer" );
  729. Vector vecForce = inputInfo.GetDamageForce() * 0.1;
  730. vecForce += Vector( 0, 0, 2000.0 );
  731. ReleaseHeadcrab( EyePosition(), vecForce, true, false, true );
  732. }
  733. break;
  734. case RELEASE_VAPORIZE:
  735. RemoveHead();
  736. break;
  737. case RELEASE_SCHEDULED:
  738. SetCondition( COND_ZOMBIE_RELEASECRAB );
  739. break;
  740. }
  741. if( ShouldBecomeTorso( info, flDamageThreshold ) )
  742. {
  743. bool bHitByCombineCannon = (inputInfo.GetAmmoType() == GetAmmoDef()->Index("CombineHeavyCannon"));
  744. if ( CanBecomeLiveTorso() )
  745. {
  746. BecomeTorso( vec3_origin, inputInfo.GetDamageForce() * 0.50 );
  747. if ( ( info.GetDamageType() & DMG_BLAST) && random->RandomInt( 0, 1 ) == 0 )
  748. {
  749. Ignite( 5.0 + random->RandomFloat( 0.0, 5.0 ) );
  750. }
  751. // For Combine cannon impacts
  752. if ( hl2_episodic.GetBool() )
  753. {
  754. if ( bHitByCombineCannon )
  755. {
  756. // Catch on fire.
  757. Ignite( 5.0f + random->RandomFloat( 0.0f, 5.0f ) );
  758. }
  759. }
  760. if (flDamageThreshold >= 1.0)
  761. {
  762. m_iHealth = 0;
  763. BecomeRagdollOnClient( info.GetDamageForce() );
  764. }
  765. }
  766. else if ( random->RandomInt(1, 3) == 1 )
  767. DieChopped( info );
  768. }
  769. }
  770. if( tookDamage > 0 && (info.GetDamageType() & (DMG_BURN|DMG_DIRECT)) && m_ActBusyBehavior.IsActive() )
  771. {
  772. //!!!HACKHACK- Stuff a light_damage condition if an actbusying zombie takes direct burn damage. This will cause an
  773. // ignited zombie to 'wake up' and rise out of its actbusy slump. (sjb)
  774. SetCondition( COND_LIGHT_DAMAGE );
  775. }
  776. // IMPORTANT: always clear the headshot flag after applying damage. No early outs!
  777. m_bHeadShot = false;
  778. return tookDamage;
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Purpose: make a sound Alyx can hear when in darkness mode
  782. // Input : volume (radius) of the sound.
  783. // Output :
  784. //-----------------------------------------------------------------------------
  785. void CNPC_BaseZombie::MakeAISpookySound( float volume, float duration )
  786. {
  787. #ifdef HL2_EPISODIC
  788. if ( HL2GameRules()->IsAlyxInDarknessMode() )
  789. {
  790. CSoundEnt::InsertSound( SOUND_COMBAT, EyePosition(), volume, duration, this, SOUNDENT_CHANNEL_SPOOKY_NOISE );
  791. }
  792. #endif // HL2_EPISODIC
  793. }
  794. //-----------------------------------------------------------------------------
  795. //-----------------------------------------------------------------------------
  796. bool CNPC_BaseZombie::CanPlayMoanSound()
  797. {
  798. if( HasSpawnFlags( SF_NPC_GAG ) )
  799. return false;
  800. // Burning zombies play their moan loop at full volume for as long as they're
  801. // burning. Don't let a moan envelope play cause it will turn the volume down when done.
  802. if( IsOnFire() )
  803. return false;
  804. // Members of a small group of zombies can vocalize whenever they want
  805. if( s_iAngryZombies <= 4 )
  806. return true;
  807. // This serves to limit the number of zombies that can moan at one time when there are a lot.
  808. if( random->RandomInt( 1, zombie_moanfreq.GetInt() * (s_iAngryZombies/2) ) == 1 )
  809. {
  810. return true;
  811. }
  812. return false;
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Purpose: Open a window and let a little bit of the looping moan sound
  816. // come through.
  817. //-----------------------------------------------------------------------------
  818. void CNPC_BaseZombie::MoanSound( envelopePoint_t *pEnvelope, int iEnvelopeSize )
  819. {
  820. if( HasSpawnFlags( SF_NPC_GAG ) )
  821. {
  822. // Not yet!
  823. return;
  824. }
  825. if( !m_pMoanSound )
  826. {
  827. // Don't set this up until the code calls for it.
  828. const char *pszSound = GetMoanSound( m_iMoanSound );
  829. m_flMoanPitch = random->RandomInt( zombie_basemin.GetInt(), zombie_basemax.GetInt() );
  830. //m_pMoanSound = ENVELOPE_CONTROLLER.SoundCreate( entindex(), CHAN_STATIC, pszSound, ATTN_NORM );
  831. CPASAttenuationFilter filter( this );
  832. m_pMoanSound = ENVELOPE_CONTROLLER.SoundCreate( filter, entindex(), CHAN_STATIC, pszSound, ATTN_NORM );
  833. ENVELOPE_CONTROLLER.Play( m_pMoanSound, 1.0, m_flMoanPitch );
  834. }
  835. //HACKHACK get these from chia chin's console vars.
  836. envDefaultZombieMoanVolumeFast[ 1 ].durationMin = zombie_decaymin.GetFloat();
  837. envDefaultZombieMoanVolumeFast[ 1 ].durationMax = zombie_decaymax.GetFloat();
  838. if( random->RandomInt( 1, 2 ) == 1 )
  839. {
  840. IdleSound();
  841. }
  842. float duration = ENVELOPE_CONTROLLER.SoundPlayEnvelope( m_pMoanSound, SOUNDCTRL_CHANGE_VOLUME, pEnvelope, iEnvelopeSize );
  843. float flPitch = random->RandomInt( m_flMoanPitch + zombie_changemin.GetInt(), m_flMoanPitch + zombie_changemax.GetInt() );
  844. ENVELOPE_CONTROLLER.SoundChangePitch( m_pMoanSound, flPitch, 0.3 );
  845. m_flNextMoanSound = gpGlobals->curtime + duration + 9999;
  846. }
  847. //-----------------------------------------------------------------------------
  848. // Purpose: Determine whether the zombie is chopped up by some physics item
  849. //-----------------------------------------------------------------------------
  850. bool CNPC_BaseZombie::IsChopped( const CTakeDamageInfo &info )
  851. {
  852. float flDamageThreshold = MIN( 1, info.GetDamage() / m_iMaxHealth );
  853. if ( m_iHealth > 0 || flDamageThreshold <= 0.5 )
  854. return false;
  855. if ( !( info.GetDamageType() & DMG_SLASH) )
  856. return false;
  857. if ( !( info.GetDamageType() & DMG_CRUSH) )
  858. return false;
  859. if ( info.GetDamageType() & DMG_REMOVENORAGDOLL )
  860. return false;
  861. // If you take crush and slash damage, you're hit by a sharp physics item.
  862. return true;
  863. }
  864. //-----------------------------------------------------------------------------
  865. // Purpose: Return true if this gibbing zombie should ignite its gibs
  866. //-----------------------------------------------------------------------------
  867. bool CNPC_BaseZombie::ShouldIgniteZombieGib( void )
  868. {
  869. #ifdef HL2_EPISODIC
  870. // If we're in darkness mode, don't ignite giblets, because we don't want to
  871. // pay the perf cost of multiple dynamic lights per giblet.
  872. return ( IsOnFire() && !HL2GameRules()->IsAlyxInDarknessMode() );
  873. #else
  874. return IsOnFire();
  875. #endif
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose: Handle the special case of a zombie killed by a physics chopper.
  879. //-----------------------------------------------------------------------------
  880. void CNPC_BaseZombie::DieChopped( const CTakeDamageInfo &info )
  881. {
  882. bool bSquashed = IsSquashed(info);
  883. Vector forceVector( vec3_origin );
  884. forceVector += CalcDamageForceVector( info );
  885. if( !m_fIsHeadless && !bSquashed )
  886. {
  887. if( random->RandomInt( 0, 1 ) == 0 )
  888. {
  889. // Drop a live crab half of the time.
  890. ReleaseHeadcrab( EyePosition(), forceVector * 0.005, true, false, false );
  891. }
  892. }
  893. float flFadeTime = 0.0;
  894. if( HasSpawnFlags( SF_NPC_FADE_CORPSE ) )
  895. {
  896. flFadeTime = 5.0;
  897. }
  898. SetSolid( SOLID_NONE );
  899. AddEffects( EF_NODRAW );
  900. Vector vecLegsForce;
  901. vecLegsForce.x = random->RandomFloat( -400, 400 );
  902. vecLegsForce.y = random->RandomFloat( -400, 400 );
  903. vecLegsForce.z = random->RandomFloat( 0, 250 );
  904. if( bSquashed && vecLegsForce.z > 0 )
  905. {
  906. // Force the broken legs down. (Give some additional force, too)
  907. vecLegsForce.z *= -10;
  908. }
  909. CBaseEntity *pLegGib = CreateRagGib( GetLegsModel(), GetAbsOrigin(), GetAbsAngles(), vecLegsForce, flFadeTime, ShouldIgniteZombieGib() );
  910. if ( pLegGib )
  911. {
  912. CopyRenderColorTo( pLegGib );
  913. }
  914. forceVector *= random->RandomFloat( 0.04, 0.06 );
  915. forceVector.z = ( 100 * 12 * 5 ) * random->RandomFloat( 0.8, 1.2 );
  916. if( bSquashed && forceVector.z > 0 )
  917. {
  918. // Force the broken torso down.
  919. forceVector.z *= -1.0;
  920. }
  921. // Why do I have to fix this up?! (sjb)
  922. QAngle TorsoAngles;
  923. TorsoAngles = GetAbsAngles();
  924. TorsoAngles.x -= 90.0f;
  925. CBaseEntity *pTorsoGib = CreateRagGib( GetTorsoModel(), GetAbsOrigin() + Vector( 0, 0, 64 ), TorsoAngles, forceVector, flFadeTime, ShouldIgniteZombieGib() );
  926. if ( pTorsoGib )
  927. {
  928. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating*>(pTorsoGib);
  929. if( pAnimating )
  930. {
  931. pAnimating->SetBodygroup( ZOMBIE_BODYGROUP_HEADCRAB, !m_fIsHeadless );
  932. }
  933. pTorsoGib->SetOwnerEntity( this );
  934. CopyRenderColorTo( pTorsoGib );
  935. }
  936. if ( UTIL_ShouldShowBlood( BLOOD_COLOR_YELLOW ) )
  937. {
  938. int i;
  939. Vector vecSpot;
  940. Vector vecDir;
  941. for ( i = 0 ; i < 4; i++ )
  942. {
  943. vecSpot = WorldSpaceCenter();
  944. vecSpot.x += random->RandomFloat( -12, 12 );
  945. vecSpot.y += random->RandomFloat( -12, 12 );
  946. vecSpot.z += random->RandomFloat( -4, 16 );
  947. UTIL_BloodDrips( vecSpot, vec3_origin, BLOOD_COLOR_YELLOW, 50 );
  948. }
  949. for ( int i = 0 ; i < 4 ; i++ )
  950. {
  951. Vector vecSpot = WorldSpaceCenter();
  952. vecSpot.x += random->RandomFloat( -12, 12 );
  953. vecSpot.y += random->RandomFloat( -12, 12 );
  954. vecSpot.z += random->RandomFloat( -4, 16 );
  955. vecDir.x = random->RandomFloat(-1, 1);
  956. vecDir.y = random->RandomFloat(-1, 1);
  957. vecDir.z = 0;
  958. VectorNormalize( vecDir );
  959. UTIL_BloodImpact( vecSpot, vecDir, BloodColor(), 1 );
  960. }
  961. }
  962. }
  963. //-----------------------------------------------------------------------------
  964. // Purpose: damage has been done. Should the zombie ignite?
  965. //-----------------------------------------------------------------------------
  966. bool CNPC_BaseZombie::ShouldIgnite( const CTakeDamageInfo &info )
  967. {
  968. if ( IsOnFire() )
  969. {
  970. // Already burning!
  971. return false;
  972. }
  973. if ( info.GetDamageType() & DMG_BURN )
  974. {
  975. //
  976. // If we take more than ten percent of our health in burn damage within a five
  977. // second interval, we should catch on fire.
  978. //
  979. m_flBurnDamage += info.GetDamage();
  980. m_flBurnDamageResetTime = gpGlobals->curtime + 5;
  981. if ( m_flBurnDamage >= m_iMaxHealth * 0.1 )
  982. {
  983. return true;
  984. }
  985. }
  986. return false;
  987. }
  988. //-----------------------------------------------------------------------------
  989. // Purpose: Sufficient fire damage has been done. Zombie ignites!
  990. //-----------------------------------------------------------------------------
  991. void CNPC_BaseZombie::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner )
  992. {
  993. BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
  994. #ifdef HL2_EPISODIC
  995. if ( HL2GameRules()->IsAlyxInDarknessMode() == true && GetEffectEntity() != NULL )
  996. {
  997. GetEffectEntity()->AddEffects( EF_DIMLIGHT );
  998. }
  999. #endif // HL2_EPISODIC
  1000. // Set the zombie up to burn to death in about ten seconds.
  1001. SetHealth( MIN( m_iHealth, FLAME_DIRECT_DAMAGE_PER_SEC * (ZOMBIE_BURN_TIME + random->RandomFloat( -ZOMBIE_BURN_TIME_NOISE, ZOMBIE_BURN_TIME_NOISE)) ) );
  1002. // FIXME: use overlays when they come online
  1003. //AddOverlay( ACT_ZOM_WALK_ON_FIRE, false );
  1004. if( !m_ActBusyBehavior.IsActive() )
  1005. {
  1006. Activity activity = GetActivity();
  1007. Activity burningActivity = activity;
  1008. if ( activity == ACT_WALK )
  1009. {
  1010. burningActivity = ACT_WALK_ON_FIRE;
  1011. }
  1012. else if ( activity == ACT_RUN )
  1013. {
  1014. burningActivity = ACT_RUN_ON_FIRE;
  1015. }
  1016. else if ( activity == ACT_IDLE )
  1017. {
  1018. burningActivity = ACT_IDLE_ON_FIRE;
  1019. }
  1020. if( HaveSequenceForActivity(burningActivity) )
  1021. {
  1022. // Make sure we have a sequence for this activity (torsos don't have any, for instance)
  1023. // to prevent the baseNPC & baseAnimating code from throwing red level errors.
  1024. SetActivity( burningActivity );
  1025. }
  1026. }
  1027. }
  1028. //---------------------------------------------------------
  1029. //---------------------------------------------------------
  1030. void CNPC_BaseZombie::CopyRenderColorTo( CBaseEntity *pOther )
  1031. {
  1032. color32 color = GetRenderColor();
  1033. pOther->SetRenderColor( color.r, color.g, color.b, color.a );
  1034. }
  1035. //-----------------------------------------------------------------------------
  1036. // Purpose: Look in front and see if the claw hit anything.
  1037. //
  1038. // Input : flDist distance to trace
  1039. // iDamage damage to do if attack hits
  1040. // vecViewPunch camera punch (if attack hits player)
  1041. // vecVelocityPunch velocity punch (if attack hits player)
  1042. //
  1043. // Output : The entity hit by claws. NULL if nothing.
  1044. //-----------------------------------------------------------------------------
  1045. CBaseEntity *CNPC_BaseZombie::ClawAttack( float flDist, int iDamage, QAngle &qaViewPunch, Vector &vecVelocityPunch, int BloodOrigin )
  1046. {
  1047. // Added test because claw attack anim sometimes used when for cases other than melee
  1048. int iDriverInitialHealth = -1;
  1049. CBaseEntity *pDriver = NULL;
  1050. if ( GetEnemy() )
  1051. {
  1052. trace_t tr;
  1053. AI_TraceHull( WorldSpaceCenter(), GetEnemy()->WorldSpaceCenter(), -Vector(8,8,8), Vector(8,8,8), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1054. if ( tr.fraction < 1.0f )
  1055. return NULL;
  1056. // CheckTraceHullAttack() can damage player in vehicle as side effect of melee attack damaging physics objects, which the car forwards to the player
  1057. // need to detect this to get correct damage effects
  1058. CBaseCombatCharacter *pCCEnemy = ( GetEnemy() != NULL ) ? GetEnemy()->MyCombatCharacterPointer() : NULL;
  1059. CBaseEntity *pVehicleEntity;
  1060. if ( pCCEnemy != NULL && ( pVehicleEntity = pCCEnemy->GetVehicleEntity() ) != NULL )
  1061. {
  1062. if ( pVehicleEntity->GetServerVehicle() && dynamic_cast<CPropVehicleDriveable *>(pVehicleEntity) )
  1063. {
  1064. pDriver = static_cast<CPropVehicleDriveable *>(pVehicleEntity)->GetDriver();
  1065. if ( pDriver && pDriver->IsPlayer() )
  1066. {
  1067. iDriverInitialHealth = pDriver->GetHealth();
  1068. }
  1069. else
  1070. {
  1071. pDriver = NULL;
  1072. }
  1073. }
  1074. }
  1075. }
  1076. //
  1077. // Trace out a cubic section of our hull and see what we hit.
  1078. //
  1079. Vector vecMins = GetHullMins();
  1080. Vector vecMaxs = GetHullMaxs();
  1081. vecMins.z = vecMins.x;
  1082. vecMaxs.z = vecMaxs.x;
  1083. CBaseEntity *pHurt = NULL;
  1084. if ( GetEnemy() && GetEnemy()->Classify() == CLASS_BULLSEYE )
  1085. {
  1086. // We always hit bullseyes we're targeting
  1087. pHurt = GetEnemy();
  1088. CTakeDamageInfo info( this, this, vec3_origin, GetAbsOrigin(), iDamage, DMG_SLASH );
  1089. pHurt->TakeDamage( info );
  1090. }
  1091. else
  1092. {
  1093. // Try to hit them with a trace
  1094. pHurt = CheckTraceHullAttack( flDist, vecMins, vecMaxs, iDamage, DMG_SLASH );
  1095. }
  1096. if ( pDriver && iDriverInitialHealth != pDriver->GetHealth() )
  1097. {
  1098. pHurt = pDriver;
  1099. }
  1100. if ( !pHurt && m_hPhysicsEnt != NULL && IsCurSchedule(SCHED_ZOMBIE_ATTACKITEM) )
  1101. {
  1102. pHurt = m_hPhysicsEnt;
  1103. Vector vForce = pHurt->WorldSpaceCenter() - WorldSpaceCenter();
  1104. VectorNormalize( vForce );
  1105. vForce *= 5 * 24;
  1106. CTakeDamageInfo info( this, this, vForce, GetAbsOrigin(), iDamage, DMG_SLASH );
  1107. pHurt->TakeDamage( info );
  1108. pHurt = m_hPhysicsEnt;
  1109. }
  1110. if ( pHurt )
  1111. {
  1112. AttackHitSound();
  1113. CBasePlayer *pPlayer = ToBasePlayer( pHurt );
  1114. if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE ) )
  1115. {
  1116. pPlayer->ViewPunch( qaViewPunch );
  1117. pPlayer->VelocityPunch( vecVelocityPunch );
  1118. }
  1119. else if( !pPlayer && UTIL_ShouldShowBlood(pHurt->BloodColor()) )
  1120. {
  1121. // Hit an NPC. Bleed them!
  1122. Vector vecBloodPos;
  1123. switch( BloodOrigin )
  1124. {
  1125. case ZOMBIE_BLOOD_LEFT_HAND:
  1126. if( GetAttachment( "blood_left", vecBloodPos ) )
  1127. SpawnBlood( vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN( iDamage, 30 ) );
  1128. break;
  1129. case ZOMBIE_BLOOD_RIGHT_HAND:
  1130. if( GetAttachment( "blood_right", vecBloodPos ) )
  1131. SpawnBlood( vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN( iDamage, 30 ) );
  1132. break;
  1133. case ZOMBIE_BLOOD_BOTH_HANDS:
  1134. if( GetAttachment( "blood_left", vecBloodPos ) )
  1135. SpawnBlood( vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN( iDamage, 30 ) );
  1136. if( GetAttachment( "blood_right", vecBloodPos ) )
  1137. SpawnBlood( vecBloodPos, g_vecAttackDir, pHurt->BloodColor(), MIN( iDamage, 30 ) );
  1138. break;
  1139. case ZOMBIE_BLOOD_BITE:
  1140. // No blood for these.
  1141. break;
  1142. }
  1143. }
  1144. }
  1145. else
  1146. {
  1147. AttackMissSound();
  1148. }
  1149. if ( pHurt == m_hPhysicsEnt && IsCurSchedule(SCHED_ZOMBIE_ATTACKITEM) )
  1150. {
  1151. m_hPhysicsEnt = NULL;
  1152. m_flNextSwat = gpGlobals->curtime + random->RandomFloat( 2, 4 );
  1153. }
  1154. return pHurt;
  1155. }
  1156. //-----------------------------------------------------------------------------
  1157. // Purpose: The zombie is frustrated and pounding walls/doors. Make an appropriate noise
  1158. // Input :
  1159. //-----------------------------------------------------------------------------
  1160. void CNPC_BaseZombie::PoundSound()
  1161. {
  1162. trace_t tr;
  1163. Vector forward;
  1164. GetVectors( &forward, NULL, NULL );
  1165. AI_TraceLine( EyePosition(), EyePosition() + forward * 128, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1166. if( tr.fraction == 1.0 )
  1167. {
  1168. // Didn't hit anything!
  1169. return;
  1170. }
  1171. if( tr.fraction < 1.0 && tr.m_pEnt )
  1172. {
  1173. const surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );
  1174. if( psurf )
  1175. {
  1176. EmitSound( physprops->GetString(psurf->sounds.impactHard) );
  1177. return;
  1178. }
  1179. }
  1180. // Otherwise fall through to the default sound.
  1181. CPASAttenuationFilter filter( this,"NPC_BaseZombie.PoundDoor" );
  1182. EmitSound( filter, entindex(),"NPC_BaseZombie.PoundDoor" );
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. // Purpose: Catches the monster-specific events that occur when tagged animation
  1186. // frames are played.
  1187. // Input : pEvent -
  1188. //-----------------------------------------------------------------------------
  1189. void CNPC_BaseZombie::HandleAnimEvent( animevent_t *pEvent )
  1190. {
  1191. if ( pEvent->event == AE_NPC_ATTACK_BROADCAST )
  1192. {
  1193. if( GetEnemy() && GetEnemy()->IsNPC() )
  1194. {
  1195. if( HasCondition(COND_CAN_MELEE_ATTACK1) )
  1196. {
  1197. // This animation is sometimes played by code that doesn't intend to attack the enemy
  1198. // (For instance, code that makes a zombie take a frustrated swipe at an obstacle).
  1199. // Try not to trigger a reaction from our enemy unless we're really attacking.
  1200. GetEnemy()->MyNPCPointer()->DispatchInteraction( g_interactionZombieMeleeWarning, NULL, this );
  1201. }
  1202. }
  1203. return;
  1204. }
  1205. if ( pEvent->event == AE_ZOMBIE_POUND )
  1206. {
  1207. PoundSound();
  1208. return;
  1209. }
  1210. if ( pEvent->event == AE_ZOMBIE_ALERTSOUND )
  1211. {
  1212. AlertSound();
  1213. return;
  1214. }
  1215. if ( pEvent->event == AE_ZOMBIE_STEP_LEFT )
  1216. {
  1217. MakeAIFootstepSound( 180.0f );
  1218. FootstepSound( false );
  1219. return;
  1220. }
  1221. if ( pEvent->event == AE_ZOMBIE_STEP_RIGHT )
  1222. {
  1223. MakeAIFootstepSound( 180.0f );
  1224. FootstepSound( true );
  1225. return;
  1226. }
  1227. if ( pEvent->event == AE_ZOMBIE_GET_UP )
  1228. {
  1229. MakeAIFootstepSound( 180.0f, 3.0f );
  1230. if( !IsOnFire() )
  1231. {
  1232. // If you let this code run while a zombie is burning, it will stop wailing.
  1233. m_flNextMoanSound = gpGlobals->curtime;
  1234. MoanSound( envDefaultZombieMoanVolumeFast, ARRAYSIZE( envDefaultZombieMoanVolumeFast ) );
  1235. }
  1236. return;
  1237. }
  1238. if ( pEvent->event == AE_ZOMBIE_SCUFF_LEFT )
  1239. {
  1240. MakeAIFootstepSound( 180.0f );
  1241. FootscuffSound( false );
  1242. return;
  1243. }
  1244. if ( pEvent->event == AE_ZOMBIE_SCUFF_RIGHT )
  1245. {
  1246. MakeAIFootstepSound( 180.0f );
  1247. FootscuffSound( true );
  1248. return;
  1249. }
  1250. // all swat animations are handled as a single case.
  1251. if ( pEvent->event == AE_ZOMBIE_STARTSWAT )
  1252. {
  1253. MakeAIFootstepSound( 180.0f );
  1254. AttackSound();
  1255. return;
  1256. }
  1257. if ( pEvent->event == AE_ZOMBIE_ATTACK_SCREAM )
  1258. {
  1259. AttackSound();
  1260. return;
  1261. }
  1262. if ( pEvent->event == AE_ZOMBIE_SWATITEM )
  1263. {
  1264. CBaseEntity *pEnemy = GetEnemy();
  1265. if ( pEnemy )
  1266. {
  1267. Vector v;
  1268. CBaseEntity *pPhysicsEntity = m_hPhysicsEnt;
  1269. if( !pPhysicsEntity )
  1270. {
  1271. DevMsg( "**Zombie: Missing my physics ent!!" );
  1272. return;
  1273. }
  1274. IPhysicsObject *pPhysObj = pPhysicsEntity->VPhysicsGetObject();
  1275. if( !pPhysObj )
  1276. {
  1277. DevMsg( "**Zombie: No Physics Object for physics Ent!" );
  1278. return;
  1279. }
  1280. EmitSound( "NPC_BaseZombie.Swat" );
  1281. PhysicsImpactSound( pEnemy, pPhysObj, CHAN_BODY, pPhysObj->GetMaterialIndex(), physprops->GetSurfaceIndex("flesh"), 0.5, 800 );
  1282. Vector physicsCenter = pPhysicsEntity->WorldSpaceCenter();
  1283. v = pEnemy->WorldSpaceCenter() - physicsCenter;
  1284. VectorNormalize(v);
  1285. // Send the object at 800 in/sec toward the enemy. Add 200 in/sec up velocity to keep it
  1286. // in the air for a second or so.
  1287. v = v * 800;
  1288. v.z += 200;
  1289. // add some spin so the object doesn't appear to just fly in a straight line
  1290. // Also this spin will move the object slightly as it will press on whatever the object
  1291. // is resting on.
  1292. AngularImpulse angVelocity( random->RandomFloat(-180, 180), 20, random->RandomFloat(-360, 360) );
  1293. pPhysObj->AddVelocity( &v, &angVelocity );
  1294. // If we don't put the object scan time well into the future, the zombie
  1295. // will re-select the object he just hit as it is flying away from him.
  1296. // It will likely always be the nearest object because the zombie moved
  1297. // close enough to it to hit it.
  1298. m_hPhysicsEnt = NULL;
  1299. m_flNextSwatScan = gpGlobals->curtime + ZOMBIE_SWAT_DELAY;
  1300. return;
  1301. }
  1302. }
  1303. if ( pEvent->event == AE_ZOMBIE_ATTACK_RIGHT )
  1304. {
  1305. Vector right, forward;
  1306. AngleVectors( GetLocalAngles(), &forward, &right, NULL );
  1307. right = right * 100;
  1308. forward = forward * 200;
  1309. QAngle qa( -15, -20, -10 );
  1310. Vector vec = right + forward;
  1311. ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetFloat(), qa, vec, ZOMBIE_BLOOD_RIGHT_HAND );
  1312. return;
  1313. }
  1314. if ( pEvent->event == AE_ZOMBIE_ATTACK_LEFT )
  1315. {
  1316. Vector right, forward;
  1317. AngleVectors( GetLocalAngles(), &forward, &right, NULL );
  1318. right = right * -100;
  1319. forward = forward * 200;
  1320. QAngle qa( -15, 20, -10 );
  1321. Vector vec = right + forward;
  1322. ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetFloat(), qa, vec, ZOMBIE_BLOOD_LEFT_HAND );
  1323. return;
  1324. }
  1325. if ( pEvent->event == AE_ZOMBIE_ATTACK_BOTH )
  1326. {
  1327. Vector forward;
  1328. QAngle qaPunch( 45, random->RandomInt(-5,5), random->RandomInt(-5,5) );
  1329. AngleVectors( GetLocalAngles(), &forward );
  1330. forward = forward * 200;
  1331. ClawAttack( GetClawAttackRange(), sk_zombie_dmg_one_slash.GetFloat(), qaPunch, forward, ZOMBIE_BLOOD_BOTH_HANDS );
  1332. return;
  1333. }
  1334. if ( pEvent->event == AE_ZOMBIE_POPHEADCRAB )
  1335. {
  1336. if ( GetInteractionPartner() == NULL )
  1337. return;
  1338. const char *pString = pEvent->options;
  1339. char token[128];
  1340. pString = nexttoken( token, pString, ' ' );
  1341. int boneIndex = GetInteractionPartner()->LookupBone( token );
  1342. if ( boneIndex == -1 )
  1343. {
  1344. Warning( "AE_ZOMBIE_POPHEADCRAB event using invalid bone name! Usage: event AE_ZOMBIE_POPHEADCRAB \"<BoneName> <Speed>\" \n" );
  1345. return;
  1346. }
  1347. pString = nexttoken( token, pString, ' ' );
  1348. if ( !token )
  1349. {
  1350. Warning( "AE_ZOMBIE_POPHEADCRAB event format missing velocity parameter! Usage: event AE_ZOMBIE_POPHEADCRAB \"<BoneName> <Speed>\" \n" );
  1351. return;
  1352. }
  1353. Vector vecBonePosition;
  1354. QAngle angles;
  1355. Vector vecHeadCrabPosition;
  1356. int iCrabAttachment = LookupAttachment( "headcrab" );
  1357. int iSpeed = atoi( token );
  1358. GetInteractionPartner()->GetBonePosition( boneIndex, vecBonePosition, angles );
  1359. GetAttachment( iCrabAttachment, vecHeadCrabPosition );
  1360. Vector vVelocity = vecHeadCrabPosition - vecBonePosition;
  1361. VectorNormalize( vVelocity );
  1362. CTakeDamageInfo dmgInfo( this, GetInteractionPartner(), m_iHealth, DMG_DIRECT );
  1363. dmgInfo.SetDamagePosition( vecHeadCrabPosition );
  1364. ReleaseHeadcrab( EyePosition(), vVelocity * iSpeed, true, false, true );
  1365. GuessDamageForce( &dmgInfo, vVelocity, vecHeadCrabPosition, 0.5f );
  1366. TakeDamage( dmgInfo );
  1367. return;
  1368. }
  1369. BaseClass::HandleAnimEvent( pEvent );
  1370. }
  1371. //-----------------------------------------------------------------------------
  1372. // Purpose: Spawn function for the base zombie.
  1373. //
  1374. // !!!IMPORTANT!!! YOUR DERIVED CLASS'S SPAWN() RESPONSIBILITIES:
  1375. //
  1376. // Call Precache();
  1377. // Set status for m_fIsTorso & m_fIsHeadless
  1378. // Set blood color
  1379. // Set health
  1380. // Set field of view
  1381. // Call CapabilitiesClear() & then set relevant capabilities
  1382. // THEN Call BaseClass::Spawn()
  1383. //-----------------------------------------------------------------------------
  1384. void CNPC_BaseZombie::Spawn( void )
  1385. {
  1386. SetSolid( SOLID_BBOX );
  1387. SetMoveType( MOVETYPE_STEP );
  1388. #ifdef _XBOX
  1389. // Always fade the corpse
  1390. AddSpawnFlags( SF_NPC_FADE_CORPSE );
  1391. #endif // _XBOX
  1392. m_NPCState = NPC_STATE_NONE;
  1393. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_MELEE_ATTACK1 );
  1394. CapabilitiesAdd( bits_CAP_SQUAD );
  1395. m_flNextSwat = gpGlobals->curtime;
  1396. m_flNextSwatScan = gpGlobals->curtime;
  1397. m_pMoanSound = NULL;
  1398. m_flNextMoanSound = gpGlobals->curtime + 9999;
  1399. SetZombieModel();
  1400. NPCInit();
  1401. m_bIsSlumped = false;
  1402. // Zombies get to cheat for 6 seconds (sjb)
  1403. GetEnemies()->SetFreeKnowledgeDuration( 6.0 );
  1404. m_ActBusyBehavior.SetUseRenderBounds(true);
  1405. }
  1406. //-----------------------------------------------------------------------------
  1407. // Purpose: Pecaches all resources this NPC needs.
  1408. //-----------------------------------------------------------------------------
  1409. void CNPC_BaseZombie::Precache( void )
  1410. {
  1411. UTIL_PrecacheOther( GetHeadcrabClassname() );
  1412. PrecacheScriptSound( "E3_Phystown.Slicer" );
  1413. PrecacheScriptSound( "NPC_BaseZombie.PoundDoor" );
  1414. PrecacheScriptSound( "NPC_BaseZombie.Swat" );
  1415. PrecacheModel( GetLegsModel() );
  1416. PrecacheModel( GetTorsoModel() );
  1417. PrecacheParticleSystem( "blood_impact_zombie_01" );
  1418. BaseClass::Precache();
  1419. }
  1420. //---------------------------------------------------------
  1421. //---------------------------------------------------------
  1422. void CNPC_BaseZombie::StartTouch( CBaseEntity *pOther )
  1423. {
  1424. BaseClass::StartTouch( pOther );
  1425. if( IsSlumped() && hl2_episodic.GetBool() )
  1426. {
  1427. if( FClassnameIs( pOther, "prop_physics" ) )
  1428. {
  1429. // Get up!
  1430. m_ActBusyBehavior.StopBusying();
  1431. }
  1432. }
  1433. }
  1434. //---------------------------------------------------------
  1435. //---------------------------------------------------------
  1436. bool CNPC_BaseZombie::CreateBehaviors()
  1437. {
  1438. AddBehavior( &m_ActBusyBehavior );
  1439. return BaseClass::CreateBehaviors();
  1440. }
  1441. //---------------------------------------------------------
  1442. //---------------------------------------------------------
  1443. int CNPC_BaseZombie::TranslateSchedule( int scheduleType )
  1444. {
  1445. switch( scheduleType )
  1446. {
  1447. case SCHED_CHASE_ENEMY:
  1448. if ( HasCondition( COND_ZOMBIE_LOCAL_MELEE_OBSTRUCTION ) && !HasCondition(COND_TASK_FAILED) && IsCurSchedule( SCHED_ZOMBIE_CHASE_ENEMY, false ) )
  1449. {
  1450. return SCHED_COMBAT_PATROL;
  1451. }
  1452. return SCHED_ZOMBIE_CHASE_ENEMY;
  1453. break;
  1454. case SCHED_ZOMBIE_SWATITEM:
  1455. // If the object is far away, move and swat it. If it's close, just swat it.
  1456. if( DistToPhysicsEnt() > ZOMBIE_PHYSOBJ_SWATDIST )
  1457. {
  1458. return SCHED_ZOMBIE_MOVE_SWATITEM;
  1459. }
  1460. else
  1461. {
  1462. return SCHED_ZOMBIE_SWATITEM;
  1463. }
  1464. break;
  1465. case SCHED_STANDOFF:
  1466. return SCHED_ZOMBIE_WANDER_STANDOFF;
  1467. case SCHED_MELEE_ATTACK1:
  1468. return SCHED_ZOMBIE_MELEE_ATTACK1;
  1469. }
  1470. return BaseClass::TranslateSchedule( scheduleType );
  1471. }
  1472. //-----------------------------------------------------------------------------
  1473. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  1474. // In the most cases the base implementation should be called first.
  1475. //-----------------------------------------------------------------------------
  1476. void CNPC_BaseZombie::BuildScheduleTestBits( void )
  1477. {
  1478. // Ignore damage if we were recently damaged or we're attacking.
  1479. if ( GetActivity() == ACT_MELEE_ATTACK1 )
  1480. {
  1481. ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  1482. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  1483. }
  1484. #ifndef HL2_EPISODIC
  1485. else if ( m_flNextFlinch >= gpGlobals->curtime )
  1486. {
  1487. ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  1488. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  1489. }
  1490. #endif // !HL2_EPISODIC
  1491. // Everything should be interrupted if we get killed.
  1492. SetCustomInterruptCondition( COND_ZOMBIE_RELEASECRAB );
  1493. BaseClass::BuildScheduleTestBits();
  1494. }
  1495. //-----------------------------------------------------------------------------
  1496. // Purpose: Called when we change schedules.
  1497. //-----------------------------------------------------------------------------
  1498. void CNPC_BaseZombie::OnScheduleChange( void )
  1499. {
  1500. //
  1501. // If we took damage and changed schedules, ignore further damage for a few seconds.
  1502. //
  1503. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ))
  1504. {
  1505. m_flNextFlinch = gpGlobals->curtime + ZOMBIE_FLINCH_DELAY;
  1506. }
  1507. BaseClass::OnScheduleChange();
  1508. }
  1509. //---------------------------------------------------------
  1510. //---------------------------------------------------------
  1511. int CNPC_BaseZombie::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1512. {
  1513. if( failedSchedule == SCHED_ZOMBIE_WANDER_MEDIUM )
  1514. {
  1515. return SCHED_ZOMBIE_WANDER_FAIL;
  1516. }
  1517. // If we can swat physics objects, see if we can swat our obstructor
  1518. if ( CanSwatPhysicsObjects() )
  1519. {
  1520. if ( !m_fIsTorso && IsPathTaskFailure( taskFailCode ) &&
  1521. m_hObstructor != NULL && m_hObstructor->VPhysicsGetObject() &&
  1522. m_hObstructor->VPhysicsGetObject()->GetMass() < 100 )
  1523. {
  1524. m_hPhysicsEnt = m_hObstructor;
  1525. m_hObstructor = NULL;
  1526. return SCHED_ZOMBIE_ATTACKITEM;
  1527. }
  1528. }
  1529. m_hObstructor = NULL;
  1530. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  1531. }
  1532. //---------------------------------------------------------
  1533. //---------------------------------------------------------
  1534. int CNPC_BaseZombie::SelectSchedule ( void )
  1535. {
  1536. if ( HasCondition( COND_ZOMBIE_RELEASECRAB ) )
  1537. {
  1538. // Death waits for no man. Or zombie. Or something.
  1539. return SCHED_ZOMBIE_RELEASECRAB;
  1540. }
  1541. if ( BehaviorSelectSchedule() )
  1542. {
  1543. return BaseClass::SelectSchedule();
  1544. }
  1545. switch ( m_NPCState )
  1546. {
  1547. case NPC_STATE_COMBAT:
  1548. if ( HasCondition( COND_NEW_ENEMY ) && GetEnemy() )
  1549. {
  1550. float flDist;
  1551. flDist = ( GetLocalOrigin() - GetEnemy()->GetLocalOrigin() ).Length();
  1552. // If this is a new enemy that's far away, ambush!!
  1553. if (flDist >= zombie_ambushdist.GetFloat() && MustCloseToAttack() )
  1554. {
  1555. return SCHED_ZOMBIE_MOVE_TO_AMBUSH;
  1556. }
  1557. }
  1558. if ( HasCondition( COND_LOST_ENEMY ) || ( HasCondition( COND_ENEMY_UNREACHABLE ) && MustCloseToAttack() ) )
  1559. {
  1560. return SCHED_ZOMBIE_WANDER_MEDIUM;
  1561. }
  1562. if( HasCondition( COND_ZOMBIE_CAN_SWAT_ATTACK ) )
  1563. {
  1564. return SCHED_ZOMBIE_SWATITEM;
  1565. }
  1566. break;
  1567. case NPC_STATE_ALERT:
  1568. if ( HasCondition( COND_LOST_ENEMY ) || HasCondition( COND_ENEMY_DEAD ) || ( HasCondition( COND_ENEMY_UNREACHABLE ) && MustCloseToAttack() ) )
  1569. {
  1570. ClearCondition( COND_LOST_ENEMY );
  1571. ClearCondition( COND_ENEMY_UNREACHABLE );
  1572. #ifdef DEBUG_ZOMBIES
  1573. DevMsg("Wandering\n");
  1574. #endif
  1575. // Just lost track of our enemy.
  1576. // Wander around a bit so we don't look like a dingus.
  1577. return SCHED_ZOMBIE_WANDER_MEDIUM;
  1578. }
  1579. break;
  1580. }
  1581. return BaseClass::SelectSchedule();
  1582. }
  1583. //---------------------------------------------------------
  1584. //---------------------------------------------------------
  1585. bool CNPC_BaseZombie::IsSlumped( void )
  1586. {
  1587. if( hl2_episodic.GetBool() )
  1588. {
  1589. if( m_ActBusyBehavior.IsInsideActBusy() && !m_ActBusyBehavior.IsStopBusying() )
  1590. {
  1591. return true;
  1592. }
  1593. }
  1594. else
  1595. {
  1596. int sequence = GetSequence();
  1597. if ( sequence != -1 )
  1598. {
  1599. return ( strncmp( GetSequenceName( sequence ), "slump", 5 ) == 0 );
  1600. }
  1601. }
  1602. return false;
  1603. }
  1604. //---------------------------------------------------------
  1605. //---------------------------------------------------------
  1606. bool CNPC_BaseZombie::IsGettingUp( void )
  1607. {
  1608. if( m_ActBusyBehavior.IsActive() && m_ActBusyBehavior.IsStopBusying() )
  1609. {
  1610. return true;
  1611. }
  1612. return false;
  1613. }
  1614. //---------------------------------------------------------
  1615. //---------------------------------------------------------
  1616. int CNPC_BaseZombie::GetSwatActivity( void )
  1617. {
  1618. // Hafta figure out whether to swat with left or right arm.
  1619. // Also hafta figure out whether to swat high or low. (later)
  1620. float flDot;
  1621. Vector vecRight, vecDirToObj;
  1622. AngleVectors( GetLocalAngles(), NULL, &vecRight, NULL );
  1623. vecDirToObj = m_hPhysicsEnt->GetLocalOrigin() - GetLocalOrigin();
  1624. VectorNormalize(vecDirToObj);
  1625. // compare in 2D.
  1626. vecRight.z = 0.0;
  1627. vecDirToObj.z = 0.0;
  1628. flDot = DotProduct( vecRight, vecDirToObj );
  1629. Vector vecMyCenter;
  1630. Vector vecObjCenter;
  1631. vecMyCenter = WorldSpaceCenter();
  1632. vecObjCenter = m_hPhysicsEnt->WorldSpaceCenter();
  1633. float flZDiff;
  1634. flZDiff = vecMyCenter.z - vecObjCenter.z;
  1635. if( flDot >= 0 )
  1636. {
  1637. // Right
  1638. if( flZDiff < 0 )
  1639. {
  1640. return ACT_ZOM_SWATRIGHTMID;
  1641. }
  1642. return ACT_ZOM_SWATRIGHTLOW;
  1643. }
  1644. else
  1645. {
  1646. // Left
  1647. if( flZDiff < 0 )
  1648. {
  1649. return ACT_ZOM_SWATLEFTMID;
  1650. }
  1651. return ACT_ZOM_SWATLEFTLOW;
  1652. }
  1653. }
  1654. //---------------------------------------------------------
  1655. //---------------------------------------------------------
  1656. void CNPC_BaseZombie::GatherConditions( void )
  1657. {
  1658. ClearCondition( COND_ZOMBIE_LOCAL_MELEE_OBSTRUCTION );
  1659. BaseClass::GatherConditions();
  1660. if( m_NPCState == NPC_STATE_COMBAT && !m_fIsTorso )
  1661. {
  1662. // This check for !m_pPhysicsEnt prevents a crashing bug, but also
  1663. // eliminates the zombie picking a better physics object if one happens to fall
  1664. // between him and the object he's heading for already.
  1665. if( gpGlobals->curtime >= m_flNextSwatScan && (m_hPhysicsEnt == NULL) )
  1666. {
  1667. FindNearestPhysicsObject( ZOMBIE_MAX_PHYSOBJ_MASS );
  1668. m_flNextSwatScan = gpGlobals->curtime + 2.0;
  1669. }
  1670. }
  1671. if( (m_hPhysicsEnt != NULL) && gpGlobals->curtime >= m_flNextSwat && HasCondition( COND_SEE_ENEMY ) && !HasCondition( COND_ZOMBIE_RELEASECRAB ) )
  1672. {
  1673. SetCondition( COND_ZOMBIE_CAN_SWAT_ATTACK );
  1674. }
  1675. else
  1676. {
  1677. ClearCondition( COND_ZOMBIE_CAN_SWAT_ATTACK );
  1678. }
  1679. }
  1680. //---------------------------------------------------------
  1681. //---------------------------------------------------------
  1682. void CNPC_BaseZombie::PrescheduleThink( void )
  1683. {
  1684. BaseClass::PrescheduleThink();
  1685. #if 0
  1686. DevMsg(" ** %d Angry Zombies **\n", s_iAngryZombies );
  1687. #endif
  1688. #if 0
  1689. if( m_NPCState == NPC_STATE_COMBAT )
  1690. {
  1691. // Zombies should make idle sounds in combat
  1692. if( random->RandomInt( 0, 30 ) == 0 )
  1693. {
  1694. IdleSound();
  1695. }
  1696. }
  1697. #endif
  1698. //
  1699. // Cool off if we aren't burned for five seconds or so.
  1700. //
  1701. if ( ( m_flBurnDamageResetTime ) && ( gpGlobals->curtime >= m_flBurnDamageResetTime ) )
  1702. {
  1703. m_flBurnDamage = 0;
  1704. }
  1705. }
  1706. //---------------------------------------------------------
  1707. //---------------------------------------------------------
  1708. void CNPC_BaseZombie::StartTask( const Task_t *pTask )
  1709. {
  1710. switch( pTask->iTask )
  1711. {
  1712. case TASK_ZOMBIE_DIE:
  1713. // Go to ragdoll
  1714. KillMe();
  1715. TaskComplete();
  1716. break;
  1717. case TASK_ZOMBIE_GET_PATH_TO_PHYSOBJ:
  1718. {
  1719. Vector vecGoalPos;
  1720. Vector vecDir;
  1721. vecDir = GetLocalOrigin() - m_hPhysicsEnt->GetLocalOrigin();
  1722. VectorNormalize(vecDir);
  1723. vecDir.z = 0;
  1724. AI_NavGoal_t goal( m_hPhysicsEnt->WorldSpaceCenter() );
  1725. goal.pTarget = m_hPhysicsEnt;
  1726. GetNavigator()->SetGoal( goal );
  1727. TaskComplete();
  1728. }
  1729. break;
  1730. case TASK_ZOMBIE_SWAT_ITEM:
  1731. {
  1732. if( m_hPhysicsEnt == NULL )
  1733. {
  1734. // Physics Object is gone! Probably was an explosive
  1735. // or something else broke it.
  1736. TaskFail("Physics ent NULL");
  1737. }
  1738. else if ( DistToPhysicsEnt() > ZOMBIE_PHYSOBJ_SWATDIST )
  1739. {
  1740. // Physics ent is no longer in range! Probably another zombie swatted it or it moved
  1741. // for some other reason.
  1742. TaskFail( "Physics swat item has moved" );
  1743. }
  1744. else
  1745. {
  1746. SetIdealActivity( (Activity)GetSwatActivity() );
  1747. }
  1748. break;
  1749. }
  1750. break;
  1751. case TASK_ZOMBIE_DELAY_SWAT:
  1752. m_flNextSwat = gpGlobals->curtime + pTask->flTaskData;
  1753. TaskComplete();
  1754. break;
  1755. case TASK_ZOMBIE_RELEASE_HEADCRAB:
  1756. {
  1757. // make the crab look like it's pushing off the body
  1758. Vector vecForward;
  1759. Vector vecVelocity;
  1760. AngleVectors( GetAbsAngles(), &vecForward );
  1761. vecVelocity = vecForward * 30;
  1762. vecVelocity.z += 100;
  1763. ReleaseHeadcrab( EyePosition(), vecVelocity, true, true );
  1764. TaskComplete();
  1765. }
  1766. break;
  1767. case TASK_ZOMBIE_WAIT_POST_MELEE:
  1768. {
  1769. #ifndef HL2_EPISODIC
  1770. TaskComplete();
  1771. return;
  1772. #endif
  1773. // Don't wait when attacking the player
  1774. if ( GetEnemy() && GetEnemy()->IsPlayer() )
  1775. {
  1776. TaskComplete();
  1777. return;
  1778. }
  1779. // Wait a single think
  1780. SetWait( 0.1 );
  1781. }
  1782. break;
  1783. default:
  1784. BaseClass::StartTask( pTask );
  1785. }
  1786. }
  1787. //---------------------------------------------------------
  1788. //---------------------------------------------------------
  1789. void CNPC_BaseZombie::RunTask( const Task_t *pTask )
  1790. {
  1791. switch( pTask->iTask )
  1792. {
  1793. case TASK_ZOMBIE_SWAT_ITEM:
  1794. if( IsActivityFinished() )
  1795. {
  1796. TaskComplete();
  1797. }
  1798. break;
  1799. case TASK_ZOMBIE_WAIT_POST_MELEE:
  1800. {
  1801. if ( IsWaitFinished() )
  1802. {
  1803. TaskComplete();
  1804. }
  1805. }
  1806. break;
  1807. default:
  1808. BaseClass::RunTask( pTask );
  1809. break;
  1810. }
  1811. }
  1812. //---------------------------------------------------------
  1813. // Make the necessary changes to a zombie to make him a
  1814. // torso!
  1815. //---------------------------------------------------------
  1816. void CNPC_BaseZombie::BecomeTorso( const Vector &vecTorsoForce, const Vector &vecLegsForce )
  1817. {
  1818. if( m_fIsTorso )
  1819. {
  1820. DevMsg( "*** Zombie is already a torso!\n" );
  1821. return;
  1822. }
  1823. if( IsOnFire() )
  1824. {
  1825. Extinguish();
  1826. Ignite( 30 );
  1827. }
  1828. if ( !m_fIsHeadless )
  1829. {
  1830. m_iMaxHealth = ZOMBIE_TORSO_HEALTH_FACTOR * m_iMaxHealth;
  1831. m_iHealth = m_iMaxHealth;
  1832. // No more opening doors!
  1833. CapabilitiesRemove( bits_CAP_DOORS_GROUP );
  1834. ClearSchedule( "Becoming torso" );
  1835. GetNavigator()->ClearGoal();
  1836. m_hPhysicsEnt = NULL;
  1837. // Put the zombie in a TOSS / fall schedule
  1838. // Otherwise he fails and sits on the ground for a sec.
  1839. SetSchedule( SCHED_FALL_TO_GROUND );
  1840. m_fIsTorso = true;
  1841. // Put the torso up where the torso was when the zombie
  1842. // was whole.
  1843. Vector origin = GetAbsOrigin();
  1844. origin.z += 40;
  1845. SetAbsOrigin( origin );
  1846. SetGroundEntity( NULL );
  1847. // assume zombie mass ~ 100 kg
  1848. ApplyAbsVelocityImpulse( vecTorsoForce * (1.0 / 100.0) );
  1849. }
  1850. float flFadeTime = 0.0;
  1851. if( HasSpawnFlags( SF_NPC_FADE_CORPSE ) )
  1852. {
  1853. flFadeTime = 5.0;
  1854. }
  1855. if ( m_fIsTorso == true )
  1856. {
  1857. // -40 on Z to make up for the +40 on Z that we did above. This stops legs spawning above the head.
  1858. CBaseEntity *pGib = CreateRagGib( GetLegsModel(), GetAbsOrigin() - Vector(0, 0, 40), GetAbsAngles(), vecLegsForce, flFadeTime );
  1859. // don't collide with this thing ever
  1860. if ( pGib )
  1861. {
  1862. pGib->SetOwnerEntity( this );
  1863. }
  1864. }
  1865. SetZombieModel();
  1866. }
  1867. //---------------------------------------------------------
  1868. //---------------------------------------------------------
  1869. void CNPC_BaseZombie::Event_Killed( const CTakeDamageInfo &info )
  1870. {
  1871. if ( info.GetDamageType() & DMG_VEHICLE )
  1872. {
  1873. Vector vecDamageDir = info.GetDamageForce();
  1874. VectorNormalize( vecDamageDir );
  1875. // Big blood splat
  1876. UTIL_BloodSpray( WorldSpaceCenter(), vecDamageDir, BLOOD_COLOR_YELLOW, 8, FX_BLOODSPRAY_CLOUD );
  1877. }
  1878. BaseClass::Event_Killed( info );
  1879. }
  1880. //---------------------------------------------------------
  1881. //---------------------------------------------------------
  1882. bool CNPC_BaseZombie::BecomeRagdoll( const CTakeDamageInfo &info, const Vector &forceVector )
  1883. {
  1884. bool bKilledByVehicle = ( ( info.GetDamageType() & DMG_VEHICLE ) != 0 );
  1885. if( m_fIsTorso || (!IsChopped(info) && !IsSquashed(info)) || bKilledByVehicle )
  1886. {
  1887. return BaseClass::BecomeRagdoll( info, forceVector );
  1888. }
  1889. if( !(GetFlags()&FL_TRANSRAGDOLL) )
  1890. {
  1891. RemoveDeferred();
  1892. }
  1893. return true;
  1894. }
  1895. //---------------------------------------------------------
  1896. //---------------------------------------------------------
  1897. void CNPC_BaseZombie::StopLoopingSounds()
  1898. {
  1899. ENVELOPE_CONTROLLER.SoundDestroy( m_pMoanSound );
  1900. m_pMoanSound = NULL;
  1901. BaseClass::StopLoopingSounds();
  1902. }
  1903. //---------------------------------------------------------
  1904. //---------------------------------------------------------
  1905. void CNPC_BaseZombie::RemoveHead( void )
  1906. {
  1907. m_fIsHeadless = true;
  1908. SetZombieModel();
  1909. }
  1910. bool CNPC_BaseZombie::ShouldPlayFootstepMoan( void )
  1911. {
  1912. if( random->RandomInt( 1, zombie_stepfreq.GetInt() * s_iAngryZombies ) == 1 )
  1913. {
  1914. return true;
  1915. }
  1916. return false;
  1917. }
  1918. #define ZOMBIE_CRAB_INHERITED_SPAWNFLAGS (SF_NPC_GAG|SF_NPC_LONG_RANGE|SF_NPC_FADE_CORPSE|SF_NPC_ALWAYSTHINK)
  1919. #define CRAB_HULL_EXPAND 1.1f
  1920. //-----------------------------------------------------------------------------
  1921. //-----------------------------------------------------------------------------
  1922. bool CNPC_BaseZombie::HeadcrabFits( CBaseAnimating *pCrab )
  1923. {
  1924. Vector vecSpawnLoc = pCrab->GetAbsOrigin();
  1925. CTraceFilterSimpleList traceFilter( COLLISION_GROUP_NONE );
  1926. traceFilter.AddEntityToIgnore( pCrab );
  1927. traceFilter.AddEntityToIgnore( this );
  1928. if ( GetInteractionPartner() )
  1929. {
  1930. traceFilter.AddEntityToIgnore( GetInteractionPartner() );
  1931. }
  1932. trace_t tr;
  1933. AI_TraceHull( vecSpawnLoc,
  1934. vecSpawnLoc - Vector( 0, 0, 1 ),
  1935. NAI_Hull::Mins(HULL_TINY) * CRAB_HULL_EXPAND,
  1936. NAI_Hull::Maxs(HULL_TINY) * CRAB_HULL_EXPAND,
  1937. MASK_NPCSOLID,
  1938. &traceFilter,
  1939. &tr );
  1940. if( tr.fraction != 1.0 )
  1941. {
  1942. //NDebugOverlay::Box( vecSpawnLoc, NAI_Hull::Mins(HULL_TINY) * CRAB_HULL_EXPAND, NAI_Hull::Maxs(HULL_TINY) * CRAB_HULL_EXPAND, 255, 0, 0, 100, 10.0 );
  1943. return false;
  1944. }
  1945. //NDebugOverlay::Box( vecSpawnLoc, NAI_Hull::Mins(HULL_TINY) * CRAB_HULL_EXPAND, NAI_Hull::Maxs(HULL_TINY) * CRAB_HULL_EXPAND, 0, 255, 0, 100, 10.0 );
  1946. return true;
  1947. }
  1948. //-----------------------------------------------------------------------------
  1949. // Purpose:
  1950. // Input : &vecOrigin -
  1951. // &vecVelocity -
  1952. // fRemoveHead -
  1953. // fRagdollBody -
  1954. //-----------------------------------------------------------------------------
  1955. void CNPC_BaseZombie::ReleaseHeadcrab( const Vector &vecOrigin, const Vector &vecVelocity, bool fRemoveHead, bool fRagdollBody, bool fRagdollCrab )
  1956. {
  1957. CAI_BaseNPC *pCrab;
  1958. Vector vecSpot = vecOrigin;
  1959. // Until the headcrab is a bodygroup, we have to approximate the
  1960. // location of the head with magic numbers.
  1961. if( !m_fIsTorso )
  1962. {
  1963. vecSpot.z -= 16;
  1964. }
  1965. if( fRagdollCrab )
  1966. {
  1967. //Vector vecForce = Vector( 0, 0, random->RandomFloat( 700, 1100 ) );
  1968. CBaseEntity *pGib = CreateRagGib( GetHeadcrabModel(), vecOrigin, GetLocalAngles(), vecVelocity, 15, ShouldIgniteZombieGib() );
  1969. if ( pGib )
  1970. {
  1971. CBaseAnimating *pAnimatingGib = dynamic_cast<CBaseAnimating*>(pGib);
  1972. // don't collide with this thing ever
  1973. int iCrabAttachment = LookupAttachment( "headcrab" );
  1974. if (iCrabAttachment > 0 && pAnimatingGib )
  1975. {
  1976. SetHeadcrabSpawnLocation( iCrabAttachment, pAnimatingGib );
  1977. }
  1978. if( !HeadcrabFits(pAnimatingGib) )
  1979. {
  1980. UTIL_Remove(pGib);
  1981. return;
  1982. }
  1983. pGib->SetOwnerEntity( this );
  1984. CopyRenderColorTo( pGib );
  1985. if( UTIL_ShouldShowBlood(BLOOD_COLOR_YELLOW) )
  1986. {
  1987. UTIL_BloodImpact( pGib->WorldSpaceCenter(), Vector(0,0,1), BLOOD_COLOR_YELLOW, 1 );
  1988. for ( int i = 0 ; i < 3 ; i++ )
  1989. {
  1990. Vector vecSpot = pGib->WorldSpaceCenter();
  1991. vecSpot.x += random->RandomFloat( -8, 8 );
  1992. vecSpot.y += random->RandomFloat( -8, 8 );
  1993. vecSpot.z += random->RandomFloat( -8, 8 );
  1994. UTIL_BloodDrips( vecSpot, vec3_origin, BLOOD_COLOR_YELLOW, 50 );
  1995. }
  1996. }
  1997. }
  1998. }
  1999. else
  2000. {
  2001. pCrab = (CAI_BaseNPC*)CreateEntityByName( GetHeadcrabClassname() );
  2002. if ( !pCrab )
  2003. {
  2004. Warning( "**%s: Can't make %s!\n", GetClassname(), GetHeadcrabClassname() );
  2005. return;
  2006. }
  2007. // Stick the crab in whatever squad the zombie was in.
  2008. pCrab->SetSquadName( m_SquadName );
  2009. // don't pop to floor, fall
  2010. pCrab->AddSpawnFlags( SF_NPC_FALL_TO_GROUND );
  2011. // add on the parent flags
  2012. pCrab->AddSpawnFlags( m_spawnflags & ZOMBIE_CRAB_INHERITED_SPAWNFLAGS );
  2013. // make me the crab's owner to avoid collision issues
  2014. pCrab->SetOwnerEntity( this );
  2015. pCrab->SetAbsOrigin( vecSpot );
  2016. pCrab->SetAbsAngles( GetAbsAngles() );
  2017. DispatchSpawn( pCrab );
  2018. pCrab->GetMotor()->SetIdealYaw( GetAbsAngles().y );
  2019. // FIXME: npc's with multiple headcrabs will need some way to query different attachments.
  2020. // NOTE: this has till after spawn is called so that the model is set up
  2021. int iCrabAttachment = LookupAttachment( "headcrab" );
  2022. if (iCrabAttachment > 0)
  2023. {
  2024. SetHeadcrabSpawnLocation( iCrabAttachment, pCrab );
  2025. pCrab->GetMotor()->SetIdealYaw( pCrab->GetAbsAngles().y );
  2026. // Take out any pitch
  2027. QAngle angles = pCrab->GetAbsAngles();
  2028. angles.x = 0.0;
  2029. pCrab->SetAbsAngles( angles );
  2030. }
  2031. if( !HeadcrabFits(pCrab) )
  2032. {
  2033. UTIL_Remove(pCrab);
  2034. return;
  2035. }
  2036. pCrab->SetActivity( ACT_IDLE );
  2037. pCrab->SetNextThink( gpGlobals->curtime );
  2038. pCrab->PhysicsSimulate();
  2039. pCrab->SetAbsVelocity( vecVelocity );
  2040. // if I have an enemy, stuff that to the headcrab.
  2041. CBaseEntity *pEnemy;
  2042. pEnemy = GetEnemy();
  2043. pCrab->m_flNextAttack = gpGlobals->curtime + 1.0f;
  2044. if( pEnemy )
  2045. {
  2046. pCrab->SetEnemy( pEnemy );
  2047. }
  2048. if( ShouldIgniteZombieGib() )
  2049. {
  2050. pCrab->Ignite( 30 );
  2051. }
  2052. CopyRenderColorTo( pCrab );
  2053. pCrab->Activate();
  2054. }
  2055. if( fRemoveHead )
  2056. {
  2057. RemoveHead();
  2058. }
  2059. if( fRagdollBody )
  2060. {
  2061. BecomeRagdollOnClient( vec3_origin );
  2062. }
  2063. }
  2064. void CNPC_BaseZombie::SetHeadcrabSpawnLocation( int iCrabAttachment, CBaseAnimating *pCrab )
  2065. {
  2066. Assert( iCrabAttachment > 0 );
  2067. // get world location of intended headcrab root bone
  2068. matrix3x4_t attachmentToWorld;
  2069. GetAttachment( iCrabAttachment, attachmentToWorld );
  2070. // find offset of root bone from origin
  2071. pCrab->SetAbsOrigin( Vector( 0, 0, 0 ) );
  2072. pCrab->SetAbsAngles( QAngle( 0, 0, 0 ) );
  2073. pCrab->InvalidateBoneCache();
  2074. matrix3x4_t rootLocal;
  2075. pCrab->GetBoneTransform( 0, rootLocal );
  2076. // invert it
  2077. matrix3x4_t rootInvLocal;
  2078. MatrixInvert( rootLocal, rootInvLocal );
  2079. // find spawn location needed for rootLocal transform to match attachmentToWorld
  2080. matrix3x4_t spawnOrigin;
  2081. ConcatTransforms( attachmentToWorld, rootInvLocal, spawnOrigin );
  2082. // reset location of headcrab
  2083. Vector vecOrigin;
  2084. QAngle vecAngles;
  2085. MatrixAngles( spawnOrigin, vecAngles, vecOrigin );
  2086. pCrab->SetAbsOrigin( vecOrigin );
  2087. // FIXME: head crabs don't like pitch or roll!
  2088. vecAngles.z = 0;
  2089. pCrab->SetAbsAngles( vecAngles );
  2090. pCrab->InvalidateBoneCache();
  2091. }
  2092. //---------------------------------------------------------
  2093. // Provides a standard way for the zombie to get the
  2094. // distance to a physics ent. Since the code to find physics
  2095. // objects uses a fast dis approx, we have to use that here
  2096. // as well.
  2097. //---------------------------------------------------------
  2098. float CNPC_BaseZombie::DistToPhysicsEnt( void )
  2099. {
  2100. //return ( GetLocalOrigin() - m_hPhysicsEnt->GetLocalOrigin() ).Length();
  2101. if ( m_hPhysicsEnt != NULL )
  2102. return UTIL_DistApprox2D( GetAbsOrigin(), m_hPhysicsEnt->WorldSpaceCenter() );
  2103. return ZOMBIE_PHYSOBJ_SWATDIST + 1;
  2104. }
  2105. //-----------------------------------------------------------------------------
  2106. //-----------------------------------------------------------------------------
  2107. void CNPC_BaseZombie::OnStateChange( NPC_STATE OldState, NPC_STATE NewState )
  2108. {
  2109. switch( NewState )
  2110. {
  2111. case NPC_STATE_COMBAT:
  2112. {
  2113. RemoveSpawnFlags( SF_NPC_GAG );
  2114. s_iAngryZombies++;
  2115. }
  2116. break;
  2117. default:
  2118. if( OldState == NPC_STATE_COMBAT )
  2119. {
  2120. // Only decrement if coming OUT of combat state.
  2121. s_iAngryZombies--;
  2122. }
  2123. break;
  2124. }
  2125. }
  2126. //-----------------------------------------------------------------------------
  2127. // Purpose: Refines a base activity into something more specific to our internal state.
  2128. //-----------------------------------------------------------------------------
  2129. Activity CNPC_BaseZombie::NPC_TranslateActivity( Activity baseAct )
  2130. {
  2131. if ( baseAct == ACT_WALK && IsCurSchedule( SCHED_COMBAT_PATROL, false) )
  2132. baseAct = ACT_RUN;
  2133. if ( IsOnFire() )
  2134. {
  2135. switch ( baseAct )
  2136. {
  2137. case ACT_RUN_ON_FIRE:
  2138. {
  2139. return ( Activity )ACT_WALK_ON_FIRE;
  2140. }
  2141. case ACT_WALK:
  2142. {
  2143. // I'm on fire. Put ME out.
  2144. return ( Activity )ACT_WALK_ON_FIRE;
  2145. }
  2146. case ACT_IDLE:
  2147. {
  2148. // I'm on fire. Put ME out.
  2149. return ( Activity )ACT_IDLE_ON_FIRE;
  2150. }
  2151. }
  2152. }
  2153. return BaseClass::NPC_TranslateActivity( baseAct );
  2154. }
  2155. //-----------------------------------------------------------------------------
  2156. //-----------------------------------------------------------------------------
  2157. Vector CNPC_BaseZombie::BodyTarget( const Vector &posSrc, bool bNoisy )
  2158. {
  2159. if( IsCurSchedule(SCHED_BIG_FLINCH) || m_ActBusyBehavior.IsActive() )
  2160. {
  2161. // This zombie is assumed to be standing up.
  2162. // Return a position that's centered over the absorigin,
  2163. // halfway between the origin and the head.
  2164. Vector vecTarget = GetAbsOrigin();
  2165. Vector vecHead = HeadTarget( posSrc );
  2166. vecTarget.z = ((vecTarget.z + vecHead.z) * 0.5f);
  2167. return vecTarget;
  2168. }
  2169. return BaseClass::BodyTarget( posSrc, bNoisy );
  2170. }
  2171. //-----------------------------------------------------------------------------
  2172. //-----------------------------------------------------------------------------
  2173. Vector CNPC_BaseZombie::HeadTarget( const Vector &posSrc )
  2174. {
  2175. int iCrabAttachment = LookupAttachment( "headcrab" );
  2176. Assert( iCrabAttachment > 0 );
  2177. Vector vecPosition;
  2178. GetAttachment( iCrabAttachment, vecPosition );
  2179. return vecPosition;
  2180. }
  2181. //-----------------------------------------------------------------------------
  2182. //-----------------------------------------------------------------------------
  2183. float CNPC_BaseZombie::GetAutoAimRadius()
  2184. {
  2185. if( m_fIsTorso )
  2186. {
  2187. return 12.0f;
  2188. }
  2189. return BaseClass::GetAutoAimRadius();
  2190. }
  2191. //-----------------------------------------------------------------------------
  2192. //-----------------------------------------------------------------------------
  2193. bool CNPC_BaseZombie::OnInsufficientStopDist( AILocalMoveGoal_t *pMoveGoal, float distClear, AIMoveResult_t *pResult )
  2194. {
  2195. if ( pMoveGoal->directTrace.fStatus == AIMR_BLOCKED_ENTITY && gpGlobals->curtime >= m_flNextSwat )
  2196. {
  2197. m_hObstructor = pMoveGoal->directTrace.pObstruction;
  2198. }
  2199. return false;
  2200. }
  2201. //-----------------------------------------------------------------------------
  2202. // Purpose:
  2203. // Input : *pEnemy -
  2204. // &chasePosition -
  2205. //-----------------------------------------------------------------------------
  2206. void CNPC_BaseZombie::TranslateNavGoal( CBaseEntity *pEnemy, Vector &chasePosition )
  2207. {
  2208. // If our enemy is in a vehicle, we need them to tell us where to navigate to them
  2209. if ( pEnemy == NULL )
  2210. return;
  2211. CBaseCombatCharacter *pBCC = pEnemy->MyCombatCharacterPointer();
  2212. if ( pBCC && pBCC->IsInAVehicle() )
  2213. {
  2214. Vector vecForward, vecRight;
  2215. pBCC->GetVectors( &vecForward, &vecRight, NULL );
  2216. chasePosition = pBCC->WorldSpaceCenter() + ( vecForward * 24.0f ) + ( vecRight * 48.0f );
  2217. return;
  2218. }
  2219. BaseClass::TranslateNavGoal( pEnemy, chasePosition );
  2220. }
  2221. //-----------------------------------------------------------------------------
  2222. //
  2223. // Schedules
  2224. //
  2225. //-----------------------------------------------------------------------------
  2226. AI_BEGIN_CUSTOM_NPC( base_zombie, CNPC_BaseZombie )
  2227. DECLARE_TASK( TASK_ZOMBIE_DELAY_SWAT )
  2228. DECLARE_TASK( TASK_ZOMBIE_SWAT_ITEM )
  2229. DECLARE_TASK( TASK_ZOMBIE_GET_PATH_TO_PHYSOBJ )
  2230. DECLARE_TASK( TASK_ZOMBIE_DIE )
  2231. DECLARE_TASK( TASK_ZOMBIE_RELEASE_HEADCRAB )
  2232. DECLARE_TASK( TASK_ZOMBIE_WAIT_POST_MELEE )
  2233. DECLARE_ACTIVITY( ACT_ZOM_SWATLEFTMID )
  2234. DECLARE_ACTIVITY( ACT_ZOM_SWATRIGHTMID )
  2235. DECLARE_ACTIVITY( ACT_ZOM_SWATLEFTLOW )
  2236. DECLARE_ACTIVITY( ACT_ZOM_SWATRIGHTLOW )
  2237. DECLARE_ACTIVITY( ACT_ZOM_RELEASECRAB )
  2238. DECLARE_ACTIVITY( ACT_ZOM_FALL )
  2239. DECLARE_CONDITION( COND_ZOMBIE_CAN_SWAT_ATTACK )
  2240. DECLARE_CONDITION( COND_ZOMBIE_RELEASECRAB )
  2241. DECLARE_CONDITION( COND_ZOMBIE_LOCAL_MELEE_OBSTRUCTION )
  2242. //Adrian: events go here
  2243. DECLARE_ANIMEVENT( AE_ZOMBIE_ATTACK_RIGHT )
  2244. DECLARE_ANIMEVENT( AE_ZOMBIE_ATTACK_LEFT )
  2245. DECLARE_ANIMEVENT( AE_ZOMBIE_ATTACK_BOTH )
  2246. DECLARE_ANIMEVENT( AE_ZOMBIE_SWATITEM )
  2247. DECLARE_ANIMEVENT( AE_ZOMBIE_STARTSWAT )
  2248. DECLARE_ANIMEVENT( AE_ZOMBIE_STEP_LEFT )
  2249. DECLARE_ANIMEVENT( AE_ZOMBIE_STEP_RIGHT )
  2250. DECLARE_ANIMEVENT( AE_ZOMBIE_SCUFF_LEFT )
  2251. DECLARE_ANIMEVENT( AE_ZOMBIE_SCUFF_RIGHT )
  2252. DECLARE_ANIMEVENT( AE_ZOMBIE_ATTACK_SCREAM )
  2253. DECLARE_ANIMEVENT( AE_ZOMBIE_GET_UP )
  2254. DECLARE_ANIMEVENT( AE_ZOMBIE_POUND )
  2255. DECLARE_ANIMEVENT( AE_ZOMBIE_ALERTSOUND )
  2256. DECLARE_ANIMEVENT( AE_ZOMBIE_POPHEADCRAB )
  2257. DECLARE_INTERACTION( g_interactionZombieMeleeWarning )
  2258. DEFINE_SCHEDULE
  2259. (
  2260. SCHED_ZOMBIE_MOVE_SWATITEM,
  2261. " Tasks"
  2262. " TASK_ZOMBIE_DELAY_SWAT 3"
  2263. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  2264. " TASK_ZOMBIE_GET_PATH_TO_PHYSOBJ 0"
  2265. " TASK_WALK_PATH 0"
  2266. " TASK_WAIT_FOR_MOVEMENT 0"
  2267. " TASK_FACE_ENEMY 0"
  2268. " TASK_ZOMBIE_SWAT_ITEM 0"
  2269. " "
  2270. " Interrupts"
  2271. " COND_ZOMBIE_RELEASECRAB"
  2272. " COND_ENEMY_DEAD"
  2273. " COND_NEW_ENEMY"
  2274. )
  2275. //=========================================================
  2276. // SwatItem
  2277. //=========================================================
  2278. DEFINE_SCHEDULE
  2279. (
  2280. SCHED_ZOMBIE_SWATITEM,
  2281. " Tasks"
  2282. " TASK_ZOMBIE_DELAY_SWAT 3"
  2283. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY"
  2284. " TASK_FACE_ENEMY 0"
  2285. " TASK_ZOMBIE_SWAT_ITEM 0"
  2286. " "
  2287. " Interrupts"
  2288. " COND_ZOMBIE_RELEASECRAB"
  2289. " COND_ENEMY_DEAD"
  2290. " COND_NEW_ENEMY"
  2291. )
  2292. //=========================================================
  2293. //=========================================================
  2294. DEFINE_SCHEDULE
  2295. (
  2296. SCHED_ZOMBIE_ATTACKITEM,
  2297. " Tasks"
  2298. " TASK_FACE_ENEMY 0"
  2299. " TASK_MELEE_ATTACK1 0"
  2300. " "
  2301. " Interrupts"
  2302. " COND_ZOMBIE_RELEASECRAB"
  2303. " COND_ENEMY_DEAD"
  2304. " COND_NEW_ENEMY"
  2305. )
  2306. //=========================================================
  2307. // ChaseEnemy
  2308. //=========================================================
  2309. #ifdef HL2_EPISODIC
  2310. DEFINE_SCHEDULE
  2311. (
  2312. SCHED_ZOMBIE_CHASE_ENEMY,
  2313. " Tasks"
  2314. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  2315. " TASK_SET_TOLERANCE_DISTANCE 24"
  2316. " TASK_GET_CHASE_PATH_TO_ENEMY 600"
  2317. " TASK_RUN_PATH 0"
  2318. " TASK_WAIT_FOR_MOVEMENT 0"
  2319. " TASK_FACE_ENEMY 0"
  2320. " "
  2321. " Interrupts"
  2322. " COND_NEW_ENEMY"
  2323. " COND_ENEMY_DEAD"
  2324. " COND_ENEMY_UNREACHABLE"
  2325. " COND_CAN_RANGE_ATTACK1"
  2326. " COND_CAN_MELEE_ATTACK1"
  2327. " COND_CAN_RANGE_ATTACK2"
  2328. " COND_CAN_MELEE_ATTACK2"
  2329. " COND_TOO_CLOSE_TO_ATTACK"
  2330. " COND_TASK_FAILED"
  2331. " COND_ZOMBIE_CAN_SWAT_ATTACK"
  2332. " COND_ZOMBIE_RELEASECRAB"
  2333. " COND_HEAVY_DAMAGE"
  2334. )
  2335. #else
  2336. DEFINE_SCHEDULE
  2337. (
  2338. SCHED_ZOMBIE_CHASE_ENEMY,
  2339. " Tasks"
  2340. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  2341. " TASK_SET_TOLERANCE_DISTANCE 24"
  2342. " TASK_GET_CHASE_PATH_TO_ENEMY 600"
  2343. " TASK_RUN_PATH 0"
  2344. " TASK_WAIT_FOR_MOVEMENT 0"
  2345. " TASK_FACE_ENEMY 0"
  2346. " "
  2347. " Interrupts"
  2348. " COND_NEW_ENEMY"
  2349. " COND_ENEMY_DEAD"
  2350. " COND_ENEMY_UNREACHABLE"
  2351. " COND_CAN_RANGE_ATTACK1"
  2352. " COND_CAN_MELEE_ATTACK1"
  2353. " COND_CAN_RANGE_ATTACK2"
  2354. " COND_CAN_MELEE_ATTACK2"
  2355. " COND_TOO_CLOSE_TO_ATTACK"
  2356. " COND_TASK_FAILED"
  2357. " COND_ZOMBIE_CAN_SWAT_ATTACK"
  2358. " COND_ZOMBIE_RELEASECRAB"
  2359. )
  2360. #endif // HL2_EPISODIC
  2361. //=========================================================
  2362. //=========================================================
  2363. DEFINE_SCHEDULE
  2364. (
  2365. SCHED_ZOMBIE_RELEASECRAB,
  2366. " Tasks"
  2367. " TASK_PLAY_PRIVATE_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_ZOM_RELEASECRAB"
  2368. " TASK_ZOMBIE_RELEASE_HEADCRAB 0"
  2369. " TASK_ZOMBIE_DIE 0"
  2370. " "
  2371. " Interrupts"
  2372. " COND_TASK_FAILED"
  2373. )
  2374. //=========================================================
  2375. //=========================================================
  2376. DEFINE_SCHEDULE
  2377. (
  2378. SCHED_ZOMBIE_MOVE_TO_AMBUSH,
  2379. " Tasks"
  2380. " TASK_WAIT 1.0" // don't react as soon as you see the player.
  2381. " TASK_FIND_COVER_FROM_ENEMY 0"
  2382. " TASK_WALK_PATH 0"
  2383. " TASK_WAIT_FOR_MOVEMENT 0"
  2384. " TASK_STOP_MOVING 0"
  2385. " TASK_TURN_LEFT 180"
  2386. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ZOMBIE_WAIT_AMBUSH"
  2387. " "
  2388. " Interrupts"
  2389. " COND_TASK_FAILED"
  2390. " COND_NEW_ENEMY"
  2391. )
  2392. //=========================================================
  2393. //=========================================================
  2394. DEFINE_SCHEDULE
  2395. (
  2396. SCHED_ZOMBIE_WAIT_AMBUSH,
  2397. " Tasks"
  2398. " TASK_WAIT_FACE_ENEMY 99999"
  2399. " "
  2400. " Interrupts"
  2401. " COND_NEW_ENEMY"
  2402. " COND_SEE_ENEMY"
  2403. )
  2404. //=========================================================
  2405. // Wander around for a while so we don't look stupid.
  2406. // this is done if we ever lose track of our enemy.
  2407. //=========================================================
  2408. DEFINE_SCHEDULE
  2409. (
  2410. SCHED_ZOMBIE_WANDER_MEDIUM,
  2411. " Tasks"
  2412. " TASK_STOP_MOVING 0"
  2413. " TASK_WANDER 480384" // 4 feet to 32 feet
  2414. " TASK_WALK_PATH 0"
  2415. " TASK_WAIT_FOR_MOVEMENT 0"
  2416. " TASK_STOP_MOVING 0"
  2417. " TASK_WAIT_PVS 0" // if the player left my PVS, just wait.
  2418. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ZOMBIE_WANDER_MEDIUM" // keep doing it
  2419. " "
  2420. " Interrupts"
  2421. " COND_NEW_ENEMY"
  2422. " COND_SEE_ENEMY"
  2423. " COND_LIGHT_DAMAGE"
  2424. " COND_HEAVY_DAMAGE"
  2425. )
  2426. DEFINE_SCHEDULE
  2427. (
  2428. SCHED_ZOMBIE_WANDER_STANDOFF,
  2429. " Tasks"
  2430. " TASK_STOP_MOVING 0"
  2431. " TASK_WANDER 480384" // 4 feet to 32 feet
  2432. " TASK_WALK_PATH 0"
  2433. " TASK_WAIT_FOR_MOVEMENT 0"
  2434. " TASK_STOP_MOVING 0"
  2435. " TASK_WAIT_PVS 0" // if the player left my PVS, just wait.
  2436. " "
  2437. " Interrupts"
  2438. " COND_NEW_ENEMY"
  2439. " COND_LIGHT_DAMAGE"
  2440. " COND_HEAVY_DAMAGE"
  2441. " COND_ENEMY_DEAD"
  2442. " COND_CAN_RANGE_ATTACK1"
  2443. " COND_CAN_MELEE_ATTACK1"
  2444. " COND_CAN_RANGE_ATTACK2"
  2445. " COND_CAN_MELEE_ATTACK2"
  2446. " COND_ZOMBIE_RELEASECRAB"
  2447. )
  2448. //=========================================================
  2449. // If you fail to wander, wait just a bit and try again.
  2450. //=========================================================
  2451. DEFINE_SCHEDULE
  2452. (
  2453. SCHED_ZOMBIE_WANDER_FAIL,
  2454. " Tasks"
  2455. " TASK_STOP_MOVING 0"
  2456. " TASK_WAIT 1"
  2457. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ZOMBIE_WANDER_MEDIUM"
  2458. " Interrupts"
  2459. " COND_NEW_ENEMY"
  2460. " COND_LIGHT_DAMAGE"
  2461. " COND_HEAVY_DAMAGE"
  2462. " COND_ENEMY_DEAD"
  2463. " COND_CAN_RANGE_ATTACK1"
  2464. " COND_CAN_MELEE_ATTACK1"
  2465. " COND_CAN_RANGE_ATTACK2"
  2466. " COND_CAN_MELEE_ATTACK2"
  2467. " COND_ZOMBIE_RELEASECRAB"
  2468. )
  2469. //=========================================================
  2470. // Like the base class, only don't stop in the middle of
  2471. // swinging if the enemy is killed, hides, or new enemy.
  2472. //=========================================================
  2473. DEFINE_SCHEDULE
  2474. (
  2475. SCHED_ZOMBIE_MELEE_ATTACK1,
  2476. " Tasks"
  2477. " TASK_STOP_MOVING 0"
  2478. " TASK_FACE_ENEMY 0"
  2479. " TASK_ANNOUNCE_ATTACK 1" // 1 = primary attack
  2480. " TASK_MELEE_ATTACK1 0"
  2481. " TASK_SET_SCHEDULE SCHEDULE:SCHED_ZOMBIE_POST_MELEE_WAIT"
  2482. ""
  2483. " Interrupts"
  2484. " COND_LIGHT_DAMAGE"
  2485. " COND_HEAVY_DAMAGE"
  2486. )
  2487. //=========================================================
  2488. // Make the zombie wait a frame after a melee attack, to
  2489. // allow itself & it's enemy to test for dynamic scripted sequences.
  2490. //=========================================================
  2491. DEFINE_SCHEDULE
  2492. (
  2493. SCHED_ZOMBIE_POST_MELEE_WAIT,
  2494. " Tasks"
  2495. " TASK_ZOMBIE_WAIT_POST_MELEE 0"
  2496. )
  2497. AI_END_CUSTOM_NPC()