📄 hl1_npc_aflock.cpp
字号:
SetThink( &CNPC_FlockingFlyer::FlockFollowerThink );
}
SetActivity ( ACT_FLY );
ResetSequenceInfo( );
BoidAdvanceFrame( );
m_flSpeed = AFLOCK_FLY_SPEED;// no delay!
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer::BoidAdvanceFrame ( void )
{
float flapspeed = ( m_flSpeed - m_flTempVar ) / AFLOCK_ACCELERATE;
m_flTempVar = m_flTempVar * .8 + m_flSpeed * .2;
if (flapspeed < 0) flapspeed = -flapspeed;
if (flapspeed < 0.25) flapspeed = 0.25;
if (flapspeed > 1.9) flapspeed = 1.9;
m_flPlaybackRate = flapspeed;
QAngle angVel = GetLocalAngularVelocity();
// lean
angVel.x = - GetAbsAngles().x + flapspeed * 5;
// bank
angVel.z = - GetAbsAngles().z + angVel.y;
SetLocalAngularVelocity( angVel );
// pev->framerate = flapspeed;
StudioFrameAdvance();
}
//=========================================================
// Leader boids use this think every tenth
//=========================================================
void CNPC_FlockingFlyer::FlockLeaderThink( void )
{
trace_t tr;
Vector vecDist;// used for general measurements
Vector vecDir;// used for general measurements
float flLeftSide;
float flRightSide;
Vector vForward, vRight, vUp;
SetNextThink( gpGlobals->curtime + 0.1f );
AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
// is the way ahead clear?
if ( !FPathBlocked () )
{
// if the boid is turning, stop the trend.
if ( m_fTurning )
{
m_fTurning = FALSE;
QAngle angVel = GetLocalAngularVelocity();
angVel.y = 0;
SetLocalAngularVelocity( angVel );
}
m_fPathBlocked = FALSE;
if ( m_flSpeed <= AFLOCK_FLY_SPEED )
m_flSpeed += 5;
SetAbsVelocity( vForward * m_flSpeed );
BoidAdvanceFrame( );
return;
}
// IF we get this far in the function, the leader's path is blocked!
m_fPathBlocked = TRUE;
if ( !m_fTurning)// something in the way and boid is not already turning to avoid
{
// measure clearance on left and right to pick the best dir to turn
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vRight * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
vecDist = (tr.endpos - GetAbsOrigin());
flRightSide = vecDist.Length();
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() - vRight * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
vecDist = (tr.endpos - GetAbsOrigin());
flLeftSide = vecDist.Length();
// turn right if more clearance on right side
if ( flRightSide > flLeftSide )
{
QAngle angVel = GetLocalAngularVelocity();
angVel.y = -AFLOCK_TURN_RATE;
SetLocalAngularVelocity( angVel );
m_fTurning = TRUE;
}
// default to left turn :)
else if ( flLeftSide > flRightSide )
{
QAngle angVel = GetLocalAngularVelocity();
angVel.y = AFLOCK_TURN_RATE;
SetLocalAngularVelocity( angVel );
m_fTurning = TRUE;
}
else
{
// equidistant. Pick randomly between left and right.
m_fTurning = TRUE;
QAngle angVel = GetLocalAngularVelocity();
if ( random->RandomInt( 0, 1 ) == 0 )
{
angVel.y = AFLOCK_TURN_RATE;
}
else
{
angVel.y = -AFLOCK_TURN_RATE;
}
SetLocalAngularVelocity( angVel );
}
}
SpreadFlock( );
SetAbsVelocity( vForward * m_flSpeed );
// check and make sure we aren't about to plow into the ground, don't let it happen
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() - vUp * 16, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction != 1.0 && GetAbsVelocity().z < 0 )
{
Vector vecVel = GetAbsVelocity();
vecVel.z = 0;
SetAbsVelocity( vecVel );
}
// maybe it did, though.
if ( GetFlags() & FL_ONGROUND )
{
UTIL_SetOrigin( this, GetAbsOrigin() + Vector ( 0 , 0 , 1 ) );
Vector vecVel = GetAbsVelocity();
vecVel.z = 0;
SetAbsVelocity( vecVel );
}
if ( m_flFlockNextSoundTime < gpGlobals->curtime )
{
// MakeSound();
m_flFlockNextSoundTime = gpGlobals->curtime + random->RandomFloat( 1, 3 );
}
BoidAdvanceFrame( );
return;
}
//=========================================================
// FBoidPathBlocked - returns TRUE if there is an obstacle ahead
//=========================================================
bool CNPC_FlockingFlyer::FPathBlocked( void )
{
trace_t tr;
Vector vecDist;// used for general measurements
Vector vecDir;// used for general measurements
bool fBlocked;
Vector vForward, vRight, vUp;
if ( m_flFakeBlockedTime > gpGlobals->curtime )
{
m_flLastBlockedTime = gpGlobals->curtime;
return TRUE;
}
// use VELOCITY, not angles, not all boids point the direction they are flying
//vecDir = UTIL_VecToAngles( pevBoid->velocity );
AngleVectors ( GetAbsAngles(), &vForward, &vRight, &vUp );
fBlocked = FALSE;// assume the way ahead is clear
// check for obstacle ahead
UTIL_TraceLine(GetAbsOrigin(), GetAbsOrigin() + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction != 1.0)
{
m_flLastBlockedTime = gpGlobals->curtime;
fBlocked = TRUE;
}
// extra wide checks
UTIL_TraceLine(GetAbsOrigin() + vRight * 12, GetAbsOrigin() + vRight * 12 + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction != 1.0)
{
m_flLastBlockedTime = gpGlobals->curtime;
fBlocked = TRUE;
}
UTIL_TraceLine(GetAbsOrigin() - vRight * 12, GetAbsOrigin() - vRight * 12 + vForward * AFLOCK_CHECK_DIST, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr);
if (tr.fraction != 1.0)
{
m_flLastBlockedTime = gpGlobals->curtime;
fBlocked = TRUE;
}
if ( !fBlocked && gpGlobals->curtime - m_flLastBlockedTime > 6 )
{
// not blocked, and it's been a few seconds since we've actually been blocked.
m_flFakeBlockedTime = gpGlobals->curtime + random->RandomInt(1, 3);
}
return fBlocked;
}
//=========================================================
// Searches for boids that are too close and pushes them away
//=========================================================
void CNPC_FlockingFlyer::SpreadFlock( )
{
Vector vecDir;
float flSpeed;// holds vector magnitude while we fiddle with the direction
CNPC_FlockingFlyer *pList = m_pSquadLeader;
while ( pList )
{
if ( pList != this && ( GetAbsOrigin() - pList->GetAbsOrigin() ).Length() <= AFLOCK_TOO_CLOSE )
{
// push the other away
vecDir = ( pList->GetAbsOrigin() - GetAbsOrigin() );
VectorNormalize( vecDir );
// store the magnitude of the other boid's velocity, and normalize it so we
// can average in a course that points away from the leader.
flSpeed = pList->GetAbsVelocity().Length();
Vector vecVel = pList->GetAbsVelocity();
VectorNormalize( vecVel );
pList->SetAbsVelocity( ( vecVel + vecDir ) * 0.5 * flSpeed );
}
pList = pList->m_pSquadNext;
}
}
//=========================================================
// Alters the caller's course if he's too close to others
//
// This function should **ONLY** be called when Caller's velocity is normalized!!
//=========================================================
void CNPC_FlockingFlyer::SpreadFlock2 ( )
{
Vector vecDir;
CNPC_FlockingFlyer *pList = m_pSquadLeader;
while ( pList )
{
if ( pList != this && ( GetAbsOrigin() - pList->GetAbsOrigin() ).Length() <= AFLOCK_TOO_CLOSE )
{
vecDir = ( GetAbsOrigin() - pList->GetAbsOrigin() );
VectorNormalize( vecDir );
SetAbsVelocity( ( GetAbsVelocity() + vecDir ) );
}
pList = pList->m_pSquadNext;
}
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer::MakeSound( void )
{
if ( m_flAlertTime > gpGlobals->curtime )
{
CPASAttenuationFilter filter1( this );
// make agitated sounds
switch ( random->RandomInt( 0, 1 ) )
{
case 0: enginesound->EmitSound( filter1, entindex(), CHAN_WEAPON, "boid/boid_alert1.wav", 1, ATTN_NORM ); break;
case 1: enginesound->EmitSound( filter1, entindex(), CHAN_WEAPON, "boid/boid_alert2.wav", 1, ATTN_NORM ); break;
}
return;
}
// make normal sound
CPASAttenuationFilter filter2( this );
switch ( random->RandomInt( 0, 1 ) )
{
case 0: enginesound->EmitSound( filter2, entindex(), CHAN_WEAPON, "boid/boid_idle1.wav", 1, ATTN_NORM ); break;
case 1: enginesound->EmitSound( filter2, entindex(), CHAN_WEAPON, "boid/boid_idle2.wav", 1, ATTN_NORM ); break;
}
}
//=========================================================
// follower boids execute this code when flocking
//=========================================================
void CNPC_FlockingFlyer::FlockFollowerThink( void )
{
Vector vecDist;
Vector vecDir;
Vector vecDirToLeader;
float flDistToLeader;
SetNextThink( gpGlobals->curtime + 0.1f );
if ( IsLeader() || !InSquad() )
{
// the leader has been killed and this flyer suddenly finds himself the leader.
SetThink ( FlockLeaderThink );
return;
}
vecDirToLeader = ( m_pSquadLeader->GetAbsOrigin() - GetAbsOrigin() );
flDistToLeader = vecDirToLeader.Length();
// match heading with leader
SetAbsAngles( m_pSquadLeader->GetAbsAngles() );
//
// We can see the leader, so try to catch up to it
//
if ( FInViewCone ( m_pSquadLeader ) )
{
// if we're too far away, speed up
if ( flDistToLeader > AFLOCK_TOO_FAR )
{
m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 1.5;
}
// if we're too close, slow down
else if ( flDistToLeader < AFLOCK_TOO_CLOSE )
{
m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 0.5;
}
}
else
{
// wait up! the leader isn't out in front, so we slow down to let him pass
m_flGoalSpeed = m_pSquadLeader->GetAbsVelocity().Length() * 0.5;
}
SpreadFlock2();
Vector vecVel = GetAbsVelocity();
m_flSpeed = vecVel.Length();
VectorNormalize( vecVel );
// if we are too far from leader, average a vector towards it into our current velocity
if ( flDistToLeader > AFLOCK_TOO_FAR )
{
VectorNormalize( vecDirToLeader );
vecVel = (vecVel + vecDirToLeader) * 0.5;
}
// clamp speeds and handle acceleration
if ( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2 )
{
m_flGoalSpeed = AFLOCK_FLY_SPEED * 2;
}
if ( m_flSpeed < m_flGoalSpeed )
{
m_flSpeed += AFLOCK_ACCELERATE;
}
else if ( m_flSpeed > m_flGoalSpeed )
{
m_flSpeed -= AFLOCK_ACCELERATE;
}
SetAbsVelocity( vecVel * m_flSpeed );
BoidAdvanceFrame( );
}
//=========================================================
//=========================================================
void CNPC_FlockingFlyer::Event_Killed( const CTakeDamageInfo &info )
{
CNPC_FlockingFlyer *pSquad;
pSquad = (CNPC_FlockingFlyer *)m_pSquadLeader;
while ( pSquad )
{
pSquad->m_flAlertTime = gpGlobals->curtime + 15;
pSquad = (CNPC_FlockingFlyer *)pSquad->m_pSquadNext;
}
if ( m_pSquadLeader )
{
m_pSquadLeader->SquadRemove( this );
}
m_lifeState = LIFE_DEAD;
m_flPlaybackRate = 0;
m_fEffects = EF_NOINTERP;
UTIL_SetSize( this, Vector(0,0,0), Vector(0,0,0) );
SetMoveType( MOVETYPE_FLYGRAVITY );
SetThink ( FallHack );
SetNextThink( gpGlobals->curtime + 0.1f );
}
void CNPC_FlockingFlyer::FallHack( void )
{
if ( GetFlags() & FL_ONGROUND )
{
CBaseEntity *groundentity = GetContainingEntity( GetGroundEntity()->edict() );
if ( !FClassnameIs ( groundentity, "worldspawn" ) )
{
RemoveFlag( FL_ONGROUND );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
SetAbsVelocity( Vector( 0, 0, 0 ) );
SetThink( NULL );
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -