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.

3991 lines
106 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the headcrab, a tiny, jumpy alien parasite.
  4. //
  5. // TODO: make poison headcrab hop in response to nearby bullet impacts?
  6. //
  7. //=============================================================================//
  8. #include "cbase.h"
  9. #include "game.h"
  10. #include "antlion_dust.h"
  11. #include "ai_default.h"
  12. #include "ai_schedule.h"
  13. #include "ai_hint.h"
  14. #include "ai_hull.h"
  15. #include "ai_navigator.h"
  16. #include "ai_moveprobe.h"
  17. #include "ai_memory.h"
  18. #include "bitstring.h"
  19. #include "hl2_shareddefs.h"
  20. #include "npcevent.h"
  21. #include "soundent.h"
  22. #include "npc_headcrab.h"
  23. #include "gib.h"
  24. #include "ai_interactions.h"
  25. #include "ndebugoverlay.h"
  26. #include "vstdlib/random.h"
  27. #include "engine/IEngineSound.h"
  28. #include "movevars_shared.h"
  29. #include "world.h"
  30. #include "npc_bullseye.h"
  31. #include "physics_npc_solver.h"
  32. #include "hl2_gamerules.h"
  33. #include "decals.h"
  34. // memdbgon must be the last include file in a .cpp file!!!
  35. #include "tier0/memdbgon.h"
  36. #define CRAB_ATTN_IDLE (float)1.5
  37. #define HEADCRAB_GUTS_GIB_COUNT 1
  38. #define HEADCRAB_LEGS_GIB_COUNT 3
  39. #define HEADCRAB_ALL_GIB_COUNT 5
  40. #define HEADCRAB_RUNMODE_ACCELERATE 1
  41. #define HEADCRAB_RUNMODE_IDLE 2
  42. #define HEADCRAB_RUNMODE_DECELERATE 3
  43. #define HEADCRAB_RUNMODE_FULLSPEED 4
  44. #define HEADCRAB_RUNMODE_PAUSE 5
  45. #define HEADCRAB_RUN_MINSPEED 0.5
  46. #define HEADCRAB_RUN_MAXSPEED 1.0
  47. const float HEADCRAB_BURROWED_FOV = -1.0f;
  48. const float HEADCRAB_UNBURROWED_FOV = 0.5f;
  49. #define HEADCRAB_IGNORE_WORLD_COLLISION_TIME 0.5
  50. const int HEADCRAB_MIN_JUMP_DIST = 48;
  51. const int HEADCRAB_MAX_JUMP_DIST = 256;
  52. #define HEADCRAB_BURROW_POINT_SEARCH_RADIUS 256.0
  53. // Debugging
  54. #define HEADCRAB_DEBUG_HIDING 1
  55. #define HEADCRAB_BURN_SOUND_FREQUENCY 10
  56. ConVar g_debug_headcrab( "g_debug_headcrab", "0", FCVAR_CHEAT );
  57. //------------------------------------
  58. // Spawnflags
  59. //------------------------------------
  60. #define SF_HEADCRAB_START_HIDDEN (1 << 16)
  61. #define SF_HEADCRAB_START_HANGING (1 << 17)
  62. //-----------------------------------------------------------------------------
  63. // Think contexts.
  64. //-----------------------------------------------------------------------------
  65. static const char *s_pPitchContext = "PitchContext";
  66. //-----------------------------------------------------------------------------
  67. // Animation events.
  68. //-----------------------------------------------------------------------------
  69. int AE_HEADCRAB_JUMPATTACK;
  70. int AE_HEADCRAB_JUMP_TELEGRAPH;
  71. int AE_POISONHEADCRAB_FLINCH_HOP;
  72. int AE_POISONHEADCRAB_FOOTSTEP;
  73. int AE_POISONHEADCRAB_THREAT_SOUND;
  74. int AE_HEADCRAB_BURROW_IN;
  75. int AE_HEADCRAB_BURROW_IN_FINISH;
  76. int AE_HEADCRAB_BURROW_OUT;
  77. int AE_HEADCRAB_CEILING_DETACH;
  78. //-----------------------------------------------------------------------------
  79. // Custom schedules.
  80. //-----------------------------------------------------------------------------
  81. enum
  82. {
  83. SCHED_HEADCRAB_RANGE_ATTACK1 = LAST_SHARED_SCHEDULE,
  84. SCHED_HEADCRAB_WAKE_ANGRY,
  85. SCHED_HEADCRAB_WAKE_ANGRY_NO_DISPLAY,
  86. SCHED_HEADCRAB_DROWN,
  87. SCHED_HEADCRAB_FAIL_DROWN,
  88. SCHED_HEADCRAB_AMBUSH,
  89. SCHED_HEADCRAB_HOP_RANDOMLY, // get off something you're not supposed to be on.
  90. SCHED_HEADCRAB_BARNACLED,
  91. SCHED_HEADCRAB_UNHIDE,
  92. SCHED_HEADCRAB_HARASS_ENEMY,
  93. SCHED_HEADCRAB_FALL_TO_GROUND,
  94. SCHED_HEADCRAB_RUN_TO_BURROW_IN,
  95. SCHED_HEADCRAB_RUN_TO_SPECIFIC_BURROW,
  96. SCHED_HEADCRAB_BURROW_IN,
  97. SCHED_HEADCRAB_BURROW_WAIT,
  98. SCHED_HEADCRAB_BURROW_OUT,
  99. SCHED_HEADCRAB_WAIT_FOR_CLEAR_UNBURROW,
  100. SCHED_HEADCRAB_CRAWL_FROM_CANISTER,
  101. SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
  102. SCHED_HEADCRAB_CEILING_WAIT,
  103. SCHED_HEADCRAB_CEILING_DROP,
  104. };
  105. //=========================================================
  106. // tasks
  107. //=========================================================
  108. enum
  109. {
  110. TASK_HEADCRAB_HOP_ASIDE = LAST_SHARED_TASK,
  111. TASK_HEADCRAB_HOP_OFF_NPC,
  112. TASK_HEADCRAB_DROWN,
  113. TASK_HEADCRAB_WAIT_FOR_BARNACLE_KILL,
  114. TASK_HEADCRAB_UNHIDE,
  115. TASK_HEADCRAB_HARASS_HOP,
  116. TASK_HEADCRAB_FIND_BURROW_IN_POINT,
  117. TASK_HEADCRAB_BURROW,
  118. TASK_HEADCRAB_UNBURROW,
  119. TASK_HEADCRAB_BURROW_WAIT,
  120. TASK_HEADCRAB_CHECK_FOR_UNBURROW,
  121. TASK_HEADCRAB_JUMP_FROM_CANISTER,
  122. TASK_HEADCRAB_CLIMB_FROM_CANISTER,
  123. TASK_HEADCRAB_CEILING_WAIT,
  124. TASK_HEADCRAB_CEILING_POSITION,
  125. TASK_HEADCRAB_CEILING_DETACH,
  126. TASK_HEADCRAB_CEILING_FALL,
  127. TASK_HEADCRAB_CEILING_LAND,
  128. };
  129. //=========================================================
  130. // conditions
  131. //=========================================================
  132. enum
  133. {
  134. COND_HEADCRAB_IN_WATER = LAST_SHARED_CONDITION,
  135. COND_HEADCRAB_ILLEGAL_GROUNDENT,
  136. COND_HEADCRAB_BARNACLED,
  137. COND_HEADCRAB_UNHIDE,
  138. };
  139. //=========================================================
  140. // private activities
  141. //=========================================================
  142. int ACT_HEADCRAB_THREAT_DISPLAY;
  143. int ACT_HEADCRAB_HOP_LEFT;
  144. int ACT_HEADCRAB_HOP_RIGHT;
  145. int ACT_HEADCRAB_DROWN;
  146. int ACT_HEADCRAB_BURROW_IN;
  147. int ACT_HEADCRAB_BURROW_OUT;
  148. int ACT_HEADCRAB_BURROW_IDLE;
  149. int ACT_HEADCRAB_CRAWL_FROM_CANISTER_LEFT;
  150. int ACT_HEADCRAB_CRAWL_FROM_CANISTER_CENTER;
  151. int ACT_HEADCRAB_CRAWL_FROM_CANISTER_RIGHT;
  152. int ACT_HEADCRAB_CEILING_IDLE;
  153. int ACT_HEADCRAB_CEILING_DETACH;
  154. int ACT_HEADCRAB_CEILING_FALL;
  155. int ACT_HEADCRAB_CEILING_LAND;
  156. //-----------------------------------------------------------------------------
  157. // Skill settings.
  158. //-----------------------------------------------------------------------------
  159. ConVar sk_headcrab_health( "sk_headcrab_health","0");
  160. ConVar sk_headcrab_fast_health( "sk_headcrab_fast_health","0");
  161. ConVar sk_headcrab_poison_health( "sk_headcrab_poison_health","0");
  162. ConVar sk_headcrab_melee_dmg( "sk_headcrab_melee_dmg","0");
  163. ConVar sk_headcrab_poison_npc_damage( "sk_headcrab_poison_npc_damage", "0" );
  164. BEGIN_DATADESC( CBaseHeadcrab )
  165. // m_nGibCount - don't save
  166. DEFINE_FIELD( m_bHidden, FIELD_BOOLEAN ),
  167. DEFINE_FIELD( m_flTimeDrown, FIELD_TIME ),
  168. DEFINE_FIELD( m_bCommittedToJump, FIELD_BOOLEAN ),
  169. DEFINE_FIELD( m_vecCommittedJumpPos, FIELD_POSITION_VECTOR ),
  170. DEFINE_FIELD( m_flNextNPCThink, FIELD_TIME ),
  171. DEFINE_FIELD( m_flIgnoreWorldCollisionTime, FIELD_TIME ),
  172. DEFINE_KEYFIELD( m_bStartBurrowed, FIELD_BOOLEAN, "startburrowed" ),
  173. DEFINE_FIELD( m_bBurrowed, FIELD_BOOLEAN ),
  174. DEFINE_FIELD( m_flBurrowTime, FIELD_TIME ),
  175. DEFINE_FIELD( m_nContext, FIELD_INTEGER ),
  176. DEFINE_FIELD( m_bCrawlFromCanister, FIELD_BOOLEAN ),
  177. DEFINE_FIELD( m_bMidJump, FIELD_BOOLEAN ),
  178. DEFINE_FIELD( m_nJumpFromCanisterDir, FIELD_INTEGER ),
  179. DEFINE_FIELD( m_bHangingFromCeiling, FIELD_BOOLEAN ),
  180. DEFINE_FIELD( m_flIlluminatedTime, FIELD_TIME ),
  181. DEFINE_INPUTFUNC( FIELD_VOID, "Burrow", InputBurrow ),
  182. DEFINE_INPUTFUNC( FIELD_VOID, "BurrowImmediate", InputBurrowImmediate ),
  183. DEFINE_INPUTFUNC( FIELD_VOID, "Unburrow", InputUnburrow ),
  184. DEFINE_INPUTFUNC( FIELD_VOID, "StartHangingFromCeiling", InputStartHangingFromCeiling ),
  185. DEFINE_INPUTFUNC( FIELD_VOID, "DropFromCeiling", InputDropFromCeiling ),
  186. // Function Pointers
  187. DEFINE_THINKFUNC( EliminateRollAndPitch ),
  188. DEFINE_THINKFUNC( ThrowThink ),
  189. DEFINE_ENTITYFUNC( LeapTouch ),
  190. END_DATADESC()
  191. //-----------------------------------------------------------------------------
  192. // Purpose:
  193. //-----------------------------------------------------------------------------
  194. void CBaseHeadcrab::Spawn( void )
  195. {
  196. //Precache();
  197. //SetModel( "models/headcrab.mdl" );
  198. //m_iHealth = sk_headcrab_health.GetFloat();
  199. #ifdef _XBOX
  200. // Always fade the corpse
  201. AddSpawnFlags( SF_NPC_FADE_CORPSE );
  202. #endif // _XBOX
  203. SetHullType(HULL_TINY);
  204. SetHullSizeNormal();
  205. SetSolid( SOLID_BBOX );
  206. AddSolidFlags( FSOLID_NOT_STANDABLE );
  207. SetMoveType( MOVETYPE_STEP );
  208. SetCollisionGroup( HL2COLLISION_GROUP_HEADCRAB );
  209. SetViewOffset( Vector(6, 0, 11) ) ; // Position of the eyes relative to NPC's origin.
  210. SetBloodColor( BLOOD_COLOR_GREEN );
  211. m_flFieldOfView = 0.5;
  212. m_NPCState = NPC_STATE_NONE;
  213. m_nGibCount = HEADCRAB_ALL_GIB_COUNT;
  214. // Are we starting hidden?
  215. if ( m_spawnflags & SF_HEADCRAB_START_HIDDEN )
  216. {
  217. m_bHidden = true;
  218. AddSolidFlags( FSOLID_NOT_SOLID );
  219. SetRenderColorA( 0 );
  220. m_nRenderMode = kRenderTransTexture;
  221. AddEffects( EF_NODRAW );
  222. }
  223. else
  224. {
  225. m_bHidden = false;
  226. }
  227. CapabilitiesClear();
  228. CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_INNATE_RANGE_ATTACK1 );
  229. CapabilitiesAdd( bits_CAP_SQUAD );
  230. // headcrabs get to cheat for 5 seconds (sjb)
  231. GetEnemies()->SetFreeKnowledgeDuration( 5.0 );
  232. m_bHangingFromCeiling = false;
  233. m_flIlluminatedTime = -1;
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose: Stuff that must happen after NPCInit is called.
  237. //-----------------------------------------------------------------------------
  238. void CBaseHeadcrab::HeadcrabInit()
  239. {
  240. // See if we're supposed to start burrowed
  241. if ( m_bStartBurrowed )
  242. {
  243. SetBurrowed( true );
  244. SetSchedule( SCHED_HEADCRAB_BURROW_WAIT );
  245. }
  246. if ( GetSpawnFlags() & SF_HEADCRAB_START_HANGING )
  247. {
  248. SetSchedule( SCHED_HEADCRAB_CEILING_WAIT );
  249. m_flIlluminatedTime = -1;
  250. }
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose: Precaches all resources this monster needs.
  254. //-----------------------------------------------------------------------------
  255. void CBaseHeadcrab::Precache( void )
  256. {
  257. BaseClass::Precache();
  258. }
  259. //-----------------------------------------------------------------------------
  260. // The headcrab will crawl from the cannister, then jump to a burrow point
  261. //-----------------------------------------------------------------------------
  262. void CBaseHeadcrab::CrawlFromCanister()
  263. {
  264. // This is necessary to prevent ground computations, etc. from happening
  265. // while the crawling animation is occuring
  266. AddFlag( FL_FLY );
  267. m_bCrawlFromCanister = true;
  268. SetNextThink( gpGlobals->curtime );
  269. }
  270. //-----------------------------------------------------------------------------
  271. // Purpose:
  272. // Input : NewActivity -
  273. //-----------------------------------------------------------------------------
  274. void CBaseHeadcrab::OnChangeActivity( Activity NewActivity )
  275. {
  276. bool fRandomize = false;
  277. float flRandomRange = 0.0;
  278. // If this crab is starting to walk or idle, pick a random point within
  279. // the animation to begin. This prevents lots of crabs being in lockstep.
  280. if ( NewActivity == ACT_IDLE )
  281. {
  282. flRandomRange = 0.75;
  283. fRandomize = true;
  284. }
  285. else if ( NewActivity == ACT_RUN )
  286. {
  287. flRandomRange = 0.25;
  288. fRandomize = true;
  289. }
  290. BaseClass::OnChangeActivity( NewActivity );
  291. if( fRandomize )
  292. {
  293. SetCycle( random->RandomFloat( 0.0, flRandomRange ) );
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose: Indicates this monster's place in the relationship table.
  298. // Output :
  299. //-----------------------------------------------------------------------------
  300. Class_T CBaseHeadcrab::Classify( void )
  301. {
  302. if( m_bHidden )
  303. {
  304. // Effectively invisible to other AI's while hidden.
  305. return( CLASS_NONE );
  306. }
  307. else
  308. {
  309. return( CLASS_HEADCRAB );
  310. }
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Purpose:
  314. // Input : &posSrc -
  315. // Output : Vector
  316. //-----------------------------------------------------------------------------
  317. Vector CBaseHeadcrab::BodyTarget( const Vector &posSrc, bool bNoisy )
  318. {
  319. Vector vecResult;
  320. vecResult = GetAbsOrigin();
  321. vecResult.z += 6;
  322. return vecResult;
  323. }
  324. //-----------------------------------------------------------------------------
  325. //-----------------------------------------------------------------------------
  326. float CBaseHeadcrab::GetAutoAimRadius()
  327. {
  328. if( g_pGameRules->GetAutoAimMode() == AUTOAIM_ON_CONSOLE )
  329. {
  330. return 24.0f;
  331. }
  332. return 12.0f;
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: Allows each sequence to have a different turn rate associated with it.
  336. // Output : float
  337. //-----------------------------------------------------------------------------
  338. float CBaseHeadcrab::MaxYawSpeed( void )
  339. {
  340. return BaseClass::MaxYawSpeed();
  341. }
  342. //-----------------------------------------------------------------------------
  343. // Because the AI code does a tracehull to find the ground under an NPC, headcrabs
  344. // can often be seen standing with one edge of their box perched on a ledge and
  345. // 80% or more of their body hanging out over nothing. This is often a case
  346. // where a headcrab will be unable to pathfind out of its location. This heuristic
  347. // very crudely tries to determine if this is the case by casting a simple ray
  348. // down from the center of the headcrab.
  349. //-----------------------------------------------------------------------------
  350. #define HEADCRAB_MAX_LEDGE_HEIGHT 12.0f
  351. bool CBaseHeadcrab::IsFirmlyOnGround()
  352. {
  353. if( !(GetFlags()&FL_ONGROUND) )
  354. return false;
  355. trace_t tr;
  356. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, HEADCRAB_MAX_LEDGE_HEIGHT ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  357. return tr.fraction != 1.0;
  358. }
  359. //-----------------------------------------------------------------------------
  360. //-----------------------------------------------------------------------------
  361. void CBaseHeadcrab::MoveOrigin( const Vector &vecDelta )
  362. {
  363. UTIL_SetOrigin( this, GetLocalOrigin() + vecDelta );
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose:
  367. // Input : vecPos -
  368. //-----------------------------------------------------------------------------
  369. void CBaseHeadcrab::ThrowAt( const Vector &vecPos )
  370. {
  371. JumpAttack( false, vecPos, true );
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose:
  375. // Input : vecPos -
  376. //-----------------------------------------------------------------------------
  377. void CBaseHeadcrab::JumpToBurrowHint( CAI_Hint *pHint )
  378. {
  379. Vector vecVel = VecCheckToss( this, GetAbsOrigin(), pHint->GetAbsOrigin(), 0.5f, 1.0f, false, NULL, NULL );
  380. // Undershoot by a little because it looks bad if we overshoot and turn around to burrow.
  381. vecVel *= 0.9f;
  382. Leap( vecVel );
  383. GrabHintNode( pHint );
  384. }
  385. //-----------------------------------------------------------------------------
  386. // Purpose:
  387. // Input : vecVel -
  388. //-----------------------------------------------------------------------------
  389. void CBaseHeadcrab::Leap( const Vector &vecVel )
  390. {
  391. SetTouch( &CBaseHeadcrab::LeapTouch );
  392. SetCondition( COND_FLOATING_OFF_GROUND );
  393. SetGroundEntity( NULL );
  394. m_flIgnoreWorldCollisionTime = gpGlobals->curtime + HEADCRAB_IGNORE_WORLD_COLLISION_TIME;
  395. if( HasHeadroom() )
  396. {
  397. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  398. MoveOrigin( Vector( 0, 0, 1 ) );
  399. }
  400. SetAbsVelocity( vecVel );
  401. // Think every frame so the player sees the headcrab where he actually is...
  402. m_bMidJump = true;
  403. SetThink( &CBaseHeadcrab::ThrowThink );
  404. SetNextThink( gpGlobals->curtime );
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose:
  408. //-----------------------------------------------------------------------------
  409. void CBaseHeadcrab::ThrowThink( void )
  410. {
  411. if (gpGlobals->curtime > m_flNextNPCThink)
  412. {
  413. NPCThink();
  414. m_flNextNPCThink = gpGlobals->curtime + 0.1;
  415. }
  416. if( GetFlags() & FL_ONGROUND )
  417. {
  418. SetThink( &CBaseHeadcrab::CallNPCThink );
  419. SetNextThink( gpGlobals->curtime + 0.1 );
  420. return;
  421. }
  422. SetNextThink( gpGlobals->curtime );
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Does a jump attack at the given position.
  426. // Input : bRandomJump - Just hop in a random direction.
  427. // vecPos - Position to jump at, ignored if bRandom is set to true.
  428. // bThrown -
  429. //-----------------------------------------------------------------------------
  430. void CBaseHeadcrab::JumpAttack( bool bRandomJump, const Vector &vecPos, bool bThrown )
  431. {
  432. Vector vecJumpVel;
  433. if ( !bRandomJump )
  434. {
  435. float gravity = GetCurrentGravity();
  436. if ( gravity <= 1 )
  437. {
  438. gravity = 1;
  439. }
  440. // How fast does the headcrab need to travel to reach the position given gravity?
  441. float flActualHeight = vecPos.z - GetAbsOrigin().z;
  442. float height = flActualHeight;
  443. if ( height < 16 )
  444. {
  445. height = 16;
  446. }
  447. else
  448. {
  449. float flMaxHeight = bThrown ? 400 : 120;
  450. if ( height > flMaxHeight )
  451. {
  452. height = flMaxHeight;
  453. }
  454. }
  455. // overshoot the jump by an additional 8 inches
  456. // NOTE: This calculation jumps at a position INSIDE the box of the enemy (player)
  457. // so if you make the additional height too high, the crab can land on top of the
  458. // enemy's head. If we want to jump high, we'll need to move vecPos to the surface/outside
  459. // of the enemy's box.
  460. float additionalHeight = 0;
  461. if ( height < 32 )
  462. {
  463. additionalHeight = 8;
  464. }
  465. height += additionalHeight;
  466. // NOTE: This equation here is from vf^2 = vi^2 + 2*a*d
  467. float speed = sqrt( 2 * gravity * height );
  468. float time = speed / gravity;
  469. // add in the time it takes to fall the additional height
  470. // So the impact takes place on the downward slope at the original height
  471. time += sqrt( (2 * additionalHeight) / gravity );
  472. // Scale the sideways velocity to get there at the right time
  473. VectorSubtract( vecPos, GetAbsOrigin(), vecJumpVel );
  474. vecJumpVel /= time;
  475. // Speed to offset gravity at the desired height.
  476. vecJumpVel.z = speed;
  477. // Don't jump too far/fast.
  478. float flJumpSpeed = vecJumpVel.Length();
  479. float flMaxSpeed = bThrown ? 1000.0f : 650.0f;
  480. if ( flJumpSpeed > flMaxSpeed )
  481. {
  482. vecJumpVel *= flMaxSpeed / flJumpSpeed;
  483. }
  484. }
  485. else
  486. {
  487. //
  488. // Jump hop, don't care where.
  489. //
  490. Vector forward, up;
  491. AngleVectors( GetLocalAngles(), &forward, NULL, &up );
  492. vecJumpVel = Vector( forward.x, forward.y, up.z ) * 350;
  493. }
  494. AttackSound();
  495. Leap( vecJumpVel );
  496. }
  497. //-----------------------------------------------------------------------------
  498. // Purpose: Catches the monster-specific messages that occur when tagged
  499. // animation frames are played.
  500. // Input : *pEvent -
  501. //-----------------------------------------------------------------------------
  502. void CBaseHeadcrab::HandleAnimEvent( animevent_t *pEvent )
  503. {
  504. if ( pEvent->event == AE_HEADCRAB_JUMPATTACK )
  505. {
  506. // Ignore if we're in mid air
  507. if ( m_bMidJump )
  508. return;
  509. CBaseEntity *pEnemy = GetEnemy();
  510. if ( pEnemy )
  511. {
  512. if ( m_bCommittedToJump )
  513. {
  514. JumpAttack( false, m_vecCommittedJumpPos );
  515. }
  516. else
  517. {
  518. // Jump at my enemy's eyes.
  519. JumpAttack( false, pEnemy->EyePosition() );
  520. }
  521. m_bCommittedToJump = false;
  522. }
  523. else
  524. {
  525. // Jump hop, don't care where.
  526. JumpAttack( true );
  527. }
  528. return;
  529. }
  530. if ( pEvent->event == AE_HEADCRAB_CEILING_DETACH )
  531. {
  532. SetMoveType( MOVETYPE_STEP );
  533. RemoveFlag( FL_ONGROUND );
  534. RemoveFlag( FL_FLY );
  535. SetAbsVelocity( Vector ( 0, 0, -128 ) );
  536. return;
  537. }
  538. if ( pEvent->event == AE_HEADCRAB_JUMP_TELEGRAPH )
  539. {
  540. TelegraphSound();
  541. CBaseEntity *pEnemy = GetEnemy();
  542. if ( pEnemy )
  543. {
  544. // Once we telegraph, we MUST jump. This is also when commit to what point
  545. // we jump at. Jump at our enemy's eyes.
  546. m_vecCommittedJumpPos = pEnemy->EyePosition();
  547. m_bCommittedToJump = true;
  548. }
  549. return;
  550. }
  551. if ( pEvent->event == AE_HEADCRAB_BURROW_IN )
  552. {
  553. EmitSound( "NPC_Headcrab.BurrowIn" );
  554. CreateDust();
  555. return;
  556. }
  557. if ( pEvent->event == AE_HEADCRAB_BURROW_IN_FINISH )
  558. {
  559. SetBurrowed( true );
  560. return;
  561. }
  562. if ( pEvent->event == AE_HEADCRAB_BURROW_OUT )
  563. {
  564. Assert( m_bBurrowed );
  565. if ( m_bBurrowed )
  566. {
  567. EmitSound( "NPC_Headcrab.BurrowOut" );
  568. CreateDust();
  569. SetBurrowed( false );
  570. // We're done with this burrow hint node. It might be NULL here
  571. // because we may have started burrowed (no hint node in that case).
  572. GrabHintNode( NULL );
  573. }
  574. return;
  575. }
  576. CAI_BaseNPC::HandleAnimEvent( pEvent );
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Purpose: Does all the fixup for going to/from the burrowed state.
  580. //-----------------------------------------------------------------------------
  581. void CBaseHeadcrab::SetBurrowed( bool bBurrowed )
  582. {
  583. if ( bBurrowed )
  584. {
  585. AddEffects( EF_NODRAW );
  586. AddFlag( FL_NOTARGET );
  587. m_spawnflags |= SF_NPC_GAG;
  588. AddSolidFlags( FSOLID_NOT_SOLID );
  589. m_takedamage = DAMAGE_NO;
  590. m_flFieldOfView = HEADCRAB_BURROWED_FOV;
  591. SetState( NPC_STATE_IDLE );
  592. SetActivity( (Activity) ACT_HEADCRAB_BURROW_IDLE );
  593. }
  594. else
  595. {
  596. RemoveEffects( EF_NODRAW );
  597. RemoveFlag( FL_NOTARGET );
  598. m_spawnflags &= ~SF_NPC_GAG;
  599. RemoveSolidFlags( FSOLID_NOT_SOLID );
  600. m_takedamage = DAMAGE_YES;
  601. m_flFieldOfView = HEADCRAB_UNBURROWED_FOV;
  602. }
  603. m_bBurrowed = bBurrowed;
  604. }
  605. //-----------------------------------------------------------------------------
  606. // Purpose:
  607. // Input : *pTask -
  608. //-----------------------------------------------------------------------------
  609. void CBaseHeadcrab::RunTask( const Task_t *pTask )
  610. {
  611. switch ( pTask->iTask )
  612. {
  613. case TASK_HEADCRAB_CLIMB_FROM_CANISTER:
  614. AutoMovement( );
  615. if ( IsActivityFinished() )
  616. {
  617. TaskComplete();
  618. }
  619. break;
  620. case TASK_HEADCRAB_JUMP_FROM_CANISTER:
  621. GetMotor()->UpdateYaw();
  622. if ( FacingIdeal() )
  623. {
  624. TaskComplete();
  625. }
  626. break;
  627. case TASK_HEADCRAB_WAIT_FOR_BARNACLE_KILL:
  628. if ( m_flNextFlinchTime < gpGlobals->curtime )
  629. {
  630. m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
  631. CTakeDamageInfo info;
  632. PainSound( info );
  633. }
  634. break;
  635. case TASK_HEADCRAB_HOP_OFF_NPC:
  636. if( GetFlags() & FL_ONGROUND )
  637. {
  638. TaskComplete();
  639. }
  640. else
  641. {
  642. // Face the direction I've been forced to jump.
  643. GetMotor()->SetIdealYawToTargetAndUpdate( GetAbsOrigin() + GetAbsVelocity() );
  644. }
  645. break;
  646. case TASK_HEADCRAB_DROWN:
  647. if( gpGlobals->curtime > m_flTimeDrown )
  648. {
  649. OnTakeDamage( CTakeDamageInfo( this, this, m_iHealth * 2, DMG_DROWN ) );
  650. }
  651. break;
  652. case TASK_RANGE_ATTACK1:
  653. case TASK_RANGE_ATTACK2:
  654. case TASK_HEADCRAB_HARASS_HOP:
  655. {
  656. if ( IsActivityFinished() )
  657. {
  658. TaskComplete();
  659. m_bMidJump = false;
  660. SetTouch( NULL );
  661. SetThink( &CBaseHeadcrab::CallNPCThink );
  662. SetIdealActivity( ACT_IDLE );
  663. if ( m_bAttackFailed )
  664. {
  665. // our attack failed because we just ran into something solid.
  666. // delay attacking for a while so we don't just repeatedly leap
  667. // at the enemy from a bad location.
  668. m_bAttackFailed = false;
  669. m_flNextAttack = gpGlobals->curtime + 1.2f;
  670. }
  671. }
  672. break;
  673. }
  674. case TASK_HEADCRAB_CHECK_FOR_UNBURROW:
  675. {
  676. // Must wait for our next check time
  677. if ( m_flBurrowTime > gpGlobals->curtime )
  678. return;
  679. // See if we can pop up
  680. if ( ValidBurrowPoint( GetAbsOrigin() ) )
  681. {
  682. m_spawnflags &= ~SF_NPC_GAG;
  683. RemoveSolidFlags( FSOLID_NOT_SOLID );
  684. TaskComplete();
  685. return;
  686. }
  687. // Try again in a couple of seconds
  688. m_flBurrowTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );
  689. break;
  690. }
  691. case TASK_HEADCRAB_BURROW_WAIT:
  692. {
  693. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) || HasCondition( COND_CAN_RANGE_ATTACK2 ) )
  694. {
  695. TaskComplete();
  696. }
  697. break;
  698. }
  699. case TASK_HEADCRAB_CEILING_WAIT:
  700. {
  701. #ifdef HL2_EPISODIC
  702. if ( DarknessLightSourceWithinRadius( this, DARKNESS_LIGHTSOURCE_SIZE ) )
  703. {
  704. DropFromCeiling();
  705. }
  706. #endif
  707. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) || HasCondition( COND_CAN_RANGE_ATTACK2 ) )
  708. {
  709. TaskComplete();
  710. }
  711. break;
  712. }
  713. case TASK_HEADCRAB_CEILING_DETACH:
  714. {
  715. if ( IsActivityFinished() )
  716. {
  717. ClearCondition( COND_CAN_RANGE_ATTACK1 );
  718. RemoveFlag(FL_FLY);
  719. TaskComplete();
  720. }
  721. }
  722. break;
  723. case TASK_HEADCRAB_CEILING_FALL:
  724. {
  725. Vector vecPrPos;
  726. trace_t tr;
  727. //Figure out where the headcrab is going to be in quarter of a second.
  728. vecPrPos = GetAbsOrigin() + ( GetAbsVelocity() * 0.25f );
  729. UTIL_TraceHull( vecPrPos, vecPrPos, GetHullMins(), GetHullMaxs(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  730. if ( tr.startsolid == true || GetFlags() & FL_ONGROUND )
  731. {
  732. RemoveSolidFlags( FSOLID_NOT_SOLID );
  733. TaskComplete();
  734. }
  735. }
  736. break;
  737. case TASK_HEADCRAB_CEILING_LAND:
  738. {
  739. if ( IsActivityFinished() )
  740. {
  741. RemoveSolidFlags( FSOLID_NOT_SOLID ); //double-dog verify that we're solid.
  742. TaskComplete();
  743. m_bHangingFromCeiling = false;
  744. }
  745. }
  746. break;
  747. default:
  748. {
  749. BaseClass::RunTask( pTask );
  750. }
  751. }
  752. }
  753. //-----------------------------------------------------------------------------
  754. // Before jumping, headcrabs usually use SetOrigin() to lift themselves off the
  755. // ground. If the headcrab doesn't have the clearance to so, they'll be stuck
  756. // in the world. So this function makes sure there's headroom first.
  757. //-----------------------------------------------------------------------------
  758. bool CBaseHeadcrab::HasHeadroom()
  759. {
  760. trace_t tr;
  761. UTIL_TraceEntity( this, GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, 1 ), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  762. #if 0
  763. if( tr.fraction == 1.0f )
  764. {
  765. Msg("Headroom\n");
  766. }
  767. else
  768. {
  769. Msg("NO Headroom\n");
  770. }
  771. #endif
  772. return (tr.fraction == 1.0);
  773. }
  774. //-----------------------------------------------------------------------------
  775. // Purpose: LeapTouch - this is the headcrab's touch function when it is in the air.
  776. // Input : *pOther -
  777. //-----------------------------------------------------------------------------
  778. void CBaseHeadcrab::LeapTouch( CBaseEntity *pOther )
  779. {
  780. m_bMidJump = false;
  781. if ( IRelationType( pOther ) == D_HT )
  782. {
  783. // Don't hit if back on ground
  784. if ( !( GetFlags() & FL_ONGROUND ) )
  785. {
  786. if ( pOther->m_takedamage != DAMAGE_NO )
  787. {
  788. BiteSound();
  789. TouchDamage( pOther );
  790. // attack succeeded, so don't delay our next attack if we previously thought we failed
  791. m_bAttackFailed = false;
  792. }
  793. else
  794. {
  795. ImpactSound();
  796. }
  797. }
  798. else
  799. {
  800. ImpactSound();
  801. }
  802. }
  803. else if( !(GetFlags() & FL_ONGROUND) )
  804. {
  805. // Still in the air...
  806. if( !pOther->IsSolid() )
  807. {
  808. // Touching a trigger or something.
  809. return;
  810. }
  811. // just ran into something solid, so the attack probably failed. make a note of it
  812. // so that when the attack is done, we'll delay attacking for a while so we don't
  813. // just repeatedly leap at the enemy from a bad location.
  814. m_bAttackFailed = true;
  815. if( gpGlobals->curtime < m_flIgnoreWorldCollisionTime )
  816. {
  817. // Headcrabs try to ignore the world, static props, and friends for a
  818. // fraction of a second after they jump. This is because they often brush
  819. // doorframes or props as they leap, and touching those objects turns off
  820. // this touch function, which can cause them to hit the player and not bite.
  821. // A timer probably isn't the best way to fix this, but it's one of our
  822. // safer options at this point (sjb).
  823. return;
  824. }
  825. }
  826. // Shut off the touch function.
  827. SetTouch( NULL );
  828. SetThink ( &CBaseHeadcrab::CallNPCThink );
  829. }
  830. //-----------------------------------------------------------------------------
  831. //-----------------------------------------------------------------------------
  832. int CBaseHeadcrab::CalcDamageInfo( CTakeDamageInfo *pInfo )
  833. {
  834. pInfo->Set( this, this, sk_headcrab_melee_dmg.GetFloat(), DMG_SLASH );
  835. CalculateMeleeDamageForce( pInfo, GetAbsVelocity(), GetAbsOrigin() );
  836. return pInfo->GetDamage();
  837. }
  838. //-----------------------------------------------------------------------------
  839. // Purpose: Deal the damage from the headcrab's touch attack.
  840. //-----------------------------------------------------------------------------
  841. void CBaseHeadcrab::TouchDamage( CBaseEntity *pOther )
  842. {
  843. CTakeDamageInfo info;
  844. CalcDamageInfo( &info );
  845. pOther->TakeDamage( info );
  846. }
  847. //---------------------------------------------------------
  848. //---------------------------------------------------------
  849. void CBaseHeadcrab::GatherConditions( void )
  850. {
  851. // If we're hidden, just check to see if we should unhide
  852. if ( m_bHidden )
  853. {
  854. // See if there's enough room for our hull to fit here. If so, unhide.
  855. trace_t tr;
  856. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin(),GetHullMins(), GetHullMaxs(), MASK_SHOT, this, GetCollisionGroup(), &tr );
  857. if ( tr.fraction == 1.0 )
  858. {
  859. SetCondition( COND_PROVOKED );
  860. SetCondition( COND_HEADCRAB_UNHIDE );
  861. if ( g_debug_headcrab.GetInt() == HEADCRAB_DEBUG_HIDING )
  862. {
  863. NDebugOverlay::Box( GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0,255,0, true, 1.0 );
  864. }
  865. }
  866. else if ( g_debug_headcrab.GetInt() == HEADCRAB_DEBUG_HIDING )
  867. {
  868. NDebugOverlay::Box( GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 255,0,0, true, 0.1 );
  869. }
  870. // Prevent baseclass thinking, so we don't respond to enemy fire, etc.
  871. return;
  872. }
  873. BaseClass::GatherConditions();
  874. if( m_lifeState == LIFE_ALIVE && GetWaterLevel() > 1 )
  875. {
  876. // Start Drowning!
  877. SetCondition( COND_HEADCRAB_IN_WATER );
  878. }
  879. // See if I've landed on an NPC or player or something else illegal
  880. ClearCondition( COND_HEADCRAB_ILLEGAL_GROUNDENT );
  881. CBaseEntity *ground = GetGroundEntity();
  882. if( (GetFlags() & FL_ONGROUND) && ground && !ground->IsWorld() )
  883. {
  884. if ( IsHangingFromCeiling() == false )
  885. {
  886. if( ( ground->IsNPC() || ground->IsPlayer() ) )
  887. {
  888. SetCondition( COND_HEADCRAB_ILLEGAL_GROUNDENT );
  889. }
  890. else if( ground->VPhysicsGetObject() && (ground->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD) )
  891. {
  892. SetCondition( COND_HEADCRAB_ILLEGAL_GROUNDENT );
  893. }
  894. }
  895. }
  896. }
  897. //-----------------------------------------------------------------------------
  898. // Purpose:
  899. //-----------------------------------------------------------------------------
  900. void CBaseHeadcrab::PrescheduleThink( void )
  901. {
  902. BaseClass::PrescheduleThink();
  903. // Are we fading in after being hidden?
  904. if ( !m_bHidden && (m_nRenderMode != kRenderNormal) )
  905. {
  906. int iNewAlpha = MIN( 255, GetRenderColor().a + 120 );
  907. if ( iNewAlpha >= 255 )
  908. {
  909. m_nRenderMode = kRenderNormal;
  910. SetRenderColorA( 0 );
  911. }
  912. else
  913. {
  914. SetRenderColorA( iNewAlpha );
  915. }
  916. }
  917. //
  918. // Make the crab coo a little bit in combat state.
  919. //
  920. if (( m_NPCState == NPC_STATE_COMBAT ) && ( random->RandomFloat( 0, 5 ) < 0.1 ))
  921. {
  922. IdleSound();
  923. }
  924. // Make sure we've turned off our burrow state if we're not in it
  925. Activity eActivity = GetActivity();
  926. if ( m_bBurrowed &&
  927. ( eActivity != ACT_HEADCRAB_BURROW_IDLE ) &&
  928. ( eActivity != ACT_HEADCRAB_BURROW_OUT ) &&
  929. ( eActivity != ACT_HEADCRAB_BURROW_IN) )
  930. {
  931. DevMsg( "Headcrab failed to unburrow properly!\n" );
  932. Assert( 0 );
  933. SetBurrowed( false );
  934. }
  935. }
  936. //-----------------------------------------------------------------------------
  937. // Eliminates roll + pitch from the headcrab
  938. //-----------------------------------------------------------------------------
  939. #define HEADCRAB_ROLL_ELIMINATION_TIME 0.3f
  940. #define HEADCRAB_PITCH_ELIMINATION_TIME 0.3f
  941. //-----------------------------------------------------------------------------
  942. // Eliminates roll + pitch potentially in the headcrab at canister jump time
  943. //-----------------------------------------------------------------------------
  944. void CBaseHeadcrab::EliminateRollAndPitch()
  945. {
  946. QAngle angles = GetAbsAngles();
  947. angles.x = AngleNormalize( angles.x );
  948. angles.z = AngleNormalize( angles.z );
  949. if ( ( angles.x == 0.0f ) && ( angles.z == 0.0f ) )
  950. return;
  951. float flPitchRate = 90.0f / HEADCRAB_PITCH_ELIMINATION_TIME;
  952. float flPitchDelta = flPitchRate * TICK_INTERVAL;
  953. if ( fabs( angles.x ) <= flPitchDelta )
  954. {
  955. angles.x = 0.0f;
  956. }
  957. else
  958. {
  959. flPitchDelta *= (angles.x > 0.0f) ? -1.0f : 1.0f;
  960. angles.x += flPitchDelta;
  961. }
  962. float flRollRate = 180.0f / HEADCRAB_ROLL_ELIMINATION_TIME;
  963. float flRollDelta = flRollRate * TICK_INTERVAL;
  964. if ( fabs( angles.z ) <= flRollDelta )
  965. {
  966. angles.z = 0.0f;
  967. }
  968. else
  969. {
  970. flRollDelta *= (angles.z > 0.0f) ? -1.0f : 1.0f;
  971. angles.z += flRollDelta;
  972. }
  973. SetAbsAngles( angles );
  974. SetContextThink( &CBaseHeadcrab::EliminateRollAndPitch, gpGlobals->curtime + TICK_INTERVAL, s_pPitchContext );
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Begins the climb from the canister
  978. //-----------------------------------------------------------------------------
  979. void CBaseHeadcrab::BeginClimbFromCanister()
  980. {
  981. Assert( GetMoveParent() );
  982. // Compute a desired position or hint
  983. Vector vecForward, vecActualForward;
  984. AngleVectors( GetMoveParent()->GetAbsAngles(), &vecActualForward );
  985. vecForward = vecActualForward;
  986. vecForward.z = 0.0f;
  987. VectorNormalize( vecForward );
  988. Vector vecSearchCenter = GetAbsOrigin();
  989. CAI_Hint *pHint = CAI_HintManager::FindHint( this, HINT_HEADCRAB_BURROW_POINT, 0, HEADCRAB_BURROW_POINT_SEARCH_RADIUS, &vecSearchCenter );
  990. if( !pHint && hl2_episodic.GetBool() )
  991. {
  992. // Look for exit points within 10 feet.
  993. pHint = CAI_HintManager::FindHint( this, HINT_HEADCRAB_EXIT_POD_POINT, 0, 120.0f, &vecSearchCenter );
  994. }
  995. if ( pHint && ( !pHint->IsLocked() ) )
  996. {
  997. // Claim the hint node so other headcrabs don't try to take it!
  998. GrabHintNode( pHint );
  999. // Compute relative yaw..
  1000. Vector vecDelta;
  1001. VectorSubtract( pHint->GetAbsOrigin(), vecSearchCenter, vecDelta );
  1002. vecDelta.z = 0.0f;
  1003. VectorNormalize( vecDelta );
  1004. float flAngle = DotProduct( vecDelta, vecForward );
  1005. if ( flAngle >= 0.707f )
  1006. {
  1007. m_nJumpFromCanisterDir = 1;
  1008. }
  1009. else
  1010. {
  1011. // Check the cross product to see if it's on the left or right.
  1012. // All we care about is the sign of the z component. If it's +, the hint is on the left.
  1013. // If it's -, then the hint is on the right.
  1014. float flCrossZ = vecForward.x * vecDelta.y - vecDelta.x * vecForward.y;
  1015. m_nJumpFromCanisterDir = ( flCrossZ > 0 ) ? 0 : 2;
  1016. }
  1017. }
  1018. else
  1019. {
  1020. // Choose a random direction (forward, left, or right)
  1021. m_nJumpFromCanisterDir = random->RandomInt( 0, 2 );
  1022. }
  1023. Activity act;
  1024. switch( m_nJumpFromCanisterDir )
  1025. {
  1026. case 0:
  1027. act = (Activity)ACT_HEADCRAB_CRAWL_FROM_CANISTER_LEFT;
  1028. break;
  1029. default:
  1030. case 1:
  1031. act = (Activity)ACT_HEADCRAB_CRAWL_FROM_CANISTER_CENTER;
  1032. break;
  1033. case 2:
  1034. act = (Activity)ACT_HEADCRAB_CRAWL_FROM_CANISTER_RIGHT;
  1035. break;
  1036. }
  1037. SetIdealActivity( act );
  1038. }
  1039. //-----------------------------------------------------------------------------
  1040. // Jumps from the canister
  1041. //-----------------------------------------------------------------------------
  1042. #define HEADCRAB_ATTACK_PLAYER_FROM_CANISTER_DIST 250.0f
  1043. #define HEADCRAB_ATTACK_PLAYER_FROM_CANISTER_COSANGLE 0.866f
  1044. void CBaseHeadcrab::JumpFromCanister()
  1045. {
  1046. Assert( GetMoveParent() );
  1047. Vector vecForward, vecActualForward, vecActualRight;
  1048. AngleVectors( GetMoveParent()->GetAbsAngles(), &vecActualForward, &vecActualRight, NULL );
  1049. switch( m_nJumpFromCanisterDir )
  1050. {
  1051. case 0:
  1052. VectorMultiply( vecActualRight, -1.0f, vecForward );
  1053. break;
  1054. case 1:
  1055. vecForward = vecActualForward;
  1056. break;
  1057. case 2:
  1058. vecForward = vecActualRight;
  1059. break;
  1060. }
  1061. vecForward.z = 0.0f;
  1062. VectorNormalize( vecForward );
  1063. QAngle headCrabAngles;
  1064. VectorAngles( vecForward, headCrabAngles );
  1065. SetActivity( ACT_RANGE_ATTACK1 );
  1066. StudioFrameAdvanceManual( 0.0 );
  1067. SetParent( NULL );
  1068. RemoveFlag( FL_FLY );
  1069. IncrementInterpolationFrame();
  1070. GetMotor()->SetIdealYaw( headCrabAngles.y );
  1071. // Check to see if the player is within jump range. If so, jump at him!
  1072. bool bJumpedAtEnemy = false;
  1073. // FIXME: Can't use GetEnemy() here because enemy only updates during
  1074. // schedules which are interruptible by COND_NEW_ENEMY or COND_LOST_ENEMY
  1075. CBaseEntity *pEnemy = BestEnemy();
  1076. if ( pEnemy )
  1077. {
  1078. Vector vecDirToEnemy;
  1079. VectorSubtract( pEnemy->GetAbsOrigin(), GetAbsOrigin(), vecDirToEnemy );
  1080. vecDirToEnemy.z = 0.0f;
  1081. float flDist = VectorNormalize( vecDirToEnemy );
  1082. if ( ( flDist < HEADCRAB_ATTACK_PLAYER_FROM_CANISTER_DIST ) &&
  1083. ( DotProduct( vecDirToEnemy, vecForward ) >= HEADCRAB_ATTACK_PLAYER_FROM_CANISTER_COSANGLE ) )
  1084. {
  1085. GrabHintNode( NULL );
  1086. JumpAttack( false, pEnemy->EyePosition(), false );
  1087. bJumpedAtEnemy = true;
  1088. }
  1089. }
  1090. if ( !bJumpedAtEnemy )
  1091. {
  1092. if ( GetHintNode() )
  1093. {
  1094. JumpToBurrowHint( GetHintNode() );
  1095. }
  1096. else
  1097. {
  1098. vecForward *= 100.0f;
  1099. vecForward += GetAbsOrigin();
  1100. JumpAttack( false, vecForward, false );
  1101. }
  1102. }
  1103. EliminateRollAndPitch();
  1104. }
  1105. #define HEADCRAB_ILLUMINATED_TIME 0.15f
  1106. void CBaseHeadcrab::DropFromCeiling( void )
  1107. {
  1108. #ifdef HL2_EPISODIC
  1109. if ( HL2GameRules()->IsAlyxInDarknessMode() )
  1110. {
  1111. if ( IsHangingFromCeiling() )
  1112. {
  1113. if ( m_flIlluminatedTime == -1 )
  1114. {
  1115. m_flIlluminatedTime = gpGlobals->curtime + HEADCRAB_ILLUMINATED_TIME;
  1116. return;
  1117. }
  1118. if ( m_flIlluminatedTime <= gpGlobals->curtime )
  1119. {
  1120. if ( IsCurSchedule( SCHED_HEADCRAB_CEILING_DROP ) == false )
  1121. {
  1122. SetSchedule( SCHED_HEADCRAB_CEILING_DROP );
  1123. CBaseEntity *pPlayer = AI_GetSinglePlayer();
  1124. if ( pPlayer )
  1125. {
  1126. SetEnemy( pPlayer ); //Is this a bad thing to do?
  1127. UpdateEnemyMemory( pPlayer, pPlayer->GetAbsOrigin());
  1128. }
  1129. }
  1130. }
  1131. }
  1132. }
  1133. #endif // HL2_EPISODIC
  1134. }
  1135. //-----------------------------------------------------------------------------
  1136. // Purpose: Player has illuminated this NPC with the flashlight
  1137. //-----------------------------------------------------------------------------
  1138. void CBaseHeadcrab::PlayerHasIlluminatedNPC( CBasePlayer *pPlayer, float flDot )
  1139. {
  1140. if ( flDot < 0.97387f )
  1141. return;
  1142. DropFromCeiling();
  1143. }
  1144. bool CBaseHeadcrab::CanBeAnEnemyOf( CBaseEntity *pEnemy )
  1145. {
  1146. #ifdef HL2_EPISODIC
  1147. if ( IsHangingFromCeiling() )
  1148. return false;
  1149. #endif
  1150. return BaseClass::CanBeAnEnemyOf( pEnemy );
  1151. }
  1152. //-----------------------------------------------------------------------------
  1153. // Purpose:
  1154. // Input : pTask -
  1155. //-----------------------------------------------------------------------------
  1156. void CBaseHeadcrab::StartTask( const Task_t *pTask )
  1157. {
  1158. switch ( pTask->iTask )
  1159. {
  1160. case TASK_HEADCRAB_WAIT_FOR_BARNACLE_KILL:
  1161. break;
  1162. case TASK_HEADCRAB_BURROW_WAIT:
  1163. break;
  1164. case TASK_HEADCRAB_CLIMB_FROM_CANISTER:
  1165. BeginClimbFromCanister();
  1166. break;
  1167. case TASK_HEADCRAB_JUMP_FROM_CANISTER:
  1168. JumpFromCanister();
  1169. break;
  1170. case TASK_HEADCRAB_CEILING_POSITION:
  1171. {
  1172. trace_t tr;
  1173. UTIL_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector( 0, 0, 512 ), NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1174. // SetMoveType( MOVETYPE_NONE );
  1175. AddFlag(FL_FLY);
  1176. m_bHangingFromCeiling = true;
  1177. //Don't need this anymore
  1178. RemoveSpawnFlags( SF_HEADCRAB_START_HANGING );
  1179. SetAbsOrigin( tr.endpos );
  1180. TaskComplete();
  1181. }
  1182. break;
  1183. case TASK_HEADCRAB_CEILING_WAIT:
  1184. break;
  1185. case TASK_HEADCRAB_CEILING_DETACH:
  1186. {
  1187. SetIdealActivity( (Activity)ACT_HEADCRAB_CEILING_DETACH );
  1188. }
  1189. break;
  1190. case TASK_HEADCRAB_CEILING_FALL:
  1191. {
  1192. SetIdealActivity( (Activity)ACT_HEADCRAB_CEILING_FALL );
  1193. }
  1194. break;
  1195. case TASK_HEADCRAB_CEILING_LAND:
  1196. {
  1197. SetIdealActivity( (Activity)ACT_HEADCRAB_CEILING_LAND );
  1198. }
  1199. break;
  1200. case TASK_HEADCRAB_HARASS_HOP:
  1201. {
  1202. // Just pop up into the air like you're trying to get at the
  1203. // enemy, even though it's known you can't reach them.
  1204. if ( GetEnemy() )
  1205. {
  1206. Vector forward, up;
  1207. GetVectors( &forward, NULL, &up );
  1208. m_vecCommittedJumpPos = GetAbsOrigin();
  1209. m_vecCommittedJumpPos += up * random->RandomFloat( 80, 150 );
  1210. m_vecCommittedJumpPos += forward * random->RandomFloat( 32, 80 );
  1211. m_bCommittedToJump = true;
  1212. SetIdealActivity( ACT_RANGE_ATTACK1 );
  1213. }
  1214. else
  1215. {
  1216. TaskFail( "No enemy" );
  1217. }
  1218. }
  1219. break;
  1220. case TASK_HEADCRAB_HOP_OFF_NPC:
  1221. {
  1222. CBaseEntity *ground = GetGroundEntity();
  1223. if( ground )
  1224. {
  1225. // If jumping off of a physics object that the player is holding, create a
  1226. // solver to prevent the headcrab from colliding with that object for a
  1227. // short time.
  1228. if( ground && ground->VPhysicsGetObject() )
  1229. {
  1230. if( ground->VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  1231. {
  1232. NPCPhysics_CreateSolver( this, ground, true, 0.5 );
  1233. }
  1234. }
  1235. Vector vecJumpDir;
  1236. // Jump in some random direction. This way if the person I'm standing on is
  1237. // against a wall, I'll eventually get away.
  1238. vecJumpDir.z = 0;
  1239. vecJumpDir.x = 0;
  1240. vecJumpDir.y = 0;
  1241. while( vecJumpDir.x == 0 && vecJumpDir.y == 0 )
  1242. {
  1243. vecJumpDir.x = random->RandomInt( -1, 1 );
  1244. vecJumpDir.y = random->RandomInt( -1, 1 );
  1245. }
  1246. vecJumpDir.NormalizeInPlace();
  1247. SetGroundEntity( NULL );
  1248. if( HasHeadroom() )
  1249. {
  1250. // Bump up
  1251. MoveOrigin( Vector( 0, 0, 1 ) );
  1252. }
  1253. SetAbsVelocity( vecJumpDir * 200 + Vector( 0, 0, 200 ) );
  1254. }
  1255. else
  1256. {
  1257. // *shrug* I guess they're gone now. Or dead.
  1258. TaskComplete();
  1259. }
  1260. }
  1261. break;
  1262. case TASK_HEADCRAB_DROWN:
  1263. {
  1264. // Set the gravity really low here! Sink slowly
  1265. SetGravity( UTIL_ScaleForGravity( 80 ) );
  1266. m_flTimeDrown = gpGlobals->curtime + 4;
  1267. break;
  1268. }
  1269. case TASK_RANGE_ATTACK1:
  1270. {
  1271. #ifdef WEDGE_FIX_THIS
  1272. CPASAttenuationFilter filter( this, ATTN_IDLE );
  1273. EmitSound( filter, entindex(), CHAN_WEAPON, pAttackSounds[0], GetSoundVolume(), ATTN_IDLE, 0, GetVoicePitch() );
  1274. #endif
  1275. SetIdealActivity( ACT_RANGE_ATTACK1 );
  1276. break;
  1277. }
  1278. case TASK_HEADCRAB_UNHIDE:
  1279. {
  1280. m_bHidden = false;
  1281. RemoveSolidFlags( FSOLID_NOT_SOLID );
  1282. RemoveEffects( EF_NODRAW );
  1283. TaskComplete();
  1284. break;
  1285. }
  1286. case TASK_HEADCRAB_CHECK_FOR_UNBURROW:
  1287. {
  1288. if ( ValidBurrowPoint( GetAbsOrigin() ) )
  1289. {
  1290. m_spawnflags &= ~SF_NPC_GAG;
  1291. RemoveSolidFlags( FSOLID_NOT_SOLID );
  1292. TaskComplete();
  1293. }
  1294. break;
  1295. }
  1296. case TASK_HEADCRAB_FIND_BURROW_IN_POINT:
  1297. {
  1298. if ( FindBurrow( GetAbsOrigin(), pTask->flTaskData, true ) == false )
  1299. {
  1300. TaskFail( "TASK_HEADCRAB_FIND_BURROW_IN_POINT: Unable to find burrow in position\n" );
  1301. }
  1302. else
  1303. {
  1304. TaskComplete();
  1305. }
  1306. break;
  1307. }
  1308. case TASK_HEADCRAB_BURROW:
  1309. {
  1310. Burrow();
  1311. TaskComplete();
  1312. break;
  1313. }
  1314. case TASK_HEADCRAB_UNBURROW:
  1315. {
  1316. Unburrow();
  1317. TaskComplete();
  1318. break;
  1319. }
  1320. default:
  1321. {
  1322. BaseClass::StartTask( pTask );
  1323. }
  1324. }
  1325. }
  1326. //-----------------------------------------------------------------------------
  1327. // Purpose: For innate melee attack
  1328. // Input :
  1329. // Output :
  1330. //-----------------------------------------------------------------------------
  1331. float CBaseHeadcrab::InnateRange1MinRange( void )
  1332. {
  1333. return HEADCRAB_MIN_JUMP_DIST;
  1334. }
  1335. float CBaseHeadcrab::InnateRange1MaxRange( void )
  1336. {
  1337. return HEADCRAB_MAX_JUMP_DIST;
  1338. }
  1339. int CBaseHeadcrab::RangeAttack1Conditions( float flDot, float flDist )
  1340. {
  1341. if ( gpGlobals->curtime < m_flNextAttack )
  1342. return 0;
  1343. if ( ( GetFlags() & FL_ONGROUND ) == false )
  1344. return 0;
  1345. // When we're burrowed ignore facing, because when we unburrow we'll cheat and face our enemy.
  1346. if ( !m_bBurrowed && ( flDot < 0.65 ) )
  1347. return COND_NOT_FACING_ATTACK;
  1348. // This code stops lots of headcrabs swarming you and blocking you
  1349. // whilst jumping up and down in your face over and over. It forces
  1350. // them to back up a bit. If this causes problems, consider using it
  1351. // for the fast headcrabs only, rather than just removing it.(sjb)
  1352. if ( flDist < HEADCRAB_MIN_JUMP_DIST )
  1353. return COND_TOO_CLOSE_TO_ATTACK;
  1354. if ( flDist > HEADCRAB_MAX_JUMP_DIST )
  1355. return COND_TOO_FAR_TO_ATTACK;
  1356. // Make sure the way is clear!
  1357. CBaseEntity *pEnemy = GetEnemy();
  1358. if( pEnemy )
  1359. {
  1360. bool bEnemyIsBullseye = ( dynamic_cast<CNPC_Bullseye *>(pEnemy) != NULL );
  1361. trace_t tr;
  1362. AI_TraceLine( EyePosition(), pEnemy->EyePosition(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
  1363. if ( tr.m_pEnt != GetEnemy() )
  1364. {
  1365. if ( !bEnemyIsBullseye || tr.m_pEnt != NULL )
  1366. return COND_NONE;
  1367. }
  1368. if( GetEnemy()->EyePosition().z - 36.0f > GetAbsOrigin().z )
  1369. {
  1370. // Only run this test if trying to jump at a player who is higher up than me, else this
  1371. // code will always prevent a headcrab from jumping down at an enemy, and sometimes prevent it
  1372. // jumping just slightly up at an enemy.
  1373. Vector vStartHullTrace = GetAbsOrigin();
  1374. vStartHullTrace.z += 1.0;
  1375. Vector vEndHullTrace = GetEnemy()->EyePosition() - GetAbsOrigin();
  1376. vEndHullTrace.NormalizeInPlace();
  1377. vEndHullTrace *= 8.0;
  1378. vEndHullTrace += GetAbsOrigin();
  1379. AI_TraceHull( vStartHullTrace, vEndHullTrace,GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1380. if ( tr.m_pEnt != NULL && tr.m_pEnt != GetEnemy() )
  1381. {
  1382. return COND_TOO_CLOSE_TO_ATTACK;
  1383. }
  1384. }
  1385. }
  1386. return COND_CAN_RANGE_ATTACK1;
  1387. }
  1388. //------------------------------------------------------------------------------
  1389. // Purpose: Override to do headcrab specific gibs
  1390. // Output :
  1391. //------------------------------------------------------------------------------
  1392. bool CBaseHeadcrab::CorpseGib( const CTakeDamageInfo &info )
  1393. {
  1394. EmitSound( "NPC_HeadCrab.Gib" );
  1395. return BaseClass::CorpseGib( info );
  1396. }
  1397. //------------------------------------------------------------------------------
  1398. // Purpose:
  1399. // Input :
  1400. //------------------------------------------------------------------------------
  1401. void CBaseHeadcrab::Touch( CBaseEntity *pOther )
  1402. {
  1403. // If someone has smacked me into a wall then gib!
  1404. if (m_NPCState == NPC_STATE_DEAD)
  1405. {
  1406. if (GetAbsVelocity().Length() > 250)
  1407. {
  1408. trace_t tr;
  1409. Vector vecDir = GetAbsVelocity();
  1410. VectorNormalize(vecDir);
  1411. AI_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vecDir * 100,
  1412. MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
  1413. float dotPr = DotProduct(vecDir,tr.plane.normal);
  1414. if ((tr.fraction != 1.0) &&
  1415. (dotPr < -0.8) )
  1416. {
  1417. CTakeDamageInfo info( GetWorldEntity(), GetWorldEntity(), 100.0f, DMG_CRUSH );
  1418. info.SetDamagePosition( tr.endpos );
  1419. Event_Gibbed( info );
  1420. }
  1421. }
  1422. }
  1423. BaseClass::Touch(pOther);
  1424. }
  1425. //-----------------------------------------------------------------------------
  1426. // Purpose:
  1427. // Input : pevInflictor -
  1428. // pevAttacker -
  1429. // flDamage -
  1430. // bitsDamageType -
  1431. // Output :
  1432. //-----------------------------------------------------------------------------
  1433. int CBaseHeadcrab::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  1434. {
  1435. CTakeDamageInfo info = inputInfo;
  1436. //
  1437. // Don't take any acid damage.
  1438. //
  1439. if ( info.GetDamageType() & DMG_ACID )
  1440. {
  1441. info.SetDamage( 0 );
  1442. }
  1443. //
  1444. // Certain death from melee bludgeon weapons!
  1445. //
  1446. if ( info.GetDamageType() & DMG_CLUB )
  1447. {
  1448. info.SetDamage( m_iHealth );
  1449. }
  1450. if( info.GetDamageType() & DMG_BLAST )
  1451. {
  1452. if( random->RandomInt( 0 , 1 ) == 0 )
  1453. {
  1454. // Catch on fire randomly if damaged in a blast.
  1455. Ignite( 30 );
  1456. }
  1457. }
  1458. if( info.GetDamageType() & DMG_BURN )
  1459. {
  1460. // Slow down burn damage so that headcrabs live longer while on fire.
  1461. info.ScaleDamage( 0.25 );
  1462. #define HEADCRAB_SCORCH_RATE 5
  1463. #define HEADCRAB_SCORCH_FLOOR 30
  1464. if( IsOnFire() )
  1465. {
  1466. Scorch( HEADCRAB_SCORCH_RATE, HEADCRAB_SCORCH_FLOOR );
  1467. if( m_iHealth <= 1 && (entindex() % 2) )
  1468. {
  1469. // Some headcrabs leap at you with their dying breath
  1470. if( !IsCurSchedule( SCHED_HEADCRAB_RANGE_ATTACK1 ) && !IsRunningDynamicInteraction() )
  1471. {
  1472. SetSchedule( SCHED_HEADCRAB_RANGE_ATTACK1 );
  1473. }
  1474. }
  1475. }
  1476. Ignite( 30 );
  1477. }
  1478. return CAI_BaseNPC::OnTakeDamage_Alive( info );
  1479. }
  1480. //-----------------------------------------------------------------------------
  1481. //-----------------------------------------------------------------------------
  1482. void CBaseHeadcrab::ClampRagdollForce( const Vector &vecForceIn, Vector *vecForceOut )
  1483. {
  1484. // Assumes the headcrab mass is 5kg (100 feet per second)
  1485. float MAX_HEADCRAB_RAGDOLL_SPEED = 100.0f * 12.0f * 5.0f;
  1486. Vector vecClampedForce;
  1487. BaseClass::ClampRagdollForce( vecForceIn, &vecClampedForce );
  1488. // Copy the force to vecForceOut, in case we don't change it.
  1489. *vecForceOut = vecClampedForce;
  1490. float speed = VectorNormalize( vecClampedForce );
  1491. if( speed > MAX_HEADCRAB_RAGDOLL_SPEED )
  1492. {
  1493. // Don't let the ragdoll go as fast as it was going to.
  1494. vecClampedForce *= MAX_HEADCRAB_RAGDOLL_SPEED;
  1495. *vecForceOut = vecClampedForce;
  1496. }
  1497. }
  1498. //-----------------------------------------------------------------------------
  1499. //-----------------------------------------------------------------------------
  1500. void CBaseHeadcrab::Event_Killed( const CTakeDamageInfo &info )
  1501. {
  1502. // Create a little decal underneath the headcrab
  1503. // This type of damage combination happens from dynamic scripted sequences
  1504. if ( info.GetDamageType() & (DMG_GENERIC | DMG_PREVENT_PHYSICS_FORCE) )
  1505. {
  1506. trace_t tr;
  1507. AI_TraceLine( GetAbsOrigin()+Vector(0,0,1), GetAbsOrigin()-Vector(0,0,64), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1508. UTIL_DecalTrace( &tr, "YellowBlood" );
  1509. }
  1510. BaseClass::Event_Killed( info );
  1511. }
  1512. //-----------------------------------------------------------------------------
  1513. // Purpose:
  1514. // Input : Type -
  1515. //-----------------------------------------------------------------------------
  1516. int CBaseHeadcrab::TranslateSchedule( int scheduleType )
  1517. {
  1518. switch( scheduleType )
  1519. {
  1520. case SCHED_FALL_TO_GROUND:
  1521. return SCHED_HEADCRAB_FALL_TO_GROUND;
  1522. case SCHED_WAKE_ANGRY:
  1523. {
  1524. if ( HaveSequenceForActivity((Activity)ACT_HEADCRAB_THREAT_DISPLAY) )
  1525. return SCHED_HEADCRAB_WAKE_ANGRY;
  1526. else
  1527. return SCHED_HEADCRAB_WAKE_ANGRY_NO_DISPLAY;
  1528. }
  1529. case SCHED_RANGE_ATTACK1:
  1530. return SCHED_HEADCRAB_RANGE_ATTACK1;
  1531. case SCHED_FAIL_TAKE_COVER:
  1532. return SCHED_ALERT_FACE;
  1533. case SCHED_CHASE_ENEMY_FAILED:
  1534. {
  1535. if( !GetEnemy() )
  1536. break;
  1537. if( !HasCondition( COND_SEE_ENEMY ) )
  1538. break;
  1539. float flZDiff;
  1540. flZDiff = GetEnemy()->GetAbsOrigin().z - GetAbsOrigin().z;
  1541. // Make sure the enemy isn't so high above me that this would look silly.
  1542. if( flZDiff < 128.0f || flZDiff > 512.0f )
  1543. return SCHED_COMBAT_PATROL;
  1544. float flDist;
  1545. flDist = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).Length2D();
  1546. // Maybe a patrol will bring me closer.
  1547. if( flDist > 384.0f )
  1548. {
  1549. return SCHED_COMBAT_PATROL;
  1550. }
  1551. return SCHED_HEADCRAB_HARASS_ENEMY;
  1552. }
  1553. break;
  1554. }
  1555. return BaseClass::TranslateSchedule( scheduleType );
  1556. }
  1557. //-----------------------------------------------------------------------------
  1558. // Purpose:
  1559. //-----------------------------------------------------------------------------
  1560. int CBaseHeadcrab::SelectSchedule( void )
  1561. {
  1562. if ( m_bCrawlFromCanister )
  1563. {
  1564. m_bCrawlFromCanister = false;
  1565. return SCHED_HEADCRAB_CRAWL_FROM_CANISTER;
  1566. }
  1567. // If we're hidden or waiting until seen, don't do much at all
  1568. if ( m_bHidden || HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN) )
  1569. {
  1570. if( HasCondition( COND_HEADCRAB_UNHIDE ) )
  1571. {
  1572. // We've decided to unhide
  1573. return SCHED_HEADCRAB_UNHIDE;
  1574. }
  1575. return m_bBurrowed ? ( int )SCHED_HEADCRAB_BURROW_WAIT : ( int )SCHED_IDLE_STAND;
  1576. }
  1577. if ( GetSpawnFlags() & SF_HEADCRAB_START_HANGING && IsHangingFromCeiling() == false )
  1578. {
  1579. return SCHED_HEADCRAB_CEILING_WAIT;
  1580. }
  1581. if ( IsHangingFromCeiling() )
  1582. {
  1583. bool bIsAlyxInDarknessMode = false;
  1584. #ifdef HL2_EPISODIC
  1585. bIsAlyxInDarknessMode = HL2GameRules()->IsAlyxInDarknessMode();
  1586. #endif // HL2_EPISODIC
  1587. if ( bIsAlyxInDarknessMode == false && ( HasCondition( COND_CAN_RANGE_ATTACK1 ) || HasCondition( COND_NEW_ENEMY ) ) )
  1588. return SCHED_HEADCRAB_CEILING_DROP;
  1589. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  1590. return SCHED_HEADCRAB_CEILING_DROP;
  1591. return SCHED_HEADCRAB_CEILING_WAIT;
  1592. }
  1593. if ( m_bBurrowed )
  1594. {
  1595. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  1596. return SCHED_HEADCRAB_BURROW_OUT;
  1597. return SCHED_HEADCRAB_BURROW_WAIT;
  1598. }
  1599. if( HasCondition( COND_HEADCRAB_IN_WATER ) )
  1600. {
  1601. // No matter what, drown in water
  1602. return SCHED_HEADCRAB_DROWN;
  1603. }
  1604. if( HasCondition( COND_HEADCRAB_ILLEGAL_GROUNDENT ) )
  1605. {
  1606. // You're on an NPC's head. Get off.
  1607. return SCHED_HEADCRAB_HOP_RANDOMLY;
  1608. }
  1609. if ( HasCondition( COND_HEADCRAB_BARNACLED ) )
  1610. {
  1611. // Caught by a barnacle!
  1612. return SCHED_HEADCRAB_BARNACLED;
  1613. }
  1614. switch ( m_NPCState )
  1615. {
  1616. case NPC_STATE_ALERT:
  1617. {
  1618. if (HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ))
  1619. {
  1620. if ( fabs( GetMotor()->DeltaIdealYaw() ) < ( 1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction
  1621. {
  1622. return SCHED_TAKE_COVER_FROM_ORIGIN;
  1623. }
  1624. else if ( SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
  1625. {
  1626. m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 1, 3 );
  1627. return SCHED_SMALL_FLINCH;
  1628. }
  1629. }
  1630. else if (HasCondition( COND_HEAR_DANGER ) ||
  1631. HasCondition( COND_HEAR_PLAYER ) ||
  1632. HasCondition( COND_HEAR_WORLD ) ||
  1633. HasCondition( COND_HEAR_COMBAT ))
  1634. {
  1635. return SCHED_ALERT_FACE_BESTSOUND;
  1636. }
  1637. else
  1638. {
  1639. return SCHED_PATROL_WALK;
  1640. }
  1641. break;
  1642. }
  1643. }
  1644. if ( HasCondition( COND_FLOATING_OFF_GROUND ) )
  1645. {
  1646. SetGravity( 1.0 );
  1647. SetGroundEntity( NULL );
  1648. return SCHED_FALL_TO_GROUND;
  1649. }
  1650. if ( GetHintNode() && GetHintNode()->HintType() == HINT_HEADCRAB_BURROW_POINT )
  1651. {
  1652. // Only burrow if we're not within leap attack distance of our enemy.
  1653. if ( ( GetEnemy() == NULL ) || ( ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).Length() > HEADCRAB_MAX_JUMP_DIST ) )
  1654. {
  1655. return SCHED_HEADCRAB_RUN_TO_SPECIFIC_BURROW;
  1656. }
  1657. else
  1658. {
  1659. // Forget about burrowing, we've got folks to leap at!
  1660. GrabHintNode( NULL );
  1661. }
  1662. }
  1663. int nSchedule = BaseClass::SelectSchedule();
  1664. if ( nSchedule == SCHED_SMALL_FLINCH )
  1665. {
  1666. m_flNextFlinchTime = gpGlobals->curtime + random->RandomFloat( 1, 3 );
  1667. }
  1668. return nSchedule;
  1669. }
  1670. //-----------------------------------------------------------------------------
  1671. //-----------------------------------------------------------------------------
  1672. int CBaseHeadcrab::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1673. {
  1674. if ( failedSchedule == SCHED_BACK_AWAY_FROM_ENEMY && failedTask == TASK_FIND_BACKAWAY_FROM_SAVEPOSITION )
  1675. {
  1676. if ( HasCondition( COND_SEE_ENEMY ) )
  1677. {
  1678. return SCHED_RANGE_ATTACK1;
  1679. }
  1680. }
  1681. if ( failedSchedule == SCHED_BACK_AWAY_FROM_ENEMY || failedSchedule == SCHED_PATROL_WALK || failedSchedule == SCHED_COMBAT_PATROL )
  1682. {
  1683. if( !IsFirmlyOnGround() )
  1684. {
  1685. return SCHED_HEADCRAB_HOP_RANDOMLY;
  1686. }
  1687. }
  1688. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  1689. }
  1690. //-----------------------------------------------------------------------------
  1691. // Purpose:
  1692. // Input : &info -
  1693. // &vecDir -
  1694. // *ptr -
  1695. //-----------------------------------------------------------------------------
  1696. void CBaseHeadcrab::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  1697. {
  1698. CTakeDamageInfo newInfo = info;
  1699. // Ignore if we're in a dynamic scripted sequence
  1700. if ( info.GetDamageType() & DMG_PHYSGUN && !IsRunningDynamicInteraction() )
  1701. {
  1702. Vector puntDir = ( info.GetDamageForce() * 1000.0f );
  1703. newInfo.SetDamage( m_iMaxHealth / 3.0f );
  1704. if( info.GetDamage() >= GetHealth() )
  1705. {
  1706. // This blow will be fatal, so scale the damage force
  1707. // (it's a unit vector) so that the ragdoll will be
  1708. // affected.
  1709. newInfo.SetDamageForce( info.GetDamageForce() * 3000.0f );
  1710. }
  1711. PainSound( newInfo );
  1712. SetGroundEntity( NULL );
  1713. ApplyAbsVelocityImpulse( puntDir );
  1714. }
  1715. BaseClass::TraceAttack( newInfo, vecDir, ptr, pAccumulator );
  1716. }
  1717. //-----------------------------------------------------------------------------
  1718. //-----------------------------------------------------------------------------
  1719. void CBaseHeadcrab::Ignite( float flFlameLifetime, bool bNPCOnly, float flSize, bool bCalledByLevelDesigner )
  1720. {
  1721. // Can't start on fire if we're burrowed
  1722. if ( m_bBurrowed )
  1723. return;
  1724. bool bWasOnFire = IsOnFire();
  1725. #ifdef HL2_EPISODIC
  1726. if( GetHealth() > flFlameLifetime )
  1727. {
  1728. // Add some burn time to very healthy headcrabs to fix a bug where
  1729. // black headcrabs would sometimes spontaneously extinguish (and survive)
  1730. flFlameLifetime += 10.0f;
  1731. }
  1732. #endif// HL2_EPISODIC
  1733. BaseClass::Ignite( flFlameLifetime, bNPCOnly, flSize, bCalledByLevelDesigner );
  1734. if( !bWasOnFire )
  1735. {
  1736. #ifdef HL2_EPISODIC
  1737. if ( HL2GameRules()->IsAlyxInDarknessMode() == true )
  1738. {
  1739. GetEffectEntity()->AddEffects( EF_DIMLIGHT );
  1740. }
  1741. #endif // HL2_EPISODIC
  1742. // For the poison headcrab, who runs around when ignited
  1743. SetActivity( TranslateActivity(GetIdealActivity()) );
  1744. }
  1745. }
  1746. //-----------------------------------------------------------------------------
  1747. // Purpose: This is a generic function (to be implemented by sub-classes) to
  1748. // handle specific interactions between different types of characters
  1749. // (For example the barnacle grabbing an NPC)
  1750. // Input : Constant for the type of interaction
  1751. // Output : true - if sub-class has a response for the interaction
  1752. // false - if sub-class has no response
  1753. //-----------------------------------------------------------------------------
  1754. bool CBaseHeadcrab::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
  1755. {
  1756. if (interactionType == g_interactionBarnacleVictimDangle)
  1757. {
  1758. // Die instantly
  1759. return false;
  1760. }
  1761. else if (interactionType == g_interactionVortigauntStomp)
  1762. {
  1763. SetIdealState( NPC_STATE_PRONE );
  1764. return true;
  1765. }
  1766. else if (interactionType == g_interactionVortigauntStompFail)
  1767. {
  1768. SetIdealState( NPC_STATE_COMBAT );
  1769. return true;
  1770. }
  1771. else if (interactionType == g_interactionVortigauntStompHit)
  1772. {
  1773. // Gib the existing guy, but only with legs and guts
  1774. m_nGibCount = HEADCRAB_LEGS_GIB_COUNT;
  1775. OnTakeDamage ( CTakeDamageInfo( sourceEnt, sourceEnt, m_iHealth, DMG_CRUSH|DMG_ALWAYSGIB ) );
  1776. // Create dead headcrab in its place
  1777. CBaseHeadcrab *pEntity = (CBaseHeadcrab*) CreateEntityByName( "npc_headcrab" );
  1778. pEntity->Spawn();
  1779. pEntity->SetLocalOrigin( GetLocalOrigin() );
  1780. pEntity->SetLocalAngles( GetLocalAngles() );
  1781. pEntity->m_NPCState = NPC_STATE_DEAD;
  1782. return true;
  1783. }
  1784. else if ( interactionType == g_interactionVortigauntKick
  1785. /* || (interactionType == g_interactionBullsquidThrow) */
  1786. )
  1787. {
  1788. SetIdealState( NPC_STATE_PRONE );
  1789. if( HasHeadroom() )
  1790. {
  1791. MoveOrigin( Vector( 0, 0, 1 ) );
  1792. }
  1793. Vector vHitDir = GetLocalOrigin() - sourceEnt->GetLocalOrigin();
  1794. VectorNormalize(vHitDir);
  1795. CTakeDamageInfo info( sourceEnt, sourceEnt, m_iHealth+1, DMG_CLUB );
  1796. CalculateMeleeDamageForce( &info, vHitDir, GetAbsOrigin() );
  1797. TakeDamage( info );
  1798. return true;
  1799. }
  1800. return BaseClass::HandleInteraction( interactionType, data, sourceEnt );
  1801. }
  1802. //-----------------------------------------------------------------------------
  1803. // Purpose:
  1804. //-----------------------------------------------------------------------------
  1805. bool CBaseHeadcrab::FValidateHintType( CAI_Hint *pHint )
  1806. {
  1807. return true;
  1808. }
  1809. //-----------------------------------------------------------------------------
  1810. // Purpose:
  1811. // Input : &origin -
  1812. //-----------------------------------------------------------------------------
  1813. void CBaseHeadcrab::ClearBurrowPoint( const Vector &origin )
  1814. {
  1815. CBaseEntity *pEntity = NULL;
  1816. float flDist;
  1817. Vector vecSpot, vecCenter, vecForce;
  1818. //Cause a ruckus
  1819. UTIL_ScreenShake( origin, 1.0f, 80.0f, 1.0f, 256.0f, SHAKE_START );
  1820. //Iterate on all entities in the vicinity.
  1821. for ( CEntitySphereQuery sphere( origin, 128 ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
  1822. {
  1823. if ( pEntity->m_takedamage != DAMAGE_NO && pEntity->Classify() != CLASS_PLAYER && pEntity->VPhysicsGetObject() )
  1824. {
  1825. vecSpot = pEntity->BodyTarget( origin );
  1826. vecForce = ( vecSpot - origin ) + Vector( 0, 0, 16 );
  1827. // decrease damage for an ent that's farther from the bomb.
  1828. flDist = VectorNormalize( vecForce );
  1829. //float mass = pEntity->VPhysicsGetObject()->GetMass();
  1830. CollisionProp()->RandomPointInBounds( vec3_origin, Vector( 1.0f, 1.0f, 1.0f ), &vecCenter );
  1831. if ( flDist <= 128.0f )
  1832. {
  1833. pEntity->VPhysicsGetObject()->Wake();
  1834. pEntity->VPhysicsGetObject()->ApplyForceOffset( vecForce * 250.0f, vecCenter );
  1835. }
  1836. }
  1837. }
  1838. }
  1839. //-----------------------------------------------------------------------------
  1840. // Purpose: Determine whether a point is valid or not for burrowing up into
  1841. // Input : &point - point to test for validity
  1842. // Output : Returns true on success, false on failure.
  1843. //-----------------------------------------------------------------------------
  1844. bool CBaseHeadcrab::ValidBurrowPoint( const Vector &point )
  1845. {
  1846. trace_t tr;
  1847. AI_TraceHull( point, point+Vector(0,0,1), GetHullMins(), GetHullMaxs(),
  1848. MASK_NPCSOLID, this, GetCollisionGroup(), &tr );
  1849. // See if we were able to get there
  1850. if ( ( tr.startsolid ) || ( tr.allsolid ) || ( tr.fraction < 1.0f ) )
  1851. {
  1852. CBaseEntity *pEntity = tr.m_pEnt;
  1853. //If it's a physics object, attempt to knock is away, unless it's a car
  1854. if ( ( pEntity ) && ( pEntity->VPhysicsGetObject() ) && ( pEntity->GetServerVehicle() == NULL ) )
  1855. {
  1856. ClearBurrowPoint( point );
  1857. }
  1858. return false;
  1859. }
  1860. return true;
  1861. }
  1862. //-----------------------------------------------------------------------------
  1863. // Purpose:
  1864. //-----------------------------------------------------------------------------
  1865. void CBaseHeadcrab::GrabHintNode( CAI_Hint *pHint )
  1866. {
  1867. // Free up the node for use
  1868. ClearHintNode();
  1869. if ( pHint )
  1870. {
  1871. SetHintNode( pHint );
  1872. pHint->Lock( this );
  1873. }
  1874. }
  1875. //-----------------------------------------------------------------------------
  1876. // Purpose: Finds a point where the headcrab can burrow underground.
  1877. // Input : distance - radius to search for burrow spot in
  1878. // Output : Returns true on success, false on failure.
  1879. //-----------------------------------------------------------------------------
  1880. bool CBaseHeadcrab::FindBurrow( const Vector &origin, float distance, bool excludeNear )
  1881. {
  1882. // Attempt to find a burrowing point
  1883. CHintCriteria hintCriteria;
  1884. hintCriteria.SetHintType( HINT_HEADCRAB_BURROW_POINT );
  1885. hintCriteria.SetFlag( bits_HINT_NODE_NEAREST );
  1886. hintCriteria.AddIncludePosition( origin, distance );
  1887. if ( excludeNear )
  1888. {
  1889. hintCriteria.AddExcludePosition( origin, 128 );
  1890. }
  1891. CAI_Hint *pHint = CAI_HintManager::FindHint( this, hintCriteria );
  1892. if ( pHint == NULL )
  1893. return false;
  1894. GrabHintNode( pHint );
  1895. // Setup our path and attempt to run there
  1896. Vector vHintPos;
  1897. pHint->GetPosition( this, &vHintPos );
  1898. AI_NavGoal_t goal( vHintPos, ACT_RUN );
  1899. return GetNavigator()->SetGoal( goal );
  1900. }
  1901. //-----------------------------------------------------------------------------
  1902. // Purpose:
  1903. //-----------------------------------------------------------------------------
  1904. void CBaseHeadcrab::Burrow( void )
  1905. {
  1906. // Stop us from taking damage and being solid
  1907. m_spawnflags |= SF_NPC_GAG;
  1908. }
  1909. //-----------------------------------------------------------------------------
  1910. // Purpose:
  1911. //-----------------------------------------------------------------------------
  1912. void CBaseHeadcrab::Unburrow( void )
  1913. {
  1914. // Become solid again and visible
  1915. m_spawnflags &= ~SF_NPC_GAG;
  1916. RemoveSolidFlags( FSOLID_NOT_SOLID );
  1917. m_takedamage = DAMAGE_YES;
  1918. SetGroundEntity( NULL );
  1919. // If we have an enemy, come out facing them
  1920. if ( GetEnemy() )
  1921. {
  1922. Vector dir = GetEnemy()->GetAbsOrigin() - GetAbsOrigin();
  1923. VectorNormalize(dir);
  1924. GetMotor()->SetIdealYaw( dir );
  1925. QAngle angles = GetLocalAngles();
  1926. angles[YAW] = UTIL_VecToYaw( dir );
  1927. SetLocalAngles( angles );
  1928. }
  1929. }
  1930. //-----------------------------------------------------------------------------
  1931. // Purpose: Tells the headcrab to unburrow as soon the space is clear.
  1932. //-----------------------------------------------------------------------------
  1933. void CBaseHeadcrab::InputUnburrow( inputdata_t &inputdata )
  1934. {
  1935. if ( IsAlive() == false )
  1936. return;
  1937. SetSchedule( SCHED_HEADCRAB_WAIT_FOR_CLEAR_UNBURROW );
  1938. }
  1939. //-----------------------------------------------------------------------------
  1940. // Purpose: Tells the headcrab to run to a nearby burrow point and burrow.
  1941. //-----------------------------------------------------------------------------
  1942. void CBaseHeadcrab::InputBurrow( inputdata_t &inputdata )
  1943. {
  1944. if ( IsAlive() == false )
  1945. return;
  1946. SetSchedule( SCHED_HEADCRAB_RUN_TO_BURROW_IN );
  1947. }
  1948. //-----------------------------------------------------------------------------
  1949. // Purpose: Tells the headcrab to burrow right where he is.
  1950. //-----------------------------------------------------------------------------
  1951. void CBaseHeadcrab::InputBurrowImmediate( inputdata_t &inputdata )
  1952. {
  1953. if ( IsAlive() == false )
  1954. return;
  1955. SetSchedule( SCHED_HEADCRAB_BURROW_IN );
  1956. }
  1957. //-----------------------------------------------------------------------------
  1958. // Purpose:
  1959. //-----------------------------------------------------------------------------
  1960. void CBaseHeadcrab::InputStartHangingFromCeiling( inputdata_t &inputdata )
  1961. {
  1962. if ( IsAlive() == false )
  1963. return;
  1964. SetSchedule( SCHED_HEADCRAB_CEILING_WAIT );
  1965. m_flIlluminatedTime = -1;
  1966. }
  1967. //-----------------------------------------------------------------------------
  1968. // Purpose:
  1969. //-----------------------------------------------------------------------------
  1970. void CBaseHeadcrab::InputDropFromCeiling( inputdata_t &inputdata )
  1971. {
  1972. if ( IsAlive() == false )
  1973. return;
  1974. if ( IsHangingFromCeiling() == false )
  1975. return;
  1976. SetSchedule( SCHED_HEADCRAB_CEILING_DROP );
  1977. }
  1978. //-----------------------------------------------------------------------------
  1979. // Purpose:
  1980. //-----------------------------------------------------------------------------
  1981. void CBaseHeadcrab::CreateDust( bool placeDecal )
  1982. {
  1983. trace_t tr;
  1984. AI_TraceLine( GetAbsOrigin()+Vector(0,0,1), GetAbsOrigin()-Vector(0,0,64), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  1985. if ( tr.fraction < 1.0f )
  1986. {
  1987. const surfacedata_t *pdata = physprops->GetSurfaceData( tr.surface.surfaceProps );
  1988. if ( ( (char) pdata->game.material == CHAR_TEX_CONCRETE ) || ( (char) pdata->game.material == CHAR_TEX_DIRT ) )
  1989. {
  1990. UTIL_CreateAntlionDust( tr.endpos + Vector(0, 0, 24), GetLocalAngles() );
  1991. //CEffectData data;
  1992. //data.m_vOrigin = GetAbsOrigin();
  1993. //data.m_vNormal = tr.plane.normal;
  1994. //DispatchEffect( "headcrabdust", data );
  1995. if ( placeDecal )
  1996. {
  1997. UTIL_DecalTrace( &tr, "Headcrab.Unburrow" );
  1998. }
  1999. }
  2000. }
  2001. }
  2002. //-----------------------------------------------------------------------------
  2003. // Purpose:
  2004. //-----------------------------------------------------------------------------
  2005. void CHeadcrab::Precache( void )
  2006. {
  2007. PrecacheModel( "models/headcrabclassic.mdl" );
  2008. PrecacheScriptSound( "NPC_HeadCrab.Gib" );
  2009. PrecacheScriptSound( "NPC_HeadCrab.Idle" );
  2010. PrecacheScriptSound( "NPC_HeadCrab.Alert" );
  2011. PrecacheScriptSound( "NPC_HeadCrab.Pain" );
  2012. PrecacheScriptSound( "NPC_HeadCrab.Die" );
  2013. PrecacheScriptSound( "NPC_HeadCrab.Attack" );
  2014. PrecacheScriptSound( "NPC_HeadCrab.Bite" );
  2015. PrecacheScriptSound( "NPC_Headcrab.BurrowIn" );
  2016. PrecacheScriptSound( "NPC_Headcrab.BurrowOut" );
  2017. BaseClass::Precache();
  2018. }
  2019. //-----------------------------------------------------------------------------
  2020. // Purpose:
  2021. //-----------------------------------------------------------------------------
  2022. void CHeadcrab::Spawn( void )
  2023. {
  2024. Precache();
  2025. SetModel( "models/headcrabclassic.mdl" );
  2026. BaseClass::Spawn();
  2027. m_iHealth = sk_headcrab_health.GetFloat();
  2028. m_flBurrowTime = 0.0f;
  2029. m_bCrawlFromCanister = false;
  2030. m_bMidJump = false;
  2031. NPCInit();
  2032. HeadcrabInit();
  2033. }
  2034. //-----------------------------------------------------------------------------
  2035. // Purpose:
  2036. //-----------------------------------------------------------------------------
  2037. Activity CHeadcrab::NPC_TranslateActivity( Activity eNewActivity )
  2038. {
  2039. if ( eNewActivity == ACT_WALK )
  2040. return ACT_RUN;
  2041. return BaseClass::NPC_TranslateActivity( eNewActivity );
  2042. }
  2043. //-----------------------------------------------------------------------------
  2044. // Purpose:
  2045. //-----------------------------------------------------------------------------
  2046. void CHeadcrab::IdleSound( void )
  2047. {
  2048. EmitSound( "NPC_HeadCrab.Idle" );
  2049. }
  2050. //-----------------------------------------------------------------------------
  2051. // Purpose:
  2052. //-----------------------------------------------------------------------------
  2053. void CHeadcrab::AlertSound( void )
  2054. {
  2055. EmitSound( "NPC_HeadCrab.Alert" );
  2056. }
  2057. //-----------------------------------------------------------------------------
  2058. // Purpose:
  2059. //-----------------------------------------------------------------------------
  2060. void CHeadcrab::PainSound( const CTakeDamageInfo &info )
  2061. {
  2062. if( IsOnFire() && random->RandomInt( 0, HEADCRAB_BURN_SOUND_FREQUENCY ) > 0 )
  2063. {
  2064. // Don't squeak every think when burning.
  2065. return;
  2066. }
  2067. EmitSound( "NPC_HeadCrab.Pain" );
  2068. }
  2069. //-----------------------------------------------------------------------------
  2070. // Purpose:
  2071. //-----------------------------------------------------------------------------
  2072. void CHeadcrab::DeathSound( const CTakeDamageInfo &info )
  2073. {
  2074. EmitSound( "NPC_HeadCrab.Die" );
  2075. }
  2076. //-----------------------------------------------------------------------------
  2077. // Purpose:
  2078. //-----------------------------------------------------------------------------
  2079. void CHeadcrab::TelegraphSound( void )
  2080. {
  2081. //FIXME: Need a real one
  2082. EmitSound( "NPC_HeadCrab.Alert" );
  2083. }
  2084. //-----------------------------------------------------------------------------
  2085. // Purpose:
  2086. //-----------------------------------------------------------------------------
  2087. void CHeadcrab::AttackSound( void )
  2088. {
  2089. EmitSound( "NPC_Headcrab.Attack" );
  2090. }
  2091. //-----------------------------------------------------------------------------
  2092. // Purpose:
  2093. //-----------------------------------------------------------------------------
  2094. void CHeadcrab::BiteSound( void )
  2095. {
  2096. EmitSound( "NPC_HeadCrab.Bite" );
  2097. }
  2098. //---------------------------------------------------------
  2099. // Save/Restore
  2100. //---------------------------------------------------------
  2101. BEGIN_DATADESC( CFastHeadcrab )
  2102. DEFINE_FIELD( m_iRunMode, FIELD_INTEGER ),
  2103. DEFINE_FIELD( m_flRealGroundSpeed, FIELD_FLOAT ),
  2104. DEFINE_FIELD( m_flSlowRunTime, FIELD_TIME ),
  2105. DEFINE_FIELD( m_flPauseTime, FIELD_TIME ),
  2106. DEFINE_FIELD( m_vecJumpVel, FIELD_VECTOR ),
  2107. END_DATADESC()
  2108. //-----------------------------------------------------------------------------
  2109. // Purpose:
  2110. //-----------------------------------------------------------------------------
  2111. void CFastHeadcrab::Precache( void )
  2112. {
  2113. PrecacheModel( "models/headcrab.mdl" );
  2114. PrecacheScriptSound( "NPC_FastHeadcrab.Idle" );
  2115. PrecacheScriptSound( "NPC_FastHeadcrab.Alert" );
  2116. PrecacheScriptSound( "NPC_FastHeadcrab.Pain" );
  2117. PrecacheScriptSound( "NPC_FastHeadcrab.Die" );
  2118. PrecacheScriptSound( "NPC_FastHeadcrab.Bite" );
  2119. PrecacheScriptSound( "NPC_FastHeadcrab.Attack" );
  2120. BaseClass::Precache();
  2121. }
  2122. //-----------------------------------------------------------------------------
  2123. // Purpose:
  2124. //-----------------------------------------------------------------------------
  2125. void CFastHeadcrab::Spawn( void )
  2126. {
  2127. Precache();
  2128. SetModel( "models/headcrab.mdl" );
  2129. BaseClass::Spawn();
  2130. m_iHealth = sk_headcrab_health.GetFloat();
  2131. m_iRunMode = HEADCRAB_RUNMODE_IDLE;
  2132. m_flPauseTime = 999999;
  2133. NPCInit();
  2134. HeadcrabInit();
  2135. }
  2136. //-----------------------------------------------------------------------------
  2137. // Purpose:
  2138. //-----------------------------------------------------------------------------
  2139. void CFastHeadcrab::IdleSound( void )
  2140. {
  2141. EmitSound( "NPC_FastHeadcrab.Idle" );
  2142. }
  2143. //-----------------------------------------------------------------------------
  2144. // Purpose:
  2145. //-----------------------------------------------------------------------------
  2146. void CFastHeadcrab::AlertSound( void )
  2147. {
  2148. EmitSound( "NPC_FastHeadcrab.Alert" );
  2149. }
  2150. //-----------------------------------------------------------------------------
  2151. // Purpose:
  2152. //-----------------------------------------------------------------------------
  2153. void CFastHeadcrab::PainSound( const CTakeDamageInfo &info )
  2154. {
  2155. if( IsOnFire() && random->RandomInt( 0, HEADCRAB_BURN_SOUND_FREQUENCY ) > 0 )
  2156. {
  2157. // Don't squeak every think when burning.
  2158. return;
  2159. }
  2160. EmitSound( "NPC_FastHeadcrab.Pain" );
  2161. }
  2162. //-----------------------------------------------------------------------------
  2163. // Purpose:
  2164. //-----------------------------------------------------------------------------
  2165. void CFastHeadcrab::DeathSound( const CTakeDamageInfo &info )
  2166. {
  2167. EmitSound( "NPC_FastHeadcrab.Die" );
  2168. }
  2169. //-----------------------------------------------------------------------------
  2170. // Purpose:
  2171. //-----------------------------------------------------------------------------
  2172. void CFastHeadcrab::PrescheduleThink( void )
  2173. {
  2174. #if 1 // #IF 0 this to stop the accelrating/decelerating movement.
  2175. #define HEADCRAB_ACCELERATION 0.1
  2176. if( IsAlive() && GetNavigator()->IsGoalActive() )
  2177. {
  2178. switch( m_iRunMode )
  2179. {
  2180. case HEADCRAB_RUNMODE_IDLE:
  2181. if ( GetActivity() == ACT_RUN )
  2182. {
  2183. m_flRealGroundSpeed = m_flGroundSpeed;
  2184. m_iRunMode = HEADCRAB_RUNMODE_ACCELERATE;
  2185. m_flPlaybackRate = HEADCRAB_RUN_MINSPEED;
  2186. }
  2187. break;
  2188. case HEADCRAB_RUNMODE_FULLSPEED:
  2189. if( gpGlobals->curtime > m_flSlowRunTime )
  2190. {
  2191. m_iRunMode = HEADCRAB_RUNMODE_DECELERATE;
  2192. }
  2193. break;
  2194. case HEADCRAB_RUNMODE_ACCELERATE:
  2195. if( m_flPlaybackRate < HEADCRAB_RUN_MAXSPEED )
  2196. {
  2197. m_flPlaybackRate += HEADCRAB_ACCELERATION;
  2198. }
  2199. if( m_flPlaybackRate >= HEADCRAB_RUN_MAXSPEED )
  2200. {
  2201. m_flPlaybackRate = HEADCRAB_RUN_MAXSPEED;
  2202. m_iRunMode = HEADCRAB_RUNMODE_FULLSPEED;
  2203. m_flSlowRunTime = gpGlobals->curtime + random->RandomFloat( 0.1, 1.0 );
  2204. }
  2205. break;
  2206. case HEADCRAB_RUNMODE_DECELERATE:
  2207. m_flPlaybackRate -= HEADCRAB_ACCELERATION;
  2208. if( m_flPlaybackRate <= HEADCRAB_RUN_MINSPEED )
  2209. {
  2210. m_flPlaybackRate = HEADCRAB_RUN_MINSPEED;
  2211. // Now stop the crab.
  2212. m_iRunMode = HEADCRAB_RUNMODE_PAUSE;
  2213. SetActivity( ACT_IDLE );
  2214. GetNavigator()->SetMovementActivity(ACT_IDLE);
  2215. m_flPauseTime = gpGlobals->curtime + random->RandomFloat( 0.2, 0.5 );
  2216. m_flRealGroundSpeed = 0.0;
  2217. }
  2218. break;
  2219. case HEADCRAB_RUNMODE_PAUSE:
  2220. {
  2221. if( gpGlobals->curtime > m_flPauseTime )
  2222. {
  2223. m_iRunMode = HEADCRAB_RUNMODE_IDLE;
  2224. SetActivity( ACT_RUN );
  2225. GetNavigator()->SetMovementActivity(ACT_RUN);
  2226. m_flPauseTime = gpGlobals->curtime - 1;
  2227. m_flRealGroundSpeed = m_flGroundSpeed;
  2228. }
  2229. }
  2230. break;
  2231. default:
  2232. Warning( "BIG TIME HEADCRAB ERROR\n" );
  2233. break;
  2234. }
  2235. m_flGroundSpeed = m_flRealGroundSpeed * m_flPlaybackRate;
  2236. }
  2237. else
  2238. {
  2239. m_flPauseTime = gpGlobals->curtime - 1;
  2240. }
  2241. #endif
  2242. BaseClass::PrescheduleThink();
  2243. }
  2244. //-----------------------------------------------------------------------------
  2245. // Purpose:
  2246. // Input : scheduleType -
  2247. //-----------------------------------------------------------------------------
  2248. int CFastHeadcrab::SelectSchedule( void )
  2249. {
  2250. if ( HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN) )
  2251. {
  2252. return SCHED_IDLE_STAND;
  2253. }
  2254. if ( HasCondition(COND_CAN_RANGE_ATTACK1) && IsHangingFromCeiling() == false )
  2255. {
  2256. if ( OccupyStrategySlotRange( SQUAD_SLOT_ATTACK1, SQUAD_SLOT_ATTACK2 ) )
  2257. return SCHED_RANGE_ATTACK1;
  2258. ClearCondition(COND_CAN_RANGE_ATTACK1);
  2259. }
  2260. return BaseClass::SelectSchedule();
  2261. }
  2262. //-----------------------------------------------------------------------------
  2263. // Purpose:
  2264. // Input : scheduleType -
  2265. //-----------------------------------------------------------------------------
  2266. int CFastHeadcrab::TranslateSchedule( int scheduleType )
  2267. {
  2268. switch( scheduleType )
  2269. {
  2270. case SCHED_IDLE_STAND:
  2271. return SCHED_PATROL_WALK;
  2272. break;
  2273. case SCHED_RANGE_ATTACK1:
  2274. return SCHED_FAST_HEADCRAB_RANGE_ATTACK1;
  2275. break;
  2276. case SCHED_CHASE_ENEMY:
  2277. if ( !OccupyStrategySlotRange( SQUAD_SLOT_ENGAGE1, SQUAD_SLOT_ENGAGE4 ) )
  2278. return SCHED_PATROL_WALK;
  2279. break;
  2280. }
  2281. return BaseClass::TranslateSchedule( scheduleType );
  2282. }
  2283. //-----------------------------------------------------------------------------
  2284. // Purpose:
  2285. // Input : *pTask -
  2286. //-----------------------------------------------------------------------------
  2287. void CFastHeadcrab::RunTask( const Task_t *pTask )
  2288. {
  2289. switch ( pTask->iTask )
  2290. {
  2291. case TASK_RANGE_ATTACK1:
  2292. case TASK_RANGE_ATTACK2:
  2293. if ( GetEnemy() )
  2294. // Fast headcrab faces the target in flight.
  2295. GetMotor()->SetIdealYawAndUpdate( GetEnemy()->GetAbsOrigin() - GetAbsOrigin(), AI_KEEP_YAW_SPEED );
  2296. // Call back up into base headcrab for collision.
  2297. BaseClass::RunTask( pTask );
  2298. break;
  2299. case TASK_HEADCRAB_HOP_ASIDE:
  2300. if ( GetEnemy() )
  2301. GetMotor()->SetIdealYawAndUpdate( GetEnemy()->GetAbsOrigin() - GetAbsOrigin(), AI_KEEP_YAW_SPEED );
  2302. if( GetFlags() & FL_ONGROUND )
  2303. {
  2304. SetGravity(1.0);
  2305. SetMoveType( MOVETYPE_STEP );
  2306. if( GetEnemy() && ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin() ).Length() > HEADCRAB_MAX_JUMP_DIST )
  2307. {
  2308. TaskFail( "");
  2309. }
  2310. TaskComplete();
  2311. }
  2312. break;
  2313. default:
  2314. BaseClass::RunTask( pTask );
  2315. break;
  2316. }
  2317. }
  2318. //-----------------------------------------------------------------------------
  2319. // Purpose:
  2320. // Input : pTask -
  2321. //-----------------------------------------------------------------------------
  2322. void CFastHeadcrab::StartTask( const Task_t *pTask )
  2323. {
  2324. switch ( pTask->iTask )
  2325. {
  2326. case TASK_HEADCRAB_HOP_ASIDE:
  2327. {
  2328. Vector vecDir, vecForward, vecRight;
  2329. bool fJumpIsLeft;
  2330. trace_t tr;
  2331. GetVectors( &vecForward, &vecRight, NULL );
  2332. fJumpIsLeft = false;
  2333. if( random->RandomInt( 0, 100 ) < 50 )
  2334. {
  2335. fJumpIsLeft = true;
  2336. vecRight.Negate();
  2337. }
  2338. vecDir = ( vecRight + ( vecForward * 2 ) );
  2339. VectorNormalize( vecDir );
  2340. vecDir *= 150.0;
  2341. // This could be a problem. Since I'm adjusting the headcrab's gravity for flight, this check actually
  2342. // checks farther ahead than the crab will actually jump. (sjb)
  2343. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + vecDir,GetHullMins(), GetHullMaxs(), MASK_SHOT, this, GetCollisionGroup(), &tr );
  2344. //NDebugOverlay::Line( tr.startpos, tr.endpos, 0, 255, 0, false, 1.0 );
  2345. if( tr.fraction == 1.0 )
  2346. {
  2347. AIMoveTrace_t moveTrace;
  2348. GetMoveProbe()->MoveLimit( NAV_JUMP, GetAbsOrigin(), tr.endpos, MASK_NPCSOLID, GetEnemy(), &moveTrace );
  2349. // FIXME: Where should this happen?
  2350. m_vecJumpVel = moveTrace.vJumpVelocity;
  2351. if( !IsMoveBlocked( moveTrace ) )
  2352. {
  2353. SetAbsVelocity( m_vecJumpVel );// + 0.5f * Vector(0,0,GetCurrentGravity()) * flInterval;
  2354. SetGravity( UTIL_ScaleForGravity( 1600 ) );
  2355. SetGroundEntity( NULL );
  2356. SetNavType( NAV_JUMP );
  2357. if( fJumpIsLeft )
  2358. {
  2359. SetIdealActivity( (Activity)ACT_HEADCRAB_HOP_LEFT );
  2360. GetNavigator()->SetMovementActivity( (Activity) ACT_HEADCRAB_HOP_LEFT );
  2361. }
  2362. else
  2363. {
  2364. SetIdealActivity( (Activity)ACT_HEADCRAB_HOP_RIGHT );
  2365. GetNavigator()->SetMovementActivity( (Activity) ACT_HEADCRAB_HOP_RIGHT );
  2366. }
  2367. }
  2368. else
  2369. {
  2370. // Can't jump, just fall through.
  2371. TaskComplete();
  2372. }
  2373. }
  2374. else
  2375. {
  2376. // Can't jump, just fall through.
  2377. TaskComplete();
  2378. }
  2379. }
  2380. break;
  2381. default:
  2382. {
  2383. BaseClass::StartTask( pTask );
  2384. }
  2385. }
  2386. }
  2387. LINK_ENTITY_TO_CLASS( npc_headcrab, CHeadcrab );
  2388. LINK_ENTITY_TO_CLASS( npc_headcrab_fast, CFastHeadcrab );
  2389. //-----------------------------------------------------------------------------
  2390. // Purpose: Make the sound of this headcrab chomping a target.
  2391. // Input :
  2392. //-----------------------------------------------------------------------------
  2393. void CFastHeadcrab::BiteSound( void )
  2394. {
  2395. EmitSound( "NPC_FastHeadcrab.Bite" );
  2396. }
  2397. void CFastHeadcrab::AttackSound( void )
  2398. {
  2399. EmitSound( "NPC_FastHeadcrab.Attack" );
  2400. }
  2401. //-----------------------------------------------------------------------------
  2402. // Purpose:
  2403. // Input :
  2404. // Output :
  2405. //-----------------------------------------------------------------------------
  2406. float CHeadcrab::MaxYawSpeed ( void )
  2407. {
  2408. switch ( GetActivity() )
  2409. {
  2410. case ACT_IDLE:
  2411. return 30;
  2412. case ACT_RUN:
  2413. case ACT_WALK:
  2414. return 20;
  2415. case ACT_TURN_LEFT:
  2416. case ACT_TURN_RIGHT:
  2417. return 15;
  2418. case ACT_RANGE_ATTACK1:
  2419. {
  2420. const Task_t *pCurTask = GetTask();
  2421. if ( pCurTask && pCurTask->iTask == TASK_HEADCRAB_JUMP_FROM_CANISTER )
  2422. return 15;
  2423. }
  2424. return 30;
  2425. default:
  2426. return 30;
  2427. }
  2428. return BaseClass::MaxYawSpeed();
  2429. }
  2430. //-----------------------------------------------------------------------------
  2431. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  2432. // In the most cases the base implementation should be called first.
  2433. //-----------------------------------------------------------------------------
  2434. void CBaseHeadcrab::BuildScheduleTestBits( void )
  2435. {
  2436. if ( !IsCurSchedule(SCHED_HEADCRAB_DROWN) )
  2437. {
  2438. // Interrupt any schedule unless already drowning.
  2439. SetCustomInterruptCondition( COND_HEADCRAB_IN_WATER );
  2440. }
  2441. else
  2442. {
  2443. // Don't stop drowning just because you're in water!
  2444. ClearCustomInterruptCondition( COND_HEADCRAB_IN_WATER );
  2445. }
  2446. if( !IsCurSchedule(SCHED_HEADCRAB_HOP_RANDOMLY) )
  2447. {
  2448. SetCustomInterruptCondition( COND_HEADCRAB_ILLEGAL_GROUNDENT );
  2449. }
  2450. else
  2451. {
  2452. ClearCustomInterruptCondition( COND_HEADCRAB_ILLEGAL_GROUNDENT );
  2453. }
  2454. }
  2455. //-----------------------------------------------------------------------------
  2456. // Purpose:
  2457. //-----------------------------------------------------------------------------
  2458. float CFastHeadcrab::MaxYawSpeed( void )
  2459. {
  2460. switch ( GetActivity() )
  2461. {
  2462. case ACT_IDLE:
  2463. {
  2464. return( 120 );
  2465. }
  2466. case ACT_RUN:
  2467. case ACT_WALK:
  2468. {
  2469. return( 150 );
  2470. }
  2471. case ACT_TURN_LEFT:
  2472. case ACT_TURN_RIGHT:
  2473. {
  2474. return( 120 );
  2475. }
  2476. case ACT_RANGE_ATTACK1:
  2477. {
  2478. return( 120 );
  2479. }
  2480. default:
  2481. {
  2482. return( 120 );
  2483. }
  2484. }
  2485. }
  2486. bool CFastHeadcrab::QuerySeeEntity(CBaseEntity *pSightEnt, bool bOnlyHateOrFearIfNPC )
  2487. {
  2488. if ( IsHangingFromCeiling() == true )
  2489. return BaseClass::QuerySeeEntity(pSightEnt, bOnlyHateOrFearIfNPC);
  2490. if( m_NPCState != NPC_STATE_COMBAT )
  2491. {
  2492. if( fabs( pSightEnt->GetAbsOrigin().z - GetAbsOrigin().z ) >= 150 )
  2493. {
  2494. // Don't see things much higher or lower than me unless
  2495. // I'm already pissed.
  2496. return false;
  2497. }
  2498. }
  2499. return BaseClass::QuerySeeEntity(pSightEnt, bOnlyHateOrFearIfNPC);
  2500. }
  2501. //-----------------------------------------------------------------------------
  2502. // Black headcrab stuff
  2503. //-----------------------------------------------------------------------------
  2504. int ACT_BLACKHEADCRAB_RUN_PANIC;
  2505. BEGIN_DATADESC( CBlackHeadcrab )
  2506. DEFINE_FIELD( m_bPanicState, FIELD_BOOLEAN ),
  2507. DEFINE_FIELD( m_flPanicStopTime, FIELD_TIME ),
  2508. DEFINE_FIELD( m_flNextHopTime, FIELD_TIME ),
  2509. DEFINE_ENTITYFUNC( EjectTouch ),
  2510. END_DATADESC()
  2511. LINK_ENTITY_TO_CLASS( npc_headcrab_black, CBlackHeadcrab );
  2512. LINK_ENTITY_TO_CLASS( npc_headcrab_poison, CBlackHeadcrab );
  2513. //-----------------------------------------------------------------------------
  2514. // Purpose: Make the sound of this headcrab chomping a target.
  2515. //-----------------------------------------------------------------------------
  2516. void CBlackHeadcrab::BiteSound( void )
  2517. {
  2518. EmitSound( "NPC_BlackHeadcrab.Bite" );
  2519. }
  2520. //-----------------------------------------------------------------------------
  2521. // Purpose: The sound we make when leaping at our enemy.
  2522. //-----------------------------------------------------------------------------
  2523. void CBlackHeadcrab::AttackSound( void )
  2524. {
  2525. EmitSound( "NPC_BlackHeadcrab.Attack" );
  2526. }
  2527. //-----------------------------------------------------------------------------
  2528. // Purpose:
  2529. //-----------------------------------------------------------------------------
  2530. void CBlackHeadcrab::TelegraphSound( void )
  2531. {
  2532. EmitSound( "NPC_BlackHeadcrab.Telegraph" );
  2533. }
  2534. //-----------------------------------------------------------------------------
  2535. // Purpose:
  2536. //-----------------------------------------------------------------------------
  2537. void CBlackHeadcrab::Spawn( void )
  2538. {
  2539. Precache();
  2540. SetModel( "models/headcrabblack.mdl" );
  2541. BaseClass::Spawn();
  2542. m_bPanicState = false;
  2543. m_iHealth = sk_headcrab_poison_health.GetFloat();
  2544. NPCInit();
  2545. HeadcrabInit();
  2546. }
  2547. //-----------------------------------------------------------------------------
  2548. // Purpose:
  2549. //-----------------------------------------------------------------------------
  2550. void CBlackHeadcrab::Precache( void )
  2551. {
  2552. PrecacheModel( "models/headcrabblack.mdl" );
  2553. PrecacheScriptSound( "NPC_BlackHeadcrab.Telegraph" );
  2554. PrecacheScriptSound( "NPC_BlackHeadcrab.Attack" );
  2555. PrecacheScriptSound( "NPC_BlackHeadcrab.Bite" );
  2556. PrecacheScriptSound( "NPC_BlackHeadcrab.Threat" );
  2557. PrecacheScriptSound( "NPC_BlackHeadcrab.Alert" );
  2558. PrecacheScriptSound( "NPC_BlackHeadcrab.Idle" );
  2559. PrecacheScriptSound( "NPC_BlackHeadcrab.Talk" );
  2560. PrecacheScriptSound( "NPC_BlackHeadcrab.AlertVoice" );
  2561. PrecacheScriptSound( "NPC_BlackHeadcrab.Pain" );
  2562. PrecacheScriptSound( "NPC_BlackHeadcrab.Die" );
  2563. PrecacheScriptSound( "NPC_BlackHeadcrab.Impact" );
  2564. PrecacheScriptSound( "NPC_BlackHeadcrab.ImpactAngry" );
  2565. PrecacheScriptSound( "NPC_BlackHeadcrab.FootstepWalk" );
  2566. PrecacheScriptSound( "NPC_BlackHeadcrab.Footstep" );
  2567. BaseClass::Precache();
  2568. }
  2569. //-----------------------------------------------------------------------------
  2570. // Purpose: Returns the max yaw speed for the current activity.
  2571. //-----------------------------------------------------------------------------
  2572. float CBlackHeadcrab::MaxYawSpeed( void )
  2573. {
  2574. // Not a constant, can't be in a switch statement.
  2575. if ( GetActivity() == ACT_BLACKHEADCRAB_RUN_PANIC )
  2576. {
  2577. return 30;
  2578. }
  2579. switch ( GetActivity() )
  2580. {
  2581. case ACT_WALK:
  2582. case ACT_RUN:
  2583. {
  2584. return 10;
  2585. }
  2586. case ACT_TURN_LEFT:
  2587. case ACT_TURN_RIGHT:
  2588. {
  2589. return( 30 );
  2590. }
  2591. case ACT_RANGE_ATTACK1:
  2592. {
  2593. return( 30 );
  2594. }
  2595. default:
  2596. {
  2597. return( 30 );
  2598. }
  2599. }
  2600. }
  2601. //-----------------------------------------------------------------------------
  2602. // Purpose:
  2603. //-----------------------------------------------------------------------------
  2604. Activity CBlackHeadcrab::NPC_TranslateActivity( Activity eNewActivity )
  2605. {
  2606. if ( eNewActivity == ACT_RUN || eNewActivity == ACT_WALK )
  2607. {
  2608. if( m_bPanicState || IsOnFire() )
  2609. {
  2610. return ( Activity )ACT_BLACKHEADCRAB_RUN_PANIC;
  2611. }
  2612. }
  2613. return BaseClass::NPC_TranslateActivity( eNewActivity );
  2614. }
  2615. //-----------------------------------------------------------------------------
  2616. // Purpose:
  2617. //-----------------------------------------------------------------------------
  2618. void CBlackHeadcrab::PrescheduleThink( void )
  2619. {
  2620. BaseClass::PrescheduleThink();
  2621. }
  2622. //-----------------------------------------------------------------------------
  2623. // Purpose:
  2624. //-----------------------------------------------------------------------------
  2625. int CBlackHeadcrab::TranslateSchedule( int scheduleType )
  2626. {
  2627. switch ( scheduleType )
  2628. {
  2629. // Keep trying to take cover for at least a few seconds.
  2630. case SCHED_FAIL_TAKE_COVER:
  2631. {
  2632. if ( ( m_bPanicState ) && ( gpGlobals->curtime > m_flPanicStopTime ) )
  2633. {
  2634. //DevMsg( "I'm sick of panicking\n" );
  2635. m_bPanicState = false;
  2636. return SCHED_CHASE_ENEMY;
  2637. }
  2638. break;
  2639. }
  2640. }
  2641. return BaseClass::TranslateSchedule( scheduleType );
  2642. }
  2643. //-----------------------------------------------------------------------------
  2644. // Purpose: Allows for modification of the interrupt mask for the current schedule.
  2645. // In the most cases the base implementation should be called first.
  2646. //-----------------------------------------------------------------------------
  2647. void CBlackHeadcrab::BuildScheduleTestBits( void )
  2648. {
  2649. // Ignore damage if we're attacking or are fleeing and recently flinched.
  2650. if ( IsCurSchedule( SCHED_HEADCRAB_CRAWL_FROM_CANISTER ) || IsCurSchedule( SCHED_RANGE_ATTACK1 ) || ( IsCurSchedule( SCHED_TAKE_COVER_FROM_ENEMY ) && HasMemory( bits_MEMORY_FLINCHED ) ) )
  2651. {
  2652. ClearCustomInterruptCondition( COND_LIGHT_DAMAGE );
  2653. ClearCustomInterruptCondition( COND_HEAVY_DAMAGE );
  2654. }
  2655. else
  2656. {
  2657. SetCustomInterruptCondition( COND_LIGHT_DAMAGE );
  2658. SetCustomInterruptCondition( COND_HEAVY_DAMAGE );
  2659. }
  2660. // If we're committed to jump, carry on even if our enemy hides behind a crate. Or a barrel.
  2661. if ( IsCurSchedule( SCHED_RANGE_ATTACK1 ) && m_bCommittedToJump )
  2662. {
  2663. ClearCustomInterruptCondition( COND_ENEMY_OCCLUDED );
  2664. }
  2665. }
  2666. //-----------------------------------------------------------------------------
  2667. // Purpose:
  2668. // Output :
  2669. //-----------------------------------------------------------------------------
  2670. int CBlackHeadcrab::SelectSchedule( void )
  2671. {
  2672. // don't override inherited behavior when hanging from ceiling
  2673. if ( !IsHangingFromCeiling() )
  2674. {
  2675. if ( HasSpawnFlags(SF_NPC_WAIT_TILL_SEEN) )
  2676. {
  2677. return SCHED_IDLE_STAND;
  2678. }
  2679. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  2680. {
  2681. if ( ( gpGlobals->curtime >= m_flNextHopTime ) && SelectWeightedSequence( ACT_SMALL_FLINCH ) != -1 )
  2682. {
  2683. m_flNextHopTime = gpGlobals->curtime + random->RandomFloat( 1, 3 );
  2684. return SCHED_SMALL_FLINCH;
  2685. }
  2686. }
  2687. if ( m_bPanicState )
  2688. {
  2689. // We're looking for a place to hide, and we've found one. Lurk!
  2690. if ( HasMemory( bits_MEMORY_INCOVER ) )
  2691. {
  2692. m_bPanicState = false;
  2693. m_flPanicStopTime = gpGlobals->curtime;
  2694. return SCHED_HEADCRAB_AMBUSH;
  2695. }
  2696. return SCHED_TAKE_COVER_FROM_ENEMY;
  2697. }
  2698. }
  2699. return BaseClass::SelectSchedule();
  2700. }
  2701. //-----------------------------------------------------------------------------
  2702. // Purpose: Black headcrab's touch attack damage. Evil!
  2703. //-----------------------------------------------------------------------------
  2704. void CBlackHeadcrab::TouchDamage( CBaseEntity *pOther )
  2705. {
  2706. if ( pOther->m_iHealth > 1 )
  2707. {
  2708. CTakeDamageInfo info;
  2709. if ( CalcDamageInfo( &info ) >= pOther->m_iHealth )
  2710. info.SetDamage( pOther->m_iHealth - 1 );
  2711. pOther->TakeDamage( info );
  2712. if ( pOther->IsAlive() && pOther->m_iHealth > 1)
  2713. {
  2714. // Episodic change to avoid NPCs dying too quickly from poison bites
  2715. if ( hl2_episodic.GetBool() )
  2716. {
  2717. if ( pOther->IsPlayer() )
  2718. {
  2719. // That didn't finish them. Take them down to one point with poison damage. It'll heal.
  2720. pOther->TakeDamage( CTakeDamageInfo( this, this, pOther->m_iHealth - 1, DMG_POISON ) );
  2721. }
  2722. else
  2723. {
  2724. // Just take some amount of slash damage instead
  2725. pOther->TakeDamage( CTakeDamageInfo( this, this, sk_headcrab_poison_npc_damage.GetFloat(), DMG_SLASH ) );
  2726. }
  2727. }
  2728. else
  2729. {
  2730. // That didn't finish them. Take them down to one point with poison damage. It'll heal.
  2731. pOther->TakeDamage( CTakeDamageInfo( this, this, pOther->m_iHealth - 1, DMG_POISON ) );
  2732. }
  2733. }
  2734. }
  2735. }
  2736. //-----------------------------------------------------------------------------
  2737. // Purpose: Bails out of our host zombie, either because he died or was blown
  2738. // into two pieces by an explosion.
  2739. // Input : vecAngles - The yaw direction we should face.
  2740. // flVelocityScale - A multiplier for our ejection velocity.
  2741. // pEnemy - Who we should acquire as our enemy. Usually our zombie host's enemy.
  2742. //-----------------------------------------------------------------------------
  2743. void CBlackHeadcrab::Eject( const QAngle &vecAngles, float flVelocityScale, CBaseEntity *pEnemy )
  2744. {
  2745. SetGroundEntity( NULL );
  2746. m_spawnflags |= SF_NPC_FALL_TO_GROUND;
  2747. SetIdealState( NPC_STATE_ALERT );
  2748. if ( pEnemy )
  2749. {
  2750. SetEnemy( pEnemy );
  2751. UpdateEnemyMemory(pEnemy, pEnemy->GetAbsOrigin());
  2752. }
  2753. SetActivity( ACT_RANGE_ATTACK1 );
  2754. SetNextThink( gpGlobals->curtime );
  2755. PhysicsSimulate();
  2756. GetMotor()->SetIdealYaw( vecAngles.y );
  2757. SetAbsVelocity( flVelocityScale * random->RandomInt( 20, 50 ) *
  2758. Vector( random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( -1.0, 1.0 ), random->RandomFloat( 0.5, 1.0 ) ) );
  2759. m_bMidJump = false;
  2760. SetTouch( &CBlackHeadcrab::EjectTouch );
  2761. }
  2762. //-----------------------------------------------------------------------------
  2763. // Purpose: Touch function for when we are ejected from the poison zombie.
  2764. // Panic when we hit the ground.
  2765. //-----------------------------------------------------------------------------
  2766. void CBlackHeadcrab::EjectTouch( CBaseEntity *pOther )
  2767. {
  2768. LeapTouch( pOther );
  2769. if ( GetFlags() & FL_ONGROUND )
  2770. {
  2771. // Keep trying to take cover for at least a few seconds.
  2772. Panic( random->RandomFloat( 2, 8 ) );
  2773. }
  2774. }
  2775. //-----------------------------------------------------------------------------
  2776. // Purpose: Puts us in a state in which we just want to hide. We'll stop
  2777. // hiding after the given duration.
  2778. //-----------------------------------------------------------------------------
  2779. void CBlackHeadcrab::Panic( float flDuration )
  2780. {
  2781. m_flPanicStopTime = gpGlobals->curtime + flDuration;
  2782. m_bPanicState = true;
  2783. }
  2784. #if HL2_EPISODIC
  2785. //-----------------------------------------------------------------------------
  2786. // Purpose: Black headcrabs have 360-degree vision when they are in the ambush
  2787. // schedule. This is because they ignore sounds when in ambush, and
  2788. // you could walk up behind them without having them attack you.
  2789. // This vision extends only 24 feet.
  2790. //-----------------------------------------------------------------------------
  2791. #define CRAB_360_VIEW_DIST_SQR (12 * 12 * 24 * 24)
  2792. bool CBlackHeadcrab::FInViewCone( CBaseEntity *pEntity )
  2793. {
  2794. if( IsCurSchedule( SCHED_HEADCRAB_AMBUSH ) &&
  2795. (( pEntity->IsNPC() || pEntity->IsPlayer() ) && pEntity->GetAbsOrigin().DistToSqr(GetAbsOrigin()) <= CRAB_360_VIEW_DIST_SQR ) )
  2796. {
  2797. // Only see players and NPC's with 360 cone
  2798. // For instance, DON'T tell the eyeball/head tracking code that you can see an object that is behind you!
  2799. return true;
  2800. }
  2801. else
  2802. {
  2803. return BaseClass::FInViewCone( pEntity );
  2804. }
  2805. }
  2806. #endif
  2807. //-----------------------------------------------------------------------------
  2808. // Purpose: Does a spastic hop in a random or provided direction.
  2809. // Input : pvecDir - 2D direction to hop, NULL picks a random direction.
  2810. //-----------------------------------------------------------------------------
  2811. void CBlackHeadcrab::JumpFlinch( const Vector *pvecDir )
  2812. {
  2813. SetGroundEntity( NULL );
  2814. //
  2815. // Take him off ground so engine doesn't instantly reset FL_ONGROUND.
  2816. //
  2817. if( HasHeadroom() )
  2818. {
  2819. MoveOrigin( Vector( 0, 0, 1 ) );
  2820. }
  2821. //
  2822. // Jump in a random direction.
  2823. //
  2824. Vector up;
  2825. AngleVectors( GetLocalAngles(), NULL, NULL, &up );
  2826. if (pvecDir)
  2827. {
  2828. SetAbsVelocity( Vector( pvecDir->x * 4, pvecDir->y * 4, up.z ) * random->RandomFloat( 40, 80 ) );
  2829. }
  2830. else
  2831. {
  2832. SetAbsVelocity( Vector( random->RandomFloat( -4, 4 ), random->RandomFloat( -4, 4 ), up.z ) * random->RandomFloat( 40, 80 ) );
  2833. }
  2834. }
  2835. //-----------------------------------------------------------------------------
  2836. // Purpose: Catches the monster-specific messages that occur when tagged
  2837. // animation frames are played.
  2838. // Input : pEvent -
  2839. //-----------------------------------------------------------------------------
  2840. void CBlackHeadcrab::HandleAnimEvent( animevent_t *pEvent )
  2841. {
  2842. if ( pEvent->event == AE_POISONHEADCRAB_FOOTSTEP )
  2843. {
  2844. bool walk = ( GetActivity() == ACT_WALK ); // ? 1.0 : 0.6; !!cgreen! old code had bug
  2845. if ( walk )
  2846. {
  2847. EmitSound( "NPC_BlackHeadcrab.FootstepWalk" );
  2848. }
  2849. else
  2850. {
  2851. EmitSound( "NPC_BlackHeadcrab.Footstep" );
  2852. }
  2853. return;
  2854. }
  2855. if ( pEvent->event == AE_HEADCRAB_JUMP_TELEGRAPH )
  2856. {
  2857. EmitSound( "NPC_BlackHeadcrab.Telegraph" );
  2858. CBaseEntity *pEnemy = GetEnemy();
  2859. if ( pEnemy )
  2860. {
  2861. // Once we telegraph, we MUST jump. This is also when commit to what point
  2862. // we jump at. Jump at our enemy's eyes.
  2863. m_vecCommittedJumpPos = pEnemy->EyePosition();
  2864. m_bCommittedToJump = true;
  2865. }
  2866. return;
  2867. }
  2868. if ( pEvent->event == AE_POISONHEADCRAB_THREAT_SOUND )
  2869. {
  2870. EmitSound( "NPC_BlackHeadcrab.Threat" );
  2871. EmitSound( "NPC_BlackHeadcrab.Alert" );
  2872. return;
  2873. }
  2874. if ( pEvent->event == AE_POISONHEADCRAB_FLINCH_HOP )
  2875. {
  2876. //
  2877. // Hop in a random direction, then run and hide. If we're already running
  2878. // to hide, jump forward -- hopefully that will take us closer to a hiding spot.
  2879. //
  2880. if (m_bPanicState)
  2881. {
  2882. Vector vecForward;
  2883. AngleVectors( GetLocalAngles(), &vecForward );
  2884. JumpFlinch( &vecForward );
  2885. }
  2886. else
  2887. {
  2888. JumpFlinch( NULL );
  2889. }
  2890. Panic( random->RandomFloat( 2, 5 ) );
  2891. return;
  2892. }
  2893. BaseClass::HandleAnimEvent( pEvent );
  2894. }
  2895. //-----------------------------------------------------------------------------
  2896. //-----------------------------------------------------------------------------
  2897. bool CBlackHeadcrab::IsHeavyDamage( const CTakeDamageInfo &info )
  2898. {
  2899. if ( !HasMemory(bits_MEMORY_FLINCHED) && info.GetDamage() > 1.0f )
  2900. {
  2901. // If I haven't flinched lately, any amount of damage is interpreted as heavy.
  2902. return true;
  2903. }
  2904. return false;
  2905. }
  2906. //-----------------------------------------------------------------------------
  2907. // Purpose:
  2908. //-----------------------------------------------------------------------------
  2909. void CBlackHeadcrab::IdleSound( void )
  2910. {
  2911. // TODO: hook up "Marco" / "Polo" talking with nearby buddies
  2912. if ( m_NPCState == NPC_STATE_IDLE )
  2913. {
  2914. EmitSound( "NPC_BlackHeadcrab.Idle" );
  2915. }
  2916. else if ( m_NPCState == NPC_STATE_ALERT )
  2917. {
  2918. EmitSound( "NPC_BlackHeadcrab.Talk" );
  2919. }
  2920. }
  2921. //-----------------------------------------------------------------------------
  2922. // Purpose:
  2923. //-----------------------------------------------------------------------------
  2924. void CBlackHeadcrab::AlertSound( void )
  2925. {
  2926. EmitSound( "NPC_BlackHeadcrab.AlertVoice" );
  2927. }
  2928. //-----------------------------------------------------------------------------
  2929. // Purpose:
  2930. //-----------------------------------------------------------------------------
  2931. void CBlackHeadcrab::PainSound( const CTakeDamageInfo &info )
  2932. {
  2933. if( IsOnFire() && random->RandomInt( 0, HEADCRAB_BURN_SOUND_FREQUENCY ) > 0 )
  2934. {
  2935. // Don't squeak every think when burning.
  2936. return;
  2937. }
  2938. EmitSound( "NPC_BlackHeadcrab.Pain" );
  2939. }
  2940. //-----------------------------------------------------------------------------
  2941. // Purpose:
  2942. //-----------------------------------------------------------------------------
  2943. void CBlackHeadcrab::DeathSound( const CTakeDamageInfo &info )
  2944. {
  2945. EmitSound( "NPC_BlackHeadcrab.Die" );
  2946. }
  2947. //-----------------------------------------------------------------------------
  2948. // Purpose: Played when we jump and hit something that we can't bite.
  2949. //-----------------------------------------------------------------------------
  2950. void CBlackHeadcrab::ImpactSound( void )
  2951. {
  2952. EmitSound( "NPC_BlackHeadcrab.Impact" );
  2953. if ( !( GetFlags() & FL_ONGROUND ) )
  2954. {
  2955. // Hit a wall - make a pissed off sound.
  2956. EmitSound( "NPC_BlackHeadcrab.ImpactAngry" );
  2957. }
  2958. }
  2959. //-----------------------------------------------------------------------------
  2960. //
  2961. // Schedules
  2962. //
  2963. //-----------------------------------------------------------------------------
  2964. AI_BEGIN_CUSTOM_NPC( npc_headcrab, CBaseHeadcrab )
  2965. DECLARE_TASK( TASK_HEADCRAB_HOP_ASIDE )
  2966. DECLARE_TASK( TASK_HEADCRAB_DROWN )
  2967. DECLARE_TASK( TASK_HEADCRAB_HOP_OFF_NPC )
  2968. DECLARE_TASK( TASK_HEADCRAB_WAIT_FOR_BARNACLE_KILL )
  2969. DECLARE_TASK( TASK_HEADCRAB_UNHIDE )
  2970. DECLARE_TASK( TASK_HEADCRAB_HARASS_HOP )
  2971. DECLARE_TASK( TASK_HEADCRAB_BURROW )
  2972. DECLARE_TASK( TASK_HEADCRAB_UNBURROW )
  2973. DECLARE_TASK( TASK_HEADCRAB_FIND_BURROW_IN_POINT )
  2974. DECLARE_TASK( TASK_HEADCRAB_BURROW_WAIT )
  2975. DECLARE_TASK( TASK_HEADCRAB_CHECK_FOR_UNBURROW )
  2976. DECLARE_TASK( TASK_HEADCRAB_JUMP_FROM_CANISTER )
  2977. DECLARE_TASK( TASK_HEADCRAB_CLIMB_FROM_CANISTER )
  2978. DECLARE_TASK( TASK_HEADCRAB_CEILING_POSITION )
  2979. DECLARE_TASK( TASK_HEADCRAB_CEILING_WAIT )
  2980. DECLARE_TASK( TASK_HEADCRAB_CEILING_DETACH )
  2981. DECLARE_TASK( TASK_HEADCRAB_CEILING_FALL )
  2982. DECLARE_TASK( TASK_HEADCRAB_CEILING_LAND )
  2983. DECLARE_ACTIVITY( ACT_HEADCRAB_THREAT_DISPLAY )
  2984. DECLARE_ACTIVITY( ACT_HEADCRAB_HOP_LEFT )
  2985. DECLARE_ACTIVITY( ACT_HEADCRAB_HOP_RIGHT )
  2986. DECLARE_ACTIVITY( ACT_HEADCRAB_DROWN )
  2987. DECLARE_ACTIVITY( ACT_HEADCRAB_BURROW_IN )
  2988. DECLARE_ACTIVITY( ACT_HEADCRAB_BURROW_OUT )
  2989. DECLARE_ACTIVITY( ACT_HEADCRAB_BURROW_IDLE )
  2990. DECLARE_ACTIVITY( ACT_HEADCRAB_CRAWL_FROM_CANISTER_LEFT )
  2991. DECLARE_ACTIVITY( ACT_HEADCRAB_CRAWL_FROM_CANISTER_CENTER )
  2992. DECLARE_ACTIVITY( ACT_HEADCRAB_CRAWL_FROM_CANISTER_RIGHT )
  2993. DECLARE_ACTIVITY( ACT_HEADCRAB_CEILING_FALL )
  2994. DECLARE_ACTIVITY( ACT_HEADCRAB_CEILING_IDLE )
  2995. DECLARE_ACTIVITY( ACT_HEADCRAB_CEILING_DETACH )
  2996. DECLARE_ACTIVITY( ACT_HEADCRAB_CEILING_LAND )
  2997. DECLARE_CONDITION( COND_HEADCRAB_IN_WATER )
  2998. DECLARE_CONDITION( COND_HEADCRAB_ILLEGAL_GROUNDENT )
  2999. DECLARE_CONDITION( COND_HEADCRAB_BARNACLED )
  3000. DECLARE_CONDITION( COND_HEADCRAB_UNHIDE )
  3001. //Adrian: events go here
  3002. DECLARE_ANIMEVENT( AE_HEADCRAB_JUMPATTACK )
  3003. DECLARE_ANIMEVENT( AE_HEADCRAB_JUMP_TELEGRAPH )
  3004. DECLARE_ANIMEVENT( AE_HEADCRAB_BURROW_IN )
  3005. DECLARE_ANIMEVENT( AE_HEADCRAB_BURROW_IN_FINISH )
  3006. DECLARE_ANIMEVENT( AE_HEADCRAB_BURROW_OUT )
  3007. DECLARE_ANIMEVENT( AE_HEADCRAB_CEILING_DETACH )
  3008. //=========================================================
  3009. // > SCHED_HEADCRAB_RANGE_ATTACK1
  3010. //=========================================================
  3011. DEFINE_SCHEDULE
  3012. (
  3013. SCHED_HEADCRAB_RANGE_ATTACK1,
  3014. " Tasks"
  3015. " TASK_STOP_MOVING 0"
  3016. " TASK_FACE_ENEMY 0"
  3017. " TASK_RANGE_ATTACK1 0"
  3018. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  3019. " TASK_FACE_IDEAL 0"
  3020. " TASK_WAIT_RANDOM 0.5"
  3021. ""
  3022. " Interrupts"
  3023. " COND_ENEMY_OCCLUDED"
  3024. " COND_NO_PRIMARY_AMMO"
  3025. )
  3026. //=========================================================
  3027. //
  3028. //=========================================================
  3029. DEFINE_SCHEDULE
  3030. (
  3031. SCHED_HEADCRAB_WAKE_ANGRY,
  3032. " Tasks"
  3033. " TASK_STOP_MOVING 0"
  3034. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE "
  3035. " TASK_FACE_IDEAL 0"
  3036. " TASK_SOUND_WAKE 0"
  3037. " TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_HEADCRAB_THREAT_DISPLAY"
  3038. ""
  3039. " Interrupts"
  3040. )
  3041. //=========================================================
  3042. //
  3043. //=========================================================
  3044. DEFINE_SCHEDULE
  3045. (
  3046. SCHED_HEADCRAB_WAKE_ANGRY_NO_DISPLAY,
  3047. " Tasks"
  3048. " TASK_STOP_MOVING 0"
  3049. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE "
  3050. " TASK_FACE_IDEAL 0"
  3051. " TASK_SOUND_WAKE 0"
  3052. " TASK_FACE_ENEMY 0"
  3053. ""
  3054. " Interrupts"
  3055. )
  3056. //=========================================================
  3057. // > SCHED_FAST_HEADCRAB_RANGE_ATTACK1
  3058. //=========================================================
  3059. DEFINE_SCHEDULE
  3060. (
  3061. SCHED_FAST_HEADCRAB_RANGE_ATTACK1,
  3062. " Tasks"
  3063. " TASK_STOP_MOVING 0"
  3064. " TASK_FACE_ENEMY 0"
  3065. " TASK_RANGE_ATTACK1 0"
  3066. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  3067. " TASK_FACE_IDEAL 0"
  3068. " TASK_WAIT_RANDOM 0.5"
  3069. ""
  3070. " Interrupts"
  3071. )
  3072. //=========================================================
  3073. // The irreversible process of drowning
  3074. //=========================================================
  3075. DEFINE_SCHEDULE
  3076. (
  3077. SCHED_HEADCRAB_DROWN,
  3078. " Tasks"
  3079. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HEADCRAB_FAIL_DROWN"
  3080. " TASK_SET_ACTIVITY ACTIVITY:ACT_HEADCRAB_DROWN"
  3081. " TASK_HEADCRAB_DROWN 0"
  3082. ""
  3083. " Interrupts"
  3084. )
  3085. DEFINE_SCHEDULE
  3086. (
  3087. SCHED_HEADCRAB_FAIL_DROWN,
  3088. " Tasks"
  3089. " TASK_HEADCRAB_DROWN 0"
  3090. ""
  3091. " Interrupts"
  3092. )
  3093. //=========================================================
  3094. // Headcrab lurks in place and waits for a chance to jump on
  3095. // some unfortunate soul.
  3096. //=========================================================
  3097. DEFINE_SCHEDULE
  3098. (
  3099. SCHED_HEADCRAB_AMBUSH,
  3100. " Tasks"
  3101. " TASK_STOP_MOVING 0"
  3102. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  3103. " TASK_WAIT_INDEFINITE 0"
  3104. " Interrupts"
  3105. " COND_SEE_ENEMY"
  3106. " COND_SEE_HATE"
  3107. " COND_CAN_RANGE_ATTACK1"
  3108. " COND_LIGHT_DAMAGE"
  3109. " COND_HEAVY_DAMAGE"
  3110. " COND_PROVOKED"
  3111. )
  3112. //=========================================================
  3113. // Headcrab has landed atop another NPC or has landed on
  3114. // a ledge. Get down!
  3115. //=========================================================
  3116. DEFINE_SCHEDULE
  3117. (
  3118. SCHED_HEADCRAB_HOP_RANDOMLY,
  3119. " Tasks"
  3120. " TASK_STOP_MOVING 0"
  3121. " TASK_HEADCRAB_HOP_OFF_NPC 0"
  3122. " Interrupts"
  3123. )
  3124. //=========================================================
  3125. // Headcrab is in the clutches of a barnacle
  3126. //=========================================================
  3127. DEFINE_SCHEDULE
  3128. (
  3129. SCHED_HEADCRAB_BARNACLED,
  3130. " Tasks"
  3131. " TASK_STOP_MOVING 0"
  3132. " TASK_SET_ACTIVITY ACTIVITY:ACT_HEADCRAB_DROWN"
  3133. " TASK_HEADCRAB_WAIT_FOR_BARNACLE_KILL 0"
  3134. " Interrupts"
  3135. )
  3136. //=========================================================
  3137. // Headcrab is unhiding
  3138. //=========================================================
  3139. DEFINE_SCHEDULE
  3140. (
  3141. SCHED_HEADCRAB_UNHIDE,
  3142. " Tasks"
  3143. " TASK_HEADCRAB_UNHIDE 0"
  3144. " Interrupts"
  3145. )
  3146. DEFINE_SCHEDULE
  3147. (
  3148. SCHED_HEADCRAB_HARASS_ENEMY,
  3149. " Tasks"
  3150. " TASK_FACE_ENEMY 0"
  3151. " TASK_HEADCRAB_HARASS_HOP 0"
  3152. " TASK_WAIT_FACE_ENEMY 1"
  3153. " TASK_SET_ROUTE_SEARCH_TIME 2" // Spend 2 seconds trying to build a path if stuck
  3154. " TASK_GET_PATH_TO_RANDOM_NODE 300"
  3155. " TASK_WALK_PATH 0"
  3156. " TASK_WAIT_FOR_MOVEMENT 0"
  3157. " Interrupts"
  3158. " COND_NEW_ENEMY"
  3159. )
  3160. DEFINE_SCHEDULE
  3161. (
  3162. SCHED_HEADCRAB_FALL_TO_GROUND,
  3163. " Tasks"
  3164. " TASK_SET_ACTIVITY ACTIVITY:ACT_HEADCRAB_DROWN"
  3165. " TASK_FALL_TO_GROUND 0"
  3166. ""
  3167. " Interrupts"
  3168. )
  3169. DEFINE_SCHEDULE
  3170. (
  3171. SCHED_HEADCRAB_CRAWL_FROM_CANISTER,
  3172. " Tasks"
  3173. " TASK_HEADCRAB_CLIMB_FROM_CANISTER 0"
  3174. " TASK_HEADCRAB_JUMP_FROM_CANISTER 0"
  3175. ""
  3176. " Interrupts"
  3177. )
  3178. //==================================================
  3179. // Burrow In
  3180. //==================================================
  3181. DEFINE_SCHEDULE
  3182. (
  3183. SCHED_HEADCRAB_BURROW_IN,
  3184. " Tasks"
  3185. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3186. " TASK_HEADCRAB_BURROW 0"
  3187. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_HEADCRAB_BURROW_IN"
  3188. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_HEADCRAB_BURROW_IDLE"
  3189. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_WAIT"
  3190. ""
  3191. " Interrupts"
  3192. " COND_TASK_FAILED"
  3193. )
  3194. //==================================================
  3195. // Run to a nearby burrow hint and burrow there
  3196. //==================================================
  3197. DEFINE_SCHEDULE
  3198. (
  3199. SCHED_HEADCRAB_RUN_TO_BURROW_IN,
  3200. " Tasks"
  3201. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3202. " TASK_HEADCRAB_FIND_BURROW_IN_POINT 512"
  3203. " TASK_SET_TOLERANCE_DISTANCE 8"
  3204. " TASK_RUN_PATH 0"
  3205. " TASK_WAIT_FOR_MOVEMENT 0"
  3206. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_IN"
  3207. ""
  3208. " Interrupts"
  3209. " COND_TASK_FAILED"
  3210. " COND_GIVE_WAY"
  3211. " COND_CAN_RANGE_ATTACK1"
  3212. )
  3213. //==================================================
  3214. // Run to m_pHintNode and burrow there
  3215. //==================================================
  3216. DEFINE_SCHEDULE
  3217. (
  3218. SCHED_HEADCRAB_RUN_TO_SPECIFIC_BURROW,
  3219. " Tasks"
  3220. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_CHASE_ENEMY_FAILED"
  3221. " TASK_SET_TOLERANCE_DISTANCE 8"
  3222. " TASK_GET_PATH_TO_HINTNODE 0"
  3223. " TASK_RUN_PATH 0"
  3224. " TASK_WAIT_FOR_MOVEMENT 0"
  3225. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_IN"
  3226. ""
  3227. " Interrupts"
  3228. " COND_TASK_FAILED"
  3229. " COND_GIVE_WAY"
  3230. )
  3231. //==================================================
  3232. // Wait until we can unburrow and attack something
  3233. //==================================================
  3234. DEFINE_SCHEDULE
  3235. (
  3236. SCHED_HEADCRAB_BURROW_WAIT,
  3237. " Tasks"
  3238. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_WAIT"
  3239. " TASK_HEADCRAB_BURROW_WAIT 1"
  3240. ""
  3241. " Interrupts"
  3242. " COND_TASK_FAILED"
  3243. " COND_NEW_ENEMY" // HACK: We don't actually choose a new schedule on new enemy, but
  3244. // we need this interrupt so that the headcrab actually acquires
  3245. // new enemies while burrowed. (look in ai_basenpc.cpp for "DO NOT mess")
  3246. " COND_CAN_RANGE_ATTACK1"
  3247. )
  3248. //==================================================
  3249. // Burrow Out
  3250. //==================================================
  3251. DEFINE_SCHEDULE
  3252. (
  3253. SCHED_HEADCRAB_BURROW_OUT,
  3254. " Tasks"
  3255. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_WAIT"
  3256. " TASK_HEADCRAB_UNBURROW 0"
  3257. " TASK_PLAY_SEQUENCE ACTIVITY:ACT_HEADCRAB_BURROW_OUT"
  3258. ""
  3259. " Interrupts"
  3260. " COND_TASK_FAILED"
  3261. )
  3262. //==================================================
  3263. // Wait for it to be clear for unburrowing
  3264. //==================================================
  3265. DEFINE_SCHEDULE
  3266. (
  3267. SCHED_HEADCRAB_WAIT_FOR_CLEAR_UNBURROW,
  3268. " Tasks"
  3269. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_WAIT"
  3270. " TASK_HEADCRAB_CHECK_FOR_UNBURROW 1"
  3271. " TASK_SET_SCHEDULE SCHEDULE:SCHED_HEADCRAB_BURROW_OUT"
  3272. ""
  3273. " Interrupts"
  3274. " COND_TASK_FAILED"
  3275. )
  3276. //==================================================
  3277. // Wait until we can drop.
  3278. //==================================================
  3279. DEFINE_SCHEDULE
  3280. (
  3281. SCHED_HEADCRAB_CEILING_WAIT,
  3282. " Tasks"
  3283. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HEADCRAB_CEILING_DROP"
  3284. " TASK_SET_ACTIVITY ACTIVITY:ACT_HEADCRAB_CEILING_IDLE"
  3285. " TASK_HEADCRAB_CEILING_POSITION 0"
  3286. " TASK_HEADCRAB_CEILING_WAIT 1"
  3287. ""
  3288. " Interrupts"
  3289. " COND_TASK_FAILED"
  3290. " COND_NEW_ENEMY"
  3291. " COND_CAN_RANGE_ATTACK1"
  3292. )
  3293. //==================================================
  3294. // Deatch from ceiling.
  3295. //==================================================
  3296. DEFINE_SCHEDULE
  3297. (
  3298. SCHED_HEADCRAB_CEILING_DROP,
  3299. " Tasks"
  3300. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_HEADCRAB_CEILING_WAIT"
  3301. " TASK_HEADCRAB_CEILING_DETACH 0"
  3302. " TASK_HEADCRAB_CEILING_FALL 0"
  3303. " TASK_HEADCRAB_CEILING_LAND 0"
  3304. ""
  3305. " Interrupts"
  3306. " COND_TASK_FAILED"
  3307. )
  3308. AI_END_CUSTOM_NPC()
  3309. //-----------------------------------------------------------------------------
  3310. AI_BEGIN_CUSTOM_NPC( npc_headcrab_poison, CBlackHeadcrab )
  3311. DECLARE_ACTIVITY( ACT_BLACKHEADCRAB_RUN_PANIC )
  3312. //Adrian: events go here
  3313. DECLARE_ANIMEVENT( AE_POISONHEADCRAB_FLINCH_HOP )
  3314. DECLARE_ANIMEVENT( AE_POISONHEADCRAB_FOOTSTEP )
  3315. DECLARE_ANIMEVENT( AE_POISONHEADCRAB_THREAT_SOUND )
  3316. AI_END_CUSTOM_NPC()
  3317. AI_BEGIN_CUSTOM_NPC( npc_headcrab_fast, CFastHeadcrab )
  3318. DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE1 )
  3319. DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE2 )
  3320. DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE3 )
  3321. DECLARE_SQUADSLOT( SQUAD_SLOT_ENGAGE4 )
  3322. AI_END_CUSTOM_NPC()