summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_vortigaunt.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_vortigaunt.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/hl1/hl1_npc_vortigaunt.cpp')
-rw-r--r--game/server/hl1/hl1_npc_vortigaunt.cpp743
1 files changed, 743 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_vortigaunt.cpp b/game/server/hl1/hl1_npc_vortigaunt.cpp
new file mode 100644
index 0000000..a6d6283
--- /dev/null
+++ b/game/server/hl1/hl1_npc_vortigaunt.cpp
@@ -0,0 +1,743 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Alien slave monster
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "beam_shared.h"
+#include "game.h"
+#include "ai_default.h"
+#include "ai_schedule.h"
+#include "ai_hull.h"
+#include "ai_route.h"
+#include "ai_squad.h"
+#include "npcevent.h"
+#include "gib.h"
+//#include "AI_Interactions.h"
+#include "ndebugoverlay.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "hl1_npc_vortigaunt.h"
+#include "soundent.h"
+#include "player.h"
+#include "IEffects.h"
+#include "basecombatweapon.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+//=========================================================
+// Monster's Anim Events Go Here
+//=========================================================
+#define ISLAVE_AE_CLAW ( 1 )
+#define ISLAVE_AE_CLAWRAKE ( 2 )
+#define ISLAVE_AE_ZAP_POWERUP ( 3 )
+#define ISLAVE_AE_ZAP_SHOOT ( 4 )
+#define ISLAVE_AE_ZAP_DONE ( 5 )
+
+
+ConVar sk_islave_health( "sk_islave_health","50");
+ConVar sk_islave_dmg_claw( "sk_islave_dmg_claw","8");
+ConVar sk_islave_dmg_clawrake( "sk_islave_dmg_clawrake","25");
+ConVar sk_islave_dmg_zap( "sk_islave_dmg_zap","15");
+
+LINK_ENTITY_TO_CLASS( monster_alien_slave, CNPC_Vortigaunt );
+
+BEGIN_DATADESC( CNPC_Vortigaunt )
+ DEFINE_FIELD( m_iBravery, FIELD_INTEGER ),
+ DEFINE_ARRAY( m_pBeam, FIELD_CLASSPTR, VORTIGAUNT_MAX_BEAMS ),
+ DEFINE_FIELD( m_iBeams, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flNextAttack, FIELD_TIME ),
+ DEFINE_FIELD( m_iVoicePitch, FIELD_INTEGER ),
+ DEFINE_FIELD( m_hDead, FIELD_EHANDLE ),
+END_DATADESC()
+
+enum
+{
+ SCHED_VORTIGAUNT_ATTACK = LAST_SHARED_SCHEDULE,
+};
+
+#define VORTIGAUNT_IGNORE_PLAYER 64
+
+//=========================================================
+// Spawn
+//=========================================================
+void CNPC_Vortigaunt::Spawn()
+{
+ Precache( );
+
+ SetModel( "models/islave.mdl" );
+
+ SetRenderColor( 255, 255, 255, 255 );
+
+ SetHullType(HULL_HUMAN);
+ SetHullSizeNormal();
+
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_STEP );
+ m_bloodColor = BLOOD_COLOR_GREEN;
+ ClearEffects();
+ m_iHealth = sk_islave_health.GetFloat();
+ //pev->view_ofs = VEC_VIEW;// position of the eyes relative to monster's origin.
+ m_flFieldOfView = VIEW_FIELD_WIDE;
+ m_NPCState = NPC_STATE_NONE;
+
+ m_iVoicePitch = random->RandomInt( 85, 110 );
+
+ CapabilitiesClear();
+ CapabilitiesAdd( bits_CAP_MOVE_GROUND );
+ CapabilitiesAdd( bits_CAP_SQUAD );
+
+ CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP );
+
+ CapabilitiesAdd ( bits_CAP_INNATE_RANGE_ATTACK1 );
+ CapabilitiesAdd ( bits_CAP_INNATE_MELEE_ATTACK1 );
+
+ m_iBravery = 0;
+
+ NPCInit();
+
+ BaseClass::Spawn();
+}
+
+//=========================================================
+// Precache - precaches all resources this monster needs
+//=========================================================
+void CNPC_Vortigaunt::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheModel("models/islave.mdl");
+ PrecacheModel("sprites/lgtning.vmt");
+
+ PrecacheScriptSound( "Vortigaunt.Pain" );
+ PrecacheScriptSound( "Vortigaunt.Die" );
+ PrecacheScriptSound( "Vortigaunt.AttackHit" );
+ PrecacheScriptSound( "Vortigaunt.AttackMiss" );
+ PrecacheScriptSound( "Vortigaunt.ZapPowerup" );
+ PrecacheScriptSound( "Vortigaunt.ZapShoot" );
+}
+
+Disposition_t CNPC_Vortigaunt::IRelationType ( CBaseEntity *pTarget )
+{
+ if ( (pTarget->IsPlayer()) )
+ {
+ if ( (GetSpawnFlags() & VORTIGAUNT_IGNORE_PLAYER ) && !HasMemory( bits_MEMORY_PROVOKED ) )
+ return D_NU;
+ }
+
+ return BaseClass::IRelationType( pTarget );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+Class_T CNPC_Vortigaunt::Classify ( void )
+{
+ return CLASS_ALIEN_MILITARY;
+}
+
+void CNPC_Vortigaunt::CallForHelp( char *szClassname, float flDist, CBaseEntity * pEnemy, Vector &vecLocation )
+{
+ // ALERT( at_aiconsole, "help " );
+
+ // skip ones not on my netname
+ if ( !m_pSquad )
+ return;
+
+ AISquadIter_t iter;
+ for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) )
+ {
+ float d = ( GetAbsOrigin() - pSquadMember->GetAbsOrigin() ).Length();
+
+ if ( d < flDist )
+ {
+ pSquadMember->Remember( bits_MEMORY_PROVOKED );
+ pSquadMember->UpdateEnemyMemory( pEnemy, vecLocation );
+ }
+ }
+}
+
+
+//=========================================================
+// ALertSound - scream
+//=========================================================
+void CNPC_Vortigaunt::AlertSound( void )
+{
+ if ( GetEnemy() != NULL )
+ {
+ SENTENCEG_PlayRndSz( edict(), "SLV_ALERT", 0.85, SNDLVL_NORM, 0, m_iVoicePitch );
+
+ Vector vecTmp = GetEnemy()->GetAbsOrigin();
+ CallForHelp( "monster_alien_slave", 512, GetEnemy(), vecTmp );
+ }
+}
+
+//=========================================================
+// IdleSound
+//=========================================================
+void CNPC_Vortigaunt::IdleSound( void )
+{
+ if ( random->RandomInt( 0, 2 ) == 0)
+ SENTENCEG_PlayRndSz( edict(), "SLV_IDLE", 0.85, SNDLVL_NORM, 0, m_iVoicePitch);
+}
+
+//=========================================================
+// PainSound
+//=========================================================
+void CNPC_Vortigaunt::PainSound( const CTakeDamageInfo &info )
+{
+ if ( random->RandomInt( 0, 2 ) == 0)
+ {
+ CPASAttenuationFilter filter( this );
+
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.Pain", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ params.pitch = m_iVoicePitch;
+
+ EmitSound( filter, entindex(), ep );
+ }
+ }
+}
+
+//=========================================================
+// DieSound
+//=========================================================
+
+void CNPC_Vortigaunt::DeathSound( const CTakeDamageInfo &info )
+{
+ CPASAttenuationFilter filter( this );
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.Die", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ params.pitch = m_iVoicePitch;
+
+ EmitSound( filter, entindex(), ep );
+ }
+}
+
+int CNPC_Vortigaunt::GetSoundInterests ( void )
+{
+ return SOUND_WORLD |
+ SOUND_COMBAT |
+ SOUND_DANGER |
+ SOUND_PLAYER;
+}
+
+void CNPC_Vortigaunt::Event_Killed( const CTakeDamageInfo &info )
+{
+ ClearBeams( );
+ BaseClass::Event_Killed( info );
+}
+
+//=========================================================
+// SetYawSpeed - allows each sequence to have a different
+// turn rate associated with it.
+//=========================================================
+float CNPC_Vortigaunt::MaxYawSpeed ( void )
+{
+ float flYS;
+
+ switch ( GetActivity() )
+ {
+ case ACT_WALK:
+ flYS = 50;
+ break;
+ case ACT_RUN:
+ flYS = 70;
+ break;
+ case ACT_IDLE:
+ flYS = 50;
+ break;
+ default:
+ flYS = 90;
+ break;
+ }
+
+ return flYS;
+}
+
+//=========================================================
+// HandleAnimEvent - catches the monster-specific messages
+// that occur when tagged animation frames are played.
+//
+// Returns number of events handled, 0 if none.
+//=========================================================
+void CNPC_Vortigaunt::HandleAnimEvent( animevent_t *pEvent )
+{
+ // ALERT( at_console, "event %d : %f\n", pEvent->event, pev->frame );
+ switch( pEvent->event )
+ {
+ case ISLAVE_AE_CLAW:
+ {
+ // SOUND HERE!
+ CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10), sk_islave_dmg_claw.GetFloat(), DMG_SLASH );
+ CPASAttenuationFilter filter( this );
+ if ( pHurt )
+ {
+ if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
+ pHurt->ViewPunch( QAngle( 5, 0, -18 ) );
+
+ // Play a random attack hit sound
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.AttackHit", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ params.pitch = m_iVoicePitch;
+
+ EmitSound( filter, entindex(), ep );
+ }
+ }
+ else
+ {
+ // Play a random attack miss sound
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.AttackMiss", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ params.pitch = m_iVoicePitch;
+
+ EmitSound( filter, entindex(), ep );
+ }
+ }
+ }
+ break;
+
+ case ISLAVE_AE_CLAWRAKE:
+ {
+ CBaseEntity *pHurt = CheckTraceHullAttack( 40, Vector(-10,-10,-10), Vector(10,10,10), sk_islave_dmg_clawrake.GetFloat(), DMG_SLASH );
+ CPASAttenuationFilter filter2( this );
+ if ( pHurt )
+ {
+ if ( pHurt->GetFlags() & ( FL_NPC | FL_CLIENT ) )
+ pHurt->ViewPunch( QAngle( 5, 0, 18 ) );
+
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.AttackHit", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ params.pitch = m_iVoicePitch;
+
+ EmitSound( filter2, entindex(), ep );
+ }
+ }
+ else
+ {
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.AttackMiss", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ params.pitch = m_iVoicePitch;
+
+ EmitSound( filter2, entindex(), ep );
+ }
+ }
+ }
+ break;
+
+ case ISLAVE_AE_ZAP_POWERUP:
+ {
+ // speed up attack when on hard
+ if ( g_iSkillLevel == SKILL_HARD )
+ m_flPlaybackRate = 1.5;
+
+ Vector v_forward;
+ GetVectors( &v_forward, NULL, NULL );
+
+ CBroadcastRecipientFilter filter;
+ te->DynamicLight( filter, 0.0, &GetAbsOrigin(), 125, 200, 100, 2, 120, 0.2 / m_flPlaybackRate, 0 );
+
+ if ( m_hDead != NULL )
+ {
+ WackBeam( -1, m_hDead );
+ WackBeam( 1, m_hDead );
+ }
+ else
+ {
+ ArmBeam( -1 );
+ ArmBeam( 1 );
+ BeamGlow( );
+ }
+
+ CPASAttenuationFilter filter3( this );
+ CSoundParameters params;
+ if ( GetParametersForSound( "Vortigaunt.ZapPowerup", params, NULL ) )
+ {
+ EmitSound_t ep( params );
+ ep.m_nPitch = 100 + m_iBeams * 10;
+ EmitSound( filter3, entindex(), ep );
+ }
+
+// Huh? Model doesn't have multiple texturegroups, commented this out. -LH
+// m_nSkin = m_iBeams / 2;
+ }
+ break;
+
+ case ISLAVE_AE_ZAP_SHOOT:
+ {
+ ClearBeams( );
+
+ if ( m_hDead != NULL )
+ {
+ Vector vecDest = m_hDead->GetAbsOrigin() + Vector( 0, 0, 38 );
+ trace_t trace;
+ UTIL_TraceHull( vecDest, vecDest, GetHullMins(), GetHullMaxs(),MASK_SOLID, m_hDead, COLLISION_GROUP_NONE, &trace );
+
+ if ( !trace.startsolid )
+ {
+ CBaseEntity *pNew = Create( "monster_alien_slave", m_hDead->GetAbsOrigin(), m_hDead->GetAbsAngles() );
+
+ pNew->AddSpawnFlags( 1 );
+ WackBeam( -1, pNew );
+ WackBeam( 1, pNew );
+ UTIL_Remove( m_hDead );
+ break;
+ }
+ }
+
+ ClearMultiDamage();
+
+ ZapBeam( -1 );
+ ZapBeam( 1 );
+
+ CPASAttenuationFilter filter4( this );
+ EmitSound( filter4, entindex(), "Vortigaunt.ZapShoot" );
+ ApplyMultiDamage();
+
+ m_flNextAttack = gpGlobals->curtime + random->RandomFloat( 0.5, 4.0 );
+ }
+ break;
+
+ case ISLAVE_AE_ZAP_DONE:
+ {
+ ClearBeams();
+ }
+ break;
+
+ default:
+ BaseClass::HandleAnimEvent( pEvent );
+ break;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose : For innate range attack
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+int CNPC_Vortigaunt::RangeAttack1Conditions( float flDot, float flDist )
+{
+ if ( GetEnemy() == NULL )
+ return( COND_LOST_ENEMY );
+
+ if ( gpGlobals->curtime < m_flNextAttack )
+ return COND_NONE;
+
+ if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
+ return COND_NONE;
+
+ return COND_CAN_RANGE_ATTACK1;
+}
+
+
+void CNPC_Vortigaunt::StartTask( const Task_t *pTask )
+{
+ ClearBeams();
+ BaseClass::StartTask( pTask );
+}
+
+//=========================================================
+// TakeDamage - get provoked when injured
+//=========================================================
+
+int CNPC_Vortigaunt::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
+{
+ // don't slash one of your own
+ if ( ( inputInfo.GetDamageType() & DMG_SLASH ) && inputInfo.GetAttacker() && IRelationType( inputInfo.GetAttacker() ) == D_NU )
+ return 0;
+
+ Remember( bits_MEMORY_PROVOKED );
+
+ return BaseClass::OnTakeDamage_Alive( inputInfo );
+}
+
+
+void CNPC_Vortigaunt::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ if ( info.GetDamageType() & DMG_SHOCK )
+ return;
+
+ BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
+}
+
+//=========================================================
+//=========================================================
+int CNPC_Vortigaunt::SelectSchedule( void )
+{
+ ClearBeams();
+
+ switch ( m_NPCState )
+ {
+ case NPC_STATE_COMBAT:
+// dead enemy
+ if ( HasCondition( COND_ENEMY_DEAD ) )
+ {
+ // call base class, all code to handle dead enemies is centralized there.
+ return BaseClass::SelectSchedule();
+ }
+
+ if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) )
+ return SCHED_RANGE_ATTACK1;
+
+ if ( m_iHealth < 20 || m_iBravery < 0)
+ {
+ if ( !HasCondition( COND_CAN_MELEE_ATTACK1 ) )
+ {
+ SetDefaultFailSchedule( SCHED_CHASE_ENEMY );
+ if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
+ return SCHED_TAKE_COVER_FROM_ENEMY;
+ if ( HasCondition ( COND_SEE_ENEMY ) && HasCondition ( COND_ENEMY_FACING_ME ) )
+ return SCHED_TAKE_COVER_FROM_ENEMY;
+ }
+ }
+ break;
+ }
+ return BaseClass::SelectSchedule( );
+}
+
+int CNPC_Vortigaunt::TranslateSchedule( int scheduleType )
+{
+ //Oops can't get to my enemy.
+ if ( scheduleType == SCHED_CHASE_ENEMY_FAILED )
+ {
+ return SCHED_ESTABLISH_LINE_OF_FIRE;
+ }
+
+ switch ( scheduleType )
+ {
+ case SCHED_FAIL:
+
+ if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
+ {
+ return ( SCHED_MELEE_ATTACK1 );
+ }
+
+ break;
+
+ case SCHED_RANGE_ATTACK1:
+ {
+ //Adrian - HACK HACK! This should've been done up there ^^^^
+ if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) )
+ {
+ return ( SCHED_MELEE_ATTACK1 );
+ }
+
+ return SCHED_VORTIGAUNT_ATTACK;
+ }
+
+ break;
+ }
+
+ return BaseClass::TranslateSchedule( scheduleType );
+}
+
+//=========================================================
+// ArmBeam - small beam from arm to nearby geometry
+//=========================================================
+void CNPC_Vortigaunt::ArmBeam( int side )
+{
+ trace_t tr;
+ float flDist = 1.0;
+
+ if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
+ return;
+
+ Vector forward, right, up;
+ Vector vecAim;
+ AngleVectors( GetAbsAngles(), &forward, &right, &up );
+ Vector vecSrc = GetAbsOrigin() + up * 36 + right * side * 16 + forward * 32;
+
+ for (int i = 0; i < 3; i++)
+ {
+ vecAim = right * side * random->RandomFloat( 0, 1 ) + up * random->RandomFloat( -1, 1 );
+ trace_t tr1;
+ UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 512, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr1);
+ if (flDist > tr1.fraction)
+ {
+ tr = tr1;
+ flDist = tr.fraction;
+ }
+ }
+
+ // Couldn't find anything close enough
+ if ( flDist == 1.0 )
+ return;
+
+ if( tr.m_pEnt && tr.m_pEnt->m_takedamage && !tr.m_pEnt->IsNPC() )
+ {
+ CTakeDamageInfo info( this, this, 10, DMG_SHOCK );
+ CalculateMeleeDamageForce( &info, vecAim, tr.endpos );
+
+ tr.m_pEnt->TakeDamage( info );
+ }
+
+ UTIL_DecalTrace( &tr, "FadingScorch" );
+
+ m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0f );
+
+ if ( m_pBeam[m_iBeams] == NULL )
+ return;
+
+ m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
+ m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
+
+ m_pBeam[m_iBeams]->SetColor( 96, 128, 16 );
+
+ m_pBeam[m_iBeams]->SetBrightness( 64 );
+ m_pBeam[m_iBeams]->SetNoise( 12.8 );
+ m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
+
+ m_iBeams++;
+}
+
+//=========================================================
+// BeamGlow - brighten all beams
+//=========================================================
+void CNPC_Vortigaunt::BeamGlow( )
+{
+ int b = m_iBeams * 32;
+
+ if ( b > 255 )
+ b = 255;
+
+ for ( int i = 0; i < m_iBeams; i++ )
+ {
+ if ( m_pBeam[i] != NULL )
+ {
+ if ( m_pBeam[i]->GetBrightness() != 255 )
+ m_pBeam[i]->SetBrightness( b );
+ }
+ }
+}
+
+//=========================================================
+// WackBeam - regenerate dead colleagues
+//=========================================================
+void CNPC_Vortigaunt::WackBeam( int side, CBaseEntity *pEntity )
+{
+ Vector vecDest;
+
+ if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
+ return;
+
+ if ( pEntity == NULL )
+ return;
+
+ m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 3.0f );
+ if ( m_pBeam[m_iBeams] == NULL )
+ return;
+
+ m_pBeam[m_iBeams]->PointEntInit( pEntity->WorldSpaceCenter(), this );
+ m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
+ m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
+ m_pBeam[m_iBeams]->SetBrightness( 255 );
+ m_pBeam[m_iBeams]->SetNoise( 12.8 );
+ m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
+ m_iBeams++;
+}
+
+//=========================================================
+// ZapBeam - heavy damage directly forward
+//=========================================================
+void CNPC_Vortigaunt::ZapBeam( int side )
+{
+ Vector vecSrc, vecAim;
+ trace_t tr;
+ CBaseEntity *pEntity;
+
+ if ( m_iBeams >= VORTIGAUNT_MAX_BEAMS )
+ return;
+
+ Vector forward, right, up;
+ AngleVectors( GetAbsAngles(), &forward, &right, &up );
+
+ vecSrc = GetAbsOrigin() + up * 36;
+ vecAim = GetShootEnemyDir( vecSrc );
+ float deflection = 0.01;
+ vecAim = vecAim + side * right * random->RandomFloat( 0, deflection ) + up * random->RandomFloat( -deflection, deflection );
+ UTIL_TraceLine ( vecSrc, vecSrc + vecAim * 1024, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+
+ m_pBeam[m_iBeams] = CBeam::BeamCreate( "sprites/lgtning.vmt", 5.0f );
+ if ( m_pBeam[m_iBeams] == NULL )
+ return;
+
+ m_pBeam[m_iBeams]->PointEntInit( tr.endpos, this );
+ m_pBeam[m_iBeams]->SetEndAttachment( side < 0 ? 2 : 1 );
+ m_pBeam[m_iBeams]->SetColor( 180, 255, 96 );
+ m_pBeam[m_iBeams]->SetBrightness( 255 );
+ m_pBeam[m_iBeams]->SetNoise( 3.2f );
+ m_pBeam[m_iBeams]->AddSpawnFlags( SF_BEAM_TEMPORARY );
+ m_iBeams++;
+
+ pEntity = tr.m_pEnt;
+
+ if ( pEntity != NULL && m_takedamage )
+ {
+ CTakeDamageInfo info( this, this, sk_islave_dmg_zap.GetFloat(), DMG_SHOCK );
+ CalculateMeleeDamageForce( &info, vecAim, tr.endpos );
+ pEntity->DispatchTraceAttack( info, vecAim, &tr );
+ }
+}
+
+//=========================================================
+// ClearBeams - remove all beams
+//=========================================================
+void CNPC_Vortigaunt::ClearBeams( )
+{
+ for (int i = 0; i < VORTIGAUNT_MAX_BEAMS; i++)
+ {
+ if (m_pBeam[i])
+ {
+ UTIL_Remove( m_pBeam[i] );
+ m_pBeam[i] = NULL;
+ }
+ }
+
+ m_iBeams = 0;
+ m_nSkin = 0;
+}
+
+
+//------------------------------------------------------------------------------
+//
+// Schedules
+//
+//------------------------------------------------------------------------------
+
+AI_BEGIN_CUSTOM_NPC( monster_alien_slave, CNPC_Vortigaunt )
+
+ //=========================================================
+ // > SCHED_VORTIGAUNT_ATTACK
+ //=========================================================
+ DEFINE_SCHEDULE
+ (
+ SCHED_VORTIGAUNT_ATTACK,
+
+ " Tasks"
+ " TASK_STOP_MOVING 0"
+ " TASK_FACE_IDEAL 0"
+ " TASK_RANGE_ATTACK1 0"
+ " "
+ " Interrupts"
+ " COND_CAN_MELEE_ATTACK1"
+ " COND_HEAVY_DAMAGE"
+ " COND_HEAR_DANGER"
+ )
+
+AI_END_CUSTOM_NPC()