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.

698 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the headcrab, a tiny, jumpy alien parasite.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "game.h"
  9. #include "ai_default.h"
  10. #include "ai_schedule.h"
  11. #include "ai_hull.h"
  12. #include "ai_route.h"
  13. #include "ai_motor.h"
  14. #include "npcevent.h"
  15. #include "hl1_npc_headcrab.h"
  16. #include "gib.h"
  17. //#include "AI_Interactions.h"
  18. #include "ndebugoverlay.h"
  19. #include "vstdlib/random.h"
  20. #include "engine/IEngineSound.h"
  21. #include "movevars_shared.h"
  22. #include "SoundEmitterSystem/isoundemittersystembase.h"
  23. extern void ClearMultiDamage(void);
  24. extern void ApplyMultiDamage( void );
  25. ConVar sk_headcrab_health( "sk_headcrab_health","20");
  26. ConVar sk_headcrab_dmg_bite( "sk_headcrab_dmg_bite","10");
  27. #define CRAB_ATTN_IDLE (float)1.5
  28. #define HEADCRAB_GUTS_GIB_COUNT 1
  29. #define HEADCRAB_LEGS_GIB_COUNT 3
  30. #define HEADCRAB_ALL_GIB_COUNT 5
  31. #define HEADCRAB_MAX_JUMP_DIST 256
  32. #define HEADCRAB_RUNMODE_ACCELERATE 1
  33. #define HEADCRAB_RUNMODE_IDLE 2
  34. #define HEADCRAB_RUNMODE_DECELERATE 3
  35. #define HEADCRAB_RUNMODE_FULLSPEED 4
  36. #define HEADCRAB_RUNMODE_PAUSE 5
  37. #define HEADCRAB_RUN_MINSPEED 0.5
  38. #define HEADCRAB_RUN_MAXSPEED 1.0
  39. #define HC_AE_JUMPATTACK ( 2 )
  40. BEGIN_DATADESC( CNPC_Headcrab )
  41. // m_nGibCount - don't save
  42. // Function Pointers
  43. DEFINE_ENTITYFUNC( LeapTouch ),
  44. DEFINE_FIELD( m_vecJumpVel, FIELD_VECTOR ),
  45. END_DATADESC()
  46. LINK_ENTITY_TO_CLASS( monster_headcrab, CNPC_Headcrab );
  47. enum
  48. {
  49. SCHED_HEADCRAB_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE,
  50. SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
  51. };
  52. //-----------------------------------------------------------------------------
  53. // Purpose:
  54. // Input :
  55. // Output :
  56. //-----------------------------------------------------------------------------
  57. void CNPC_Headcrab::Spawn( void )
  58. {
  59. Precache();
  60. SetRenderColor( 255, 255, 255, 255 );
  61. SetModel( "models/headcrab.mdl" );
  62. m_iHealth = sk_headcrab_health.GetFloat();
  63. SetHullType(HULL_TINY);
  64. SetHullSizeNormal();
  65. SetSolid( SOLID_BBOX );
  66. AddSolidFlags( FSOLID_NOT_STANDABLE );
  67. SetMoveType( MOVETYPE_STEP );
  68. SetViewOffset( Vector(6, 0, 11) ); // Position of the eyes relative to NPC's origin.
  69. m_bloodColor = BLOOD_COLOR_GREEN;
  70. m_flFieldOfView = 0.5;
  71. m_NPCState = NPC_STATE_NONE;
  72. m_nGibCount = HEADCRAB_ALL_GIB_COUNT;
  73. CapabilitiesClear();
  74. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 );
  75. NPCInit();
  76. }
  77. //-----------------------------------------------------------------------------
  78. // Purpose:
  79. // Input :
  80. // Output :
  81. //-----------------------------------------------------------------------------
  82. void CNPC_Headcrab::Precache( void )
  83. {
  84. PrecacheModel( "models/headcrab.mdl" );
  85. // PrecacheModel( "models/hc_squashed01.mdl" );
  86. // PrecacheModel( "models/gibs/hc_gibs.mdl" );
  87. PrecacheScriptSound( "Headcrab.Bite" );
  88. PrecacheScriptSound( "Headcrab.Attack" );
  89. PrecacheScriptSound( "Headcrab.Idle" );
  90. PrecacheScriptSound( "Headcrab.Die" );
  91. PrecacheScriptSound( "Headcrab.Alert" );
  92. PrecacheScriptSound( "Headcrab.Pain" );
  93. BaseClass::Precache();
  94. }
  95. //-----------------------------------------------------------------------------
  96. //-----------------------------------------------------------------------------
  97. void CNPC_Headcrab::IdleSound()
  98. {
  99. HeadCrabSound( "Headcrab.Idle" );
  100. }
  101. void CNPC_Headcrab::AlertSound()
  102. {
  103. HeadCrabSound( "Headcrab.Alert" );
  104. }
  105. void CNPC_Headcrab::PainSound( const CTakeDamageInfo &info )
  106. {
  107. HeadCrabSound( "Headcrab.Pain" );
  108. }
  109. void CNPC_Headcrab::DeathSound( const CTakeDamageInfo &info )
  110. {
  111. HeadCrabSound( "Headcrab.Die" );
  112. }
  113. void CNPC_Headcrab::HeadCrabSound( const char *pchSound )
  114. {
  115. CPASAttenuationFilter filter( this, ATTN_IDLE );
  116. CSoundParameters params;
  117. if ( GetParametersForSound( pchSound, params, NULL ) )
  118. {
  119. EmitSound_t ep( params );
  120. ep.m_flVolume = GetSoundVolume();
  121. ep.m_nPitch = GetVoicePitch();
  122. EmitSound( filter, entindex(), ep );
  123. }
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose:
  127. // Input : pTask -
  128. //-----------------------------------------------------------------------------
  129. void CNPC_Headcrab::StartTask( const Task_t *pTask )
  130. {
  131. switch ( pTask->iTask )
  132. {
  133. case TASK_RANGE_ATTACK1:
  134. {
  135. SetIdealActivity( ACT_RANGE_ATTACK1 );
  136. SetTouch( &CNPC_Headcrab::LeapTouch );
  137. break;
  138. }
  139. default:
  140. {
  141. BaseClass::StartTask( pTask );
  142. }
  143. }
  144. }
  145. //-----------------------------------------------------------------------------
  146. // Purpose:
  147. // Input : *pTask -
  148. //-----------------------------------------------------------------------------
  149. void CNPC_Headcrab::RunTask( const Task_t *pTask )
  150. {
  151. switch ( pTask->iTask )
  152. {
  153. case TASK_RANGE_ATTACK1:
  154. case TASK_RANGE_ATTACK2:
  155. {
  156. if ( IsSequenceFinished() )
  157. {
  158. TaskComplete();
  159. SetTouch( NULL );
  160. SetIdealActivity( ACT_IDLE );
  161. }
  162. break;
  163. }
  164. default:
  165. {
  166. CAI_BaseNPC::RunTask( pTask );
  167. }
  168. }
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. // Output :
  173. //-----------------------------------------------------------------------------
  174. int CNPC_Headcrab::SelectSchedule( void )
  175. {
  176. switch ( m_NPCState )
  177. {
  178. case NPC_STATE_ALERT:
  179. {
  180. if (HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ))
  181. {
  182. if ( fabs( GetMotor()->DeltaIdealYaw() ) < ( 1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction
  183. {
  184. return SCHED_TAKE_COVER_FROM_ORIGIN;
  185. }
  186. else if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
  187. {
  188. return SCHED_SMALL_FLINCH;
  189. }
  190. }
  191. else if (HasCondition( COND_HEAR_DANGER ) ||
  192. HasCondition( COND_HEAR_PLAYER ) ||
  193. HasCondition( COND_HEAR_WORLD ) ||
  194. HasCondition( COND_HEAR_COMBAT ))
  195. {
  196. return SCHED_ALERT_FACE_BESTSOUND;
  197. }
  198. else
  199. {
  200. return SCHED_PATROL_WALK;
  201. }
  202. break;
  203. }
  204. }
  205. // no special cases here, call the base class
  206. return BaseClass::SelectSchedule();
  207. }
  208. //------------------------------------------------------------------------------
  209. // Purpose :
  210. // Input :
  211. // Output :
  212. //------------------------------------------------------------------------------
  213. void CNPC_Headcrab::Touch( CBaseEntity *pOther )
  214. {
  215. // If someone has smacked me into a wall then gib!
  216. /* if (m_NPCState == NPC_STATE_DEAD)
  217. {
  218. if (GetAbsVelocity().Length() > 250)
  219. {
  220. trace_t tr;
  221. Vector vecDir = GetAbsVelocity();
  222. VectorNormalize(vecDir);
  223. UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vecDir * 100,
  224. MASK_SOLID_BRUSHONLY, pev, COLLISION_GROUP_NONE, &tr);
  225. float dotPr = DotProduct(vecDir,tr.plane.normal);
  226. if ((tr.fraction != 1.0) &&
  227. (dotPr < -0.8) )
  228. {
  229. Event_Gibbed();
  230. // Throw headcrab guts
  231. CGib::SpawnSpecificGibs( this, HEADCRAB_GUTS_GIB_COUNT, 300, 400, "models/gibs/hc_gibs.mdl");
  232. }
  233. }
  234. }*/
  235. BaseClass::Touch(pOther);
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. // Input : pevInflictor -
  240. // pevAttacker -
  241. // flDamage -
  242. // bitsDamageType -
  243. // Output :
  244. //-----------------------------------------------------------------------------
  245. int CNPC_Headcrab::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  246. {
  247. CTakeDamageInfo info = inputInfo;
  248. //
  249. // Don't take any acid damage.
  250. //
  251. if ( info.GetDamageType() & DMG_ACID )
  252. {
  253. return 0;
  254. }
  255. return BaseClass::OnTakeDamage_Alive( info );
  256. }
  257. float CNPC_Headcrab::GetDamageAmount( void )
  258. {
  259. return sk_headcrab_dmg_bite.GetFloat();
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose:
  263. // Input : Type -
  264. // Output : CAI_Schedule *
  265. //-----------------------------------------------------------------------------
  266. int CNPC_Headcrab::TranslateSchedule( int scheduleType )
  267. {
  268. switch( scheduleType )
  269. {
  270. case SCHED_RANGE_ATTACK1:
  271. return SCHED_HEADCRAB_RANGE_ATTACK1;
  272. case SCHED_FAIL_TAKE_COVER:
  273. return SCHED_ALERT_FACE;
  274. }
  275. return BaseClass::TranslateSchedule( scheduleType );
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. //-----------------------------------------------------------------------------
  280. void CNPC_Headcrab::PrescheduleThink( void )
  281. {
  282. BaseClass::PrescheduleThink();
  283. //
  284. // Make the crab coo a little bit in combat state.
  285. //
  286. if (( m_NPCState == NPC_STATE_COMBAT ) && ( random->RandomFloat( 0, 5 ) < 0.1 ))
  287. {
  288. IdleSound();
  289. }
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose: For innate melee attack
  293. // Input :
  294. // Output :
  295. //-----------------------------------------------------------------------------
  296. int CNPC_Headcrab::RangeAttack1Conditions ( float flDot, float flDist )
  297. {
  298. if ( gpGlobals->curtime < m_flNextAttack )
  299. {
  300. return( 0 );
  301. }
  302. if ( !(GetFlags() & FL_ONGROUND) )
  303. {
  304. return( 0 );
  305. }
  306. if ( flDist > 256 )
  307. {
  308. return( COND_TOO_FAR_TO_ATTACK );
  309. }
  310. else if ( flDot < 0.65 )
  311. {
  312. return( COND_NOT_FACING_ATTACK );
  313. }
  314. return( COND_CAN_RANGE_ATTACK1 );
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: Indicates this monster's place in the relationship table.
  318. // Output :
  319. //-----------------------------------------------------------------------------
  320. Class_T CNPC_Headcrab::Classify( void )
  321. {
  322. return CLASS_ALIEN_PREY;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Returns the real center of the monster. The bounding box is much larger
  326. // than the actual creature so this is needed for targetting.
  327. // Output : Vector
  328. //-----------------------------------------------------------------------------
  329. Vector CNPC_Headcrab::Center( void )
  330. {
  331. return Vector( GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z + 6 );
  332. }
  333. //-----------------------------------------------------------------------------
  334. // Purpose:
  335. // Input : &posSrc -
  336. // Output : Vector
  337. //-----------------------------------------------------------------------------
  338. Vector CNPC_Headcrab::BodyTarget( const Vector &posSrc, bool bNoisy )
  339. {
  340. return( Center() );
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Purpose:
  344. // Input :
  345. // Output :
  346. //-----------------------------------------------------------------------------
  347. float CNPC_Headcrab::MaxYawSpeed ( void )
  348. {
  349. switch ( GetActivity() )
  350. {
  351. case ACT_IDLE:
  352. return 30;
  353. break;
  354. case ACT_RUN:
  355. case ACT_WALK:
  356. return 20;
  357. break;
  358. case ACT_TURN_LEFT:
  359. case ACT_TURN_RIGHT:
  360. return 15;
  361. break;
  362. case ACT_RANGE_ATTACK1:
  363. return 30;
  364. break;
  365. default:
  366. return 30;
  367. break;
  368. }
  369. return BaseClass::MaxYawSpeed();
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: LeapTouch - this is the headcrab's touch function when it is in the air.
  373. // Input : *pOther -
  374. //-----------------------------------------------------------------------------
  375. void CNPC_Headcrab::LeapTouch( CBaseEntity *pOther )
  376. {
  377. if ( pOther->Classify() == Classify() )
  378. {
  379. return;
  380. }
  381. // Don't hit if back on ground
  382. if ( !(GetFlags() & FL_ONGROUND) && ( pOther->IsNPC() || pOther->IsPlayer() ) )
  383. {
  384. BiteSound();
  385. TouchDamage( pOther );
  386. }
  387. SetTouch( NULL );
  388. }
  389. //-----------------------------------------------------------------------------
  390. // Purpose: Make the sound of this headcrab chomping a target.
  391. // Input :
  392. //-----------------------------------------------------------------------------
  393. void CNPC_Headcrab::BiteSound( void )
  394. {
  395. HeadCrabSound( "Headcrab.Bite" );
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Purpose: Deal the damage from the headcrab's touch attack.
  399. //-----------------------------------------------------------------------------
  400. void CNPC_Headcrab::TouchDamage( CBaseEntity *pOther )
  401. {
  402. CTakeDamageInfo info( this, this, GetDamageAmount(), DMG_SLASH );
  403. CalculateMeleeDamageForce( &info, GetAbsVelocity(), GetAbsOrigin() );
  404. pOther->TakeDamage( info );
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: Catches the monster-specific messages that occur when tagged
  408. // animation frames are played.
  409. // Input : *pEvent -
  410. //-----------------------------------------------------------------------------
  411. void CNPC_Headcrab::HandleAnimEvent( animevent_t *pEvent )
  412. {
  413. switch ( pEvent->event )
  414. {
  415. case HC_AE_JUMPATTACK:
  416. {
  417. SetGroundEntity( NULL );
  418. //
  419. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  420. //
  421. UTIL_SetOrigin( this, GetAbsOrigin() + Vector( 0 , 0 , 1 ));
  422. Vector vecJumpDir;
  423. CBaseEntity *pEnemy = GetEnemy();
  424. if ( pEnemy )
  425. {
  426. Vector vecEnemyEyePos = pEnemy->EyePosition();
  427. float gravity = GetCurrentGravity();
  428. if ( gravity <= 1 )
  429. {
  430. gravity = 1;
  431. }
  432. //
  433. // How fast does the headcrab need to travel to reach my enemy's eyes given gravity?
  434. //
  435. float height = ( vecEnemyEyePos.z - GetAbsOrigin().z );
  436. if ( height < 16 )
  437. {
  438. height = 16;
  439. }
  440. else if ( height > 120 )
  441. {
  442. height = 120;
  443. }
  444. float speed = sqrt( 2 * gravity * height );
  445. float time = speed / gravity;
  446. //
  447. // Scale the sideways velocity to get there at the right time
  448. //
  449. vecJumpDir = vecEnemyEyePos - GetAbsOrigin();
  450. vecJumpDir = vecJumpDir / time;
  451. //
  452. // Speed to offset gravity at the desired height.
  453. //
  454. vecJumpDir.z = speed;
  455. //
  456. // Don't jump too far/fast.
  457. //
  458. float distance = vecJumpDir.Length();
  459. if ( distance > 650 )
  460. {
  461. vecJumpDir = vecJumpDir * ( 650.0 / distance );
  462. }
  463. }
  464. else
  465. {
  466. //
  467. // Jump hop, don't care where.
  468. //
  469. Vector forward, up;
  470. AngleVectors( GetAbsAngles(), &forward, NULL, &up );
  471. vecJumpDir = Vector( forward.x, forward.y, up.z ) * 350;
  472. }
  473. int iSound = random->RandomInt( 0 , 2 );
  474. if ( iSound != 0 )
  475. {
  476. AttackSound();
  477. }
  478. SetAbsVelocity( vecJumpDir );
  479. m_flNextAttack = gpGlobals->curtime + 2;
  480. break;
  481. }
  482. default:
  483. {
  484. CAI_BaseNPC::HandleAnimEvent( pEvent );
  485. break;
  486. }
  487. }
  488. }
  489. void CNPC_Headcrab::AttackSound( void )
  490. {
  491. HeadCrabSound( "Headcrab.Attack" );
  492. }
  493. //------------------------------------------------------------------------------
  494. //
  495. // Schedules
  496. //
  497. //------------------------------------------------------------------------------
  498. AI_BEGIN_CUSTOM_NPC( monster_headcrab, CNPC_Headcrab )
  499. //=========================================================
  500. // > SCHED_HEADCRAB_RANGE_ATTACK1
  501. //=========================================================
  502. DEFINE_SCHEDULE
  503. (
  504. SCHED_HEADCRAB_RANGE_ATTACK1,
  505. " Tasks"
  506. " TASK_STOP_MOVING 0"
  507. " TASK_FACE_IDEAL 0"
  508. " TASK_RANGE_ATTACK1 0"
  509. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  510. " TASK_FACE_IDEAL 0"
  511. " TASK_WAIT_RANDOM 0.5"
  512. " "
  513. " Interrupts"
  514. " COND_ENEMY_OCCLUDED"
  515. " COND_NO_PRIMARY_AMMO"
  516. )
  517. //=========================================================
  518. // > SCHED_FAST_HEADCRAB_RANGE_ATTACK1
  519. //=========================================================
  520. DEFINE_SCHEDULE
  521. (
  522. SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
  523. " Tasks"
  524. " TASK_STOP_MOVING 0"
  525. " TASK_FACE_IDEAL 0"
  526. " TASK_RANGE_ATTACK1 0"
  527. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  528. " "
  529. " Interrupts"
  530. " COND_ENEMY_OCCLUDED"
  531. " COND_NO_PRIMARY_AMMO"
  532. )
  533. AI_END_CUSTOM_NPC()
  534. class CNPC_BabyCrab : public CNPC_Headcrab
  535. {
  536. DECLARE_CLASS( CNPC_BabyCrab, CNPC_Headcrab );
  537. public:
  538. void Spawn( void );
  539. void Precache( void );
  540. unsigned int PhysicsSolidMaskForEntity( void ) const;
  541. int RangeAttack1Conditions ( float flDot, float flDist );
  542. float MaxYawSpeed( void ){ return 120.0f; }
  543. float GetDamageAmount( void );
  544. virtual int GetVoicePitch( void ) { return PITCH_NORM + random->RandomInt( 40,50 ); }
  545. virtual float GetSoundVolume( void ) { return 0.8; }
  546. };
  547. LINK_ENTITY_TO_CLASS( monster_babycrab, CNPC_BabyCrab );
  548. unsigned int CNPC_BabyCrab::PhysicsSolidMaskForEntity( void ) const
  549. {
  550. unsigned int iMask = BaseClass::PhysicsSolidMaskForEntity();
  551. iMask &= ~CONTENTS_MONSTERCLIP;
  552. return iMask;
  553. }
  554. void CNPC_BabyCrab::Spawn( void )
  555. {
  556. CNPC_Headcrab::Spawn();
  557. SetModel( "models/baby_headcrab.mdl" );
  558. m_nRenderMode = kRenderTransTexture;
  559. SetRenderColor( 255, 255, 255, 192 );
  560. UTIL_SetSize(this, Vector(-12, -12, 0), Vector(12, 12, 24));
  561. m_iHealth = sk_headcrab_health.GetFloat() * 0.25; // less health than full grown
  562. }
  563. void CNPC_BabyCrab::Precache( void )
  564. {
  565. PrecacheModel( "models/baby_headcrab.mdl" );
  566. CNPC_Headcrab::Precache();
  567. }
  568. int CNPC_BabyCrab::RangeAttack1Conditions( float flDot, float flDist )
  569. {
  570. if ( GetFlags() & FL_ONGROUND )
  571. {
  572. if ( GetGroundEntity() && ( GetGroundEntity()->GetFlags() & ( FL_CLIENT | FL_NPC ) ) )
  573. return COND_CAN_RANGE_ATTACK1;
  574. // A little less accurate, but jump from closer
  575. if ( flDist <= 180 && flDot >= 0.55 )
  576. return COND_CAN_RANGE_ATTACK1;
  577. }
  578. return COND_NONE;
  579. }
  580. float CNPC_BabyCrab::GetDamageAmount( void )
  581. {
  582. return sk_headcrab_dmg_bite.GetFloat() * 0.3;
  583. }