/***
*
*	Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*	
*	This product contains software technology licensed from Id 
*	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
*	All Rights Reserved.
*
*   This source code contains proprietary and confidential information of
*   Valve LLC and its suppliers.  Access to this code is restricted to
*   persons who have executed a written SDK license with Valve.  Any access,
*   use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
//=========================================================

#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
#include	"monsters.h"
#include	"squadmonster.h"

#define		AFLOCK_MAX_RECRUIT_RADIUS		1024
#define		AFLOCK_FLY_SPEED			125.0f
#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 CFlockingFlyerFlock : public CBaseMonster
{
public:
	void Spawn( void );
	void Precache( void );
	void KeyValue( KeyValueData *pkvd );
	void SpawnFlock( void );

	virtual int Save( CSave &save );
	virtual int Restore( CRestore &restore );
	static TYPEDESCRIPTION m_SaveData[];

	// Sounds are shared by the flock
	static void PrecacheFlockSounds( void );

	int m_cFlockSize;
	float m_flFlockRadius;
};

TYPEDESCRIPTION	CFlockingFlyerFlock::m_SaveData[] =
{
	DEFINE_FIELD( CFlockingFlyerFlock, m_cFlockSize, FIELD_INTEGER ),
	DEFINE_FIELD( CFlockingFlyerFlock, m_flFlockRadius, FIELD_FLOAT ),
};

IMPLEMENT_SAVERESTORE( CFlockingFlyerFlock, CBaseMonster )

//=========================================================
//=========================================================
class CFlockingFlyer : public CBaseMonster
{
public:
	void Spawn( void );
	void Precache( void );
	void SpawnCommonCode( void );
	void EXPORT IdleThink( void );
	void BoidAdvanceFrame( void );
	void EXPORT FormFlock( void );
	void EXPORT Start( void );
	void EXPORT FlockLeaderThink( void );
	void EXPORT FlockFollowerThink( void );
	void EXPORT FallHack( void );
	void MakeSound( void );
	void AlertFlock( void );
	void SpreadFlock( void );
	void SpreadFlock2( void );
	void Killed( entvars_t *pevAttacker, int iGib );
	void Poop ( void );
	BOOL FPathBlocked( void );
	//void KeyValue( KeyValueData *pkvd );

	virtual int Save( CSave &save );
	virtual int Restore( CRestore &restore );
	static TYPEDESCRIPTION m_SaveData[];

	int IsLeader( void ) { return m_pSquadLeader == this; }
	int InSquad( void ) { return m_pSquadLeader != NULL; }
	int SquadCount( void );
	void SquadRemove( CFlockingFlyer *pRemove );
	void SquadUnlink( void );
	void SquadAdd( CFlockingFlyer *pAdd );
	void SquadDisband( void );

	CFlockingFlyer *m_pSquadLeader;
	CFlockingFlyer *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;
};

LINK_ENTITY_TO_CLASS( monster_flyer, CFlockingFlyer )
LINK_ENTITY_TO_CLASS( monster_flyer_flock, CFlockingFlyerFlock )

TYPEDESCRIPTION	CFlockingFlyer::m_SaveData[] =
{
	DEFINE_FIELD( CFlockingFlyer, m_pSquadLeader, FIELD_CLASSPTR ),
	DEFINE_FIELD( CFlockingFlyer, m_pSquadNext, FIELD_CLASSPTR ),
	DEFINE_FIELD( CFlockingFlyer, m_fTurning, FIELD_BOOLEAN ),
	DEFINE_FIELD( CFlockingFlyer, m_fCourseAdjust, FIELD_BOOLEAN ),
	DEFINE_FIELD( CFlockingFlyer, m_fPathBlocked, FIELD_BOOLEAN ),
	DEFINE_FIELD( CFlockingFlyer, m_vecReferencePoint, FIELD_POSITION_VECTOR ),
	DEFINE_FIELD( CFlockingFlyer, m_vecAdjustedVelocity, FIELD_VECTOR ),
	DEFINE_FIELD( CFlockingFlyer, m_flGoalSpeed, FIELD_FLOAT ),
	DEFINE_FIELD( CFlockingFlyer, m_flLastBlockedTime, FIELD_TIME ),
	DEFINE_FIELD( CFlockingFlyer, m_flFakeBlockedTime, FIELD_TIME ),
	DEFINE_FIELD( CFlockingFlyer, m_flAlertTime, FIELD_TIME ),
	//DEFINE_FIELD( CFlockingFlyer, m_flFlockNextSoundTime, FIELD_TIME ),	// don't need to save
};

IMPLEMENT_SAVERESTORE( CFlockingFlyer, CBaseMonster )

//=========================================================
//=========================================================
void CFlockingFlyerFlock::KeyValue( KeyValueData *pkvd )
{
	if( FStrEq( pkvd->szKeyName, "iFlockSize" ) )
	{
		m_cFlockSize = atoi( pkvd->szValue );
		pkvd->fHandled = TRUE;
	}
	else if( FStrEq( pkvd->szKeyName, "flFlockRadius" ) )
	{
		m_flFlockRadius = atof( pkvd->szValue );
		pkvd->fHandled = TRUE;
	}
}

//=========================================================
//=========================================================
void CFlockingFlyerFlock::Spawn()
{
	Precache();
	SpawnFlock();

	REMOVE_ENTITY( ENT( pev ) );		// dump the spawn ent
}

//=========================================================
//=========================================================
void CFlockingFlyerFlock::Precache()
{
	//PRECACHE_MODEL( "models/aflock.mdl" );		
	PRECACHE_MODEL( "models/boid.mdl" );

	PrecacheFlockSounds();
}

void CFlockingFlyerFlock::PrecacheFlockSounds( void )
{
	PRECACHE_SOUND( "boid/boid_alert1.wav" );
	PRECACHE_SOUND( "boid/boid_alert2.wav" );

	PRECACHE_SOUND( "boid/boid_idle1.wav" );
	PRECACHE_SOUND( "boid/boid_idle2.wav" );
}

//=========================================================
//=========================================================
void CFlockingFlyerFlock::SpawnFlock( void )
{
	float R = m_flFlockRadius;
	int iCount;
	Vector vecSpot;
	CFlockingFlyer *pBoid, *pLeader;

	pLeader = pBoid = NULL;

	for( iCount = 0; iCount < m_cFlockSize; iCount++ )
	{
		pBoid = GetClassPtr( (CFlockingFlyer *)NULL );

		if( !pLeader )
		{
			// make this guy the leader.
			pLeader = pBoid;

			pLeader->m_pSquadLeader = pLeader;
			pLeader->m_pSquadNext = NULL;
		}

		vecSpot.x = RANDOM_FLOAT( -R, R );
		vecSpot.y = RANDOM_FLOAT( -R, R );
		vecSpot.z = RANDOM_FLOAT( 0.0f, 16.0f );
		vecSpot = pev->origin + vecSpot;

		UTIL_SetOrigin( pBoid->pev, vecSpot );
		pBoid->pev->movetype = MOVETYPE_FLY;
		pBoid->SpawnCommonCode();
		pBoid->pev->flags &= ~FL_ONGROUND;
		pBoid->pev->velocity = g_vecZero;
		pBoid->pev->angles = pev->angles;

		pBoid->pev->frame = 0;
		pBoid->pev->nextthink = gpGlobals->time + 0.2f;
		pBoid->SetThink( &CFlockingFlyer::IdleThink );

		if( pBoid != pLeader )
		{
			pLeader->SquadAdd( pBoid );
		}
	}
}

//=========================================================
//=========================================================
void CFlockingFlyer::Spawn()
{
	Precache();
	SpawnCommonCode();

	pev->frame = 0;
	pev->nextthink = gpGlobals->time + 0.1f;
	SetThink( &CFlockingFlyer::IdleThink );
}

//=========================================================
//=========================================================
void CFlockingFlyer::Precache()
{
	//PRECACHE_MODEL( "models/aflock.mdl" );
	PRECACHE_MODEL( "models/boid.mdl" );
	CFlockingFlyerFlock::PrecacheFlockSounds();
}

//=========================================================
//=========================================================
void CFlockingFlyer::MakeSound( void )
{
	if( m_flAlertTime > gpGlobals->time )
	{
		// make agitated sounds
		switch ( RANDOM_LONG( 0, 1 ) )
		{
		case 0:
			EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "boid/boid_alert1.wav", 1, ATTN_NORM );
			break;
		case 1:
			EMIT_SOUND( ENT( pev ), CHAN_WEAPON, "boid/boid_alert2.wav", 1, ATTN_NORM );
			break;
		}

		return;
	}

	// make normal sound
	switch( RANDOM_LONG( 0, 1 ) )
	{
	case 0:
		EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle1.wav", 1, ATTN_NORM );
		break;
	case 1:
		EMIT_SOUND( ENT(pev), CHAN_WEAPON, "boid/boid_idle2.wav", 1, ATTN_NORM );
		break;
	}
}

//=========================================================
//=========================================================
void CFlockingFlyer::Killed( entvars_t *pevAttacker, int iGib )
{
	CFlockingFlyer *pSquad;

	pSquad = (CFlockingFlyer *)m_pSquadLeader;

	while( pSquad )
	{
		pSquad->m_flAlertTime = gpGlobals->time + 15.0f;
		pSquad = (CFlockingFlyer *)pSquad->m_pSquadNext;
	}

	if( m_pSquadLeader )
	{
		m_pSquadLeader->SquadRemove( this );
	}

	pev->deadflag = DEAD_DEAD;

	pev->framerate = 0;
	pev->effects = EF_NOINTERP;

	UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );
	pev->movetype = MOVETYPE_TOSS;

	SetThink( &CFlockingFlyer::FallHack );
	pev->nextthink = gpGlobals->time + 0.1f;
}

void CFlockingFlyer::FallHack( void )
{
	if( pev->flags & FL_ONGROUND )
	{
		if( !FClassnameIs ( pev->groundentity, "worldspawn" ) )
		{
			pev->flags &= ~FL_ONGROUND;
			pev->nextthink = gpGlobals->time + 0.1f;
		}
		else
		{
			pev->velocity = g_vecZero;
			SetThink( NULL );
		}
	}
}

//=========================================================
//=========================================================
void CFlockingFlyer::SpawnCommonCode()
{
	pev->deadflag = DEAD_NO;
	pev->classname = MAKE_STRING( "monster_flyer" );
	pev->solid = SOLID_SLIDEBOX;
	pev->movetype = MOVETYPE_FLY;
	pev->takedamage	= DAMAGE_NO;
	pev->health = 1;

	m_fPathBlocked	= FALSE;// obstacles will be detected
	m_flFieldOfView	= 0.2f;

	//SET_MODEL( ENT( pev ), "models/aflock.mdl" );
	SET_MODEL( ENT( pev ), "models/boid.mdl" );

	//UTIL_SetSize( pev, Vector( 0.0f, 0.0f, 0.0f ), Vector( 0.0f, 0.0f, 0.0f ) );
	UTIL_SetSize( pev, Vector( -5.0f, -5.0f, 0.0f ), Vector( 5.0f, 5.0f, 2.0f ) );
}

//=========================================================
//=========================================================
void CFlockingFlyer::BoidAdvanceFrame()
{
	float flapspeed = ( pev->speed - pev->armorvalue ) / AFLOCK_ACCELERATE;
	pev->armorvalue = pev->armorvalue * 0.8f + pev->speed * 0.2f;

	if( flapspeed < 0.0f )
		flapspeed = -flapspeed;
	if( flapspeed < 0.25f )
		flapspeed = 0.25f;
	if( flapspeed > 1.9f )
		flapspeed = 1.9f;

	pev->framerate = flapspeed;

	// lean
	pev->avelocity.x = -( pev->angles.x + flapspeed * 5.0f );

	// bank
	pev->avelocity.z = -( pev->angles.z + pev->avelocity.y );

	// pev->framerate = flapspeed;
	StudioFrameAdvance( 0.1f );
}

//=========================================================
//=========================================================
void CFlockingFlyer::IdleThink( void )
{
	pev->nextthink = gpGlobals->time + 0.2f;

	// see if there's a client in the same pvs as the monster
	if( !FNullEnt( FIND_CLIENT_IN_PVS( edict() ) ) )
	{
		SetThink( &CFlockingFlyer::Start );
		pev->nextthink = gpGlobals->time + 0.1f;
	}
}

//=========================================================
// Start - player enters the pvs, so get things going.
//=========================================================
void CFlockingFlyer::Start( void )
{
	pev->nextthink = gpGlobals->time + 0.1f;

	if( IsLeader() )
	{
		SetThink( &CFlockingFlyer::FlockLeaderThink );
	}
	else
	{
		SetThink( &CFlockingFlyer::FlockFollowerThink );
	}
/*
	Vector vecTakeOff;
	vecTakeOff = Vector( 0, 0, 0 );

	vecTakeOff.z = 50 + RANDOM_FLOAT( 0, 100 );
	vecTakeOff.x = 20 - RANDOM_FLOAT( 0, 40 );
	vecTakeOff.y = 20 - RANDOM_FLOAT( 0, 40 );

	pev->velocity = vecTakeOff;

	pev->speed = pev->velocity.Length();
	pev->sequence = 0;
*/
	SetActivity( ACT_FLY );
	ResetSequenceInfo();
	BoidAdvanceFrame();

	pev->speed = AFLOCK_FLY_SPEED;// no delay!
}

