summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_leech.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_leech.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_leech.cpp')
-rw-r--r--game/server/hl1/hl1_npc_leech.cpp724
1 files changed, 724 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_leech.cpp b/game/server/hl1/hl1_npc_leech.cpp
new file mode 100644
index 0000000..9e00fc0
--- /dev/null
+++ b/game/server/hl1/hl1_npc_leech.cpp
@@ -0,0 +1,724 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "ai_default.h"
+#include "ai_task.h"
+#include "ai_schedule.h"
+#include "ai_node.h"
+#include "ai_hull.h"
+#include "ai_hint.h"
+#include "ai_memory.h"
+#include "ai_route.h"
+#include "ai_motor.h"
+#include "soundent.h"
+#include "game.h"
+#include "npcevent.h"
+#include "entitylist.h"
+#include "activitylist.h"
+#include "animation.h"
+#include "basecombatweapon.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "hl1_ai_basenpc.h"
+#include "ai_senses.h"
+
+// Animation events
+#define LEECH_AE_ATTACK 1
+#define LEECH_AE_FLOP 2
+
+//#define DEBUG_BEAMS 0
+
+ConVar sk_leech_health( "sk_leech_health", "2" );
+ConVar sk_leech_dmg_bite( "sk_leech_dmg_bite", "2" );
+
+// Movement constants
+
+#define LEECH_ACCELERATE 10
+#define LEECH_CHECK_DIST 45
+#define LEECH_SWIM_SPEED 50
+#define LEECH_SWIM_ACCEL 80
+#define LEECH_SWIM_DECEL 10
+#define LEECH_TURN_RATE 70
+#define LEECH_SIZEX 10
+#define LEECH_FRAMETIME 0.1
+
+class CNPC_Leech : public CHL1BaseNPC
+{
+ DECLARE_CLASS( CNPC_Leech, CHL1BaseNPC );
+public:
+
+ DECLARE_DATADESC();
+
+ void Spawn( void );
+ void Precache( void );
+
+ static const char *pAlertSounds[];
+
+ void SwimThink( void );
+ void DeadThink( void );
+
+ void SwitchLeechState( void );
+ float ObstacleDistance( CBaseEntity *pTarget );
+ void UpdateMotion( void );
+
+ void RecalculateWaterlevel( void );
+ void Touch( CBaseEntity *pOther );
+
+ Disposition_t IRelationType(CBaseEntity *pTarget);
+
+ void HandleAnimEvent( animevent_t *pEvent );
+
+ void AttackSound( void );
+ void AlertSound( void );
+
+ void Activate( void );
+
+ Class_T Classify( void ) { return CLASS_INSECT; };
+
+ void Event_Killed( const CTakeDamageInfo &info );
+
+
+ bool ShouldGib( const CTakeDamageInfo &info );
+
+
+/* // Base entity functions
+ void Killed( entvars_t *pevAttacker, int iGib );
+ int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType );
+*/
+
+private:
+ // UNDONE: Remove unused boid vars, do group behavior
+ float m_flTurning;// is this boid turning?
+ bool m_fPathBlocked;// TRUE if there is an obstacle ahead
+ float m_flAccelerate;
+ float m_obstacle;
+ float m_top;
+ float m_bottom;
+ float m_height;
+ float m_waterTime;
+ float m_sideTime; // Timer to randomly check clearance on sides
+ float m_zTime;
+ float m_stateTime;
+ float m_attackSoundTime;
+ Vector m_oldOrigin;
+};
+
+LINK_ENTITY_TO_CLASS( monster_leech, CNPC_Leech );
+
+BEGIN_DATADESC( CNPC_Leech )
+ DEFINE_FIELD( m_flTurning, FIELD_FLOAT ),
+ DEFINE_FIELD( m_fPathBlocked, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flAccelerate, FIELD_FLOAT ),
+ DEFINE_FIELD( m_obstacle, FIELD_FLOAT ),
+ DEFINE_FIELD( m_top, FIELD_FLOAT ),
+ DEFINE_FIELD( m_bottom, FIELD_FLOAT ),
+ DEFINE_FIELD( m_height, FIELD_FLOAT ),
+ DEFINE_FIELD( m_waterTime, FIELD_TIME ),
+ DEFINE_FIELD( m_sideTime, FIELD_TIME ),
+ DEFINE_FIELD( m_zTime, FIELD_TIME ),
+ DEFINE_FIELD( m_stateTime, FIELD_TIME ),
+ DEFINE_FIELD( m_attackSoundTime, FIELD_TIME ),
+ DEFINE_FIELD( m_oldOrigin, FIELD_VECTOR ),
+
+ DEFINE_THINKFUNC( SwimThink ),
+ DEFINE_THINKFUNC( DeadThink ),
+END_DATADESC()
+
+
+bool CNPC_Leech::ShouldGib( const CTakeDamageInfo &info )
+{
+ return false;
+}
+
+void CNPC_Leech::Spawn( void )
+{
+ Precache();
+ SetModel( "models/leech.mdl" );
+
+ SetHullType(HULL_TINY_CENTERED);
+ SetHullSizeNormal();
+
+ UTIL_SetSize( this, Vector(-1,-1,0), Vector(1,1,2));
+
+ Vector vecSurroundingMins(-8,-8,0);
+ Vector vecSurroundingMaxs(8,8,2);
+ CollisionProp()->SetSurroundingBoundsType( USE_SPECIFIED_BOUNDS, &vecSurroundingMins, &vecSurroundingMaxs );
+
+ // Don't push the minz down too much or the water check will fail because this entity is really point-sized
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_FLY );
+ AddFlag( FL_SWIM );
+ m_iHealth = sk_leech_health.GetInt();
+
+ m_flFieldOfView = -0.5; // 180 degree FOV
+ SetDistLook( 750 );
+ NPCInit();
+ SetThink( &CNPC_Leech::SwimThink );
+ SetUse( NULL );
+ SetTouch( NULL );
+ SetViewOffset( vec3_origin );
+
+ m_flTurning = 0;
+ m_fPathBlocked = FALSE;
+ SetActivity( ACT_SWIM );
+ SetState( NPC_STATE_IDLE );
+ m_stateTime = gpGlobals->curtime + random->RandomFloat( 1, 5 );
+
+ SetRenderColor( 255, 255, 255, 255 );
+
+ m_bloodColor = DONT_BLEED;
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+}
+
+void CNPC_Leech::Activate( void )
+{
+ RecalculateWaterlevel();
+
+ BaseClass::Activate();
+}
+
+void CNPC_Leech::DeadThink( void )
+{
+ if ( IsSequenceFinished() )
+ {
+ if ( GetActivity() == ACT_DIEFORWARD )
+ {
+ SetThink( NULL );
+ StopAnimation();
+ return;
+ }
+ else if ( GetFlags() & FL_ONGROUND )
+ {
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ SetActivity( ACT_DIEFORWARD );
+ }
+ }
+ StudioFrameAdvance();
+ SetNextThink( gpGlobals->curtime + 0.1 );
+
+ // Apply damage velocity, but keep out of the walls
+ if ( GetAbsVelocity().x != 0 || GetAbsVelocity().y != 0 )
+ {
+ trace_t tr;
+
+ // Look 0.5 seconds ahead
+ UTIL_TraceLine( GetLocalOrigin(), GetLocalOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if (tr.fraction != 1.0)
+ {
+ Vector vVelocity = GetAbsVelocity();
+
+ vVelocity.x = 0;
+ vVelocity.y = 0;
+
+ SetAbsVelocity( vVelocity );
+ }
+ }
+}
+
+
+Disposition_t CNPC_Leech::IRelationType( CBaseEntity *pTarget )
+{
+ if ( pTarget->IsPlayer() )
+ return D_HT;
+
+ return BaseClass::IRelationType( pTarget );
+}
+
+void CNPC_Leech::Touch( CBaseEntity *pOther )
+{
+ if ( !pOther->IsPlayer() )
+ return;
+
+ if ( pOther == GetTouchTrace().m_pEnt )
+ {
+ if ( pOther->GetAbsVelocity() == vec3_origin )
+ return;
+
+ SetBaseVelocity( pOther->GetAbsVelocity() );
+ AddFlag( FL_BASEVELOCITY );
+ }
+}
+
+void CNPC_Leech::HandleAnimEvent( animevent_t *pEvent )
+{
+ CBaseEntity *pEnemy = GetEnemy();
+
+ switch( pEvent->event )
+ {
+ case LEECH_AE_FLOP:
+ // Play flop sound
+ break;
+
+ case LEECH_AE_ATTACK:
+ AttackSound();
+
+ if ( pEnemy != NULL )
+ {
+ Vector dir, face;
+
+ AngleVectors( GetAbsAngles(), &face );
+
+ face.z = 0;
+ dir = (pEnemy->GetLocalOrigin() - GetLocalOrigin() );
+ dir.z = 0;
+
+ VectorNormalize( dir );
+ VectorNormalize( face );
+
+ if ( DotProduct(dir, face) > 0.9 ) // Only take damage if the leech is facing the prey
+ {
+ CTakeDamageInfo info( this, this, sk_leech_dmg_bite.GetInt(), DMG_SLASH );
+ CalculateMeleeDamageForce( &info, dir, pEnemy->GetAbsOrigin() );
+ pEnemy->TakeDamage( info );
+ }
+ }
+ m_stateTime -= 2;
+ break;
+
+
+
+ default:
+ BaseClass::HandleAnimEvent( pEvent );
+ break;
+ }
+}
+
+
+void CNPC_Leech::Precache( void )
+{
+ PrecacheModel("models/leech.mdl");
+
+ PrecacheScriptSound( "Leech.Attack" );
+ PrecacheScriptSound( "Leech.Alert" );
+}
+
+
+void CNPC_Leech::AttackSound( void )
+{
+ if ( gpGlobals->curtime > m_attackSoundTime )
+ {
+ CPASAttenuationFilter filter( this );
+
+ EmitSound(filter, entindex(), "Leech.Attack" );
+ m_attackSoundTime = gpGlobals->curtime + 0.5;
+ }
+}
+
+
+void CNPC_Leech::AlertSound( void )
+{
+ CPASAttenuationFilter filter( this );
+ EmitSound(filter, entindex(), "Leech.Alert" );
+}
+
+void CNPC_Leech::SwitchLeechState( void )
+{
+ m_stateTime = gpGlobals->curtime + random->RandomFloat( 3, 6 );
+ if ( m_NPCState == NPC_STATE_COMBAT )
+ {
+ SetEnemy ( NULL );
+ SetState( NPC_STATE_IDLE );
+ // We may be up against the player, so redo the side checks
+ m_sideTime = 0;
+ }
+ else
+ {
+ GetSenses()->Look( GetSenses()->GetDistLook() );
+ CBaseEntity *pEnemy = BestEnemy();
+ if ( pEnemy && pEnemy->GetWaterLevel() != 0 )
+ {
+ SetEnemy ( pEnemy );
+ SetState( NPC_STATE_COMBAT );
+ m_stateTime = gpGlobals->curtime + random->RandomFloat( 18, 25 );
+ AlertSound();
+ }
+ }
+}
+
+void CNPC_Leech::RecalculateWaterlevel( void )
+{
+ // Calculate boundaries
+ Vector vecTest = GetLocalOrigin() - Vector(0,0,400);
+
+ trace_t tr;
+
+ UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+
+ if ( tr.fraction != 1.0 )
+ m_bottom = tr.endpos.z + 1;
+ else
+ m_bottom = vecTest.z;
+
+ m_top = UTIL_WaterLevel( GetLocalOrigin(), GetLocalOrigin().z, GetLocalOrigin().z + 400 ) - 1;
+
+#if DEBUG_BEAMS
+ NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + Vector( 0, 0, m_bottom ), 0, 255, 0, false, 0.1f );
+ NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + Vector( 0, 0, m_top ), 0, 255, 255, false, 0.1f );
+#endif
+
+ // Chop off 20% of the outside range
+ float newBottom = m_bottom * 0.8 + m_top * 0.2;
+ m_top = m_bottom * 0.2 + m_top * 0.8;
+ m_bottom = newBottom;
+ m_height = random->RandomFloat( m_bottom, m_top );
+ m_waterTime = gpGlobals->curtime + random->RandomFloat( 5, 7 );
+}
+
+void CNPC_Leech::SwimThink( void )
+{
+ trace_t tr;
+ float flLeftSide;
+ float flRightSide;
+ float targetSpeed;
+ float targetYaw = 0;
+ CBaseEntity *pTarget;
+
+ /*if ( !UTIL_FindClientInPVS( edict() ) )
+ {
+ m_flNextThink = gpGlobals->curtime + random->RandomFloat( 1.0f, 1.5f );
+ SetAbsVelocity( vec3_origin );
+ return;
+ }
+ else*/
+ SetNextThink( gpGlobals->curtime + 0.1 );
+
+ targetSpeed = LEECH_SWIM_SPEED;
+
+ if ( m_waterTime < gpGlobals->curtime )
+ RecalculateWaterlevel();
+
+ if ( m_stateTime < gpGlobals->curtime )
+ SwitchLeechState();
+
+ ClearCondition( COND_CAN_MELEE_ATTACK1 );
+
+ switch( m_NPCState )
+ {
+ case NPC_STATE_COMBAT:
+ pTarget = GetEnemy();
+ if ( !pTarget )
+ SwitchLeechState();
+ else
+ {
+ // Chase the enemy's eyes
+ m_height = pTarget->GetLocalOrigin().z + pTarget->GetViewOffset().z - 5;
+ // Clip to viable water area
+ if ( m_height < m_bottom )
+ m_height = m_bottom;
+ else if ( m_height > m_top )
+ m_height = m_top;
+ Vector location = pTarget->GetLocalOrigin() - GetLocalOrigin();
+ location.z += (pTarget->GetViewOffset().z);
+ if ( location.Length() < 80 )
+ SetCondition( COND_CAN_MELEE_ATTACK1 );
+ // Turn towards target ent
+ targetYaw = UTIL_VecToYaw( location );
+
+ QAngle vTestAngle = GetAbsAngles();
+
+ targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( GetAbsAngles().y ) );
+
+ if ( targetYaw < (-LEECH_TURN_RATE) )
+ targetYaw = (-LEECH_TURN_RATE);
+ else if ( targetYaw > (LEECH_TURN_RATE) )
+ targetYaw = (LEECH_TURN_RATE);
+ else
+ targetSpeed *= 2;
+ }
+
+ break;
+
+ default:
+ if ( m_zTime < gpGlobals->curtime )
+ {
+ float newHeight = random->RandomFloat( m_bottom, m_top );
+ m_height = 0.5 * m_height + 0.5 * newHeight;
+ m_zTime = gpGlobals->curtime + random->RandomFloat( 1, 4 );
+ }
+ if ( random->RandomInt( 0, 100 ) < 10 )
+ targetYaw = random->RandomInt( -30, 30 );
+ pTarget = NULL;
+ // oldorigin test
+ if ( ( GetLocalOrigin() - m_oldOrigin ).Length() < 1 )
+ {
+ // If leech didn't move, there must be something blocking it, so try to turn
+ m_sideTime = 0;
+ }
+
+ break;
+ }
+
+ m_obstacle = ObstacleDistance( pTarget );
+ m_oldOrigin = GetLocalOrigin();
+ if ( m_obstacle < 0.1 )
+ m_obstacle = 0.1;
+
+ Vector vForward, vRight;
+
+ AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL );
+
+ // is the way ahead clear?
+ if ( m_obstacle == 1.0 )
+ {
+ // if the leech is turning, stop the trend.
+ if ( m_flTurning != 0 )
+ {
+ m_flTurning = 0;
+ }
+
+ m_fPathBlocked = FALSE;
+ m_flSpeed = UTIL_Approach( targetSpeed, m_flSpeed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME );
+ SetAbsVelocity( vForward * m_flSpeed );
+
+ }
+ else
+ {
+ m_obstacle = 1.0 / m_obstacle;
+ // IF we get this far in the function, the leader's path is blocked!
+ m_fPathBlocked = TRUE;
+
+ if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid
+ {
+ Vector vecTest;
+ // measure clearance on left and right to pick the best dir to turn
+ vecTest = GetLocalOrigin() + ( vRight * LEECH_SIZEX) + ( vForward * LEECH_CHECK_DIST);
+ UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ flRightSide = tr.fraction;
+
+ vecTest = GetLocalOrigin() + ( vRight * -LEECH_SIZEX) + ( vForward * LEECH_CHECK_DIST);
+ UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+
+ flLeftSide = tr.fraction;
+
+ // turn left, right or random depending on clearance ratio
+ float delta = (flRightSide - flLeftSide);
+ if ( delta > 0.1 || (delta > -0.1 && random->RandomInt( 0,100 ) < 50 ) )
+ m_flTurning = -LEECH_TURN_RATE;
+ else
+ m_flTurning = LEECH_TURN_RATE;
+ }
+
+ m_flSpeed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), m_flSpeed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle );
+ SetAbsVelocity( vForward * m_flSpeed );
+ }
+
+ GetMotor()->SetIdealYaw( m_flTurning + targetYaw );
+ UpdateMotion();
+}
+
+//
+// ObstacleDistance - returns normalized distance to obstacle
+//
+float CNPC_Leech::ObstacleDistance( CBaseEntity *pTarget )
+{
+ trace_t tr;
+ Vector vecTest;
+ Vector vForward, vRight;
+
+ // use VELOCITY, not angles, not all boids point the direction they are flying
+ //Vector vecDir = UTIL_VecToAngles( pev->velocity );
+ QAngle tmp = GetAbsAngles();
+ tmp.x = -tmp.x;
+ AngleVectors ( tmp, &vForward, &vRight, NULL );
+
+ // check for obstacle ahead
+ vecTest = GetLocalOrigin() + vForward * LEECH_CHECK_DIST;
+ UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+
+ if ( tr.startsolid )
+ {
+ m_flSpeed = -LEECH_SWIM_SPEED * 0.5;
+ }
+
+ if ( tr.fraction != 1.0 )
+ {
+ if ( (pTarget == NULL || tr.m_pEnt != pTarget ) )
+ {
+ return tr.fraction;
+ }
+ else
+ {
+ if ( fabs( m_height - GetLocalOrigin().z ) > 10 )
+ return tr.fraction;
+ }
+ }
+
+ if ( m_sideTime < gpGlobals->curtime )
+ {
+ // extra wide checks
+ vecTest = GetLocalOrigin() + vRight * LEECH_SIZEX * 2 + vForward * LEECH_CHECK_DIST;
+ UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+
+ if (tr.fraction != 1.0)
+ return tr.fraction;
+
+ vecTest = GetLocalOrigin() - vRight * LEECH_SIZEX * 2 + vForward * LEECH_CHECK_DIST;
+ UTIL_TraceLine( GetLocalOrigin(), vecTest, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if (tr.fraction != 1.0)
+ return tr.fraction;
+
+ // Didn't hit either side, so stop testing for another 0.5 - 1 seconds
+ m_sideTime = gpGlobals->curtime + random->RandomFloat(0.5,1);
+ }
+
+ return 1.0;
+}
+
+void CNPC_Leech::UpdateMotion( void )
+{
+ float flapspeed = ( m_flSpeed - m_flAccelerate) / LEECH_ACCELERATE;
+ m_flAccelerate = m_flAccelerate * 0.8 + m_flSpeed * 0.2;
+
+ if (flapspeed < 0)
+ flapspeed = -flapspeed;
+ flapspeed += 1.0;
+ if (flapspeed < 0.5)
+ flapspeed = 0.5;
+ if (flapspeed > 1.9)
+ flapspeed = 1.9;
+
+ m_flPlaybackRate = flapspeed;
+
+ QAngle vAngularVelocity = GetLocalAngularVelocity();
+ QAngle vAngles = GetLocalAngles();
+
+ if ( !m_fPathBlocked )
+ vAngularVelocity.y = GetMotor()->GetIdealYaw();
+ else
+ vAngularVelocity.y = GetMotor()->GetIdealYaw() * m_obstacle;
+
+ if ( vAngularVelocity.y > 150 )
+ SetIdealActivity( ACT_TURN_LEFT );
+ else if ( vAngularVelocity.y < -150 )
+ SetIdealActivity( ACT_TURN_RIGHT );
+ else
+ SetIdealActivity( ACT_SWIM );
+
+ // lean
+ float targetPitch, delta;
+ delta = m_height - GetLocalOrigin().z;
+
+/* if ( delta < -10 )
+ targetPitch = -30;
+ else if ( delta > 10 )
+ targetPitch = 30;
+ else*/
+ targetPitch = 0;
+
+ vAngles.x = UTIL_Approach( targetPitch, vAngles.x, 60 * LEECH_FRAMETIME );
+
+ // bank
+ vAngularVelocity.z = - ( vAngles.z + (vAngularVelocity.y * 0.25));
+
+ if ( m_NPCState == NPC_STATE_COMBAT && HasCondition( COND_CAN_MELEE_ATTACK1 ) )
+ SetIdealActivity( ACT_MELEE_ATTACK1 );
+
+ // Out of water check
+ if ( !GetWaterLevel() )
+ {
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+ SetIdealActivity( ACT_HOP );
+ SetAbsVelocity( vec3_origin );
+
+ // Animation will intersect the floor if either of these is non-zero
+ vAngles.z = 0;
+ vAngles.x = 0;
+
+ m_flPlaybackRate = random->RandomFloat( 0.8, 1.2 );
+ }
+ else if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
+ {
+ SetMoveType( MOVETYPE_FLY );
+ SetGroundEntity( NULL );
+
+ // TODO
+ RecalculateWaterlevel();
+ m_waterTime = gpGlobals->curtime + 2; // Recalc again soon, water may be rising
+ }
+
+ if ( GetActivity() != GetIdealActivity() )
+ {
+ SetActivity ( GetIdealActivity() );
+ }
+ StudioFrameAdvance();
+
+ DispatchAnimEvents ( this );
+
+ SetLocalAngles( vAngles );
+ SetLocalAngularVelocity( vAngularVelocity );
+
+ Vector vForward, vRight;
+
+ AngleVectors( vAngles, &vForward, &vRight, NULL );
+
+#if DEBUG_BEAMS
+ if ( m_fPathBlocked )
+ {
+ float color = m_obstacle * 30;
+ if ( m_obstacle == 1.0 )
+ color = 0;
+ if ( color > 255 )
+ color = 255;
+ NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vForward * LEECH_CHECK_DIST, 255, color, color, false, 0.1f );
+ }
+ else
+ NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vForward * LEECH_CHECK_DIST, 255, 255, 0, false, 0.1f );
+
+ NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + vRight * (vAngularVelocity.y*0.25), 0, 0, 255, false, 0.1f );
+#endif
+
+}
+
+void CNPC_Leech::Event_Killed( const CTakeDamageInfo &info )
+{
+ Vector vecSplatDir;
+ trace_t tr;
+
+ //ALERT(at_aiconsole, "Leech: killed\n");
+ // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
+ CBaseEntity *pOwner = GetOwnerEntity();
+ if (pOwner)
+ pOwner->DeathNotice( this );
+
+ // When we hit the ground, play the "death_end" activity
+ if ( GetWaterLevel() )
+ {
+ QAngle qAngles = GetAbsAngles();
+ QAngle qAngularVel = GetLocalAngularVelocity();
+ Vector vOrigin = GetLocalOrigin();
+
+ qAngles.z = 0;
+ qAngles.x = 0;
+
+ vOrigin.z += 1;
+
+ SetAbsVelocity( vec3_origin );
+
+ if ( random->RandomInt( 0, 99 ) < 70 )
+ qAngularVel.y = random->RandomInt( -720, 720 );
+
+ SetAbsAngles( qAngles );
+ SetLocalAngularVelocity( qAngularVel );
+ SetAbsOrigin( vOrigin );
+
+
+ SetGravity ( 0.02 );
+ SetGroundEntity( NULL );
+ SetActivity( ACT_DIESIMPLE );
+ }
+ else
+ SetActivity( ACT_DIEFORWARD );
+
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+ m_takedamage = DAMAGE_NO;
+
+ SetThink( &CNPC_Leech::DeadThink );
+}