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.
724 lines
17 KiB
724 lines
17 KiB
//========= 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 ); |
|
}
|
|
|