//=========================================================
// Leader boid calls this to form a flock from surrounding boids
//=========================================================
void CFlockingFlyer::FormFlock( void )
{
	if( !InSquad() )
	{
		// I am my own leader
		m_pSquadLeader = this;
		m_pSquadNext = NULL;
		int squadCount = 1;

		CBaseEntity *pEntity = NULL;

		while( ( pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, AFLOCK_MAX_RECRUIT_RADIUS ) ) != NULL )
		{
			CBaseMonster *pRecruit = pEntity->MyMonsterPointer();

			if( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_pCine )
			{
				// Can we recruit this guy?
				if( FClassnameIs ( pRecruit->pev, "monster_flyer" ) )
				{
					squadCount++;
					SquadAdd( (CFlockingFlyer *)pRecruit );
				}
			}
		}
	}

	SetThink( &CFlockingFlyer::IdleThink );// now that flock is formed, go to idle and wait for a player to come along.
	pev->nextthink = gpGlobals->time;
}
 
//=========================================================
// Searches for boids that are too close and pushes them away
//=========================================================
void CFlockingFlyer::SpreadFlock()
{
	Vector vecDir;
	float flSpeed;// holds vector magnitude while we fiddle with the direction

	CFlockingFlyer *pList = m_pSquadLeader;
	while( pList )
	{
		if( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
		{
			// push the other away
			vecDir = pList->pev->origin - pev->origin;
			vecDir = vecDir.Normalize();

			// 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->pev->velocity.Length();
			pList->pev->velocity = pList->pev->velocity.Normalize();
			pList->pev->velocity = ( pList->pev->velocity + vecDir ) * 0.5f;
			pList->pev->velocity = pList->pev->velocity * 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 CFlockingFlyer::SpreadFlock2()
{
	Vector vecDir;

	CFlockingFlyer *pList = m_pSquadLeader;
	while( pList )
	{
		if( pList != this && ( pev->origin - pList->pev->origin ).Length() <= AFLOCK_TOO_CLOSE )
		{
			vecDir = pev->origin - pList->pev->origin;
			vecDir = vecDir.Normalize();

			pev->velocity = pev->velocity + vecDir;
		}

		pList = pList->m_pSquadNext;
	}
}

//=========================================================
// FBoidPathBlocked - returns TRUE if there is an obstacle ahead
//=========================================================
BOOL CFlockingFlyer::FPathBlocked()
{
	TraceResult tr;
	Vector vecDist;// used for general measurements
	Vector vecDir;// used for general measurements
	BOOL fBlocked;

	if( m_flFakeBlockedTime > gpGlobals->time )
	{
		m_flLastBlockedTime = gpGlobals->time;
		return TRUE;
	}

	// use VELOCITY, not angles, not all boids point the direction they are flying
	//vecDir = UTIL_VecToAngles( pevBoid->velocity );
	UTIL_MakeVectors ( pev->angles );

	fBlocked = FALSE;// assume the way ahead is clear

	// check for obstacle ahead
	UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
	if( tr.flFraction != 1.0f )
	{
		m_flLastBlockedTime = gpGlobals->time;
		fBlocked = TRUE;
	}

	// extra wide checks
	UTIL_TraceLine( pev->origin + gpGlobals->v_right * 12.0f, pev->origin + gpGlobals->v_right * 12.0f + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
	if( tr.flFraction != 1.0f )
	{
		m_flLastBlockedTime = gpGlobals->time;
		fBlocked = TRUE;
	}

	UTIL_TraceLine( pev->origin - gpGlobals->v_right * 12.0f, pev->origin - gpGlobals->v_right * 12.0f + gpGlobals->v_forward * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
	if( tr.flFraction != 1.0f )
	{
		m_flLastBlockedTime = gpGlobals->time;
		fBlocked = TRUE;
	}

	if( !fBlocked && gpGlobals->time - m_flLastBlockedTime > 6.0f )
	{
		// not blocked, and it's been a few seconds since we've actually been blocked.
		m_flFakeBlockedTime = gpGlobals->time + RANDOM_LONG( 1, 3 ); 
	}

	return	fBlocked;
}

//=========================================================
// Leader boids use this think every tenth
//=========================================================
void CFlockingFlyer::FlockLeaderThink( void )
{
	TraceResult tr;
	Vector vecDist;// used for general measurements
	Vector vecDir;// used for general measurements
	float flLeftSide;
	float flRightSide;

	pev->nextthink = gpGlobals->time + 0.1f;

	UTIL_MakeVectors( pev->angles );

	// is the way ahead clear?
	if( !FPathBlocked () )
	{
		// if the boid is turning, stop the trend.
		if( m_fTurning )
		{
			m_fTurning = FALSE;
			pev->avelocity.y = 0;
		}

		m_fPathBlocked = FALSE;

		if( pev->speed <= AFLOCK_FLY_SPEED )
			pev->speed += 5.0f;

		pev->velocity = gpGlobals->v_forward * pev->speed;

		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( pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
		vecDist = ( tr.vecEndPos - pev->origin );
		flRightSide = vecDist.Length();

		UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
		vecDist = tr.vecEndPos - pev->origin;
		flLeftSide = vecDist.Length();

		// turn right if more clearance on right side
		if( flRightSide > flLeftSide )
		{
			pev->avelocity.y = -AFLOCK_TURN_RATE;
			m_fTurning = TRUE;
		}
		// default to left turn :)
		else if( flLeftSide > flRightSide )
		{
			pev->avelocity.y = AFLOCK_TURN_RATE;
			m_fTurning = TRUE;
		}
		else
		{
			// equidistant. Pick randomly between left and right.
			m_fTurning = TRUE;

			if( RANDOM_LONG( 0, 1 ) == 0 )
			{
				pev->avelocity.y = AFLOCK_TURN_RATE;
			}
			else
			{
				pev->avelocity.y = -AFLOCK_TURN_RATE;
			}
		}
	}
	SpreadFlock();

	pev->velocity = gpGlobals->v_forward * pev->speed;

	// check and make sure we aren't about to plow into the ground, don't let it happen
	UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_up * 16, ignore_monsters, ENT( pev ), &tr );
	if( tr.flFraction != 1.0f && pev->velocity.z < 0.0f )
		pev->velocity.z = 0.0f;

	// maybe it did, though.
	if( FBitSet( pev->flags, FL_ONGROUND ) )
	{
		UTIL_SetOrigin( pev, pev->origin + Vector( 0, 0, 1 ) );
		pev->velocity.z = 0;
	}

	if( m_flFlockNextSoundTime < gpGlobals->time )
	{
		MakeSound();
		m_flFlockNextSoundTime = gpGlobals->time + RANDOM_FLOAT( 1, 3 );
	}

	BoidAdvanceFrame( );

	return;
}

//=========================================================
// follower boids execute this code when flocking
//=========================================================
void CFlockingFlyer::FlockFollowerThink( void )	
{
	TraceResult tr;
	Vector vecDist;
	Vector vecDir;
	Vector vecDirToLeader;
	float flDistToLeader;

	pev->nextthink = gpGlobals->time + 0.1f;

	if( IsLeader() || !InSquad() )
	{
		// the leader has been killed and this flyer suddenly finds himself the leader. 
		SetThink( &CFlockingFlyer::FlockLeaderThink );
		return;
	}

	vecDirToLeader = ( m_pSquadLeader->pev->origin - pev->origin );
	flDistToLeader = vecDirToLeader.Length();
	
	// match heading with leader
	pev->angles = m_pSquadLeader->pev->angles;

	//
	// 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->pev->velocity.Length() * 1.5f;
		}

		// if we're too close, slow down
		else if( flDistToLeader < AFLOCK_TOO_CLOSE )
		{
			m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5f;
		}
	}
	else
	{
		// wait up! the leader isn't out in front, so we slow down to let him pass
		m_flGoalSpeed = m_pSquadLeader->pev->velocity.Length() * 0.5f;
	}

	SpreadFlock2();

	pev->speed = pev->velocity.Length();
	pev->velocity = pev->velocity.Normalize();

	// if we are too far from leader, average a vector towards it into our current velocity
	if( flDistToLeader > AFLOCK_TOO_FAR )
	{
		vecDirToLeader = vecDirToLeader.Normalize();
		pev->velocity = (pev->velocity + vecDirToLeader) * 0.5f;
	}

	// clamp speeds and handle acceleration
	if( m_flGoalSpeed > AFLOCK_FLY_SPEED * 2.0f )
	{
		m_flGoalSpeed  = AFLOCK_FLY_SPEED * 2.0f;
	}

	if( pev->speed < m_flGoalSpeed )
	{
		pev->speed += AFLOCK_ACCELERATE;
	}
	else if( pev->speed > m_flGoalSpeed )
	{
		pev->speed -= AFLOCK_ACCELERATE;
	}

	pev->velocity = pev->velocity * pev->speed;

	BoidAdvanceFrame( );
}

/*
	// Is this boid's course blocked?
	if( FBoidPathBlocked( pev ) )
	{
		// course is still blocked from last time. Just keep flying along adjusted 
		// velocity
		if( m_fCourseAdjust )
		{
			pev->velocity = m_vecAdjustedVelocity * pev->speed;
			return;
		}
		else // set course adjust flag and calculate adjusted velocity
		{
			m_fCourseAdjust = TRUE;

			// use VELOCITY, not angles, not all boids point the direction they are flying
			//vecDir = UTIL_VecToAngles( pev->velocity );
			//UTIL_MakeVectors( vecDir );

			UTIL_MakeVectors( pev->angles );

			// measure clearance on left and right to pick the best dir to turn
			UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
			vecDist = tr.vecEndPos - pev->origin;
			flRightSide = vecDist.Length();

			UTIL_TraceLine( pev->origin, pev->origin - gpGlobals->v_right * AFLOCK_CHECK_DIST, ignore_monsters, ENT( pev ), &tr );
			vecDist = tr.vecEndPos - pev->origin;
			flLeftSide = vecDist.Length();

			// slide right if more clearance on right side
			if( flRightSide > flLeftSide )
			{
				m_vecAdjustedVelocity = gpGlobals->v_right;
			}
			// else slide left
			else
			{
				m_vecAdjustedVelocity = gpGlobals->v_right * -1.0f;
			}
		}
		return;
	}

	// if we make it this far, boids path is CLEAR!
	m_fCourseAdjust = FALSE;
*/

//=========================================================
//
// SquadUnlink(), Unlink the squad pointers.
//
//=========================================================
void CFlockingFlyer::SquadUnlink( void )
{
	m_pSquadLeader = NULL;
	m_pSquadNext = NULL;
}

//=========================================================
//
// SquadAdd(), add pAdd to my squad
//
//=========================================================
void CFlockingFlyer::SquadAdd( CFlockingFlyer *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 CFlockingFlyer::SquadRemove( CFlockingFlyer *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 )
		{
			CFlockingFlyer *pLeader = m_pSquadNext;

			if( pLeader )
			{
				// copy the enemy LKP to the new leader
				pLeader->m_vecEnemyLKP = m_vecEnemyLKP;

				CFlockingFlyer *pList = pLeader;

				while( pList )
				{
					pList->m_pSquadLeader = pLeader;
					pList = pList->m_pSquadNext;
				}

			}
			SquadUnlink();
		}
		else	// removing a node
		{
			CFlockingFlyer *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 CFlockingFlyer::SquadCount( void )
{
	CFlockingFlyer *pList = m_pSquadLeader;
	int squadCount = 0;
	while( pList )
	{
		squadCount++;
		pList = pList->m_pSquadNext;
	}

	return squadCount;
}

//=========================================================
//
// SquadDisband(), Unlink all squad members
//
//=========================================================
void CFlockingFlyer::SquadDisband( void )
{
	CFlockingFlyer *pList = m_pSquadLeader;
	CFlockingFlyer *pNext;

	while( pList )
	{
		pNext = pList->m_pSquadNext;
		pList->SquadUnlink();
		pList = pNext;
	}
}
