diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl1/hl1_npc_aflock.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/hl1/hl1_npc_aflock.cpp')
| -rw-r--r-- | game/server/hl1/hl1_npc_aflock.cpp | 858 |
1 files changed, 858 insertions, 0 deletions
diff --git a/game/server/hl1/hl1_npc_aflock.cpp b/game/server/hl1/hl1_npc_aflock.cpp new file mode 100644 index 0000000..01b1c76 --- /dev/null +++ b/game/server/hl1/hl1_npc_aflock.cpp @@ -0,0 +1,858 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger +// events +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $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_route.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" + +#define AFLOCK_MAX_RECRUIT_RADIUS 1024 +#define AFLOCK_FLY_SPEED 125 +#define AFLOCK_TURN_RATE 75 +#define AFLOCK_ACCELERATE 10 +#define AFLOCK_CHECK_DIST 192 +#define AFLOCK_TOO_CLOSE 100 +#define AFLOCK_TOO_FAR 256 + +//========================================================= +//========================================================= +class CNPC_FlockingFlyerFlock : public CHL1BaseNPC +{ + DECLARE_CLASS( CNPC_FlockingFlyerFlock, CHL1BaseNPC ); +public: + + void Spawn( void ); + void Precache( void ); + bool KeyValue( const char *szKeyName, const char *szValue ); + void SpawnFlock( void ); + + // Sounds are shared by the flock + static void PrecacheFlockSounds( void ); + + DECLARE_DATADESC(); + + int m_cFlockSize; + float m_flFlockRadius; +}; + +BEGIN_DATADESC( CNPC_FlockingFlyerFlock ) + DEFINE_FIELD( m_cFlockSize, FIELD_INTEGER ), + DEFINE_FIELD( m_flFlockRadius, FIELD_FLOAT ), +END_DATADESC() + +class CNPC_FlockingFlyer : public CHL1BaseNPC +{ + DECLARE_CLASS( CNPC_FlockingFlyer, CHL1BaseNPC ); +public: + void Spawn( void ); + void Precache( void ); + void SpawnCommonCode( void ); + void IdleThink( void ); + void BoidAdvanceFrame( void ); + void Start( void ); + bool FPathBlocked( void ); + void FlockLeaderThink( void ); + void SpreadFlock( void ); + void SpreadFlock2( void ); + void MakeSound( void ); + void FlockFollowerThink( void ); + void Event_Killed( const CTakeDamageInfo &info ); + void FallHack( void ); + //void Poop ( void ); Adrian - wtf?! + + + + int IsLeader( void ) { return m_pSquadLeader == this; } + int InSquad( void ) { return m_pSquadLeader != NULL; } + int SquadCount( void ); + void SquadRemove( CNPC_FlockingFlyer *pRemove ); + void SquadUnlink( void ); + void SquadAdd( CNPC_FlockingFlyer *pAdd ); + void SquadDisband( void ); + + CNPC_FlockingFlyer *m_pSquadLeader; + CNPC_FlockingFlyer *m_pSquadNext; + bool m_fTurning;// is this boid turning? + bool m_fCourseAdjust;// followers set this flag TRUE to override flocking while they avoid something + bool m_fPathBlocked;// TRUE if there is an obstacle ahead + Vector m_vecReferencePoint;// last place we saw leader + Vector m_vecAdjustedVelocity;// adjusted velocity (used when fCourseAdjust is TRUE) + float m_flGoalSpeed; + float m_flLastBlockedTime; + float m_flFakeBlockedTime; + float m_flAlertTime; + float m_flFlockNextSoundTime; + float m_flTempVar; + + DECLARE_DATADESC(); +}; + +BEGIN_DATADESC( CNPC_FlockingFlyer ) + DEFINE_FIELD( m_pSquadLeader, FIELD_CLASSPTR ), + DEFINE_FIELD( m_pSquadNext, FIELD_CLASSPTR ), + DEFINE_FIELD( m_fTurning, FIELD_BOOLEAN ), + DEFINE_FIELD( m_fCourseAdjust, FIELD_BOOLEAN ), + DEFINE_FIELD( m_fPathBlocked, FIELD_BOOLEAN ), + DEFINE_FIELD( m_vecReferencePoint, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_vecAdjustedVelocity, FIELD_VECTOR ), + DEFINE_FIELD( m_flGoalSpeed, FIELD_FLOAT ), + DEFINE_FIELD( m_flLastBlockedTime, FIELD_TIME ), + DEFINE_FIELD( m_flFakeBlockedTime, FIELD_TIME ), + DEFINE_FIELD( m_flAlertTime, FIELD_TIME ), + DEFINE_THINKFUNC( IdleThink ), + DEFINE_THINKFUNC( Start ), + DEFINE_THINKFUNC( FlockLeaderThink ), + DEFINE_THINKFUNC( FlockFollowerThink ), + DEFINE_THINKFUNC( FallHack ), + + DEFINE_FIELD( m_flFlockNextSoundTime, FIELD_TIME ), + DEFINE_FIELD( m_flTempVar, FIELD_FLOAT ), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( monster_flyer, CNPC_FlockingFlyer ); +LINK_ENTITY_TO_CLASS( monster_flyer_flock, CNPC_FlockingFlyerFlock ); + +bool CNPC_FlockingFlyerFlock::KeyValue( const char *szKeyName, const char *szValue ) +{ + if ( FStrEq( szKeyName, "iFlockSize" ) ) + { + m_cFlockSize = atoi( szValue ); + return true; + } + else if ( FStrEq( szKeyName, "flFlockRadius" ) ) + { + m_flFlockRadius = atof( szValue ); + return true; + } + else + BaseClass::KeyValue( szKeyName, szValue ); + + return false; +} + +//========================================================= +//========================================================= +void CNPC_FlockingFlyerFlock::Spawn( void ) +{ + Precache( ); + + SetRenderColor( 255, 255, 255, 255 ); + SpawnFlock(); + + + SetThink( &CBaseEntity::SUB_Remove ); + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +//========================================================= +//========================================================= +void CNPC_FlockingFlyerFlock::Precache( void ) +{ + //PRECACHE_MODEL("models/aflock.mdl"); + PrecacheModel("models/boid.mdl"); + + PrecacheFlockSounds(); +} + +void CNPC_FlockingFlyerFlock::SpawnFlock( void ) +{ + float R = m_flFlockRadius; + int iCount; + Vector vecSpot; + CNPC_FlockingFlyer *pBoid, *pLeader; + + pLeader = pBoid = NULL; + + for ( iCount = 0 ; iCount < m_cFlockSize ; iCount++ ) + { + pBoid = CREATE_ENTITY( CNPC_FlockingFlyer, "monster_flyer" ); + + if ( !pLeader ) + { + // make this guy the leader. + pLeader = pBoid; + + pLeader->m_pSquadLeader = pLeader; + pLeader->m_pSquadNext = NULL; + } + + vecSpot.x = random->RandomFloat( -R, R ); + vecSpot.y = random->RandomFloat( -R, R ); + vecSpot.z = random->RandomFloat( 0, 16 ); + vecSpot = GetAbsOrigin() + vecSpot; + + UTIL_SetOrigin( pBoid, vecSpot); + pBoid->SetMoveType( MOVETYPE_FLY ); + pBoid->SpawnCommonCode(); + pBoid->SetGroundEntity( NULL ); + pBoid->SetAbsVelocity( Vector ( 0, 0, 0 ) ); + pBoid->SetAbsAngles( GetAbsAngles() ); + + pBoid->SetCycle( 0 ); + pBoid->SetThink( &CNPC_FlockingFlyer::IdleThink ); + pBoid->SetNextThink( gpGlobals->curtime + 0.2 ); + + if ( pBoid != pLeader ) + { + pLeader->SquadAdd( pBoid ); + } + } +} + + +void CNPC_FlockingFlyerFlock::PrecacheFlockSounds( void ) +{ +} + +//========================================================= +//========================================================= +void CNPC_FlockingFlyer::Spawn( ) +{ + Precache( ); + SpawnCommonCode(); + + SetCycle( 0 ); + SetNextThink( gpGlobals->curtime + 0.1f ); + SetThink( &CNPC_FlockingFlyer::IdleThink ); +} + +//========================================================= +//========================================================= +void CNPC_FlockingFlyer::SpawnCommonCode( ) +{ + m_lifeState = LIFE_ALIVE; + SetClassname( "monster_flyer" ); + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + SetMoveType( MOVETYPE_FLY ); + m_takedamage = DAMAGE_NO; + m_iHealth = 1; + + m_fPathBlocked = FALSE;// obstacles will be detected + m_flFieldOfView = 0.2; + m_flTempVar = 0; + + //SET_MODEL(ENT(pev), "models/aflock.mdl"); + SetModel( "models/boid.mdl" ); + +// UTIL_SetSize(this, Vector(0,0,0), Vector(0,0,0)); + UTIL_SetSize(this, Vector(-5,-5,0), Vector(5,5,2)); +} + +//========================================================= +//========================================================= +void CNPC_FlockingFlyer::Precache( ) +{ + //PRECACHE_MODEL("models/aflock.mdl"); + PrecacheModel("models/boid.mdl"); + CNPC_FlockingFlyerFlock::PrecacheFlockSounds(); + + PrecacheScriptSound( "FlockingFlyer.Alert" ); + PrecacheScriptSound( "FlockingFlyer.Idle" ); +} + +//========================================================= +//========================================================= +void CNPC_FlockingFlyer::IdleThink( void ) +{ + SetNextThink( gpGlobals->curtime + 0.2 ); + + // see if there's a client in the same pvs as the monster + if ( !FNullEnt( UTIL_FindClientInPVS( edict() ) ) ) + { + SetThink( &CNPC_FlockingFlyer::Start ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } +} + +//========================================================= +// +// SquadUnlink(), Unlink the squad pointers. +// +//========================================================= +void CNPC_FlockingFlyer::SquadUnlink( void ) +{ + m_pSquadLeader = NULL; + m_pSquadNext = NULL; +} + +//========================================================= +// +// SquadAdd(), add pAdd to my squad +// +//========================================================= +void CNPC_FlockingFlyer::SquadAdd( CNPC_FlockingFlyer *pAdd ) +{ + ASSERT( pAdd!=NULL ); + ASSERT( !pAdd->InSquad() ); + ASSERT( this->IsLeader() ); + + pAdd->m_pSquadNext = m_pSquadNext; + m_pSquadNext = pAdd; + pAdd->m_pSquadLeader = this; +} +//========================================================= +// +// SquadRemove(), remove pRemove from my squad. +// If I am pRemove, promote m_pSquadNext to leader +// +//========================================================= +void CNPC_FlockingFlyer::SquadRemove( CNPC_FlockingFlyer *pRemove ) +{ + ASSERT( pRemove!=NULL ); + ASSERT( this->IsLeader() ); + ASSERT( pRemove->m_pSquadLeader == this ); + + if ( SquadCount() > 2 ) + { + // Removing the leader, promote m_pSquadNext to leader + if ( pRemove == this ) + { + CNPC_FlockingFlyer *pLeader = m_pSquadNext; + + // copy the enemy LKP to the new leader + + // if ( GetEnemy() ) + // pLeader->m_vecEnemyLKP = m_vecEnemyLKP; + + if ( pLeader ) + { + CNPC_FlockingFlyer *pList = pLeader; + + while ( pList ) + { + pList->m_pSquadLeader = pLeader; + pList = pList->m_pSquadNext; + } + + } + SquadUnlink(); + } + else // removing a node + { + CNPC_FlockingFlyer *pList = this; + + // Find the node before pRemove + while ( pList->m_pSquadNext != pRemove ) + { + // assert to test valid list construction + ASSERT( pList->m_pSquadNext != NULL ); + pList = pList->m_pSquadNext; + } + // List validity + ASSERT( pList->m_pSquadNext == pRemove ); + + // Relink without pRemove + pList->m_pSquadNext = pRemove->m_pSquadNext; + + // Unlink pRemove + pRemove->SquadUnlink(); + } + } + else + SquadDisband(); +} +//========================================================= +// +// SquadCount(), return the number of members of this squad +// callable from leaders & followers +// +//========================================================= +int CNPC_FlockingFlyer::SquadCount( void ) +{ + CNPC_FlockingFlyer *pList = m_pSquadLeader; + int squadCount = 0; + while ( pList ) + { + squadCount++; + pList = pList->m_pSquadNext; + } + + return squadCount; +} + +//========================================================= +// +// SquadDisband(), Unlink all squad members +// +//========================================================= +void CNPC_FlockingFlyer::SquadDisband( void ) +{ + CNPC_FlockingFlyer *pList = m_pSquadLeader; + CNPC_FlockingFlyer *pNext; + + while ( pList ) + { + pNext = pList->m_pSquadNext; + pList->SquadUnlink(); + pList = pNext; + } +} + +//========================================================= +// Start - player enters the pvs, so get things going. +//========================================================= +void CNPC_FlockingFlyer::Start( void ) +{ + SetNextThink( gpGlobals->curtime + 0.1f ); + + if ( IsLeader() ) + { + SetThink( &CNPC_FlockingFlyer::FlockLeaderThink ); + } + else + { + 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 + EmitSound( filter1, entindex(), "FlockingFlyer.Alert" ); + return; + } + + // make normal sound + CPASAttenuationFilter filter2( this ); + + EmitSound( filter2, entindex(), "FlockingFlyer.Idle" ); +} + +//========================================================= +// 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 ( &CNPC_FlockingFlyer::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; + IncrementInterpolationFrame(); + + UTIL_SetSize( this, Vector(0,0,0), Vector(0,0,0) ); + SetMoveType( MOVETYPE_FLYGRAVITY ); + + SetThink ( &CNPC_FlockingFlyer::FallHack ); + SetNextThink( gpGlobals->curtime + 0.1f ); +} + +void CNPC_FlockingFlyer::FallHack( void ) +{ + if ( GetFlags() & FL_ONGROUND ) + { + CBaseEntity *groundentity = GetContainingEntity( GetGroundEntity()->edict() ); + + if ( !FClassnameIs ( groundentity, "worldspawn" ) ) + { + SetGroundEntity( NULL ); + SetNextThink( gpGlobals->curtime + 0.1f ); + } + else + { + SetAbsVelocity( Vector( 0, 0, 0 ) ); + SetThink( NULL ); + } + } +} |