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.

3410 lines
100 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Vortigaunt - now much friendlier!
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "beam_shared.h"
  8. #include "globalstate.h"
  9. #include "npcevent.h"
  10. #include "Sprite.h"
  11. #include "IEffects.h"
  12. #include "te_effect_dispatch.h"
  13. #include "SoundEmitterSystem/isoundemittersystembase.h"
  14. #include "physics_prop_ragdoll.h"
  15. #include "RagdollBoogie.h"
  16. #include "ai_squadslot.h"
  17. #include "npc_antlion.h"
  18. #include "particle_parse.h"
  19. #include "particle_system.h"
  20. #include "ai_senses.h"
  21. #include "npc_vortigaunt_episodic.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. #define HAND_LEFT 0
  25. #define HAND_RIGHT 1
  26. #define HAND_BOTH 2
  27. class CVortigauntChargeToken;
  28. #define VORTIGAUNT_LIMP_HEALTH 20
  29. #define VORTIGAUNT_SENTENCE_VOLUME (float)0.35 // volume of vortigaunt sentences
  30. #define VORTIGAUNT_VOL 0.35 // volume of vortigaunt sounds
  31. #define VORTIGAUNT_ATTN ATTN_NORM // attenutation of vortigaunt sentences
  32. #define VORTIGAUNT_HEAL_RECHARGE 30.0 // How long to rest between heals
  33. #define VORTIGAUNT_ZAP_GLOWGROW_TIME 0.5 // How long does glow last
  34. #define VORTIGAUNT_HEAL_GLOWGROW_TIME 1.4 // How long does glow last
  35. #define VORTIGAUNT_GLOWFADE_TIME 0.5 // How long does it fade
  36. #define VORTIGAUNT_CURE_LIFESPAN 8.0 // cure tokens only live this long (so they don't get stuck on geometry)
  37. #define VORTIGAUNT_BLUE_FADE_TIME 2.25f // takes this long to fade from green to blue or back
  38. #define VORTIGAUNT_FEAR_ZOMBIE_DIST_SQR Square(60) // back away from zombies inside this radius
  39. #define PLAYER_CRITICAL_HEALTH_PERC 0.15f
  40. #define TLK_SQUISHED_GRUB "TLK_SQUISHED_GRUB" // response rule for vortigaunts when they step on a grub
  41. static const char *VORTIGAUNT_LEFT_CLAW = "leftclaw";
  42. static const char *VORTIGAUNT_RIGHT_CLAW = "rightclaw";
  43. static const char *VORT_CURE = "VORT_CURE";
  44. static const char *VORT_CURESTOP = "VORT_CURESTOP";
  45. static const char *VORT_CURE_INTERRUPT = "VORT_CURE_INTERRUPT";
  46. static const char *VORT_ATTACK = "VORT_ATTACK";
  47. static const char *VORT_MAD = "VORT_MAD";
  48. static const char *VORT_SHOT = "VORT_SHOT";
  49. static const char *VORT_PAIN = "VORT_PAIN";
  50. static const char *VORT_DIE = "VORT_DIE";
  51. static const char *VORT_KILL = "VORT_KILL";
  52. static const char *VORT_LINE_FIRE = "VORT_LINE_FIRE";
  53. static const char *VORT_POK = "VORT_POK";
  54. static const char *VORT_EXTRACT_START = "VORT_EXTRACT_START";
  55. static const char *VORT_EXTRACT_FINISH = "VORT_EXTRACT_FINISH";
  56. // Target must be within this range to heal
  57. #define HEAL_RANGE (40*12) //ft
  58. #define HEAL_SEARCH_RANGE (40*12) //ft
  59. ConVar sk_vortigaunt_armor_charge( "sk_vortigaunt_armor_charge","30");
  60. ConVar sk_vortigaunt_armor_charge_per_token( "sk_vortigaunt_armor_charge_per_token","5");
  61. ConVar sk_vortigaunt_health( "sk_vortigaunt_health","0");
  62. ConVar sk_vortigaunt_dmg_claw( "sk_vortigaunt_dmg_claw","0");
  63. ConVar sk_vortigaunt_dmg_rake( "sk_vortigaunt_dmg_rake","0");
  64. ConVar sk_vortigaunt_dmg_zap( "sk_vortigaunt_dmg_zap","0");
  65. ConVar sk_vortigaunt_zap_range( "sk_vortigaunt_zap_range", "100", FCVAR_NONE, "Range of vortigaunt's ranged attack (feet)" );
  66. ConVar sk_vortigaunt_vital_antlion_worker_dmg("sk_vortigaunt_vital_antlion_worker_dmg", "0.2", FCVAR_NONE, "Vital-ally vortigaunts scale damage taken from antlion workers by this amount." );
  67. ConVar g_debug_vortigaunt_aim( "g_debug_vortigaunt_aim", "0" );
  68. // FIXME: Move to shared code!
  69. #define VORTFX_ZAPBEAM 0 // Beam that damages the target
  70. #define VORTFX_ARMBEAM 1 // Smaller beams from the hands as we charge up
  71. //-----------------------------------------------------------------------------
  72. // Interactions
  73. //-----------------------------------------------------------------------------
  74. int g_interactionVortigauntStomp = 0;
  75. int g_interactionVortigauntStompFail = 0;
  76. int g_interactionVortigauntStompHit = 0;
  77. int g_interactionVortigauntKick = 0;
  78. int g_interactionVortigauntClaw = 0;
  79. //=========================================================
  80. // Vortigaunt activities
  81. //=========================================================
  82. int ACT_VORTIGAUNT_AIM;
  83. int ACT_VORTIGAUNT_START_HEAL;
  84. int ACT_VORTIGAUNT_HEAL_LOOP;
  85. int ACT_VORTIGAUNT_END_HEAL;
  86. int ACT_VORTIGAUNT_TO_ACTION;
  87. int ACT_VORTIGAUNT_TO_IDLE;
  88. int ACT_VORTIGAUNT_HEAL; // Heal gesture
  89. int ACT_VORTIGAUNT_DISPEL;
  90. int ACT_VORTIGAUNT_ANTLION_THROW;
  91. //=========================================================
  92. // Monster's Anim Events Go Here
  93. //=========================================================
  94. int AE_VORTIGAUNT_CLAW_LEFT;
  95. int AE_VORTIGAUNT_CLAW_RIGHT;
  96. int AE_VORTIGAUNT_ZAP_POWERUP;
  97. int AE_VORTIGAUNT_ZAP_SHOOT;
  98. int AE_VORTIGAUNT_ZAP_DONE;
  99. int AE_VORTIGAUNT_HEAL_STARTGLOW;
  100. int AE_VORTIGAUNT_HEAL_STARTBEAMS;
  101. int AE_VORTIGAUNT_HEAL_STARTSOUND;
  102. int AE_VORTIGAUNT_SWING_SOUND;
  103. int AE_VORTIGAUNT_SHOOT_SOUNDSTART;
  104. int AE_VORTIGAUNT_HEAL_PAUSE;
  105. int AE_VORTIGAUNT_START_DISPEL; // Start the warm-up
  106. int AE_VORTIGAUNT_ACCEL_DISPEL; // Indicates we're ramping up
  107. int AE_VORTIGAUNT_DISPEL; // Actual blast
  108. int AE_VORTIGAUNT_START_HURT_GLOW; // Start the hurt handglow: 0=left, 1=right
  109. int AE_VORTIGAUNT_STOP_HURT_GLOW; // Turn off the hurt handglow: 0=left, 1=right
  110. int AE_VORTIGAUNT_START_HEAL_GLOW; // 0 - Left, 1 - Right
  111. int AE_VORTIGAUNT_STOP_HEAL_GLOW; // '
  112. //-----------------------------------------------------------------------------
  113. // Squad slots
  114. //-----------------------------------------------------------------------------
  115. enum SquadSlot_T
  116. {
  117. SQUAD_SLOT_HEAL_PLAYER = LAST_SHARED_SQUADSLOT,
  118. };
  119. //---------------------------------------------------------
  120. // Save/Restore
  121. //---------------------------------------------------------
  122. BEGIN_DATADESC( CNPC_Vortigaunt )
  123. DEFINE_FIELD( m_eHealState, FIELD_INTEGER ),
  124. DEFINE_FIELD( m_flNextHealTokenTime, FIELD_TIME ),
  125. DEFINE_ARRAY( m_hHandEffect, FIELD_EHANDLE, 2 ),
  126. DEFINE_FIELD( m_flNextHealTime, FIELD_TIME ),
  127. DEFINE_FIELD( m_bPlayerRequestedHeal, FIELD_BOOLEAN ),
  128. DEFINE_FIELD( m_flDispelTestTime, FIELD_TIME ),
  129. DEFINE_FIELD( m_flHealHinderedTime, FIELD_FLOAT ),
  130. DEFINE_FIELD( m_nLightningSprite, FIELD_INTEGER),
  131. DEFINE_FIELD( m_fGlowAge, FIELD_FLOAT),
  132. DEFINE_FIELD( m_fGlowChangeTime, FIELD_TIME),
  133. DEFINE_FIELD( m_bGlowTurningOn, FIELD_BOOLEAN),
  134. DEFINE_FIELD( m_nCurGlowIndex, FIELD_INTEGER),
  135. DEFINE_FIELD( m_flNextHealTime, FIELD_TIME),
  136. DEFINE_FIELD( m_flPainTime, FIELD_TIME),
  137. DEFINE_FIELD( m_nextLineFireTime, FIELD_TIME),
  138. DEFINE_KEYFIELD( m_bArmorRechargeEnabled,FIELD_BOOLEAN, "ArmorRechargeEnabled" ),
  139. DEFINE_FIELD( m_bForceArmorRecharge, FIELD_BOOLEAN),
  140. DEFINE_FIELD( m_bExtractingBugbait, FIELD_BOOLEAN),
  141. DEFINE_FIELD( m_iLeftHandAttachment, FIELD_INTEGER ),
  142. DEFINE_FIELD( m_iRightHandAttachment, FIELD_INTEGER ),
  143. DEFINE_FIELD( m_hHealTarget, FIELD_EHANDLE ),
  144. DEFINE_FIELD( m_flBlueEndFadeTime, FIELD_TIME ),
  145. DEFINE_FIELD( m_bIsBlue, FIELD_BOOLEAN ),
  146. DEFINE_FIELD( m_bIsBlack, FIELD_BOOLEAN ),
  147. DEFINE_FIELD( m_flAimDelay, FIELD_TIME ),
  148. DEFINE_FIELD( m_bCarryingNPC, FIELD_BOOLEAN ),
  149. DEFINE_KEYFIELD( m_bRegenerateHealth, FIELD_BOOLEAN, "HealthRegenerateEnabled" ),
  150. // m_AssaultBehavior (auto saved by AI)
  151. // m_LeadBehavior
  152. // DEFINE_FIELD( m_bStopLoopingSounds, FIELD_BOOLEAN ),
  153. // Function Pointers
  154. DEFINE_USEFUNC( Use ),
  155. DEFINE_INPUTFUNC( FIELD_VOID, "EnableArmorRecharge", InputEnableArmorRecharge ),
  156. DEFINE_INPUTFUNC( FIELD_VOID, "DisableArmorRecharge", InputDisableArmorRecharge ),
  157. DEFINE_INPUTFUNC( FIELD_STRING, "ChargeTarget", InputChargeTarget ),
  158. DEFINE_INPUTFUNC( FIELD_STRING, "ExtractBugbait", InputExtractBugbait ),
  159. DEFINE_INPUTFUNC( FIELD_VOID, "EnableHealthRegeneration", InputEnableHealthRegeneration ),
  160. DEFINE_INPUTFUNC( FIELD_VOID, "DisableHealthRegeneration",InputDisableHealthRegeneration ),
  161. DEFINE_INPUTFUNC( FIELD_VOID, "Dispel", InputDispel ),
  162. DEFINE_INPUTFUNC( FIELD_VOID, "BeginCarryNPC", InputBeginCarryNPC ),
  163. DEFINE_INPUTFUNC( FIELD_VOID, "EndCarryNPC", InputEndCarryNPC ),
  164. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "TurnBlue", InputTurnBlue ),
  165. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "TurnBlack", InputTurnBlack ),
  166. // Outputs
  167. DEFINE_OUTPUT(m_OnFinishedExtractingBugbait, "OnFinishedExtractingBugbait"),
  168. DEFINE_OUTPUT(m_OnFinishedChargingTarget, "OnFinishedChargingTarget"),
  169. DEFINE_OUTPUT(m_OnPlayerUse, "OnPlayerUse" ),
  170. END_DATADESC()
  171. LINK_ENTITY_TO_CLASS( npc_vortigaunt, CNPC_Vortigaunt );
  172. IMPLEMENT_SERVERCLASS_ST( CNPC_Vortigaunt, DT_NPC_Vortigaunt )
  173. SendPropTime( SENDINFO (m_flBlueEndFadeTime ) ),
  174. SendPropBool( SENDINFO( m_bIsBlue )),
  175. SendPropBool( SENDINFO ( m_bIsBlack ) ),
  176. END_SEND_TABLE()
  177. // for special behavior with rollermines
  178. static bool IsRoller( CBaseEntity *pRoller )
  179. {
  180. return FClassnameIs( pRoller, "npc_rollermine" );
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. //-----------------------------------------------------------------------------
  185. CNPC_Vortigaunt::CNPC_Vortigaunt( void ) :
  186. m_bPlayerRequestedHeal( false ),
  187. m_flNextHealTime( 3.0f ), // Let the player settle before we decide to do this
  188. m_nNumTokensToSpawn( 0 ),
  189. m_flAimDelay( 0.0f ),
  190. m_eHealState( HEAL_STATE_NONE )
  191. {
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose: Determines whether or not the player is below a certain percentage
  195. // of their maximum health
  196. // Input : flPerc - Percentage to test against
  197. // Output : Returns true if less than supplied parameter
  198. //-----------------------------------------------------------------------------
  199. bool CNPC_Vortigaunt::PlayerBelowHealthPercentage( CBasePlayer *pPlayer, float flPerc )
  200. {
  201. if ( pPlayer == NULL )
  202. return false;
  203. if ( pPlayer->ArmorValue() )
  204. return false;
  205. float flMaxHealth = pPlayer->GetMaxHealth();
  206. if ( flMaxHealth == 0.0f )
  207. return false;
  208. float flHealthPerc = (flMaxHealth != 0 ) ? (float) pPlayer->GetHealth() / flMaxHealth : 0.0f;
  209. return ( flHealthPerc <= flPerc );
  210. }
  211. #define VORT_START_EXTRACT_SENTENCE 500
  212. #define VORT_FINISH_EXTRACT_SENTENCE 501
  213. //------------------------------------------------------------------------------
  214. // Purpose: Make the vort speak a line
  215. //------------------------------------------------------------------------------
  216. void CNPC_Vortigaunt::SpeakSentence( int sentenceType )
  217. {
  218. if (sentenceType == VORT_START_EXTRACT_SENTENCE)
  219. {
  220. Speak( VORT_EXTRACT_START );
  221. }
  222. else if (sentenceType == VORT_FINISH_EXTRACT_SENTENCE)
  223. {
  224. Speak( VORT_EXTRACT_FINISH );
  225. }
  226. }
  227. //-----------------------------------------------------------------------------
  228. // Purpose:
  229. //-----------------------------------------------------------------------------
  230. void CNPC_Vortigaunt::StartTask( const Task_t *pTask )
  231. {
  232. switch ( pTask->iTask)
  233. {
  234. case TASK_ANNOUNCE_ATTACK:
  235. {
  236. // We override this to add our innate weapon
  237. if ( m_AnnounceAttackTimer.Expired() )
  238. {
  239. if ( SpeakIfAllowed( TLK_ATTACKING, "attacking_with_weapon:zap" ) )
  240. {
  241. m_AnnounceAttackTimer.Set( 10, 30 );
  242. }
  243. }
  244. BaseClass::StartTask( pTask );
  245. break;
  246. }
  247. // Sets our target to the entity that we cached earlier.
  248. case TASK_VORTIGAUNT_GET_HEAL_TARGET:
  249. {
  250. if ( m_hHealTarget == NULL )
  251. {
  252. TaskFail( FAIL_NO_TARGET );
  253. }
  254. else
  255. {
  256. SetTarget( m_hHealTarget );
  257. TaskComplete();
  258. }
  259. break;
  260. }
  261. case TASK_VORTIGAUNT_EXTRACT_WARMUP:
  262. {
  263. ResetIdealActivity( (Activity) ACT_VORTIGAUNT_TO_ACTION );
  264. break;
  265. }
  266. case TASK_VORTIGAUNT_EXTRACT:
  267. {
  268. SetActivity( (Activity) ACT_RANGE_ATTACK1 );
  269. break;
  270. }
  271. case TASK_VORTIGAUNT_EXTRACT_COOLDOWN:
  272. {
  273. ResetIdealActivity( (Activity)ACT_VORTIGAUNT_TO_IDLE );
  274. break;
  275. }
  276. case TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT:
  277. {
  278. // Cheat, and fire both outputs
  279. m_OnFinishedExtractingBugbait.FireOutput( this, this );
  280. TaskComplete();
  281. break;
  282. }
  283. case TASK_VORTIGAUNT_HEAL:
  284. {
  285. // Start the layer up and give it a higher priority than normal
  286. int nLayer = AddGesture( (Activity) ACT_VORTIGAUNT_HEAL );
  287. SetLayerPriority( nLayer, 1.0f );
  288. m_eHealState = HEAL_STATE_WARMUP;
  289. CBasePlayer *pPlayer = ToBasePlayer( m_hHealTarget );
  290. if ( pPlayer == NULL )
  291. {
  292. TaskFail( "NULL Player in heal schedule!\n" );
  293. return;
  294. }
  295. // Figure out how many tokens to spawn
  296. float flArmorDelta = (float) sk_vortigaunt_armor_charge.GetInt() - pPlayer->ArmorValue();
  297. m_nNumTokensToSpawn = ceil( flArmorDelta / sk_vortigaunt_armor_charge_per_token.GetInt() );
  298. // If we're forced to recharge, then at least send one
  299. if ( m_bForceArmorRecharge && m_nNumTokensToSpawn <= 0 )
  300. m_nNumTokensToSpawn = 1;
  301. TaskComplete();
  302. break;
  303. }
  304. case TASK_VORTIGAUNT_WAIT_FOR_PLAYER:
  305. {
  306. // Wait for the player to get near (before starting the bugbait sequence)
  307. break;
  308. }
  309. case TASK_VORTIGAUNT_DISPEL_ANTLIONS:
  310. {
  311. if ( IsOkToCombatSpeak() )
  312. {
  313. Speak( TLK_VORTIGAUNT_DISPEL );
  314. }
  315. ResetIdealActivity( (Activity) ACT_VORTIGAUNT_DISPEL );
  316. break;
  317. }
  318. default:
  319. {
  320. BaseClass::StartTask( pTask );
  321. break;
  322. }
  323. }
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Purpose:
  327. //-----------------------------------------------------------------------------
  328. void CNPC_Vortigaunt::RunTask( const Task_t *pTask )
  329. {
  330. switch ( pTask->iTask )
  331. {
  332. case TASK_RANGE_ATTACK1:
  333. {
  334. // If our enemy is gone, dead or out of sight, pick a new one (only if we're not delaying this behavior)
  335. if ( ( HasCondition( COND_ENEMY_OCCLUDED ) ||
  336. GetEnemy() == NULL ||
  337. GetEnemy()->IsAlive() == false ) &&
  338. m_flAimDelay < gpGlobals->curtime )
  339. {
  340. CBaseEntity *pNewEnemy = BestEnemy();
  341. if ( pNewEnemy != NULL )
  342. {
  343. SetEnemy( pNewEnemy );
  344. SetState( NPC_STATE_COMBAT );
  345. }
  346. }
  347. BaseClass::RunTask( pTask );
  348. break;
  349. }
  350. case TASK_VORTIGAUNT_EXTRACT_WARMUP:
  351. {
  352. if ( IsActivityFinished() )
  353. {
  354. TaskComplete();
  355. }
  356. break;
  357. }
  358. case TASK_VORTIGAUNT_EXTRACT:
  359. {
  360. if ( IsActivityFinished() )
  361. {
  362. TaskComplete();
  363. }
  364. break;
  365. }
  366. case TASK_VORTIGAUNT_EXTRACT_COOLDOWN:
  367. {
  368. if ( IsActivityFinished() )
  369. {
  370. m_bExtractingBugbait = false;
  371. TaskComplete();
  372. }
  373. break;
  374. }
  375. case TASK_VORTIGAUNT_WAIT_FOR_PLAYER:
  376. {
  377. // Wait for the player to get near (before starting the bugbait sequence)
  378. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  379. if ( pPlayer != NULL )
  380. {
  381. GetMotor()->SetIdealYawToTargetAndUpdate( pPlayer->GetAbsOrigin(), AI_KEEP_YAW_SPEED );
  382. SetTurnActivity();
  383. if ( GetMotor()->DeltaIdealYaw() < 10 )
  384. {
  385. // Wait for the player to get close enough
  386. if ( ( GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr() < Square(32*12) )
  387. {
  388. TaskComplete();
  389. }
  390. }
  391. }
  392. else
  393. {
  394. TaskFail( FAIL_NO_PLAYER );
  395. }
  396. break;
  397. }
  398. case TASK_VORTIGAUNT_DISPEL_ANTLIONS:
  399. {
  400. if ( IsSequenceFinished() )
  401. {
  402. TaskComplete();
  403. }
  404. break;
  405. }
  406. default:
  407. {
  408. BaseClass::RunTask( pTask );
  409. break;
  410. }
  411. }
  412. }
  413. //-----------------------------------------------------------------------------
  414. // Purpose:
  415. //-----------------------------------------------------------------------------
  416. void CNPC_Vortigaunt::AlertSound( void )
  417. {
  418. if ( GetEnemy() != NULL && IsOkToCombatSpeak() )
  419. {
  420. Speak( VORT_ATTACK );
  421. }
  422. }
  423. //-----------------------------------------------------------------------------
  424. // Purpose: Allows each sequence to have a different turn rate associated with it.
  425. // Output : float CNPC_Vortigaunt::MaxYawSpeed
  426. //-----------------------------------------------------------------------------
  427. float CNPC_Vortigaunt::MaxYawSpeed ( void )
  428. {
  429. switch ( GetActivity() )
  430. {
  431. case ACT_IDLE:
  432. return 35;
  433. break;
  434. case ACT_WALK:
  435. return 35;
  436. break;
  437. case ACT_RUN:
  438. return 45;
  439. break;
  440. default:
  441. return 35;
  442. break;
  443. }
  444. }
  445. //-----------------------------------------------------------------------------
  446. // Purpose: Normal facing position is the eyes, but with the vort eyes on such a
  447. // long swing arm, this causes stability issues when an npc is trying to
  448. // face a vort that's also turning their head
  449. // Output :
  450. //-----------------------------------------------------------------------------
  451. Vector CNPC_Vortigaunt::FacingPosition( void )
  452. {
  453. return WorldSpaceCenter();
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: Normal body target is the mid-point between the center and the eyes, but
  457. // the vort's eyes are so far offset, that this is usually in the middle of
  458. // empty space
  459. // Output :
  460. //-----------------------------------------------------------------------------
  461. Vector CNPC_Vortigaunt::BodyTarget( const Vector &posSrc, bool bNoisy )
  462. {
  463. Vector low = WorldSpaceCenter() - ( WorldSpaceCenter() - GetAbsOrigin() ) * .25;
  464. Vector high;
  465. int iBone = LookupBone( "ValveBiped.neck1" );
  466. if (iBone >= 0)
  467. {
  468. QAngle angHigh;
  469. GetBonePosition( iBone, high, angHigh );
  470. }
  471. else
  472. {
  473. high = WorldSpaceCenter();
  474. }
  475. Vector delta = high - low;
  476. Vector result;
  477. if ( bNoisy )
  478. {
  479. // bell curve
  480. float rand1 = random->RandomFloat( 0.0, 0.5 );
  481. float rand2 = random->RandomFloat( 0.0, 0.5 );
  482. result = low + delta * rand1 + delta * rand2;
  483. }
  484. else
  485. result = low + delta * 0.5;
  486. return result;
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Purpose: Try a more predictive approach
  490. //-----------------------------------------------------------------------------
  491. bool CNPC_Vortigaunt::InnateWeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions )
  492. {
  493. // Try and figure out a rough idea of where we'll be after a certain time delta and base our
  494. // conditions on that instead. This is necessary because the vortigaunt takes a long time to
  495. // deliver his attack and looks very strange if he starts to attack when he'd never be able to hit
  496. // due to movement.
  497. const float flTimeDelta = 0.5f;
  498. Vector vecNewOwnerPos;
  499. Vector vecNewTargetPos;
  500. UTIL_PredictedPosition( this, flTimeDelta, &vecNewOwnerPos );
  501. UTIL_PredictedPosition( GetEnemy(), flTimeDelta, &vecNewTargetPos );
  502. Vector vecDelta = vecNewTargetPos - GetEnemy()->GetAbsOrigin();
  503. Vector vecFinalTargetPos = GetEnemy()->BodyTarget( vecNewOwnerPos ) + vecDelta;
  504. // Debug data
  505. /*
  506. NDebugOverlay::Box( GetEnemy()->BodyTarget( vecNewOwnerPos ), -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, 0, 3.0f );
  507. NDebugOverlay::Box( vecFinalTargetPos, -Vector(4,4,4), Vector(4,4,4), 255, 255, 0, 0, 3.0f );
  508. NDebugOverlay::HorzArrow( GetEnemy()->BodyTarget( vecNewOwnerPos ), vecFinalTargetPos, 12.0f, 255, 0, 0, 16.0f, true, 3.0f );
  509. NDebugOverlay::HorzArrow( vecNewOwnerPos, GetEnemy()->BodyTarget( vecNewOwnerPos ), 8.0f, 255, 255, 0, 32.0f, true, 3.0f );
  510. */
  511. return BaseClass::InnateWeaponLOSCondition( vecNewOwnerPos, vecFinalTargetPos, bSetConditions );
  512. }
  513. //------------------------------------------------------------------------------
  514. // Purpose : For innate range attack
  515. //------------------------------------------------------------------------------
  516. int CNPC_Vortigaunt::RangeAttack1Conditions( float flDot, float flDist )
  517. {
  518. if ( GetEnemy() == NULL )
  519. return COND_NONE;
  520. if ( gpGlobals->curtime < m_flNextAttack )
  521. return COND_NONE;
  522. // Don't do shooting while playing a scene
  523. if ( IsCurSchedule( SCHED_SCENE_GENERIC ) )
  524. return COND_NONE;
  525. // dvs: Allow up-close range attacks for episodic as the vort's melee
  526. // attack is rather ineffective.
  527. #ifndef HL2_EPISODIC
  528. if ( flDist <= 70 )
  529. {
  530. return( COND_TOO_CLOSE_TO_ATTACK );
  531. }
  532. else
  533. #else
  534. if ( flDist < 32.0f )
  535. return COND_TOO_CLOSE_TO_ATTACK;
  536. #endif // HL2_EPISODIC
  537. if ( flDist > InnateRange1MaxRange() )
  538. {
  539. return( COND_TOO_FAR_TO_ATTACK );
  540. }
  541. else if ( flDot < 0.65 )
  542. {
  543. return( COND_NOT_FACING_ATTACK );
  544. }
  545. #ifdef HL2_EPISODIC
  546. // Do an extra check for workers near myself or the player
  547. if ( IsAntlionWorker( GetEnemy() ) )
  548. {
  549. // See if it's too close to me
  550. if ( ( GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).LengthSqr() < Square( AntlionWorkerBurstRadius() ) )
  551. return COND_TOO_CLOSE_TO_ATTACK;
  552. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  553. if ( pPlayer && ( pPlayer->GetAbsOrigin() - GetEnemy()->GetAbsOrigin() ).LengthSqr() < Square( AntlionWorkerBurstRadius() ) )
  554. {
  555. // Warn the player to get away!
  556. CFmtStrN<128> modifiers( "antlion_worker:true" );
  557. SpeakIfAllowed( TLK_DANGER, modifiers );
  558. return COND_NONE;
  559. }
  560. }
  561. #endif // HL2_EPISODIC
  562. return COND_CAN_RANGE_ATTACK1;
  563. }
  564. //-----------------------------------------------------------------------------
  565. // Purpose: Test for close-up dispel
  566. //-----------------------------------------------------------------------------
  567. int CNPC_Vortigaunt::MeleeAttack1Conditions( float flDot, float flDist )
  568. {
  569. if ( m_flDispelTestTime > gpGlobals->curtime )
  570. return COND_NONE;
  571. m_flDispelTestTime = gpGlobals->curtime + 1.0f;
  572. if ( GetEnemy() && GetEnemy()->Classify() == CLASS_ANTLION )
  573. {
  574. if ( NumAntlionsInRadius(128) > 3 )
  575. {
  576. m_flDispelTestTime = gpGlobals->curtime + 15.0f;
  577. return COND_VORTIGAUNT_DISPEL_ANTLIONS;
  578. }
  579. }
  580. return COND_NONE;
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose:
  584. // Input : flRadius -
  585. // Output : int
  586. //-----------------------------------------------------------------------------
  587. int CNPC_Vortigaunt::NumAntlionsInRadius( float flRadius )
  588. {
  589. CBaseEntity *sEnemySearch[16];
  590. int nNumAntlions = 0;
  591. int nNumEnemies = UTIL_EntitiesInBox( sEnemySearch, ARRAYSIZE(sEnemySearch), GetAbsOrigin()-Vector(flRadius,flRadius,flRadius), GetAbsOrigin()+Vector(flRadius,flRadius,flRadius), FL_NPC );
  592. for ( int i = 0; i < nNumEnemies; i++ )
  593. {
  594. // We only care about antlions
  595. if ( sEnemySearch[i] == NULL || sEnemySearch[i]->Classify() != CLASS_ANTLION )
  596. continue;
  597. nNumAntlions++;
  598. }
  599. return nNumAntlions;
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose: Used for a more powerful, concussive blast
  603. //-----------------------------------------------------------------------------
  604. int CNPC_Vortigaunt::RangeAttack2Conditions( float flDot, float flDist )
  605. {
  606. return COND_NONE;
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose:
  610. //-----------------------------------------------------------------------------
  611. void CNPC_Vortigaunt::HandleAnimEvent( animevent_t *pEvent )
  612. {
  613. // Start our heal glows (choreo driven)
  614. if ( pEvent->event == AE_VORTIGAUNT_START_HEAL_GLOW )
  615. {
  616. StartHandGlow( VORTIGAUNT_BEAM_HEAL, atoi( pEvent->options ) );
  617. return;
  618. }
  619. // Stop our heal glows (choreo driven)
  620. if ( pEvent->event == AE_VORTIGAUNT_STOP_HEAL_GLOW )
  621. {
  622. EndHandGlow();
  623. return;
  624. }
  625. // Start our hurt glows (choreo driven)
  626. if ( pEvent->event == AE_VORTIGAUNT_START_HURT_GLOW )
  627. {
  628. StartHandGlow( VORTIGAUNT_BEAM_DISPEL, atoi( pEvent->options ) );
  629. return;
  630. }
  631. // Stop our hurt glows (choreo driven)
  632. if ( pEvent->event == AE_VORTIGAUNT_STOP_HURT_GLOW )
  633. {
  634. EndHandGlow();
  635. return;
  636. }
  637. // Start our dispel effect
  638. if ( pEvent->event == AE_VORTIGAUNT_START_DISPEL )
  639. {
  640. StartHandGlow( VORTIGAUNT_BEAM_DISPEL, HAND_LEFT );
  641. StartHandGlow( VORTIGAUNT_BEAM_DISPEL, HAND_RIGHT );
  642. // Boom!
  643. //EmitSound( "NPC_Vortigaunt.DispelImpact" );
  644. CSoundParameters params;
  645. if ( GetParametersForSound( "NPC_Vortigaunt.DispelImpact", params, NULL ) )
  646. {
  647. CPASAttenuationFilter filter( this );
  648. EmitSound_t ep( params );
  649. ep.m_nChannel = CHAN_BODY;
  650. EmitSound( filter, entindex(), ep );
  651. }
  652. return;
  653. }
  654. if ( pEvent->event == AE_VORTIGAUNT_ACCEL_DISPEL )
  655. {
  656. // TODO: Increase the size?
  657. return;
  658. }
  659. // Kaboom!
  660. if ( pEvent->event == AE_VORTIGAUNT_DISPEL )
  661. {
  662. DispelAntlions( GetAbsOrigin(), 400.0f );
  663. return;
  664. }
  665. // Start of our heal loop
  666. if ( pEvent->event == AE_VORTIGAUNT_HEAL_PAUSE )
  667. {
  668. StartHealing();
  669. return;
  670. }
  671. if ( pEvent->event == AE_VORTIGAUNT_ZAP_POWERUP )
  672. {
  673. if ( m_fGlowChangeTime > gpGlobals->curtime )
  674. return;
  675. int nHand = 0;
  676. if ( pEvent->options )
  677. {
  678. nHand = atoi( pEvent->options );
  679. }
  680. if ( ( nHand == HAND_LEFT ) || (nHand == HAND_BOTH ) )
  681. {
  682. ArmBeam( VORTIGAUNT_BEAM_ZAP, HAND_LEFT );
  683. }
  684. if ( ( nHand == HAND_RIGHT ) || (nHand == HAND_BOTH ) )
  685. {
  686. ArmBeam( VORTIGAUNT_BEAM_ZAP, HAND_RIGHT );
  687. }
  688. // Make hands glow if not already glowing
  689. if ( m_fGlowAge == 0 )
  690. {
  691. if ( ( nHand == HAND_LEFT ) || (nHand == HAND_BOTH ) )
  692. {
  693. StartHandGlow( VORTIGAUNT_BEAM_ZAP, HAND_LEFT );
  694. }
  695. if ( ( nHand == HAND_RIGHT ) || (nHand == HAND_BOTH ) )
  696. {
  697. StartHandGlow( VORTIGAUNT_BEAM_ZAP, HAND_RIGHT );
  698. }
  699. m_fGlowAge = 1;
  700. }
  701. CPASAttenuationFilter filter( this );
  702. CSoundParameters params;
  703. if ( GetParametersForSound( "NPC_Vortigaunt.ZapPowerup", params, NULL ) )
  704. {
  705. EmitSound_t ep( params );
  706. //ep.m_nPitch = 100 + m_iBeams * 10;
  707. ep.m_nPitch = 150;
  708. EmitSound( filter, entindex(), ep );
  709. m_bStopLoopingSounds = true;
  710. }
  711. return;
  712. }
  713. if ( pEvent->event == AE_VORTIGAUNT_ZAP_SHOOT )
  714. {
  715. ClearBeams();
  716. ClearMultiDamage();
  717. int nHand = 0;
  718. if ( pEvent->options )
  719. {
  720. nHand = atoi( pEvent->options );
  721. }
  722. if ( ( nHand == HAND_LEFT ) || (nHand == HAND_BOTH ) )
  723. {
  724. ZapBeam( HAND_LEFT );
  725. }
  726. if ( ( nHand == HAND_RIGHT ) || (nHand == HAND_BOTH ) )
  727. {
  728. ZapBeam( HAND_RIGHT );
  729. }
  730. EndHandGlow();
  731. EmitSound( "NPC_Vortigaunt.ClawBeam" );
  732. m_bStopLoopingSounds = true;
  733. ApplyMultiDamage();
  734. // Suppress our aiming until we're done with the animation
  735. m_flAimDelay = gpGlobals->curtime + 0.75f;
  736. if ( m_bExtractingBugbait )
  737. {
  738. // Spawn bugbait!
  739. CBaseCombatWeapon *pWeapon = Weapon_Create( "weapon_bugbait" );
  740. if ( pWeapon )
  741. {
  742. // Starting above the body, spawn closer and closer to the vort until it's clear
  743. Vector vecSpawnOrigin = GetTarget()->WorldSpaceCenter() + Vector(0, 0, 32);
  744. int iNumAttempts = 4;
  745. Vector vecToVort = (WorldSpaceCenter() - vecSpawnOrigin);
  746. float flDistance = VectorNormalize( vecToVort ) / (iNumAttempts-1);
  747. int i = 0;
  748. for (; i < iNumAttempts; i++ )
  749. {
  750. trace_t tr;
  751. CTraceFilterSkipTwoEntities traceFilter( GetTarget(), this, COLLISION_GROUP_NONE );
  752. AI_TraceLine( vecSpawnOrigin, vecSpawnOrigin, MASK_SHOT, &traceFilter, &tr );
  753. if ( tr.fraction == 1.0 && !tr.m_pEnt )
  754. {
  755. // Make sure it can fit there
  756. AI_TraceHull( vecSpawnOrigin, vecSpawnOrigin, -Vector(16,16,16), Vector(16,16,48), MASK_SHOT, &traceFilter, &tr );
  757. if ( tr.fraction == 1.0 && !tr.m_pEnt )
  758. break;
  759. }
  760. //NDebugOverlay::Box( vecSpawnOrigin, pWeapon->WorldAlignMins(), pWeapon->WorldAlignMins(), 255,0,0, 64, 100 );
  761. // Move towards the vort
  762. vecSpawnOrigin = vecSpawnOrigin + (vecToVort * flDistance);
  763. }
  764. // HACK: If we've still failed, just spawn it on the player
  765. if ( i == iNumAttempts )
  766. {
  767. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  768. if ( pPlayer )
  769. {
  770. vecSpawnOrigin = pPlayer->WorldSpaceCenter();
  771. }
  772. }
  773. //NDebugOverlay::Box( vecSpawnOrigin, -Vector(20,20,20), Vector(20,20,20), 0,255,0, 64, 100 );
  774. pWeapon->SetAbsOrigin( vecSpawnOrigin );
  775. pWeapon->Drop( Vector(0,0,1) );
  776. }
  777. CEffectData data;
  778. data.m_vOrigin = GetTarget()->WorldSpaceCenter();
  779. data.m_vNormal = WorldSpaceCenter() - GetTarget()->WorldSpaceCenter();
  780. VectorNormalize( data.m_vNormal );
  781. data.m_flScale = 4;
  782. DispatchEffect( "AntlionGib", data );
  783. }
  784. // Stagger the next time we can attack
  785. m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 2.0f, 3.0f );
  786. return;
  787. }
  788. if ( pEvent->event == AE_VORTIGAUNT_ZAP_DONE )
  789. {
  790. ClearBeams();
  791. return;
  792. }
  793. if ( pEvent->event == AE_VORTIGAUNT_HEAL_STARTGLOW )
  794. {
  795. // Make hands glow
  796. StartHandGlow( VORTIGAUNT_BEAM_HEAL, HAND_RIGHT );
  797. m_eHealState = HEAL_STATE_WARMUP;
  798. return;
  799. }
  800. if ( pEvent->event == AE_VORTIGAUNT_HEAL_STARTSOUND )
  801. {
  802. CPASAttenuationFilter filter( this );
  803. CSoundParameters params;
  804. if ( GetParametersForSound( "NPC_Vortigaunt.StartHealLoop", params, NULL ) )
  805. {
  806. EmitSound_t ep( params );
  807. //ep.m_nPitch = 100 + m_iBeams * 10;
  808. ep.m_nPitch = 150;
  809. EmitSound( filter, entindex(), ep );
  810. m_bStopLoopingSounds = true;
  811. }
  812. return;
  813. }
  814. if ( pEvent->event == AE_VORTIGAUNT_SWING_SOUND )
  815. {
  816. EmitSound( "NPC_Vortigaunt.Swing" );
  817. return;
  818. }
  819. if ( pEvent->event == AE_VORTIGAUNT_SHOOT_SOUNDSTART )
  820. {
  821. if ( m_fGlowChangeTime > gpGlobals->curtime )
  822. return;
  823. CPASAttenuationFilter filter( this );
  824. CSoundParameters params;
  825. if ( GetParametersForSound( "NPC_Vortigaunt.StartShootLoop", params, NULL ) )
  826. {
  827. EmitSound_t ep( params );
  828. //ep.m_nPitch = 100 + m_iBeams * 10;
  829. ep.m_nPitch = 150;
  830. EmitSound( filter, entindex(), ep );
  831. m_bStopLoopingSounds = true;
  832. }
  833. return;
  834. }
  835. if ( pEvent->event == AE_NPC_LEFTFOOT )
  836. {
  837. EmitSound( "NPC_Vortigaunt.FootstepLeft", pEvent->eventtime );
  838. return;
  839. }
  840. if ( pEvent->event == AE_NPC_RIGHTFOOT )
  841. {
  842. EmitSound( "NPC_Vortigaunt.FootstepRight", pEvent->eventtime );
  843. return;
  844. }
  845. BaseClass::HandleAnimEvent( pEvent );
  846. }
  847. //------------------------------------------------------------------------------
  848. // Purpose : Turn blue or green
  849. //------------------------------------------------------------------------------
  850. void CNPC_Vortigaunt::InputTurnBlue( inputdata_t &data )
  851. {
  852. bool goBlue = data.value.Bool();
  853. if (goBlue != m_bIsBlue)
  854. {
  855. m_bIsBlue = goBlue;
  856. m_flBlueEndFadeTime = gpGlobals->curtime + VORTIGAUNT_BLUE_FADE_TIME;
  857. }
  858. }
  859. //------------------------------------------------------------------------------
  860. // Purpose : Turn blue or green
  861. //------------------------------------------------------------------------------
  862. void CNPC_Vortigaunt::InputTurnBlack( inputdata_t &data )
  863. {
  864. bool goBlack = data.value.Bool();
  865. if (goBlack != m_bIsBlack)
  866. {
  867. m_bIsBlack = goBlack;
  868. }
  869. }
  870. //------------------------------------------------------------------------------
  871. // Purpose : Translate some activites for the Vortigaunt
  872. //------------------------------------------------------------------------------
  873. Activity CNPC_Vortigaunt::NPC_TranslateActivity( Activity eNewActivity )
  874. {
  875. // This is a hack solution for the Vort carrying Alyx in Ep2
  876. if ( IsCarryingNPC() )
  877. {
  878. if ( eNewActivity == ACT_IDLE )
  879. return ACT_IDLE_CARRY;
  880. if ( eNewActivity == ACT_WALK || eNewActivity == ACT_WALK_AIM || eNewActivity == ACT_RUN || eNewActivity == ACT_RUN_AIM )
  881. return ACT_WALK_CARRY;
  882. }
  883. // NOTE: This is a stand-in until the readiness system can handle non-weapon holding NPC's
  884. if ( eNewActivity == ACT_IDLE )
  885. {
  886. // More than relaxed means we're stimulated
  887. if ( GetReadinessLevel() >= AIRL_STIMULATED )
  888. return ACT_IDLE_STIMULATED;
  889. }
  890. if ( eNewActivity == ACT_RANGE_ATTACK2 )
  891. return (Activity) ACT_VORTIGAUNT_DISPEL;
  892. return BaseClass::NPC_TranslateActivity( eNewActivity );
  893. }
  894. //-----------------------------------------------------------------------------
  895. // Purpose:
  896. //-----------------------------------------------------------------------------
  897. void CNPC_Vortigaunt::UpdateOnRemove( void)
  898. {
  899. ClearBeams();
  900. ClearHandGlow();
  901. // Chain at end to mimic destructor unwind order
  902. BaseClass::UpdateOnRemove();
  903. }
  904. //------------------------------------------------------------------------------
  905. // Purpose :
  906. //------------------------------------------------------------------------------
  907. void CNPC_Vortigaunt::Event_Killed( const CTakeDamageInfo &info )
  908. {
  909. ClearBeams();
  910. ClearHandGlow();
  911. BaseClass::Event_Killed( info );
  912. }
  913. //-----------------------------------------------------------------------------
  914. // Purpose:
  915. //-----------------------------------------------------------------------------
  916. void CNPC_Vortigaunt::Spawn( void )
  917. {
  918. #if !defined( HL2_EPISODIC )
  919. // Disable back-away
  920. AddSpawnFlags( SF_NPC_NO_PLAYER_PUSHAWAY );
  921. #endif // HL2_EPISODIC
  922. // Allow multiple models (for slaves), but default to vortigaunt.mdl
  923. char *szModel = (char *)STRING( GetModelName() );
  924. if (!szModel || !*szModel)
  925. {
  926. szModel = "models/vortigaunt.mdl";
  927. SetModelName( AllocPooledString(szModel) );
  928. }
  929. BaseClass::Spawn();
  930. m_HackedGunPos.x = 0.0f;
  931. m_HackedGunPos.y = 0.0f;
  932. m_HackedGunPos.z = 48.0f;
  933. SetHullType( HULL_WIDE_HUMAN );
  934. SetHullSizeNormal();
  935. m_bloodColor = BLOOD_COLOR_GREEN;
  936. m_iHealth = sk_vortigaunt_health.GetFloat();
  937. SetViewOffset( Vector ( 0, 0, 64 ) );// position of the eyes relative to monster's origin.
  938. CapabilitiesAdd( bits_CAP_INNATE_MELEE_ATTACK1 | bits_CAP_INNATE_RANGE_ATTACK1 );
  939. CapabilitiesRemove( bits_CAP_USE_SHOT_REGULATOR );
  940. m_flEyeIntegRate = 0.6f; // Got a big eyeball so turn it slower
  941. m_bForceArmorRecharge = false;
  942. m_flHealHinderedTime = 0.0f;
  943. m_nCurGlowIndex = 0;
  944. m_bStopLoopingSounds = false;
  945. m_iLeftHandAttachment = LookupAttachment( VORTIGAUNT_LEFT_CLAW );
  946. m_iRightHandAttachment = LookupAttachment( VORTIGAUNT_RIGHT_CLAW );
  947. NPCInit();
  948. SetUse( &CNPC_Vortigaunt::Use );
  949. // Setup our re-fire times when moving and shooting
  950. GetShotRegulator()->SetBurstInterval( 2.0f, 2.0f );
  951. GetShotRegulator()->SetBurstShotCountRange( 1, 1 );
  952. GetShotRegulator()->SetRestInterval( 2.0f, 2.0f );
  953. }
  954. //-----------------------------------------------------------------------------
  955. // Purpose:
  956. //-----------------------------------------------------------------------------
  957. void CNPC_Vortigaunt::Precache()
  958. {
  959. UTIL_PrecacheOther( "vort_charge_token" );
  960. PrecacheModel( STRING( GetModelName() ) );
  961. m_nLightningSprite = PrecacheModel("sprites/lgtning.vmt");
  962. PrecacheModel("sprites/vortring1.vmt");
  963. // HACK: Only precache this for EP2 because reslists cannot be rebuilt - 08/22/07 - jdw
  964. if ( hl2_episodic.GetBool() )
  965. {
  966. char modDir[MAX_PATH];
  967. if ( UTIL_GetModDir( modDir, sizeof(modDir) ) )
  968. {
  969. if ( !Q_stricmp( modDir, "ep2" ) )
  970. {
  971. PrecacheMaterial( "effects/rollerglow" );
  972. }
  973. }
  974. }
  975. PrecacheScriptSound( "NPC_Vortigaunt.SuitOn" );
  976. PrecacheScriptSound( "NPC_Vortigaunt.SuitCharge" );
  977. PrecacheScriptSound( "NPC_Vortigaunt.ZapPowerup" );
  978. PrecacheScriptSound( "NPC_Vortigaunt.ClawBeam" );
  979. PrecacheScriptSound( "NPC_Vortigaunt.StartHealLoop" );
  980. PrecacheScriptSound( "NPC_Vortigaunt.Swing" );
  981. PrecacheScriptSound( "NPC_Vortigaunt.StartShootLoop" );
  982. PrecacheScriptSound( "NPC_Vortigaunt.FootstepLeft" );
  983. PrecacheScriptSound( "NPC_Vortigaunt.FootstepRight" );
  984. PrecacheScriptSound( "NPC_Vortigaunt.DispelStart" );
  985. PrecacheScriptSound( "NPC_Vortigaunt.DispelImpact" );
  986. PrecacheScriptSound( "NPC_Vortigaunt.Explode" );
  987. PrecacheParticleSystem( "vortigaunt_beam" );
  988. PrecacheParticleSystem( "vortigaunt_beam_charge" );
  989. PrecacheParticleSystem( "vortigaunt_hand_glow" );
  990. PrecacheMaterial( "sprites/light_glow02_add" );
  991. BaseClass::Precache();
  992. }
  993. //-----------------------------------------------------------------------------
  994. // Purpose: Interpret a player +USE'ing us
  995. //-----------------------------------------------------------------------------
  996. void CNPC_Vortigaunt::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
  997. {
  998. m_OnPlayerUse.FireOutput( pActivator, pCaller );
  999. // Foremost, try and heal a wounded player
  1000. if ( HealBehaviorAvailable() )
  1001. {
  1002. // See if we should heal the player
  1003. CBaseEntity *pHealTarget = FindHealTarget();
  1004. if ( pHealTarget != NULL )
  1005. {
  1006. SetCondition( COND_PROVOKED );
  1007. SetHealTarget( pHealTarget, true );
  1008. return;
  1009. }
  1010. }
  1011. // Next, try to speak the +USE concept
  1012. if ( IsOkToSpeakInResponseToPlayer() && m_eHealState == HEAL_STATE_NONE )
  1013. {
  1014. if ( Speak( TLK_USE ) == false )
  1015. {
  1016. // If we haven't said hi, say that first
  1017. if ( !SpokeConcept( TLK_HELLO ) )
  1018. {
  1019. Speak( TLK_HELLO );
  1020. }
  1021. else
  1022. {
  1023. Speak( TLK_IDLE );
  1024. }
  1025. }
  1026. else
  1027. {
  1028. // Don't say hi after you've said your +USE speech
  1029. SetSpokeConcept( TLK_HELLO, NULL );
  1030. }
  1031. }
  1032. }
  1033. //=========================================================
  1034. // PainSound
  1035. //=========================================================
  1036. void CNPC_Vortigaunt::PainSound( const CTakeDamageInfo &info )
  1037. {
  1038. if ( gpGlobals->curtime < m_flPainTime )
  1039. return;
  1040. m_flPainTime = gpGlobals->curtime + random->RandomFloat(0.5, 0.75);
  1041. Speak( VORT_PAIN );
  1042. }
  1043. //=========================================================
  1044. // DeathSound
  1045. //=========================================================
  1046. void CNPC_Vortigaunt::DeathSound( const CTakeDamageInfo &info )
  1047. {
  1048. Speak( VORT_DIE );
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. // Purpose:
  1052. //-----------------------------------------------------------------------------
  1053. void CNPC_Vortigaunt::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  1054. {
  1055. CTakeDamageInfo info = inputInfo;
  1056. if ( (info.GetDamageType() & DMG_SHOCK) && FClassnameIs( info.GetAttacker(), GetClassname() ) )
  1057. {
  1058. // mask off damage from other vorts for now
  1059. info.SetDamage( 0.01 );
  1060. }
  1061. switch( ptr->hitgroup)
  1062. {
  1063. case HITGROUP_CHEST:
  1064. case HITGROUP_STOMACH:
  1065. if (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST))
  1066. {
  1067. info.ScaleDamage( 0.5f );
  1068. }
  1069. break;
  1070. case 10:
  1071. if (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_CLUB))
  1072. {
  1073. info.SetDamage( info.GetDamage() - 20 );
  1074. if (info.GetDamage() <= 0)
  1075. {
  1076. g_pEffects->Ricochet( ptr->endpos, (vecDir*-1.0f) );
  1077. info.SetDamage( 0.01 );
  1078. }
  1079. }
  1080. // always a head shot
  1081. ptr->hitgroup = HITGROUP_HEAD;
  1082. break;
  1083. }
  1084. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. // Purpose:
  1088. //-----------------------------------------------------------------------------
  1089. int CNPC_Vortigaunt::TranslateSchedule( int scheduleType )
  1090. {
  1091. switch( scheduleType )
  1092. {
  1093. case SCHED_ALERT_FACE_BESTSOUND:
  1094. return SCHED_VORT_ALERT_FACE_BESTSOUND;
  1095. break;
  1096. case SCHED_TAKE_COVER_FROM_BEST_SOUND:
  1097. // Stand still if we're in the middle of an attack. Failing to do so can make us miss our shot!
  1098. if ( IsPlayingGesture( ACT_GESTURE_RANGE_ATTACK1 ) )
  1099. return SCHED_COMBAT_FACE;
  1100. return SCHED_VORT_FLEE_FROM_BEST_SOUND;
  1101. break;
  1102. case SCHED_COWER:
  1103. case SCHED_PC_COWER:
  1104. // Vort doesn't have cower animations
  1105. return SCHED_COMBAT_FACE;
  1106. break;
  1107. case SCHED_RANGE_ATTACK1:
  1108. // If we're told to fire when we're already firing, just face our target. If we don't do this, we get a bizarre double-shot
  1109. if ( IsPlayingGesture( ACT_GESTURE_RANGE_ATTACK1 ) )
  1110. return SCHED_COMBAT_FACE;
  1111. // Otherwise we use our own schedule to attack
  1112. return SCHED_VORTIGAUNT_RANGE_ATTACK;
  1113. break;
  1114. /*
  1115. case SCHED_CHASE_ENEMY:
  1116. case SCHED_ESTABLISH_LINE_OF_FIRE:
  1117. case SCHED_ESTABLISH_LINE_OF_FIRE_FALLBACK:
  1118. // Don't go running off after an enemy just because we're in an attack delay! This has to do with
  1119. // the base systems assuming that held weapons are driving certain decisions when this creature
  1120. // uses an innate ability.
  1121. if ( ( GetNextAttack() > gpGlobals->curtime ) && HasCondition( COND_ENEMY_TOO_FAR ) == false )
  1122. return SCHED_COMBAT_FACE;
  1123. break;
  1124. */
  1125. }
  1126. return BaseClass::TranslateSchedule( scheduleType );
  1127. }
  1128. //-----------------------------------------------------------------------------
  1129. // Purpose: Sets the heal target for the vort and preps him for completing the action
  1130. // Input : *pTarget - Target we're after
  1131. //-----------------------------------------------------------------------------
  1132. void CNPC_Vortigaunt::SetHealTarget( CBaseEntity *pTarget, bool bPlayerRequested )
  1133. {
  1134. SetCondition( COND_VORTIGAUNT_CAN_HEAL );
  1135. OccupyStrategySlot( SQUAD_SLOT_HEAL_PLAYER );
  1136. m_hHealTarget = pTarget;
  1137. m_bPlayerRequestedHeal = bPlayerRequested;
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. // Purpose: Finds a player in range that can be healed
  1141. // Output : Target that can be healed
  1142. //-----------------------------------------------------------------------------
  1143. CBaseEntity *CNPC_Vortigaunt::FindHealTarget( void )
  1144. {
  1145. // Need to be looking at the player to decide to heal them.
  1146. //if ( HasCondition( COND_SEE_PLAYER ) == false )
  1147. // return false;
  1148. // Find a likely target in range
  1149. CBaseEntity *pEntity = PlayerInRange( GetAbsOrigin(), HEAL_SEARCH_RANGE );
  1150. // Make sure we can heal that target
  1151. if ( ShouldHealTarget( pEntity ) == false )
  1152. return NULL;
  1153. return pEntity;
  1154. }
  1155. //-----------------------------------------------------------------------------
  1156. // Purpose: Whether or not the vort is able to attempt to heal targets
  1157. // Output : Returns true on success, false on failure.
  1158. //-----------------------------------------------------------------------------
  1159. bool CNPC_Vortigaunt::HealBehaviorAvailable( void )
  1160. {
  1161. // Cannot already be healing
  1162. if ( m_eHealState != HEAL_STATE_NONE )
  1163. return false;
  1164. // Must be allowed to do this behavior
  1165. if ( m_bArmorRechargeEnabled == false )
  1166. return false;
  1167. // Don't interrupt a script
  1168. if ( IsInAScript() || m_NPCState == NPC_STATE_SCRIPT )
  1169. return false;
  1170. // Cannot interrupt bugbait extraction
  1171. if ( IsCurSchedule( SCHED_VORTIGAUNT_EXTRACT_BUGBAIT ) )
  1172. return false;
  1173. // Don't bother while we're under attack
  1174. if ( GetEnemy() != NULL )
  1175. return false;
  1176. // Can't heal if we're leading the player
  1177. if ( IsLeading() )
  1178. return false;
  1179. // Must be a valid squad activity to do
  1180. if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_HEAL_PLAYER, SQUAD_SLOT_HEAL_PLAYER ) )
  1181. return false;
  1182. // Heal is valid
  1183. return true;
  1184. }
  1185. //-----------------------------------------------------------------------------
  1186. // Purpose: Determines whether or not the
  1187. // Output : Returns true on success, false on failure.
  1188. //-----------------------------------------------------------------------------
  1189. bool CNPC_Vortigaunt::ShouldHealTarget( CBaseEntity *pTarget )
  1190. {
  1191. // Must have a valid target
  1192. if ( pTarget == NULL )
  1193. return false;
  1194. // If we're scripting or waiting to run one, we won't heal a target
  1195. if ( IsInAScript() || HasSpawnFlags( SF_NPC_WAIT_FOR_SCRIPT ) )
  1196. return false;
  1197. // We only heal players
  1198. CBasePlayer *pPlayer = ToBasePlayer( pTarget );
  1199. if ( pPlayer == NULL )
  1200. return false;
  1201. // Make sure the player's got a suit
  1202. if ( pPlayer->IsSuitEquipped() == false )
  1203. return false;
  1204. // Don't heal a target we can't see..?
  1205. if ( pPlayer->GetFlags() & FL_NOTARGET )
  1206. return false;
  1207. // See if the player needs armor
  1208. if ( pPlayer->ArmorValue() >= (sk_vortigaunt_armor_charge.GetFloat()*0.66f) )
  1209. return false;
  1210. // Must be alive!
  1211. if ( pPlayer->IsAlive() == false )
  1212. return false;
  1213. // Only consider things in here if the player is NOT at critical health or the heal is a passive one (not requested)
  1214. if ( PlayerBelowHealthPercentage( pPlayer, PLAYER_CRITICAL_HEALTH_PERC ) == false || m_bPlayerRequestedHeal )
  1215. {
  1216. // Don't heal when fighting
  1217. if ( m_NPCState == NPC_STATE_COMBAT )
  1218. return false;
  1219. // No enemies
  1220. if ( GetEnemy() )
  1221. return false;
  1222. // No recent damage
  1223. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  1224. return false;
  1225. }
  1226. // Allow the heal
  1227. return true;
  1228. }
  1229. //-----------------------------------------------------------------------------
  1230. // Purpose:
  1231. // Output : int
  1232. //-----------------------------------------------------------------------------
  1233. int CNPC_Vortigaunt::SelectHealSchedule( void )
  1234. {
  1235. // If our lead behavior has a goal, don't wait around to heal anyone
  1236. if ( m_LeadBehavior.HasGoal() )
  1237. return SCHED_NONE;
  1238. // Break out of healing if a script has started
  1239. if ( IsInAScript() && m_bForceArmorRecharge == false )
  1240. {
  1241. if ( m_eHealState != HEAL_STATE_NONE )
  1242. {
  1243. StopHealing( true );
  1244. }
  1245. return SCHED_NONE;
  1246. }
  1247. // Cannot already be healing the player
  1248. if ( m_hHealTarget != NULL )
  1249. {
  1250. // For now, just grab the global, single player
  1251. CBasePlayer *pPlayer = ToBasePlayer( m_hHealTarget );
  1252. // Check for an interruption occurring
  1253. if ( PlayerBelowHealthPercentage( pPlayer, PLAYER_CRITICAL_HEALTH_PERC ) == false && HasCondition( COND_HEAVY_DAMAGE ) )
  1254. {
  1255. StopHealing( true );
  1256. return SCHED_NONE;
  1257. }
  1258. // See if we're in an ideal position to heal
  1259. if ( m_eHealState != HEAL_STATE_HEALING && m_eHealState != HEAL_STATE_WARMUP && HasCondition( COND_VORTIGAUNT_HEAL_VALID ) )
  1260. return SCHED_VORTIGAUNT_HEAL;
  1261. // If the player is too far away or blocked, give chase
  1262. if ( HasCondition( COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR ) ||
  1263. HasCondition( COND_VORTIGAUNT_HEAL_TARGET_BLOCKED ) )
  1264. return SCHED_VORTIGAUNT_RUN_TO_PLAYER;
  1265. // Stand and face the player
  1266. if ( HasCondition( COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US ) || HasCondition( COND_VORTIGAUNT_HEAL_VALID ) )
  1267. return SCHED_VORTIGAUNT_FACE_PLAYER;
  1268. }
  1269. return SCHED_NONE;
  1270. }
  1271. //-----------------------------------------------------------------------------
  1272. // Purpose: Watch this function path for a route around our normal schedule changing callbacks
  1273. //-----------------------------------------------------------------------------
  1274. void CNPC_Vortigaunt::ClearSchedule( const char *szReason )
  1275. {
  1276. MaintainGlows();
  1277. BaseClass::ClearSchedule( szReason );
  1278. }
  1279. //-----------------------------------------------------------------------------
  1280. // Purpose: Watch our glows and turn them off appropriately
  1281. //-----------------------------------------------------------------------------
  1282. void CNPC_Vortigaunt::OnScheduleChange( void )
  1283. {
  1284. BaseClass::OnScheduleChange();
  1285. // If we're in the middle of healing, don't bother doing this
  1286. if ( m_eHealState != HEAL_STATE_NONE )
  1287. return;
  1288. // If we're changing sequences, always clear
  1289. EndHandGlow( VORTIGAUNT_BEAM_ALL );
  1290. m_fGlowChangeTime = gpGlobals->curtime + 0.1f; // No more glows for this amount of time!
  1291. }
  1292. //------------------------------------------------------------------------------
  1293. // Purpose: Select a schedule
  1294. //------------------------------------------------------------------------------
  1295. int CNPC_Vortigaunt::SelectSchedule( void )
  1296. {
  1297. // Always recharge in this case
  1298. if ( m_bForceArmorRecharge )
  1299. {
  1300. m_flNextHealTime = 0;
  1301. int nSchedule = SelectHealSchedule();
  1302. return nSchedule;
  1303. }
  1304. #ifndef HL2_EPISODIC
  1305. if ( BehaviorSelectSchedule() )
  1306. return BaseClass::SelectSchedule();
  1307. #else
  1308. if ( IsInAScript() )
  1309. return BaseClass::SelectSchedule();
  1310. #endif
  1311. // If we're currently supposed to be doing something scripted, do it immediately.
  1312. if ( m_bExtractingBugbait )
  1313. return SCHED_VORTIGAUNT_EXTRACT_BUGBAIT;
  1314. int schedule = SelectHealSchedule();
  1315. if ( schedule != SCHED_NONE )
  1316. return schedule;
  1317. if ( HasCondition(COND_VORTIGAUNT_DISPEL_ANTLIONS ) )
  1318. {
  1319. ClearCondition( COND_VORTIGAUNT_DISPEL_ANTLIONS );
  1320. return SCHED_VORTIGAUNT_DISPEL_ANTLIONS;
  1321. }
  1322. // Heal a player if they can be
  1323. if ( HasCondition( COND_VORTIGAUNT_CAN_HEAL ) )
  1324. return SCHED_VORTIGAUNT_HEAL;
  1325. return BaseClass::SelectSchedule();
  1326. }
  1327. //-----------------------------------------------------------------------------
  1328. //
  1329. //-----------------------------------------------------------------------------
  1330. int CNPC_Vortigaunt::SelectFailSchedule( int failedSchedule, int failedTask, AI_TaskFailureCode_t taskFailCode )
  1331. {
  1332. if ( failedSchedule == SCHED_BACK_AWAY_FROM_ENEMY )
  1333. {
  1334. if ( GetEnemy() && GetSenses()->CanSeeEntity( GetEnemy() ) )
  1335. {
  1336. return SCHED_RANGE_ATTACK1;
  1337. }
  1338. }
  1339. return BaseClass::SelectFailSchedule( failedSchedule, failedTask, taskFailCode );
  1340. }
  1341. //-----------------------------------------------------------------------------
  1342. // Purpose:
  1343. //-----------------------------------------------------------------------------
  1344. void CNPC_Vortigaunt::DeclineFollowing( void )
  1345. {
  1346. Speak( VORT_POK );
  1347. }
  1348. //-----------------------------------------------------------------------------
  1349. // Purpose: Return true if you're willing to be idly talked to by other friends.
  1350. //-----------------------------------------------------------------------------
  1351. bool CNPC_Vortigaunt::CanBeUsedAsAFriend( void )
  1352. {
  1353. // We don't want to be used if we're busy
  1354. if ( IsCurSchedule( SCHED_VORTIGAUNT_HEAL ) )
  1355. return false;
  1356. if ( IsCurSchedule( SCHED_VORTIGAUNT_EXTRACT_BUGBAIT ) )
  1357. return false;
  1358. return BaseClass::CanBeUsedAsAFriend();
  1359. }
  1360. //-----------------------------------------------------------------------------
  1361. //-----------------------------------------------------------------------------
  1362. #define VORT_360_VIEW_DIST_SQR ((60*12)*(60*12))
  1363. bool CNPC_Vortigaunt::FInViewCone( CBaseEntity *pEntity )
  1364. {
  1365. // Vort can see 360 degrees but only at limited distance
  1366. if( ( pEntity->IsNPC() || pEntity->IsPlayer() ) && pEntity->GetAbsOrigin().DistToSqr(GetAbsOrigin()) <= VORT_360_VIEW_DIST_SQR )
  1367. return true;
  1368. return BaseClass::FInViewCone( pEntity );
  1369. }
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose: Start our heal loop
  1372. //-----------------------------------------------------------------------------
  1373. void CNPC_Vortigaunt::StartHealing( void )
  1374. {
  1375. // Find the layer and stop it from moving forward in the cycle
  1376. int nLayer = FindGestureLayer( (Activity) ACT_VORTIGAUNT_HEAL );
  1377. SetLayerPlaybackRate( nLayer, 0.0f );
  1378. // We're now in the healing loop
  1379. m_eHealState = HEAL_STATE_HEALING;
  1380. }
  1381. //-----------------------------------------------------------------------------
  1382. // Purpose:
  1383. //-----------------------------------------------------------------------------
  1384. void CNPC_Vortigaunt::StopHealing( bool bInterrupt )
  1385. {
  1386. // Clear out our healing states
  1387. m_eHealState = HEAL_STATE_NONE;
  1388. m_bForceArmorRecharge = false;
  1389. m_hHealTarget = NULL;
  1390. EndHandGlow( VORTIGAUNT_BEAM_HEAL );
  1391. VacateStrategySlot();
  1392. // See if we're completely interrupting the heal or just ending normally
  1393. if ( bInterrupt )
  1394. {
  1395. RemoveGesture( (Activity) ACT_VORTIGAUNT_HEAL );
  1396. m_flNextHealTime = gpGlobals->curtime + 2.0f;
  1397. }
  1398. else
  1399. {
  1400. // Start our animation back up again
  1401. int nLayer = FindGestureLayer( (Activity) ACT_VORTIGAUNT_HEAL );
  1402. SetLayerPlaybackRate( nLayer, 1.0f );
  1403. m_flNextHealTime = gpGlobals->curtime + VORTIGAUNT_HEAL_RECHARGE;
  1404. m_OnFinishedChargingTarget.FireOutput( this, this );
  1405. }
  1406. // Give us time to stop our animation before we start attacking (otherwise we get weird collisions)
  1407. SetNextAttack( gpGlobals->curtime + 2.0f );
  1408. }
  1409. //-----------------------------------------------------------------------------
  1410. // Purpose: Update our heal schedule and gestures if we're currently healing
  1411. //-----------------------------------------------------------------------------
  1412. void CNPC_Vortigaunt::MaintainHealSchedule( void )
  1413. {
  1414. // Need to be healing
  1415. if ( m_eHealState == HEAL_STATE_NONE )
  1416. return;
  1417. // For now, we only heal the player
  1418. CBasePlayer *pPlayer = AI_GetSinglePlayer();
  1419. if ( pPlayer == NULL )
  1420. return;
  1421. // FIXME: How can this happen?
  1422. if ( m_AssaultBehavior.GetOuter() != NULL )
  1423. {
  1424. // Interrupt us on an urgent assault
  1425. if ( m_AssaultBehavior.IsRunning() && ( m_AssaultBehavior.IsUrgent() || m_AssaultBehavior.OnStrictAssault() ) )
  1426. {
  1427. StopHealing( true );
  1428. return;
  1429. }
  1430. }
  1431. // Don't let us shoot while we're healing
  1432. GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + 0.5f );
  1433. // If we're in the healing phase, heal our target (if able)
  1434. if ( m_eHealState == HEAL_STATE_HEALING )
  1435. {
  1436. // FIXME: We need to have better logic controlling this
  1437. if ( HasCondition( COND_VORTIGAUNT_HEAL_VALID ) )
  1438. {
  1439. if ( m_flNextHealTokenTime < gpGlobals->curtime )
  1440. {
  1441. CBasePlayer *pPlayer = ToBasePlayer( m_hHealTarget );
  1442. // We're done, so stop playing the animation
  1443. if ( m_nNumTokensToSpawn <= 0 || ( m_bForceArmorRecharge == false && ( pPlayer && pPlayer->ArmorValue() >= sk_vortigaunt_armor_charge.GetInt() ) ) )
  1444. {
  1445. m_flHealHinderedTime = 0.0f;
  1446. m_nNumTokensToSpawn = 0;
  1447. SpeakIfAllowed( VORT_CURESTOP );
  1448. StopHealing( false );
  1449. return;
  1450. }
  1451. // Create a charge token
  1452. Vector vecHandPos;
  1453. QAngle vecHandAngles;
  1454. GetAttachment( m_iRightHandAttachment, vecHandPos, vecHandAngles );
  1455. CVortigauntChargeToken::CreateChargeToken( vecHandPos, this, m_hHealTarget );
  1456. m_flNextHealTokenTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 1.0f );
  1457. m_nNumTokensToSpawn--;
  1458. // If we're stopping, delay our animation a bit so it's not so robotic
  1459. if ( m_nNumTokensToSpawn <= 0 )
  1460. {
  1461. m_nNumTokensToSpawn = 0;
  1462. m_flNextHealTokenTime = gpGlobals->curtime + 1.0f;
  1463. }
  1464. }
  1465. }
  1466. else
  1467. {
  1468. /*
  1469. // NOTENOTE: It's better if the vort give up than ignore things around him to try and continue -- jdw
  1470. // Increment a counter to let us know how long we've failed
  1471. m_flHealHinderedTime += gpGlobals->curtime - GetLastThink();
  1472. if ( m_flHealHinderedTime > 2.0f )
  1473. {
  1474. // If too long, stop trying
  1475. StopHealing();
  1476. }
  1477. */
  1478. bool bInterrupt = false;
  1479. if ( HasCondition( COND_NEW_ENEMY ) )
  1480. {
  1481. bInterrupt = true;
  1482. }
  1483. StopHealing( true );
  1484. }
  1485. }
  1486. }
  1487. //-----------------------------------------------------------------------------
  1488. // Purpose:
  1489. // Output : Returns true on success, false on failure.
  1490. //-----------------------------------------------------------------------------
  1491. inline bool CNPC_Vortigaunt::InAttackSequence( void )
  1492. {
  1493. if ( m_MoveAndShootOverlay.IsMovingAndShooting() )
  1494. return true;
  1495. if ( GetActivity() == ACT_RANGE_ATTACK1 )
  1496. return true;
  1497. if ( GetActivity() == ACT_VORTIGAUNT_DISPEL )
  1498. return true;
  1499. if ( IsPlayingGesture( ACT_GESTURE_RANGE_ATTACK1 ) )
  1500. return true;
  1501. return false;
  1502. }
  1503. //-----------------------------------------------------------------------------
  1504. // Purpose: Watch our beams and make sure we don't leave them on mistakenly
  1505. //-----------------------------------------------------------------------------
  1506. void CNPC_Vortigaunt::MaintainGlows( void )
  1507. {
  1508. // Verify that if we're not in an attack gesture, that we're not doing an attack glow
  1509. if ( InAttackSequence() == false && m_eHealState == HEAL_STATE_NONE )
  1510. {
  1511. EndHandGlow( VORTIGAUNT_BEAM_ALL );
  1512. }
  1513. }
  1514. //-----------------------------------------------------------------------------
  1515. // Purpose: Squelch looping sounds and glows after a restore.
  1516. //-----------------------------------------------------------------------------
  1517. void CNPC_Vortigaunt::OnRestore( void )
  1518. {
  1519. BaseClass::OnRestore();
  1520. m_bStopLoopingSounds = true;
  1521. }
  1522. //-----------------------------------------------------------------------------
  1523. // Purpose: Do various non-schedule specific maintainence
  1524. //-----------------------------------------------------------------------------
  1525. void CNPC_Vortigaunt::PrescheduleThink( void )
  1526. {
  1527. // Update our healing (if active)
  1528. MaintainHealSchedule();
  1529. // Let the base class have a go
  1530. BaseClass::PrescheduleThink();
  1531. }
  1532. //-----------------------------------------------------------------------------
  1533. // Purpose:
  1534. // Input : &move -
  1535. // flInterval -
  1536. // Output : Returns true on success, false on failure.
  1537. //-----------------------------------------------------------------------------
  1538. bool CNPC_Vortigaunt::OverrideMoveFacing( const AILocalMoveGoal_t &move, float flInterval )
  1539. {
  1540. // If we're in our aiming gesture, then always face our target as we run
  1541. Activity nActivity = NPC_TranslateActivity( ACT_GESTURE_RANGE_ATTACK1 );
  1542. if ( IsPlayingGesture( nActivity ) ||
  1543. IsCurSchedule( SCHED_PC_MOVE_TOWARDS_COVER_FROM_BEST_SOUND ) ||
  1544. IsCurSchedule( SCHED_VORT_FLEE_FROM_BEST_SOUND ) ||
  1545. IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND ) )
  1546. {
  1547. Vector vecEnemyLKP = GetEnemyLKP();
  1548. AddFacingTarget( GetEnemy(), vecEnemyLKP, 1.0, 0.2 );
  1549. }
  1550. return BaseClass::OverrideMoveFacing( move, flInterval );
  1551. }
  1552. //-----------------------------------------------------------------------------
  1553. // Purpose:
  1554. //-----------------------------------------------------------------------------
  1555. void CNPC_Vortigaunt::BuildScheduleTestBits( void )
  1556. {
  1557. // Call to base
  1558. BaseClass::BuildScheduleTestBits();
  1559. // Allow healing to interrupt us if we're standing around
  1560. if ( IsCurSchedule( SCHED_IDLE_STAND ) ||
  1561. IsCurSchedule( SCHED_ALERT_STAND ) )
  1562. {
  1563. if ( m_eHealState == HEAL_STATE_NONE )
  1564. {
  1565. SetCustomInterruptCondition( COND_VORTIGAUNT_CAN_HEAL );
  1566. SetCustomInterruptCondition( COND_VORTIGAUNT_DISPEL_ANTLIONS );
  1567. }
  1568. }
  1569. // Always interrupt when healing
  1570. if ( m_eHealState != HEAL_STATE_NONE )
  1571. {
  1572. // Interrupt if we're not already adjusting
  1573. if ( IsCurSchedule( SCHED_VORTIGAUNT_RUN_TO_PLAYER ) == false )
  1574. {
  1575. SetCustomInterruptCondition( COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR );
  1576. SetCustomInterruptCondition( COND_VORTIGAUNT_HEAL_TARGET_BLOCKED );
  1577. // Interrupt if we're not already turning
  1578. if ( IsCurSchedule( SCHED_VORTIGAUNT_FACE_PLAYER ) == false )
  1579. {
  1580. SetCustomInterruptCondition( COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US );
  1581. }
  1582. }
  1583. }
  1584. if ( IsCurSchedule( SCHED_COMBAT_STAND ) )
  1585. {
  1586. SetCustomInterruptCondition( COND_VORTIGAUNT_DISPEL_ANTLIONS );
  1587. }
  1588. }
  1589. //-----------------------------------------------------------------------------
  1590. // Purpose: Small beam from arm to nearby geometry
  1591. //-----------------------------------------------------------------------------
  1592. void CNPC_Vortigaunt::ArmBeam( int beamType, int nHand )
  1593. {
  1594. trace_t tr;
  1595. float flDist = 1.0;
  1596. int side = ( nHand == HAND_LEFT ) ? -1 : 1;
  1597. Vector forward, right, up;
  1598. AngleVectors( GetLocalAngles(), &forward, &right, &up );
  1599. Vector vecSrc = GetLocalOrigin() + up * 36 + right * side * 16 + forward * 32;
  1600. for (int i = 0; i < 3; i++)
  1601. {
  1602. Vector vecAim = forward * random->RandomFloat( -1, 1 ) + right * side * random->RandomFloat( 0, 1 ) + up * random->RandomFloat( -1, 1 );
  1603. trace_t tr1;
  1604. AI_TraceLine ( vecSrc, vecSrc + vecAim * (10*12), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr1);
  1605. // Don't hit the sky
  1606. if ( tr1.surface.flags & SURF_SKY )
  1607. continue;
  1608. // Choose a farther distance if we have one
  1609. if ( flDist > tr1.fraction )
  1610. {
  1611. tr = tr1;
  1612. flDist = tr.fraction;
  1613. }
  1614. }
  1615. // Couldn't find anything close enough
  1616. if ( flDist == 1.0 )
  1617. return;
  1618. // Tell the client to start an arm beam
  1619. unsigned char uchAttachment = (nHand==HAND_LEFT) ? m_iLeftHandAttachment : m_iRightHandAttachment;
  1620. EntityMessageBegin( this, true );
  1621. WRITE_BYTE( VORTFX_ARMBEAM );
  1622. WRITE_LONG( entindex() );
  1623. WRITE_BYTE( uchAttachment );
  1624. WRITE_VEC3COORD( tr.endpos );
  1625. WRITE_VEC3NORMAL( tr.plane.normal );
  1626. MessageEnd();
  1627. }
  1628. //------------------------------------------------------------------------------
  1629. // Purpose : Put glowing sprites on hands
  1630. //------------------------------------------------------------------------------
  1631. void CNPC_Vortigaunt::StartHandGlow( int beamType, int nHand )
  1632. {
  1633. // We need this because there's a rare case where a scene can interrupt and turn off our hand glows, but are then
  1634. // turned back on in the same frame due to how animations are applied and anim events are executed after the AI frame.
  1635. if ( m_fGlowChangeTime > gpGlobals->curtime )
  1636. return;
  1637. switch( beamType )
  1638. {
  1639. case VORTIGAUNT_BEAM_DISPEL:
  1640. case VORTIGAUNT_BEAM_HEAL:
  1641. case VORTIGAUNT_BEAM_ZAP:
  1642. {
  1643. // Validate the hand's range
  1644. if ( nHand >= ARRAYSIZE( m_hHandEffect ) )
  1645. return;
  1646. // Start up
  1647. if ( m_hHandEffect[nHand] == NULL )
  1648. {
  1649. // Create the token if it doesn't already exist
  1650. m_hHandEffect[nHand] = CVortigauntEffectDispel::CreateEffectDispel( GetAbsOrigin(), this, NULL );
  1651. if ( m_hHandEffect[nHand] == NULL )
  1652. return;
  1653. }
  1654. // Stomp our settings
  1655. m_hHandEffect[nHand]->SetParent( this, (nHand==HAND_LEFT) ? m_iLeftHandAttachment : m_iRightHandAttachment );
  1656. m_hHandEffect[nHand]->SetMoveType( MOVETYPE_NONE );
  1657. m_hHandEffect[nHand]->SetLocalOrigin( Vector( 8.0f, 4.0f, 0.0f ) );
  1658. }
  1659. break;
  1660. case VORTIGAUNT_BEAM_ALL:
  1661. Assert( 0 );
  1662. break;
  1663. }
  1664. }
  1665. //------------------------------------------------------------------------------
  1666. // Purpose: Fade glow from hands.
  1667. //------------------------------------------------------------------------------
  1668. void CNPC_Vortigaunt::EndHandGlow( int beamType /*= VORTIGAUNT_BEAM_ALL*/ )
  1669. {
  1670. if ( m_hHandEffect[0] )
  1671. {
  1672. m_hHandEffect[0]->FadeAndDie();
  1673. m_hHandEffect[0] = NULL;
  1674. }
  1675. if ( m_hHandEffect[1] )
  1676. {
  1677. m_hHandEffect[1]->FadeAndDie();
  1678. m_hHandEffect[1] = NULL;
  1679. }
  1680. // Zap
  1681. if ( beamType == VORTIGAUNT_BEAM_ZAP || beamType == VORTIGAUNT_BEAM_ALL )
  1682. {
  1683. m_fGlowAge = 0;
  1684. // Stop our smaller beams as well
  1685. ClearBeams();
  1686. }
  1687. }
  1688. extern int ACT_ANTLION_ZAP_FLIP;
  1689. //-----------------------------------------------------------------------------
  1690. // Purpose:
  1691. //-----------------------------------------------------------------------------
  1692. bool CNPC_Vortigaunt::IsValidEnemy( CBaseEntity *pEnemy )
  1693. {
  1694. if ( IsRoller( pEnemy ) )
  1695. {
  1696. CAI_BaseNPC *pNPC = pEnemy->MyNPCPointer();
  1697. if ( pNPC && pNPC->GetEnemy() != NULL )
  1698. return true;
  1699. return false;
  1700. }
  1701. // Wait until our animation is finished
  1702. if ( GetEnemy() == NULL && m_flAimDelay > gpGlobals->curtime )
  1703. return false;
  1704. return BaseClass::IsValidEnemy( pEnemy );
  1705. }
  1706. //-----------------------------------------------------------------------------
  1707. // Purpose: Creates a blast where the beam has struck a target
  1708. // Input : &vecOrigin - position to eminate from
  1709. //-----------------------------------------------------------------------------
  1710. void CNPC_Vortigaunt::CreateBeamBlast( const Vector &vecOrigin )
  1711. {
  1712. CSprite *pBlastSprite = CSprite::SpriteCreate( "sprites/vortring1.vmt", vecOrigin, true );
  1713. if ( pBlastSprite != NULL )
  1714. {
  1715. pBlastSprite->SetTransparency( kRenderTransAddFrameBlend, 255, 255, 255, 255, kRenderFxNone );
  1716. pBlastSprite->SetBrightness( 255 );
  1717. pBlastSprite->SetScale( random->RandomFloat( 1.0f, 1.5f ) );
  1718. pBlastSprite->AnimateAndDie( 45.0f );
  1719. pBlastSprite->EmitSound( "NPC_Vortigaunt.Explode" );
  1720. }
  1721. CPVSFilter filter( vecOrigin );
  1722. te->GaussExplosion( filter, 0.0f, vecOrigin, Vector( 0, 0, 1 ), 0 );
  1723. }
  1724. #define COS_30 0.866025404f // sqrt(3) / 2
  1725. #define COS_60 0.5 // sqrt(1) / 2
  1726. //-----------------------------------------------------------------------------
  1727. // Purpose: Heavy damage directly forward
  1728. // Input : nHand - Handedness of the beam
  1729. //-----------------------------------------------------------------------------
  1730. void CNPC_Vortigaunt::ZapBeam( int nHand )
  1731. {
  1732. Vector forward;
  1733. GetVectors( &forward, NULL, NULL );
  1734. Vector vecSrc = GetAbsOrigin() + GetViewOffset();
  1735. Vector vecAim = GetShootEnemyDir( vecSrc, false ); // We want a clear shot to their core
  1736. if ( GetEnemy() )
  1737. {
  1738. Vector vecTarget = GetEnemy()->BodyTarget( vecSrc, false );
  1739. if ( g_debug_vortigaunt_aim.GetBool() )
  1740. {
  1741. NDebugOverlay::Cross3D( vecTarget, 4.0f, 255, 0, 0, true, 10.0f );
  1742. CBaseAnimating *pAnim = GetEnemy()->GetBaseAnimating();
  1743. if ( pAnim )
  1744. {
  1745. pAnim->DrawServerHitboxes( 10.0f );
  1746. }
  1747. }
  1748. }
  1749. // If we're too far off our center, the shot must miss!
  1750. if ( DotProduct( vecAim, forward ) < COS_60 )
  1751. {
  1752. // Missed, so just shoot forward
  1753. vecAim = forward;
  1754. }
  1755. trace_t tr;
  1756. if ( m_bExtractingBugbait == true )
  1757. {
  1758. CRagdollProp *pTest = dynamic_cast< CRagdollProp *>( GetTarget() );
  1759. if ( pTest )
  1760. {
  1761. ragdoll_t *m_ragdoll = pTest->GetRagdoll();
  1762. if ( m_ragdoll )
  1763. {
  1764. Vector vOrigin;
  1765. m_ragdoll->list[0].pObject->GetPosition( &vOrigin, 0 );
  1766. AI_TraceLine( vecSrc, vOrigin, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr);
  1767. }
  1768. CRagdollBoogie::Create( pTest, 200, gpGlobals->curtime, 1.0f );
  1769. }
  1770. }
  1771. else
  1772. {
  1773. AI_TraceLine( vecSrc, vecSrc + ( vecAim * InnateRange1MaxRange() ), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr);
  1774. }
  1775. if ( g_debug_vortigaunt_aim.GetBool() )
  1776. {
  1777. NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, true, 10.0f );
  1778. }
  1779. // Send a message to the client to create a "zap" beam
  1780. unsigned char uchAttachment = (nHand==HAND_LEFT) ? m_iLeftHandAttachment : m_iRightHandAttachment;
  1781. EntityMessageBegin( this, true );
  1782. WRITE_BYTE( VORTFX_ZAPBEAM );
  1783. WRITE_BYTE( uchAttachment );
  1784. WRITE_VEC3COORD( tr.endpos );
  1785. MessageEnd();
  1786. CBaseEntity *pEntity = tr.m_pEnt;
  1787. if ( pEntity != NULL && m_takedamage )
  1788. {
  1789. if ( g_debug_vortigaunt_aim.GetBool() )
  1790. {
  1791. NDebugOverlay::Box( tr.endpos, -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, 8, 10.0f );
  1792. }
  1793. CTakeDamageInfo dmgInfo( this, this, sk_vortigaunt_dmg_zap.GetFloat(), DMG_SHOCK );
  1794. dmgInfo.SetDamagePosition( tr.endpos );
  1795. VectorNormalize( vecAim );// not a unit vec yet
  1796. // hit like a 5kg object flying 100 ft/s
  1797. dmgInfo.SetDamageForce( 5 * 100 * 12 * vecAim );
  1798. // Our zaps do special things to antlions
  1799. if ( FClassnameIs( pEntity, "npc_antlion" ) )
  1800. {
  1801. // Make a worker flip instead of explode
  1802. if ( IsAntlionWorker( pEntity ) )
  1803. {
  1804. CNPC_Antlion *pAntlion = static_cast<CNPC_Antlion *>(pEntity);
  1805. pAntlion->Flip();
  1806. }
  1807. else
  1808. {
  1809. // Always gib the antlion hit!
  1810. dmgInfo.ScaleDamage( 4.0f );
  1811. }
  1812. // Look in a ring and flip other antlions nearby
  1813. DispelAntlions( tr.endpos, 200.0f, false );
  1814. }
  1815. // Send the damage to the recipient
  1816. pEntity->DispatchTraceAttack( dmgInfo, vecAim, &tr );
  1817. }
  1818. // Create a cover for the end of the beam
  1819. CreateBeamBlast( tr.endpos );
  1820. }
  1821. //------------------------------------------------------------------------------
  1822. // Purpose: Clear glow from hands immediately
  1823. //------------------------------------------------------------------------------
  1824. void CNPC_Vortigaunt::ClearHandGlow( void )
  1825. {
  1826. if ( m_hHandEffect[0] != NULL )
  1827. {
  1828. UTIL_Remove( m_hHandEffect[0] );
  1829. m_hHandEffect[0] = NULL;
  1830. }
  1831. if ( m_hHandEffect[1] != NULL )
  1832. {
  1833. UTIL_Remove( m_hHandEffect[1] );
  1834. m_hHandEffect[1] = NULL;
  1835. }
  1836. m_fGlowAge = 0;
  1837. }
  1838. //------------------------------------------------------------------------------
  1839. // Purpose: remove all beams
  1840. //------------------------------------------------------------------------------
  1841. void CNPC_Vortigaunt::ClearBeams( void )
  1842. {
  1843. // Stop looping suit charge sound.
  1844. if ( m_bStopLoopingSounds )
  1845. {
  1846. StopSound( "NPC_Vortigaunt.StartHealLoop" );
  1847. StopSound( "NPC_Vortigaunt.StartShootLoop" );
  1848. StopSound( "NPC_Vortigaunt.SuitCharge" );
  1849. StopSound( "NPC_Vortigaunt.ZapPowerup" );
  1850. m_bStopLoopingSounds = false;
  1851. }
  1852. }
  1853. //-----------------------------------------------------------------------------
  1854. // Purpose:
  1855. //-----------------------------------------------------------------------------
  1856. void CNPC_Vortigaunt::InputEnableArmorRecharge( inputdata_t &data )
  1857. {
  1858. m_bArmorRechargeEnabled = true;
  1859. }
  1860. //-----------------------------------------------------------------------------
  1861. // Purpose:
  1862. //-----------------------------------------------------------------------------
  1863. void CNPC_Vortigaunt::InputDisableArmorRecharge( inputdata_t &data )
  1864. {
  1865. m_bArmorRechargeEnabled = false;
  1866. }
  1867. //-----------------------------------------------------------------------------
  1868. // Purpose:
  1869. //-----------------------------------------------------------------------------
  1870. void CNPC_Vortigaunt::InputChargeTarget( inputdata_t &data )
  1871. {
  1872. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, data.value.String(), NULL, data.pActivator, data.pCaller );
  1873. // Must be valid
  1874. if ( pTarget == NULL )
  1875. {
  1876. DevMsg( 1, "Unable to charge from unknown entity: %s!\n", data.value.String() );
  1877. return;
  1878. }
  1879. int playerArmor = (pTarget->IsPlayer()) ? ((CBasePlayer *)pTarget)->ArmorValue() : 0;
  1880. if ( playerArmor >= 100 || ( pTarget->GetFlags() & FL_NOTARGET ) )
  1881. {
  1882. m_OnFinishedChargingTarget.FireOutput( this, this );
  1883. return;
  1884. }
  1885. m_hHealTarget = pTarget;
  1886. m_bForceArmorRecharge = true;
  1887. SetCondition( COND_PROVOKED );
  1888. }
  1889. //-----------------------------------------------------------------------------
  1890. // Purpose:
  1891. //-----------------------------------------------------------------------------
  1892. void CNPC_Vortigaunt::InputExtractBugbait( inputdata_t &data )
  1893. {
  1894. CBaseEntity *pTarget = gEntList.FindEntityByName( NULL, data.value.String(), NULL, data.pActivator, data.pCaller );
  1895. // Must be valid
  1896. if ( pTarget == NULL )
  1897. {
  1898. DevMsg( 1, "Unable to extract bugbait from unknown entity %s!\n", data.value.String() );
  1899. return;
  1900. }
  1901. // Keep this as our target
  1902. SetTarget( pTarget );
  1903. // Start to extract
  1904. m_bExtractingBugbait = true;
  1905. SetSchedule( SCHED_VORTIGAUNT_EXTRACT_BUGBAIT );
  1906. }
  1907. //-----------------------------------------------------------------------------
  1908. // Purpose: Allows the vortigaunt to use health regeneration
  1909. //-----------------------------------------------------------------------------
  1910. void CNPC_Vortigaunt::InputEnableHealthRegeneration( inputdata_t &data )
  1911. {
  1912. m_bRegenerateHealth = true;
  1913. }
  1914. //-----------------------------------------------------------------------------
  1915. // Purpose: Stops the vortigaunt from using health regeneration (default)
  1916. //-----------------------------------------------------------------------------
  1917. void CNPC_Vortigaunt::InputDisableHealthRegeneration( inputdata_t &data )
  1918. {
  1919. m_bRegenerateHealth = false;
  1920. }
  1921. //-----------------------------------------------------------------------------
  1922. // Purpose:
  1923. //-----------------------------------------------------------------------------
  1924. int CNPC_Vortigaunt::IRelationPriority( CBaseEntity *pTarget )
  1925. {
  1926. int priority = BaseClass::IRelationPriority( pTarget );
  1927. if ( pTarget == NULL )
  1928. return priority;
  1929. CBaseEntity *pEnemy = GetEnemy();
  1930. // Handle antlion cases
  1931. if ( pEnemy != NULL && pEnemy != pTarget )
  1932. {
  1933. // I have an enemy that is not this thing. If that enemy is near, I shouldn't become distracted.
  1934. if ( GetAbsOrigin().DistToSqr( pEnemy->GetAbsOrigin()) < Square(15*12) )
  1935. return priority;
  1936. }
  1937. // Targets near our follow target have a higher priority to us
  1938. if ( m_FollowBehavior.GetFollowTarget() &&
  1939. m_FollowBehavior.GetFollowTarget()->GetAbsOrigin().DistToSqr( pTarget->GetAbsOrigin() ) < Square(25*12) )
  1940. {
  1941. priority++;
  1942. }
  1943. // Flipped antlions are of lower priority
  1944. CAI_BaseNPC *pNPC = pTarget->MyNPCPointer();
  1945. if ( pNPC && pNPC->Classify() == CLASS_ANTLION && pNPC->GetActivity() == ACT_ANTLION_ZAP_FLIP )
  1946. priority--;
  1947. return priority;
  1948. }
  1949. //-----------------------------------------------------------------------------
  1950. // Purpose: back away from overly close zombies
  1951. //-----------------------------------------------------------------------------
  1952. Disposition_t CNPC_Vortigaunt::IRelationType( CBaseEntity *pTarget )
  1953. {
  1954. if ( pTarget == NULL )
  1955. return D_NU;
  1956. Disposition_t disposition = BaseClass::IRelationType( pTarget );
  1957. if ( pTarget->Classify() == CLASS_ZOMBIE && disposition == D_HT )
  1958. {
  1959. if( GetAbsOrigin().DistToSqr(pTarget->GetAbsOrigin()) < VORTIGAUNT_FEAR_ZOMBIE_DIST_SQR )
  1960. {
  1961. // Be afraid of a zombie that's near if I'm not allowed to dodge. This will make Alyx back away.
  1962. return D_FR;
  1963. }
  1964. }
  1965. return disposition;
  1966. }
  1967. //-----------------------------------------------------------------------------
  1968. // Purpose: Determines whether the heal gesture can successfully reach the player
  1969. // Output : Returns true on success, false on failure.
  1970. //-----------------------------------------------------------------------------
  1971. bool CNPC_Vortigaunt::HealGestureHasLOS( void )
  1972. {
  1973. //For now the player is always our target
  1974. CBaseEntity *pTargetEnt = AI_GetSinglePlayer();
  1975. if ( pTargetEnt == NULL )
  1976. return false;
  1977. // Find our left hand as the starting point
  1978. Vector vecHandPos;
  1979. QAngle vecHandAngle;
  1980. GetAttachment( m_iRightHandAttachment, vecHandPos, vecHandAngle );
  1981. // Trace to our target, skipping ourselves and the target
  1982. trace_t tr;
  1983. CTraceFilterSkipTwoEntities filter( this, pTargetEnt, COLLISION_GROUP_NONE );
  1984. UTIL_TraceLine( vecHandPos, pTargetEnt->WorldSpaceCenter(), MASK_SHOT, &filter, &tr );
  1985. // Must be clear
  1986. if ( tr.fraction < 1.0f || tr.startsolid || tr.allsolid )
  1987. return false;
  1988. return true;
  1989. }
  1990. //-----------------------------------------------------------------------------
  1991. // Purpose: Gather conditions for our healing behavior
  1992. //-----------------------------------------------------------------------------
  1993. void CNPC_Vortigaunt::GatherHealConditions( void )
  1994. {
  1995. ClearCondition( COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR );
  1996. ClearCondition( COND_VORTIGAUNT_HEAL_TARGET_BLOCKED );
  1997. ClearCondition( COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US );
  1998. // We stop if there are enemies around
  1999. if ( m_bArmorRechargeEnabled == false ||
  2000. HasCondition( COND_NEW_ENEMY ) ||
  2001. HasCondition( COND_HEAR_DANGER ) ||
  2002. HasCondition( COND_HEAVY_DAMAGE ) )
  2003. {
  2004. ClearCondition( COND_VORTIGAUNT_HEAL_VALID );
  2005. return;
  2006. }
  2007. // Start by assuming that we'll succeed
  2008. SetCondition( COND_VORTIGAUNT_HEAL_VALID );
  2009. // Just assume we should
  2010. if ( m_bForceArmorRecharge )
  2011. return;
  2012. // For now we only act on the player
  2013. CBasePlayer *pPlayer = ToBasePlayer( m_hHealTarget );
  2014. if ( pPlayer != NULL )
  2015. {
  2016. Vector vecToPlayer = ( pPlayer->WorldSpaceCenter() - WorldSpaceCenter() );
  2017. // Make sure he's still within heal range
  2018. if ( vecToPlayer.LengthSqr() > (HEAL_RANGE*HEAL_RANGE) )
  2019. {
  2020. SetCondition( COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR );
  2021. // NOTE: We allow him to send tokens over large distances
  2022. //ClearCondition( COND_VORTIGAUNT_HEAL_VALID );
  2023. }
  2024. vecToPlayer.z = 0.0f;
  2025. VectorNormalize( vecToPlayer );
  2026. Vector facingDir = BodyDirection2D();
  2027. // Check our direction towards the player
  2028. if ( DotProduct( vecToPlayer, facingDir ) < VIEW_FIELD_NARROW )
  2029. {
  2030. SetCondition( COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US );
  2031. ClearCondition( COND_VORTIGAUNT_HEAL_VALID );
  2032. }
  2033. // Now ensure he's not blocked
  2034. if ( HealGestureHasLOS() == false )
  2035. {
  2036. SetCondition( COND_VORTIGAUNT_HEAL_TARGET_BLOCKED );
  2037. ClearCondition( COND_VORTIGAUNT_HEAL_VALID );
  2038. }
  2039. }
  2040. else
  2041. {
  2042. ClearCondition( COND_VORTIGAUNT_HEAL_VALID );
  2043. }
  2044. }
  2045. //-----------------------------------------------------------------------------
  2046. // Purpose: Gather conditions specific to this NPC
  2047. //-----------------------------------------------------------------------------
  2048. void CNPC_Vortigaunt::GatherConditions( void )
  2049. {
  2050. // Call our base
  2051. BaseClass::GatherConditions();
  2052. // See if we're able to heal now
  2053. if ( HealBehaviorAvailable() && ( m_flNextHealTime < gpGlobals->curtime ) )
  2054. {
  2055. // See if we should heal the player
  2056. CBaseEntity *pHealTarget = FindHealTarget();
  2057. if ( pHealTarget != NULL )
  2058. {
  2059. SetHealTarget( pHealTarget, false );
  2060. }
  2061. // Don't try again for a period of time
  2062. m_flNextHealTime = gpGlobals->curtime + 2.0f;
  2063. }
  2064. // Get our state for healing
  2065. GatherHealConditions();
  2066. }
  2067. //-----------------------------------------------------------------------------
  2068. // Purpose:
  2069. //-----------------------------------------------------------------------------
  2070. void CNPC_Vortigaunt::DispelAntlions( const Vector &vecOrigin, float flRadius, bool bDispel /*= true*/ )
  2071. {
  2072. // More effects
  2073. if ( bDispel )
  2074. {
  2075. UTIL_ScreenShake( vecOrigin, 20.0f, 150.0, 1.0, 1250.0f, SHAKE_START );
  2076. CBroadcastRecipientFilter filter2;
  2077. te->BeamRingPoint( filter2, 0, vecOrigin, //origin
  2078. 64, //start radius
  2079. 800, //end radius
  2080. m_nLightningSprite, //texture
  2081. 0, //halo index
  2082. 0, //start frame
  2083. 2, //framerate
  2084. 0.1f, //life
  2085. 128, //width
  2086. 0, //spread
  2087. 0, //amplitude
  2088. 255, //r
  2089. 255, //g
  2090. 225, //b
  2091. 32, //a
  2092. 0, //speed
  2093. FBEAM_FADEOUT
  2094. );
  2095. //Shockring
  2096. te->BeamRingPoint( filter2, 0, vecOrigin + Vector( 0, 0, 16 ), //origin
  2097. 64, //start radius
  2098. 800, //end radius
  2099. m_nLightningSprite, //texture
  2100. 0, //halo index
  2101. 0, //start frame
  2102. 2, //framerate
  2103. 0.2f, //life
  2104. 64, //width
  2105. 0, //spread
  2106. 0, //amplitude
  2107. 255, //r
  2108. 255, //g
  2109. 225, //b
  2110. 200, //a
  2111. 0, //speed
  2112. FBEAM_FADEOUT
  2113. );
  2114. // Ground effects
  2115. CEffectData data;
  2116. data.m_vOrigin = vecOrigin;
  2117. DispatchEffect( "VortDispel", data );
  2118. }
  2119. // Make antlions flip all around us!
  2120. trace_t tr;
  2121. CBaseEntity *pEnemySearch[32];
  2122. int nNumEnemies = UTIL_EntitiesInBox( pEnemySearch, ARRAYSIZE(pEnemySearch), vecOrigin-Vector(flRadius,flRadius,flRadius), vecOrigin+Vector(flRadius,flRadius,flRadius), FL_NPC );
  2123. for ( int i = 0; i < nNumEnemies; i++ )
  2124. {
  2125. // We only care about antlions
  2126. if ( IsAntlion( pEnemySearch[i] ) == false )
  2127. continue;
  2128. CNPC_Antlion *pAntlion = static_cast<CNPC_Antlion *>(pEnemySearch[i]);
  2129. if ( pAntlion->IsWorker() == false )
  2130. {
  2131. // Attempt to trace a line to hit the target
  2132. UTIL_TraceLine( vecOrigin, pAntlion->BodyTarget( vecOrigin ), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr );
  2133. if ( tr.fraction < 1.0f && tr.m_pEnt != pAntlion )
  2134. continue;
  2135. Vector vecDir = ( pAntlion->GetAbsOrigin() - vecOrigin );
  2136. vecDir[2] = 0.0f;
  2137. float flDist = VectorNormalize( vecDir );
  2138. float flFalloff = RemapValClamped( flDist, 0, flRadius*0.75f, 1.0f, 0.1f );
  2139. vecDir *= ( flRadius * 1.5f * flFalloff );
  2140. vecDir[2] += ( flRadius * 0.5f * flFalloff );
  2141. pAntlion->ApplyAbsVelocityImpulse( vecDir );
  2142. // gib nearby antlions, knock over distant ones.
  2143. if ( flDist < 128 && bDispel )
  2144. {
  2145. // splat!
  2146. vecDir[2] += 400.0f * flFalloff;
  2147. CTakeDamageInfo dmgInfo( this, this, vecDir, pAntlion->GetAbsOrigin() , 100, DMG_SHOCK );
  2148. pAntlion->TakeDamage( dmgInfo );
  2149. }
  2150. else
  2151. {
  2152. // Turn them over
  2153. pAntlion->Flip( true );
  2154. // Display an effect between us and the flipped creature
  2155. // Tell the client to start an arm beam
  2156. /*
  2157. unsigned char uchAttachment = pAntlion->LookupAttachment( "mouth" );
  2158. EntityMessageBegin( this, true );
  2159. WRITE_BYTE( VORTFX_ARMBEAM );
  2160. WRITE_LONG( pAntlion->entindex() );
  2161. WRITE_BYTE( uchAttachment );
  2162. WRITE_VEC3COORD( vecOrigin );
  2163. WRITE_VEC3NORMAL( Vector( 0, 0, 1 ) );
  2164. MessageEnd();
  2165. */
  2166. }
  2167. }
  2168. }
  2169. // Stop our effects
  2170. if ( bDispel )
  2171. {
  2172. EndHandGlow( VORTIGAUNT_BEAM_ALL );
  2173. }
  2174. }
  2175. //-----------------------------------------------------------------------------
  2176. // Purpose: Simply tell us to dispel
  2177. //-----------------------------------------------------------------------------
  2178. void CNPC_Vortigaunt::InputDispel( inputdata_t &data )
  2179. {
  2180. SetCondition( COND_VORTIGAUNT_DISPEL_ANTLIONS );
  2181. }
  2182. //-----------------------------------------------------------------------------
  2183. // Purpose: Decide when we're allowed to interact with other NPCs
  2184. //-----------------------------------------------------------------------------
  2185. bool CNPC_Vortigaunt::CanRunAScriptedNPCInteraction( bool bForced /*= false*/ )
  2186. {
  2187. // Never interrupt a range attack!
  2188. if ( InAttackSequence() )
  2189. return false;
  2190. // Can't do them while we're trying to heal the player
  2191. if ( m_eHealState != HEAL_STATE_NONE )
  2192. return false;
  2193. return BaseClass::CanRunAScriptedNPCInteraction( bForced );
  2194. }
  2195. //-----------------------------------------------------------------------------
  2196. // Purpose:
  2197. // Input : interrupt -
  2198. //-----------------------------------------------------------------------------
  2199. void CNPC_Vortigaunt::SetScriptedScheduleIgnoreConditions( Interruptability_t interrupt )
  2200. {
  2201. // First add our base conditions to ignore
  2202. BaseClass::SetScriptedScheduleIgnoreConditions( interrupt );
  2203. static int g_VortConditions[] =
  2204. {
  2205. COND_VORTIGAUNT_CAN_HEAL,
  2206. COND_VORTIGAUNT_DISPEL_ANTLIONS,
  2207. COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR,
  2208. COND_VORTIGAUNT_HEAL_TARGET_BLOCKED,
  2209. COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US,
  2210. COND_VORTIGAUNT_HEAL_VALID
  2211. };
  2212. ClearIgnoreConditions( g_VortConditions, ARRAYSIZE(g_VortConditions) );
  2213. // Ignore these if we're damage only
  2214. if ( interrupt > GENERAL_INTERRUPTABILITY )
  2215. SetIgnoreConditions( g_VortConditions, ARRAYSIZE(g_VortConditions) );
  2216. }
  2217. //-----------------------------------------------------------------------------
  2218. // !!!HACKHACK - EP2 - Stop vortigaunt taking all physics damage to prevent it dying
  2219. // in freak accidents resembling spontaneous stress damage death (which are now impossible)
  2220. // Also stop it taking damage from flames: Fixes it being burnt to death from entity flames
  2221. // attached to random debris chunks while inside scripted sequences.
  2222. //-----------------------------------------------------------------------------
  2223. int CNPC_Vortigaunt::OnTakeDamage_Alive( const CTakeDamageInfo &info )
  2224. {
  2225. if( info.GetDamageType() & (DMG_CRUSH | DMG_BURN) )
  2226. return 0;
  2227. // vital vortigaunts (eg the vortigoth in ep2) take less damage from explosions
  2228. // so that zombines don't blow them up disappointingly. They take less damage
  2229. // still from antlion workers.
  2230. if ( Classify() == CLASS_PLAYER_ALLY_VITAL )
  2231. {
  2232. // half damage
  2233. CTakeDamageInfo subInfo = info;
  2234. // take less damage from antlion worker acid/poison
  2235. if ( info.GetAttacker()->Classify() == CLASS_ANTLION &&
  2236. (info.GetDamageType() & ( DMG_ACID | DMG_POISON ))!=0
  2237. )
  2238. {
  2239. subInfo.ScaleDamage( sk_vortigaunt_vital_antlion_worker_dmg.GetFloat() );
  2240. }
  2241. else if ( info.GetDamageType() & DMG_BLAST )
  2242. {
  2243. subInfo.ScaleDamage( 0.5f );
  2244. }
  2245. return BaseClass::OnTakeDamage_Alive( subInfo );
  2246. }
  2247. return BaseClass::OnTakeDamage_Alive( info );
  2248. }
  2249. //-----------------------------------------------------------------------------
  2250. // Purpose: Override move and shoot if we're following someone
  2251. //-----------------------------------------------------------------------------
  2252. bool CNPC_Vortigaunt::ShouldMoveAndShoot( void )
  2253. {
  2254. if ( m_FollowBehavior.IsActive() )
  2255. return true;
  2256. return BaseClass::ShouldMoveAndShoot();
  2257. }
  2258. //-----------------------------------------------------------------------------
  2259. // Purpose: notification from a grub that I squished it. This special case
  2260. // function is necessary because what you would think to be the ordinary
  2261. // channels are in fact missing: Event_KilledOther doesn't actually do anything
  2262. // and KilledNPC expects a BaseCombatCharacter, and always uses the same Speak
  2263. // line.
  2264. //-----------------------------------------------------------------------------
  2265. void CNPC_Vortigaunt::OnSquishedGrub( const CBaseEntity *pGrub )
  2266. {
  2267. Speak(TLK_SQUISHED_GRUB);
  2268. }
  2269. //-----------------------------------------------------------------------------
  2270. // Purpose:
  2271. //-----------------------------------------------------------------------------
  2272. void CNPC_Vortigaunt::AimGun( void )
  2273. {
  2274. // If our aim lock is on, don't bother
  2275. if ( m_flAimDelay >= gpGlobals->curtime )
  2276. return;
  2277. // Aim at our target
  2278. if ( GetEnemy() )
  2279. {
  2280. Vector vecShootOrigin;
  2281. vecShootOrigin = Weapon_ShootPosition();
  2282. Vector vecShootDir;
  2283. // Aim where it is
  2284. vecShootDir = GetShootEnemyDir( vecShootOrigin, false );
  2285. if ( g_debug_vortigaunt_aim.GetBool() )
  2286. {
  2287. NDebugOverlay::Line( WorldSpaceCenter(), WorldSpaceCenter() + vecShootDir * 256.0f, 255, 0, 0, true, 0.1f );
  2288. }
  2289. SetAim( vecShootDir );
  2290. }
  2291. else
  2292. {
  2293. RelaxAim();
  2294. }
  2295. }
  2296. //-----------------------------------------------------------------------------
  2297. // Purpose: A scripted sequence has interrupted us
  2298. //-----------------------------------------------------------------------------
  2299. void CNPC_Vortigaunt::OnStartScene( void )
  2300. {
  2301. // Watch our hand state
  2302. EndHandGlow( VORTIGAUNT_BEAM_ALL );
  2303. m_fGlowChangeTime = gpGlobals->curtime + 0.1f; // No more glows for this amount of time!
  2304. BaseClass::OnStartScene();
  2305. }
  2306. //-----------------------------------------------------------------------------
  2307. // Purpose:
  2308. // Output : Returns true on success, false on failure.
  2309. //-----------------------------------------------------------------------------
  2310. bool CNPC_Vortigaunt::IsInterruptable( void )
  2311. {
  2312. // Don't interrupt my attack schedule!
  2313. if ( InAttackSequence() )
  2314. return false;
  2315. return BaseClass::IsInterruptable();
  2316. }
  2317. //-----------------------------------------------------------------------------
  2318. // Purpose: Start overriding our animations to "carry" an NPC
  2319. //-----------------------------------------------------------------------------
  2320. void CNPC_Vortigaunt::InputBeginCarryNPC( inputdata_t &indputdata )
  2321. {
  2322. m_bCarryingNPC = true;
  2323. }
  2324. //-----------------------------------------------------------------------------
  2325. // Purpose: Stop overriding our animations for carrying an NPC
  2326. //-----------------------------------------------------------------------------
  2327. void CNPC_Vortigaunt::InputEndCarryNPC( inputdata_t &indputdata )
  2328. {
  2329. m_bCarryingNPC = false;
  2330. }
  2331. //-----------------------------------------------------------------------------
  2332. // Purpose: Turn off flinching under certain circumstances
  2333. //-----------------------------------------------------------------------------
  2334. bool CNPC_Vortigaunt::CanFlinch( void )
  2335. {
  2336. if ( IsActiveDynamicInteraction() )
  2337. return false;
  2338. if ( IsPlayingGesture( ACT_GESTURE_RANGE_ATTACK1 ) )
  2339. return false;
  2340. if ( IsCurSchedule( SCHED_VORTIGAUNT_DISPEL_ANTLIONS ) || IsCurSchedule( SCHED_RANGE_ATTACK1 ) )
  2341. return false;
  2342. return BaseClass::CanFlinch();
  2343. }
  2344. //-----------------------------------------------------------------------------
  2345. // Purpose:
  2346. //-----------------------------------------------------------------------------
  2347. void CNPC_Vortigaunt::OnUpdateShotRegulator( void )
  2348. {
  2349. // Do nothing, we're not really running this code in a normal manner
  2350. GetShotRegulator()->SetBurstInterval( 2.0f, 2.0f );
  2351. GetShotRegulator()->SetBurstShotCountRange( 1, 1 );
  2352. GetShotRegulator()->SetRestInterval( 2.0f, 2.0f );
  2353. }
  2354. /*
  2355. IMPLEMENT_SERVERCLASS_ST( CVortigauntChargeToken, DT_VortigauntChargeToken )
  2356. SendPropFloat( SENDINFO(m_flFadeOutTime), 0, SPROP_NOSCALE),
  2357. SendPropBool( SENDINFO(m_bFadeOut) ),
  2358. SendPropFloat( SENDINFO(m_flScale), 0, SPROP_NOSCALE),
  2359. END_SEND_TABLE()
  2360. */
  2361. //------------------------------------------------------------------------------
  2362. //
  2363. // Schedules
  2364. //
  2365. //------------------------------------------------------------------------------
  2366. AI_BEGIN_CUSTOM_NPC( npc_vortigaunt, CNPC_Vortigaunt )
  2367. DECLARE_USES_SCHEDULE_PROVIDER( CAI_LeadBehavior )
  2368. DECLARE_TASK(TASK_VORTIGAUNT_HEAL)
  2369. DECLARE_TASK(TASK_VORTIGAUNT_EXTRACT)
  2370. DECLARE_TASK(TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT)
  2371. DECLARE_TASK(TASK_VORTIGAUNT_WAIT_FOR_PLAYER)
  2372. DECLARE_TASK( TASK_VORTIGAUNT_EXTRACT_WARMUP )
  2373. DECLARE_TASK( TASK_VORTIGAUNT_EXTRACT_COOLDOWN )
  2374. DECLARE_TASK( TASK_VORTIGAUNT_GET_HEAL_TARGET )
  2375. DECLARE_TASK( TASK_VORTIGAUNT_DISPEL_ANTLIONS )
  2376. DECLARE_ACTIVITY( ACT_VORTIGAUNT_AIM)
  2377. DECLARE_ACTIVITY( ACT_VORTIGAUNT_START_HEAL )
  2378. DECLARE_ACTIVITY( ACT_VORTIGAUNT_HEAL_LOOP )
  2379. DECLARE_ACTIVITY( ACT_VORTIGAUNT_END_HEAL )
  2380. DECLARE_ACTIVITY( ACT_VORTIGAUNT_TO_ACTION )
  2381. DECLARE_ACTIVITY( ACT_VORTIGAUNT_TO_IDLE )
  2382. DECLARE_ACTIVITY( ACT_VORTIGAUNT_HEAL )
  2383. DECLARE_ACTIVITY( ACT_VORTIGAUNT_DISPEL )
  2384. DECLARE_ACTIVITY( ACT_VORTIGAUNT_ANTLION_THROW )
  2385. DECLARE_CONDITION( COND_VORTIGAUNT_CAN_HEAL )
  2386. DECLARE_CONDITION( COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR )
  2387. DECLARE_CONDITION( COND_VORTIGAUNT_HEAL_TARGET_BLOCKED )
  2388. DECLARE_CONDITION( COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US )
  2389. DECLARE_CONDITION( COND_VORTIGAUNT_HEAL_VALID )
  2390. DECLARE_CONDITION( COND_VORTIGAUNT_DISPEL_ANTLIONS )
  2391. DECLARE_SQUADSLOT( SQUAD_SLOT_HEAL_PLAYER )
  2392. DECLARE_ANIMEVENT( AE_VORTIGAUNT_CLAW_LEFT )
  2393. DECLARE_ANIMEVENT( AE_VORTIGAUNT_CLAW_RIGHT )
  2394. DECLARE_ANIMEVENT( AE_VORTIGAUNT_ZAP_POWERUP )
  2395. DECLARE_ANIMEVENT( AE_VORTIGAUNT_ZAP_SHOOT )
  2396. DECLARE_ANIMEVENT( AE_VORTIGAUNT_ZAP_DONE )
  2397. DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_STARTGLOW )
  2398. DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_STARTBEAMS )
  2399. DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_STARTSOUND )
  2400. DECLARE_ANIMEVENT( AE_VORTIGAUNT_SWING_SOUND )
  2401. DECLARE_ANIMEVENT( AE_VORTIGAUNT_SHOOT_SOUNDSTART )
  2402. DECLARE_ANIMEVENT( AE_VORTIGAUNT_HEAL_PAUSE )
  2403. DECLARE_ANIMEVENT( AE_VORTIGAUNT_START_DISPEL )
  2404. DECLARE_ANIMEVENT( AE_VORTIGAUNT_ACCEL_DISPEL )
  2405. DECLARE_ANIMEVENT( AE_VORTIGAUNT_DISPEL )
  2406. DECLARE_ANIMEVENT( AE_VORTIGAUNT_START_HURT_GLOW )
  2407. DECLARE_ANIMEVENT( AE_VORTIGAUNT_STOP_HURT_GLOW )
  2408. DECLARE_ANIMEVENT( AE_VORTIGAUNT_START_HEAL_GLOW )
  2409. DECLARE_ANIMEVENT( AE_VORTIGAUNT_STOP_HEAL_GLOW )
  2410. //=========================================================
  2411. // > SCHED_VORTIGAUNT_RANGE_ATTACK
  2412. //=========================================================
  2413. DEFINE_SCHEDULE
  2414. (
  2415. SCHED_VORTIGAUNT_RANGE_ATTACK,
  2416. " Tasks"
  2417. " TASK_STOP_MOVING 0"
  2418. " TASK_FACE_IDEAL 0"
  2419. " TASK_ANNOUNCE_ATTACK 0"
  2420. " TASK_RANGE_ATTACK1 0"
  2421. " TASK_WAIT 0.2" // Wait a sec before killing beams
  2422. ""
  2423. " Interrupts"
  2424. " COND_NO_CUSTOM_INTERRUPTS"
  2425. );
  2426. //=========================================================
  2427. // > SCHED_VORTIGAUNT_HEAL
  2428. //=========================================================
  2429. DEFINE_SCHEDULE
  2430. (
  2431. SCHED_VORTIGAUNT_HEAL,
  2432. " Tasks"
  2433. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_VORTIGAUNT_STAND"
  2434. " TASK_STOP_MOVING 0"
  2435. " TASK_VORTIGAUNT_GET_HEAL_TARGET 0"
  2436. " TASK_GET_PATH_TO_TARGET 0"
  2437. " TASK_MOVE_TO_TARGET_RANGE 350"
  2438. " TASK_STOP_MOVING 0"
  2439. " TASK_FACE_PLAYER 0"
  2440. " TASK_VORTIGAUNT_HEAL 0"
  2441. ""
  2442. " Interrupts"
  2443. " COND_HEAVY_DAMAGE"
  2444. );
  2445. //=========================================================
  2446. // > SCHED_VORTIGAUNT_STAND
  2447. //=========================================================
  2448. DEFINE_SCHEDULE
  2449. (
  2450. SCHED_VORTIGAUNT_STAND,
  2451. " Tasks"
  2452. " TASK_STOP_MOVING 0"
  2453. " TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
  2454. " TASK_WAIT 2" // repick IDLESTAND every two seconds."
  2455. ""
  2456. " Interrupts"
  2457. " COND_NEW_ENEMY"
  2458. " COND_LIGHT_DAMAGE"
  2459. " COND_HEAVY_DAMAGE"
  2460. " COND_SMELL"
  2461. " COND_PROVOKED"
  2462. " COND_HEAR_COMBAT"
  2463. " COND_HEAR_DANGER"
  2464. " COND_VORTIGAUNT_DISPEL_ANTLIONS"
  2465. " COND_VORTIGAUNT_CAN_HEAL"
  2466. );
  2467. //=========================================================
  2468. // > SCHED_VORTIGAUNT_EXTRACT_BUGBAIT
  2469. //=========================================================
  2470. DEFINE_SCHEDULE
  2471. (
  2472. SCHED_VORTIGAUNT_EXTRACT_BUGBAIT,
  2473. " Tasks"
  2474. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_VORTIGAUNT_STAND"
  2475. " TASK_STOP_MOVING 0"
  2476. " TASK_GET_PATH_TO_TARGET 0"
  2477. " TASK_MOVE_TO_TARGET_RANGE 128" // Move within 128 of target ent (client)
  2478. " TASK_STOP_MOVING 0"
  2479. " TASK_VORTIGAUNT_WAIT_FOR_PLAYER 0"
  2480. " TASK_SPEAK_SENTENCE 500" // Start extracting sentence
  2481. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  2482. " TASK_FACE_TARGET 0"
  2483. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  2484. " TASK_VORTIGAUNT_EXTRACT_WARMUP 0"
  2485. " TASK_VORTIGAUNT_EXTRACT 0"
  2486. " TASK_VORTIGAUNT_EXTRACT_COOLDOWN 0"
  2487. " TASK_VORTIGAUNT_FIRE_EXTRACT_OUTPUT 0"
  2488. " TASK_SPEAK_SENTENCE 501" // Finish extracting sentence
  2489. " TASK_WAIT_FOR_SPEAK_FINISH 1"
  2490. " TASK_WAIT 2"
  2491. ""
  2492. " Interrupts"
  2493. )
  2494. //=========================================================
  2495. // > SCHED_VORTIGAUNT_FACE_PLAYER
  2496. //=========================================================
  2497. DEFINE_SCHEDULE
  2498. (
  2499. SCHED_VORTIGAUNT_FACE_PLAYER,
  2500. " Tasks"
  2501. " TASK_STOP_MOVING 0"
  2502. " TASK_TARGET_PLAYER 0"
  2503. " TASK_FACE_PLAYER 0"
  2504. " TASK_WAIT 3"
  2505. ""
  2506. " Interrupts"
  2507. " COND_NEW_ENEMY"
  2508. " COND_LIGHT_DAMAGE"
  2509. " COND_HEAVY_DAMAGE"
  2510. " COND_VORTIGAUNT_DISPEL_ANTLIONS"
  2511. " COND_VORTIGAUNT_HEAL_TARGET_TOO_FAR"
  2512. " COND_VORTIGAUNT_HEAL_TARGET_BLOCKED"
  2513. " COND_VORTIGAUNT_HEAL_TARGET_BEHIND_US"
  2514. );
  2515. //=========================================================
  2516. // > SCHED_VORTIGAUNT_RUN_TO_PLAYER
  2517. //=========================================================
  2518. DEFINE_SCHEDULE
  2519. (
  2520. SCHED_VORTIGAUNT_RUN_TO_PLAYER,
  2521. " Tasks"
  2522. " TASK_TARGET_PLAYER 0"
  2523. " TASK_GET_PATH_TO_TARGET 0"
  2524. " TASK_MOVE_TO_TARGET_RANGE 350"
  2525. ""
  2526. " Interrupts"
  2527. " COND_HEAVY_DAMAGE"
  2528. );
  2529. //=========================================================
  2530. // > SCHED_VORTIGAUNT_DISPEL_ANTLIONS
  2531. //=========================================================
  2532. DEFINE_SCHEDULE
  2533. (
  2534. SCHED_VORTIGAUNT_DISPEL_ANTLIONS,
  2535. " Tasks"
  2536. " TASK_VORTIGAUNT_DISPEL_ANTLIONS 0"
  2537. ""
  2538. " Interrupts"
  2539. " COND_NO_CUSTOM_INTERRUPTS"
  2540. );
  2541. //=========================================================
  2542. //
  2543. //=========================================================
  2544. DEFINE_SCHEDULE
  2545. (
  2546. SCHED_VORT_FLEE_FROM_BEST_SOUND,
  2547. " Tasks"
  2548. " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_COWER"
  2549. " TASK_GET_PATH_AWAY_FROM_BEST_SOUND 600"
  2550. " TASK_RUN_PATH_TIMED 1.5"
  2551. " TASK_STOP_MOVING 0"
  2552. ""
  2553. " Interrupts"
  2554. )
  2555. //=========================================================
  2556. // > AlertFace best sound
  2557. //=========================================================
  2558. DEFINE_SCHEDULE
  2559. (
  2560. SCHED_VORT_ALERT_FACE_BESTSOUND,
  2561. " Tasks"
  2562. " TASK_STORE_BESTSOUND_REACTORIGIN_IN_SAVEPOSITION 0"
  2563. " TASK_STOP_MOVING 0"
  2564. " TASK_FACE_SAVEPOSITION 0"
  2565. ""
  2566. " Interrupts"
  2567. " COND_NEW_ENEMY"
  2568. " COND_SEE_FEAR"
  2569. " COND_LIGHT_DAMAGE"
  2570. " COND_HEAVY_DAMAGE"
  2571. " COND_PROVOKED"
  2572. " COND_HEAR_DANGER"
  2573. );
  2574. AI_END_CUSTOM_NPC()
  2575. //=============================================================================
  2576. //
  2577. // Charge Token
  2578. //
  2579. //=============================================================================
  2580. LINK_ENTITY_TO_CLASS( vort_charge_token, CVortigauntChargeToken );
  2581. BEGIN_DATADESC( CVortigauntChargeToken )
  2582. DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
  2583. DEFINE_FIELD( m_flLifetime, FIELD_TIME ),
  2584. DEFINE_FIELD( m_bFadeOut, FIELD_BOOLEAN ),
  2585. DEFINE_ENTITYFUNC( SeekThink ),
  2586. DEFINE_ENTITYFUNC( SeekTouch ),
  2587. END_DATADESC()
  2588. IMPLEMENT_SERVERCLASS_ST( CVortigauntChargeToken, DT_VortigauntChargeToken )
  2589. SendPropBool( SENDINFO(m_bFadeOut) ),
  2590. END_SEND_TABLE()
  2591. CVortigauntChargeToken::CVortigauntChargeToken( void ) :
  2592. m_hTarget( NULL )
  2593. {
  2594. m_bFadeOut = false;
  2595. }
  2596. //-----------------------------------------------------------------------------
  2597. // Purpose: Create a charge token for the player to collect
  2598. // Input : &vecOrigin - Where we start
  2599. // *pOwner - Who created us
  2600. // *pTarget - Who we're seeking towards
  2601. //-----------------------------------------------------------------------------
  2602. CVortigauntChargeToken *CVortigauntChargeToken::CreateChargeToken( const Vector &vecOrigin, CBaseEntity *pOwner, CBaseEntity *pTarget )
  2603. {
  2604. CVortigauntChargeToken *pToken = (CVortigauntChargeToken *) CreateEntityByName( "vort_charge_token" );
  2605. if ( pToken == NULL )
  2606. return NULL;
  2607. // Set up our internal data
  2608. UTIL_SetOrigin( pToken, vecOrigin );
  2609. pToken->SetOwnerEntity( pOwner );
  2610. pToken->SetTargetEntity( pTarget );
  2611. pToken->SetThink( &CVortigauntChargeToken::SeekThink );
  2612. pToken->SetTouch( &CVortigauntChargeToken::SeekTouch );
  2613. pToken->Spawn();
  2614. // Start out at the same velocity as our owner
  2615. Vector vecInitialVelocity;
  2616. CBaseAnimating *pAnimating = dynamic_cast<CBaseAnimating *>(pOwner);
  2617. if ( pAnimating != NULL )
  2618. {
  2619. vecInitialVelocity = pAnimating->GetGroundSpeedVelocity();
  2620. }
  2621. else
  2622. {
  2623. vecInitialVelocity = pTarget->GetSmoothedVelocity();
  2624. }
  2625. // Start out at that speed
  2626. pToken->SetAbsVelocity( vecInitialVelocity );
  2627. return pToken;
  2628. }
  2629. //-----------------------------------------------------------------------------
  2630. // Purpose:
  2631. //-----------------------------------------------------------------------------
  2632. void CVortigauntChargeToken::Precache( void )
  2633. {
  2634. PrecacheParticleSystem( "vortigaunt_charge_token" );
  2635. }
  2636. //-----------------------------------------------------------------------------
  2637. // Purpose: We want to move through grates!
  2638. //-----------------------------------------------------------------------------
  2639. unsigned int CVortigauntChargeToken::PhysicsSolidMaskForEntity( void ) const
  2640. {
  2641. return MASK_SHOT;
  2642. }
  2643. //-----------------------------------------------------------------------------
  2644. // Purpose:
  2645. //-----------------------------------------------------------------------------
  2646. void CVortigauntChargeToken::Spawn( void )
  2647. {
  2648. Precache();
  2649. // Point-sized
  2650. UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
  2651. SetMoveType( MOVETYPE_FLY );
  2652. SetSolid( SOLID_BBOX );
  2653. SetSolidFlags( FSOLID_TRIGGER | FSOLID_NOT_SOLID );
  2654. SetGravity( 0.0f );
  2655. // No model but we still need to force this!
  2656. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  2657. SetNextThink( gpGlobals->curtime + 0.05f );
  2658. m_flLifetime = gpGlobals->curtime + VORTIGAUNT_CURE_LIFESPAN;
  2659. BaseClass::Spawn();
  2660. }
  2661. //-----------------------------------------------------------------------------
  2662. // Purpose: Creates an influence vector which causes the token to move away from obstructions
  2663. //-----------------------------------------------------------------------------
  2664. Vector CVortigauntChargeToken::GetSteerVector( const Vector &vecForward )
  2665. {
  2666. Vector vecSteer = vec3_origin;
  2667. Vector vecRight, vecUp;
  2668. VectorVectors( vecForward, vecRight, vecUp );
  2669. // Use two probes fanned out a head of us
  2670. Vector vecProbe;
  2671. float flSpeed = GetAbsVelocity().Length();
  2672. // Try right
  2673. vecProbe = vecForward + vecRight;
  2674. vecProbe *= flSpeed;
  2675. // We ignore multiple targets
  2676. CTraceFilterSimpleList filterSkip( COLLISION_GROUP_NONE );
  2677. filterSkip.AddEntityToIgnore( this );
  2678. filterSkip.AddEntityToIgnore( GetOwnerEntity() );
  2679. filterSkip.AddEntityToIgnore( m_hTarget );
  2680. trace_t tr;
  2681. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecProbe, MASK_SHOT, &filterSkip, &tr );
  2682. vecSteer -= vecRight * 100.0f * ( 1.0f - tr.fraction );
  2683. // Try left
  2684. vecProbe = vecForward - vecRight;
  2685. vecProbe *= flSpeed;
  2686. UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecProbe, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
  2687. vecSteer += vecRight * 100.0f * ( 1.0f - tr.fraction );
  2688. return vecSteer;
  2689. }
  2690. #define VTOKEN_MAX_SPEED 320.0f // U/sec
  2691. #define VTOKEN_ACCEL_SPEED 320.0f // '
  2692. //-----------------------------------------------------------------------------
  2693. // Purpose: Move towards our target entity with accel/decel parameters
  2694. //-----------------------------------------------------------------------------
  2695. void CVortigauntChargeToken::SeekThink( void )
  2696. {
  2697. // Move away from the creator and towards the target
  2698. if ( m_hTarget == NULL || m_flLifetime < gpGlobals->curtime )
  2699. {
  2700. // TODO: Play an extinguish sound and fade out
  2701. FadeAndDie();
  2702. return;
  2703. }
  2704. // Find the direction towards our goal and start to go there
  2705. Vector vecDir = ( m_hTarget->WorldSpaceCenter() - GetAbsOrigin() );
  2706. VectorNormalize( vecDir );
  2707. float flSpeed = GetAbsVelocity().Length();
  2708. float flDelta = gpGlobals->curtime - GetLastThink();
  2709. if ( flSpeed < VTOKEN_MAX_SPEED )
  2710. {
  2711. // Accelerate by the desired amount
  2712. flSpeed += ( VTOKEN_ACCEL_SPEED * flDelta );
  2713. if ( flSpeed > VTOKEN_MAX_SPEED )
  2714. {
  2715. flSpeed = VTOKEN_MAX_SPEED;
  2716. }
  2717. }
  2718. // Steer!
  2719. Vector vecRight, vecUp;
  2720. VectorVectors( vecDir, vecRight, vecUp );
  2721. Vector vecOffset = vec3_origin;
  2722. vecOffset += vecUp * cos( gpGlobals->curtime * 20.0f ) * 200.0f * gpGlobals->frametime;
  2723. vecOffset += vecRight * sin( gpGlobals->curtime * 15.0f ) * 200.0f * gpGlobals->frametime;
  2724. vecOffset += GetSteerVector( vecDir );
  2725. SetAbsVelocity( ( vecDir * flSpeed ) + vecOffset );
  2726. SetNextThink( gpGlobals->curtime + 0.05f );
  2727. }
  2728. //-----------------------------------------------------------------------------
  2729. // Purpose:
  2730. //-----------------------------------------------------------------------------
  2731. void CVortigauntChargeToken::SeekTouch( CBaseEntity *pOther )
  2732. {
  2733. // Make sure this is a player
  2734. CBasePlayer *pPlayer = ToBasePlayer( pOther );
  2735. if ( pPlayer == NULL )
  2736. return;
  2737. // FIXME: This probably isn't that interesting for single player missions
  2738. if ( pPlayer != m_hTarget )
  2739. return;
  2740. // TODO: Play a special noise for this event!
  2741. EmitSound( "NPC_Vortigaunt.SuitOn" );
  2742. // Charge the suit's armor
  2743. if ( pPlayer->ArmorValue() < sk_vortigaunt_armor_charge.GetInt() )
  2744. {
  2745. pPlayer->IncrementArmorValue( sk_vortigaunt_armor_charge_per_token.GetInt()+random->RandomInt( -1, 1 ), sk_vortigaunt_armor_charge.GetInt() );
  2746. }
  2747. // Stay attached to the thing we hit as we fade away
  2748. SetSolidFlags( FSOLID_NOT_SOLID );
  2749. SetMoveType( MOVETYPE_NONE );
  2750. SetParent( pOther );
  2751. // TODO: Play a "poof!" effect here?
  2752. FadeAndDie();
  2753. }
  2754. //-----------------------------------------------------------------------------
  2755. // Purpose:
  2756. // Input : flTime -
  2757. //-----------------------------------------------------------------------------
  2758. void CVortigauntChargeToken::FadeAndDie( void )
  2759. {
  2760. SetTouch( NULL );
  2761. SetAbsVelocity( vec3_origin );
  2762. m_bFadeOut = true;
  2763. SetThink( &CBaseEntity::SUB_Remove );
  2764. SetNextThink( gpGlobals->curtime + 2.0f );
  2765. }
  2766. //=============================================================================
  2767. //
  2768. // Dispel Effect
  2769. //
  2770. //=============================================================================
  2771. LINK_ENTITY_TO_CLASS( vort_effect_dispel, CVortigauntEffectDispel );
  2772. BEGIN_DATADESC( CVortigauntEffectDispel )
  2773. DEFINE_FIELD( m_bFadeOut, FIELD_BOOLEAN ),
  2774. END_DATADESC()
  2775. IMPLEMENT_SERVERCLASS_ST( CVortigauntEffectDispel, DT_VortigauntEffectDispel )
  2776. SendPropBool( SENDINFO(m_bFadeOut) ),
  2777. END_SEND_TABLE()
  2778. CVortigauntEffectDispel::CVortigauntEffectDispel( void )
  2779. {
  2780. m_bFadeOut = false;
  2781. }
  2782. //-----------------------------------------------------------------------------
  2783. // Purpose: Create a charge token for the player to collect
  2784. // Input : &vecOrigin - Where we start
  2785. // *pOwner - Who created us
  2786. // *pTarget - Who we're seeking towards
  2787. //-----------------------------------------------------------------------------
  2788. CVortigauntEffectDispel *CVortigauntEffectDispel::CreateEffectDispel( const Vector &vecOrigin, CBaseEntity *pOwner, CBaseEntity *pTarget )
  2789. {
  2790. CVortigauntEffectDispel *pToken = (CVortigauntEffectDispel *) CreateEntityByName( "vort_effect_dispel" );
  2791. if ( pToken == NULL )
  2792. return NULL;
  2793. // Set up our internal data
  2794. UTIL_SetOrigin( pToken, vecOrigin );
  2795. pToken->SetOwnerEntity( pOwner );
  2796. pToken->Spawn();
  2797. return pToken;
  2798. }
  2799. //-----------------------------------------------------------------------------
  2800. // Purpose:
  2801. //-----------------------------------------------------------------------------
  2802. void CVortigauntEffectDispel::Spawn( void )
  2803. {
  2804. Precache();
  2805. UTIL_SetSize( this, Vector( -8, -8, -8 ), Vector( 8, 8, 8 ) );
  2806. SetSolid( SOLID_BBOX );
  2807. SetSolidFlags( FSOLID_NOT_SOLID );
  2808. // No model but we still need to force this!
  2809. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  2810. BaseClass::Spawn();
  2811. }
  2812. //-----------------------------------------------------------------------------
  2813. // Purpose:
  2814. // Input : flTime -
  2815. //-----------------------------------------------------------------------------
  2816. void CVortigauntEffectDispel::FadeAndDie( void )
  2817. {
  2818. m_bFadeOut = true;
  2819. SetThink( &CBaseEntity::SUB_Remove );
  2820. SetNextThink( gpGlobals->curtime + 2.0f );
  2821. }
  2822. //=============================================================================
  2823. //
  2824. // Flesh effect target (used for orchestrating the "Invisible Alyx" moment
  2825. //
  2826. //=============================================================================
  2827. #ifdef HL2_EPISODIC
  2828. class CFleshEffectTarget : public CPointEntity
  2829. {
  2830. DECLARE_CLASS( CFleshEffectTarget, CPointEntity );
  2831. public:
  2832. void InputSetRadius( inputdata_t &inputData );
  2833. virtual void Spawn( void )
  2834. {
  2835. BaseClass::Spawn();
  2836. AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
  2837. }
  2838. private:
  2839. CNetworkVar( float, m_flRadius );
  2840. CNetworkVar( float, m_flScaleTime );
  2841. DECLARE_SERVERCLASS();
  2842. DECLARE_DATADESC();
  2843. };
  2844. LINK_ENTITY_TO_CLASS( point_flesh_effect_target, CFleshEffectTarget );
  2845. BEGIN_DATADESC( CFleshEffectTarget )
  2846. DEFINE_FIELD( m_flScaleTime, FIELD_TIME ),
  2847. DEFINE_KEYFIELD( m_flRadius, FIELD_FLOAT, "radius" ),
  2848. DEFINE_INPUTFUNC( FIELD_VECTOR, "SetRadius", InputSetRadius ),
  2849. END_DATADESC()
  2850. IMPLEMENT_SERVERCLASS_ST( CFleshEffectTarget, DT_FleshEffectTarget )
  2851. SendPropFloat( SENDINFO(m_flRadius), 0, SPROP_NOSCALE),
  2852. SendPropFloat( SENDINFO(m_flScaleTime), 0, SPROP_NOSCALE),
  2853. END_SEND_TABLE()
  2854. void CFleshEffectTarget::InputSetRadius( inputdata_t &inputData )
  2855. {
  2856. Vector vecRadius;
  2857. inputData.value.Vector3D( vecRadius );
  2858. m_flRadius = vecRadius.x;
  2859. m_flScaleTime = vecRadius.y;
  2860. }
  2861. #endif // HL2_EPISODIC