summaryrefslogtreecommitdiff
path: root/game/server/hl1/hl1_npc_aflock.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_aflock.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_aflock.cpp')
-rw-r--r--game/server/hl1/hl1_npc_aflock.cpp858
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 );
+ }
+ }
+}