📄 hl1_npc_leech.cpp
字号:
#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() < 40 )
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*0.75) )
targetYaw = (-LEECH_TURN_RATE*0.75);
else if ( targetYaw > (LEECH_TURN_RATE*0.75) )
targetYaw = (LEECH_TURN_RATE*0.75);
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 );
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;
if ( m_flPlaybackRate < 1.0 )
m_flPlaybackRate = 1.0;
}
else if ( GetMoveType() == MOVETYPE_FLYGRAVITY )
{
SetMoveType( MOVETYPE_FLY );
RemoveFlag( FL_ONGROUND );
// 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 );
RemoveFlag( FL_ONGROUND );
SetActivity( ACT_DIESIMPLE );
}
else
SetActivity( ACT_DIEFORWARD );
SetMoveType( MOVETYPE_FLYGRAVITY );
m_takedamage = DAMAGE_NO;
SetThink( &CNPC_Leech::DeadThink );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -