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.

743 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Alien slave monster
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "beam_shared.h"
  9. #include "game.h"
  10. #include "ai_default.h"
  11. #include "ai_schedule.h"
  12. #include "ai_hull.h"
  13. #include "ai_route.h"
  14. #include "ai_squad.h"
  15. #include "npcevent.h"
  16. #include "gib.h"
  17. //#include "AI_Interactions.h"
  18. #include "ndebugoverlay.h"
  19. #include "vstdlib/random.h"
  20. #include "engine/IEngineSound.h"
  21. #include "hl1_npc_vortigaunt.h"
  22. #include "soundent.h"
  23. #include "player.h"
  24. #include "IEffects.h"
  25. #include "basecombatweapon.h"
  26. #include "SoundEmitterSystem/isoundemittersystembase.h"
  27. //=========================================================
  28. // Monster's Anim Events Go Here
  29. //=========================================================
  30. #define ISLAVE_AE_CLAW ( 1 )
  31. #define ISLAVE_AE_CLAWRAKE ( 2 )
  32. #define ISLAVE_AE_ZAP_POWERUP ( 3 )
  33. #define ISLAVE_AE_ZAP_SHOOT ( 4 )
  34. #define ISLAVE_AE_ZAP_DONE ( 5 )
  35. ConVar sk_islave_health( "sk_islave_health","50");
  36. ConVar sk_islave_dmg_claw( "sk_islave_dmg_claw","8");
  37. ConVar sk_islave_dmg_clawrake( "sk_islave_dmg_clawrake","25");
  38. ConVar sk_islave_dmg_zap( "sk_islave_dmg_zap","15");
  39. LINK_ENTITY_TO_CLASS( monster_alien_slave, CNPC_Vortigaunt );
  40. BEGIN_DATADESC( CNPC_Vortigaunt )
  41. DEFINE_FIELD( m_iBravery, FIELD_INTEGER ),
  42. DEFINE_ARRAY( m_pBeam, FIELD_CLASSPTR, VORTIGAUNT_MAX_BEAMS ),
  43. DEFINE_FIELD( m_iBeams, FIELD_INTEGER ),
  44. DEFINE_FIELD( m_flNextAttack, FIELD_TIME ),
  45. DEFINE_FIELD( m_iVoicePitch, FIELD_INTEGER ),
  46. DEFINE_FIELD( m_hDead, FIELD_EHANDLE ),
  47. END_DATADESC()
  48. enum
  49. {
  50. SCHED_VORTIGAUNT_ATTACK = LAST_SHARED_SCHEDULE,
  51. };
  52. #define VORTIGAUNT_IGNORE_PLAYER 64
  53. //=========================================================
  54. // Spawn
  55. //=========================================================
  56. void CNPC_Vortigaunt::Spawn()
  57. {
  58. Precache( );
  59. SetModel( "models/islave.mdl" );
  60. SetRenderColor( 255, 255, 255, 255 );
  61. SetHullType(HULL_HUMAN);
  62. SetHullSizeNormal();
  63. SetSolid( SOLID_BBOX );
  64. AddSolidFlags( FSOLID_NOT_STANDABLE );
  65. SetMoveType( MOVETYPE_STEP );
  66. m_bloodColor = BLOOD_COLOR_GREEN;
  67. ClearEffects();
  68. m_iHealth = sk_islave_health.GetFloat();
  69. //pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
  70. m_flFieldOfView = VIEW_FIELD_WIDE;
  71. m_NPCState = NPC_STATE_NONE;
  72. m_iVoicePitch = random->RandomInt( 85, 110 );
  73. CapabilitiesClear();
  74. CapabilitiesAdd( bits_CAP_MOVE_GROUND );
  75. CapabilitiesAdd( bits_CAP_SQUAD );
  76. CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP );
  77. CapabilitiesAdd ( bits_CAP_INNATE_RANGE_ATTACK1 );
  78. CapabilitiesAdd ( bits_CAP_INNATE_MELEE_ATTACK1 );
  79. m_iBravery = 0;
  80. NPCInit();
  81. BaseClass::Spawn();
  82. }
  83. //=========================================================
  84. // Precache - precaches all resources this monster needs
  85. //=========================================================
  86. void CNPC_Vortigaunt::Precache()
  87. {
  88. BaseClass::Precache();
  89. PrecacheModel("models/islave.mdl");
  90. PrecacheModel("sprites/lgtning.vmt");
  91. PrecacheScriptSound( "Vortigaunt.Pain" );
  92. PrecacheScriptSound( "Vortigaunt.Die" );
  93. PrecacheScriptSound( "Vortigaunt.AttackHit" );
  94. PrecacheScriptSound( "Vortigaunt.AttackMiss" );
  95. PrecacheScriptSound( "Vortigaunt.ZapPowerup" );
  96. PrecacheScriptSound( "Vortigaunt.ZapShoot" );
  97. }
  98. Disposition_t CNPC_Vortigaunt::IRelationType ( CBaseEntity *pTarget )
  99. {
  100. if ( (pTarget->IsPlayer()) )
  101. {
  102. if ( (GetSpawnFlags() & VORTIGAUNT_IGNORE_PLAYER ) && !HasMemory( bits_MEMORY_PROVOKED ) )
  103. return D_NU;
  104. }
  105. return BaseClass::IRelationType( pTarget );
  106. }
  107. //-----------------------------------------------------------------------------
  108. // Purpose:
  109. // Input :
  110. // Output :
  111. //-----------------------------------------------------------------------------
  112. Class_T CNPC_Vortigaunt::Classify ( void )
  113. {
  114. return CLASS_ALIEN_MILITARY;
  115. }
  116. void CNPC_Vortigaunt::CallForHelp( char *szClassname, float flDist, CBaseEntity * pEnemy, Vector &vecLocation )
  117. {
  118. // ALERT( at_aiconsole, "help " );
  119. // skip ones not on my netname
  120. if ( !m_pSquad )
  121. return;
  122. AISquadIter_t iter;
  123. for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
  124. {
  125. float d = ( GetAbsOrigin() - pSquadMember->GetAbsOrigin() ).Length();
  126. if ( d < flDist )
  127. {
  128. pSquadMember->Remember( bits_MEMORY_PROVOKED );
  129. pSquadMember->UpdateEnemyMemory( pEnemy, vecLocation );
  130. }
  131. }
  132. }
  133. //=========================================================
  134. // ALertSound - scream
  135. //=========================================================
  136. void CNPC_Vortigaunt::AlertSound( void )
  137. {
  138. if ( GetEnemy() != NULL )
  139. {
  140. SENTENCEG_PlayRndSz( edict(), "SLV_ALERT", 0.85, SNDLVL_NORM, 0, m_iVoicePitch );
  141. Vector vecTmp = GetEnemy()->GetAbsOrigin();
  142. CallForHelp( "monster_alien_slave", 512, GetEnemy(), vecTmp );
  143. }
  144. }
  145. //=========================================================
  146. // IdleSound
  147. //=========================================================
  148. void CNPC_Vortigaunt::IdleSound( void )
  149. {
  150. if ( random->RandomInt( 0, 2 ) == 0)
  151. SENTENCEG_PlayRndSz( edict(), "SLV_IDLE", 0.85, SNDLVL_NORM, 0, m_iVoicePitch);
  152. }
  153. //=========================================================
  154. // PainSound
  155. //=========================================================
  156. void CNPC_Vortigaunt::PainSound( const CTakeDamageInfo &info )
  157. {
  158. if ( random->RandomInt( 0, 2 ) == 0)
  159. {
  160. CPASAttenuationFilter filter( this );
  161. CSoundParameters params;
  162. if ( GetParametersForSound( "Vortigaunt.Pain", params, NULL ) )
  163. {
  164. EmitSound_t ep( params );
  165. params.pitch = m_iVoicePitch;
  166. EmitSound( filter, entindex(), ep );
  167. }
  168. }
  169. }
  170. //=========================================================
  171. // DieSound
  172. //=========================================================
  173. void CNPC_Vortigaunt::DeathSound( const CTakeDamageInfo &info )
  174. {
  175. CPASAttenuationFilter filter( this );
  176. CSoundParameters params;
  177. if ( GetParametersForSound( "Vortigaunt.Die", params, NULL ) )
  178. {
  179. EmitSound_t ep( params );
  180. params.pitch = m_iVoicePitch;
  181. EmitSound( filter, entindex(), ep );
  182. }
  183. }
  184. int CNPC_Vortigaunt::GetSoundInterests ( void )
  185. {
  186. return SOUND_WORLD |
  187. SOUND_COMBAT |
  188. SOUND_DANGER |
  189. SOUND_PLAYER;
  190. }
  191. void CNPC_Vortigaunt::Event_Killed( const CTakeDamageInfo &info )
  192. {
  193. ClearBeams( );
  194. BaseClass::Event_Killed( info );
  195. }
  196. //=========================================================
  197. // SetYawSpeed - allows each sequence to have a different
  198. // turn rate associated with it.
  199. //=========================================================
  200. float CNPC_Vortigaunt::MaxYawSpeed ( void )
  201. {
  202. float flYS;
  203. switch ( GetActivity() )
  204. {
  205. case ACT_WALK:
  206. flYS = 50;
  207. break;
  208. case ACT_RUN:
  209. flYS = 70;
  210. break;
  211. case ACT_IDLE:
  212. flYS = 50;
  213. break;
  214. default:
  215. flYS = 90;
  216. break;
  217. }
  218. return flYS;
  219. }
  220. //=========================================================
  221. // HandleAnimEvent - catches the monster-specific messages
  222. // that occur when tagged animation frames are played.
  223. //
  224. // Returns number of events handled, 0 if none.
  225. //=========================================================
  226. void CNPC_Vortigaunt::HandleAnimEvent( animevent_t *pEvent )
  227. {
  228. // ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
  229. switch( pEvent->event )
  230. {
  231. case ISLAVE_AE_CLAW:
  232. {
  233. // SOUND HERE!
  234. CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10), sk_islave_dmg_claw.GetFloat(), DMG_SLASH );
  235. CPASAttenuationFilter filter( this );
  236. if ( pHurt )
  237. {
  238. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  239. pHurt->ViewPunch( QAngle( 5, 0, -18 ) );
  240. // Play a random attack hit sound
  241. CSoundParameters params;
  242. if ( GetParametersForSound( "Vortigaunt.AttackHit", params, NULL ) )
  243. {
  244. EmitSound_t ep( params );
  245. params.pitch = m_iVoicePitch;
  246. EmitSound( filter, entindex(), ep );
  247. }
  248. }
  249. else
  250. {
  251. // Play a random attack miss sound
  252. CSoundParameters params;
  253. if ( GetParametersForSound( "Vortigaunt.AttackMiss", params, NULL ) )
  254. {
  255. EmitSound_t ep( params );
  256. params.pitch = m_iVoicePitch;
  257. EmitSound( filter, entindex(), ep );
  258. }
  259. }
  260. }
  261. break;
  262. case ISLAVE_AE_CLAWRAKE:
  263. {
  264. CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10), sk_islave_dmg_clawrake.GetFloat(), DMG_SLASH );
  265. CPASAttenuationFilter filter2( this );
  266. if ( pHurt )
  267. {
  268. if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
  269. pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
  270. CSoundParameters params;
  271. if ( GetParametersForSound( "Vortigaunt.AttackHit", params, NULL ) )
  272. {
  273. EmitSound_t ep( params );
  274. params.pitch = m_iVoicePitch;
  275. EmitSound( filter2, entindex(), ep );
  276. }
  277. }
  278. else
  279. {
  280. CSoundParameters params;
  281. if ( GetParametersForSound( "Vortigaunt.AttackMiss", params, NULL ) )
  282. {
  283. EmitSound_t ep( params );
  284. params.pitch = m_iVoicePitch;
  285. EmitSound( filter2, entindex(), ep );
  286. }
  287. }
  288. }
  289. break;
  290. case ISLAVE_AE_ZAP_POWERUP:
  291. {
  292. // speed up attack when on hard
  293. if ( g_iSkillLevel == SKILL_HARD )
  294. m_flPlaybackRate = 1.5;
  295. Vector v_forward;
  296. GetVectors( &v_forward, NULL, NULL );
  297. CBroadcastRecipientFilter filter;
  298. te->DynamicLight( filter, 0.0, &GetAbsOrigin(), 125, 200, 100, 2, 120, 0.2 / m_flPlaybackRate, 0 );
  299. if ( m_hDead != NULL )
  300. {
  301. WackBeam( -1, m_hDead );
  302. WackBeam( 1, m_hDead );
  303. }
  304. else
  305. {
  306. ArmBeam( -1 );
  307. ArmBeam( 1 );
  308. BeamGlow( );
  309. }
  310. CPASAttenuationFilter filter3( this );
  311. CSoundParameters params;
  312. if ( GetParametersForSound( "Vortigaunt.ZapPowerup", params, NULL ) )
  313. {
  314. EmitSound_t ep( params );
  315. ep.m_nPitch = 100 + m_iBeams * 10;
  316. EmitSound( filter3, entindex(), ep );
  317. }
  318. // Huh? Model doesn't have multiple texturegroups, commented this out. -LH
  319. // m_nSkin = m_iBeams / 2;
  320. }
  321. break;
  322. case ISLAVE_AE_ZAP_SHOOT:
  323. {
  324. ClearBeams( );
  325. if ( m_hDead != NULL )
  326. {
  327. Vector vecDest = m_hDead->GetAbsOrigin() + Vector( 0, 0, 38 );
  328. trace_t trace;
  329. UTIL_TraceHull( vecDest, vecDest, GetHullMins(), GetHullMaxs(),MASK_SOLID, m_hDead, COLLISION_GROUP_NONE, &trace );
  330. if ( !trace.startsolid )
  331. {
  332. CBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->GetAbsOrigin(), m_hDead->GetAbsAngles() );
  333. pNew->AddSpawnFlags( 1 );
  334. WackBeam( -1, pNew );
  335. WackBeam( 1, pNew );
  336. UTIL_Remove( m_hDead );
  337. break;
  338. }
  339. }
  340. ClearMultiDamage();
  341. ZapBeam( -1 );
  342. ZapBeam( 1 );
  343. CPASAttenuationFilter filter4( this );
  344. EmitSound( filter4, entindex(), "Vortigaunt.ZapShoot" );
  345. ApplyMultiDamage();
  346. m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 0.5, 4.0 );
  347. }
  348. break;
  349. case ISLAVE_AE_ZAP_DONE:
  350. {
  351. ClearBeams();
  352. }
  353. break;
  354. default:
  355. BaseClass::HandleAnimEvent( pEvent );
  356. break;
  357. }
  358. }
  359. //------------------------------------------------------------------------------
  360. // Purpose : For innate range attack
  361. // Input :
  362. // Output :
  363. //------------------------------------------------------------------------------
  364. int CNPC_Vortigaunt::RangeAttack1Conditions( float flDot, float flDist )
  365. {
  366. if ( GetEnemy() == NULL )
  367. return( COND_LOST_ENEMY );
  368. if ( gpGlobals->curtime < m_flNextAttack )
  369. return COND_NONE;
  370. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  371. return COND_NONE;
  372. return COND_CAN_RANGE_ATTACK1;
  373. }
  374. void CNPC_Vortigaunt::StartTask( const Task_t *pTask )
  375. {
  376. ClearBeams();
  377. BaseClass::StartTask( pTask );
  378. }
  379. //=========================================================
  380. // TakeDamage - get provoked when injured
  381. //=========================================================
  382. int CNPC_Vortigaunt::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
  383. {
  384. // don't slash one of your own
  385. if ( ( inputInfo.GetDamageType() & DMG_SLASH ) && inputInfo.GetAttacker() && IRelationType( inputInfo.GetAttacker() ) == D_NU )
  386. return 0;
  387. Remember( bits_MEMORY_PROVOKED );
  388. return BaseClass::OnTakeDamage_Alive( inputInfo );
  389. }
  390. void CNPC_Vortigaunt::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
  391. {
  392. if ( info.GetDamageType() & DMG_SHOCK )
  393. return;
  394. BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
  395. }
  396. //=========================================================
  397. //=========================================================
  398. int CNPC_Vortigaunt::SelectSchedule( void )
  399. {
  400. ClearBeams();
  401. switch ( m_NPCState )
  402. {
  403. case NPC_STATE_COMBAT:
  404. // dead enemy
  405. if ( HasCondition( COND_ENEMY_DEAD ) )
  406. {
  407. // call base class, all code to handle dead enemies is centralized there.
  408. return BaseClass::SelectSchedule();
  409. }
  410. if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
  411. return SCHED_RANGE_ATTACK1;
  412. if ( m_iHealth < 20 || m_iBravery < 0)
  413. {
  414. if ( !HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  415. {
  416. SetDefaultFailSchedule( SCHED_CHASE_ENEMY );
  417. if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
  418. return SCHED_TAKE_COVER_FROM_ENEMY;
  419. if ( HasCondition ( COND_SEE_ENEMY ) && HasCondition ( COND_ENEMY_FACING_ME ) )
  420. return SCHED_TAKE_COVER_FROM_ENEMY;
  421. }
  422. }
  423. break;
  424. }
  425. return BaseClass::SelectSchedule( );
  426. }
  427. int CNPC_Vortigaunt::TranslateSchedule( int scheduleType )
  428. {
  429. //Oops can't get to my enemy.
  430. if ( scheduleType == SCHED_CHASE_ENEMY_FAILED )
  431. {
  432. return SCHED_ESTABLISH_LINE_OF_FIRE;
  433. }
  434. switch ( scheduleType )
  435. {
  436. case SCHED_FAIL:
  437. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  438. {
  439. return ( SCHED_MELEE_ATTACK1 );
  440. }
  441. break;
  442. case SCHED_RANGE_ATTACK1:
  443. {
  444. //Adrian - HACK HACK! This should've been done up there ^^^^
  445. if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
  446. {
  447. return ( SCHED_MELEE_ATTACK1 );
  448. }
  449. return SCHED_VORTIGAUNT_ATTACK;
  450. }
  451. break;
  452. }
  453. return BaseClass::TranslateSchedule( scheduleType );
  454. }
  455. //=========================================================
  456. // ArmBeam - small beam from arm to nearby geometry
  457. //=========================================================
  458. void CNPC_Vortigaunt::ArmBeam( int side )
  459. {
  460. trace_t tr;
  461. float flDist = 1.0;
  462. if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
  463. return;
  464. Vector forward, right, up;
  465. Vector vecAim;
  466. AngleVectors( GetAbsAngles(), &forward, &right, &up );
  467. Vector vecSrc = GetAbsOrigin() + up * 36 + right * side * 16 + forward * 32;
  468. for (int i = 0; i < 3; i++)
  469. {
  470. vecAim = right * side * random->RandomFloat( 0, 1 ) + up * random->RandomFloat( -1, 1 );
  471. trace_t tr1;
  472. UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr1);
  473. if (flDist > tr1.fraction)
  474. {
  475. tr = tr1;
  476. flDist = tr.fraction;
  477. }
  478. }
  479. // Couldn't find anything close enough
  480. if ( flDist == 1.0 )
  481. return;
  482. if( tr.m_pEnt && tr.m_pEnt->m_takedamage && !tr.m_pEnt->IsNPC() )
  483. {
  484. CTakeDamageInfo info( this, this, 10, DMG_SHOCK );
  485. CalculateMeleeDamageForce( &info, vecAim, tr.endpos );
  486. tr.m_pEnt->TakeDamage( info );
  487. }
  488. UTIL_DecalTrace( &tr, "FadingScorch" );
  489. m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0f );
  490. if ( m_pBeam[m_iBeams] == NULL )
  491. return;
  492. m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
  493. m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
  494. m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
  495. m_pBeam[m_iBeams]->SetBrightness( 64 );
  496. m_pBeam[m_iBeams]->SetNoise( 12.8 );
  497. m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
  498. m_iBeams++;
  499. }
  500. //=========================================================
  501. // BeamGlow - brighten all beams
  502. //=========================================================
  503. void CNPC_Vortigaunt::BeamGlow( )
  504. {
  505. int b = m_iBeams * 32;
  506. if ( b > 255 )
  507. b = 255;
  508. for ( int i = 0; i < m_iBeams; i++ )
  509. {
  510. if ( m_pBeam[i] != NULL )
  511. {
  512. if ( m_pBeam[i]->GetBrightness() != 255 )
  513. m_pBeam[i]->SetBrightness( b );
  514. }
  515. }
  516. }
  517. //=========================================================
  518. // WackBeam - regenerate dead colleagues
  519. //=========================================================
  520. void CNPC_Vortigaunt::WackBeam( int side, CBaseEntity *pEntity )
  521. {
  522. Vector vecDest;
  523. if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
  524. return;
  525. if ( pEntity == NULL )
  526. return;
  527. m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0f );
  528. if ( m_pBeam[m_iBeams] == NULL )
  529. return;
  530. m_pBeam[m_iBeams]->PointEntInit( pEntity->WorldSpaceCenter(), this );
  531. m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
  532. m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
  533. m_pBeam[m_iBeams]->SetBrightness( 255 );
  534. m_pBeam[m_iBeams]->SetNoise( 12.8 );
  535. m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
  536. m_iBeams++;
  537. }
  538. //=========================================================
  539. // ZapBeam - heavy damage directly forward
  540. //=========================================================
  541. void CNPC_Vortigaunt::ZapBeam( int side )
  542. {
  543. Vector vecSrc, vecAim;
  544. trace_t tr;
  545. CBaseEntity *pEntity;
  546. if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
  547. return;
  548. Vector forward, right, up;
  549. AngleVectors( GetAbsAngles(), &forward, &right, &up );
  550. vecSrc = GetAbsOrigin() + up * 36;
  551. vecAim = GetShootEnemyDir( vecSrc );
  552. float deflection = 0.01;
  553. vecAim = vecAim + side * right * random->RandomFloat( 0, deflection ) + up * random->RandomFloat( -deflection, deflection );
  554. UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
  555. m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 5.0f );
  556. if ( m_pBeam[m_iBeams] == NULL )
  557. return;
  558. m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
  559. m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
  560. m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
  561. m_pBeam[m_iBeams]->SetBrightness( 255 );
  562. m_pBeam[m_iBeams]->SetNoise( 3.2f );
  563. m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
  564. m_iBeams++;
  565. pEntity = tr.m_pEnt;
  566. if ( pEntity != NULL && m_takedamage )
  567. {
  568. CTakeDamageInfo info( this, this, sk_islave_dmg_zap.GetFloat(), DMG_SHOCK );
  569. CalculateMeleeDamageForce( &info, vecAim, tr.endpos );
  570. pEntity->DispatchTraceAttack( info, vecAim, &tr );
  571. }
  572. }
  573. //=========================================================
  574. // ClearBeams - remove all beams
  575. //=========================================================
  576. void CNPC_Vortigaunt::ClearBeams( )
  577. {
  578. for (int i = 0; i < VORTIGAUNT_MAX_BEAMS; i++)
  579. {
  580. if (m_pBeam[i])
  581. {
  582. UTIL_Remove( m_pBeam[i] );
  583. m_pBeam[i] = NULL;
  584. }
  585. }
  586. m_iBeams = 0;
  587. m_nSkin = 0;
  588. }
  589. //------------------------------------------------------------------------------
  590. //
  591. // Schedules
  592. //
  593. //------------------------------------------------------------------------------
  594. AI_BEGIN_CUSTOM_NPC( monster_alien_slave, CNPC_Vortigaunt )
  595. //=========================================================
  596. // > SCHED_VORTIGAUNT_ATTACK
  597. //=========================================================
  598. DEFINE_SCHEDULE
  599. (
  600. SCHED_VORTIGAUNT_ATTACK,
  601. " Tasks"
  602. " TASK_STOP_MOVING 0"
  603. " TASK_FACE_IDEAL 0"
  604. " TASK_RANGE_ATTACK1 0"
  605. " "
  606. " Interrupts"
  607. " COND_CAN_MELEE_ATTACK1"
  608. " COND_HEAVY_DAMAGE"
  609. " COND_HEAR_DANGER"
  610. )
  611. AI_END_CUSTOM_NPC()