//---------------------------------------------------------
//---------------------------------------------------------
//-														---
//-					flybee.cpp							---
//-														---
//---------------------------------------------------------
//---------------------------------------------------------
//-	par JujU									-----------
//-		julien.lecorre@free.fr					-----------
//---------------------------------------------------------
//- code du monstre flybee						-----------
//---------------------------------------------------------
//---------------------------------------------------------



//---------------------------------------------------------
// inclusions

#include	"extdll.h"
#include	"util.h"
#include	"cbase.h"
#include	"monsters.h"
#include	"effects.h"
#include	"schedule.h"
#include	"weapons.h"
#include	"flyingmonster.h"
#include	"nodes.h"
#include	"soundent.h"

extern int gmsgClientDecal;


#define FLYBEE_SPEED		150
#define PROBE_LENGTH		150

#define FEAR_LEVEL		10
#define HATE_LEVEL		-10


//=========================================================
// monster-specific tasks and states
//=========================================================
enum 
{
	TASK_FLYBEE_CIRCLE_ENEMY = LAST_COMMON_TASK + 1,
	TASK_FLYBEE_SWIM,
	TASK_FLYBEE_STOP_MOVING,
	TASK_FLYBEE_RUN_ATTACK,
	TASK_FLYBEE_RANGE_ATTACK2,
};

//=========================================================
// Monster's Anim Events Go Here
//=========================================================

#define FLYBEE_AE_HIT		1
#define FLYBEE_AE_ATTACK1	2
#define FLYBEE_AE_ATTACK2	3


//=========================================================
// D
//=========================================================


class CFlybee : public CFlyingMonster
{
public:

	void	Spawn( void );
	void	Precache( void );
	int		Classify( void );

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

	CUSTOM_SCHEDULES;
	void	HandleAnimEvent( MonsterEvent_t *pEvent );
	Schedule_t *GetSchedule( void );
	Schedule_t *GetScheduleOfType ( int Type );

	void	StartTask( Task_t *pTask );
	void	RunTask( Task_t *pTask );

	BOOL	CheckMeleeAttack1 ( float flDot, float flDist );
	BOOL	CheckRangeAttack1 ( float flDot, float flDist );
	BOOL	CheckRangeAttack2 ( float flDot, float flDist );

	Activity GetStoppedActivity( void );

	void	MonsterThink( void );

	void	Move( float flInterval );
	void	MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval );

	void	Stop( void );
	void	Swim( void );

	void	SetYawSpeed( void );
	float	ChangeYaw( int speed );
	Vector	DoProbe(const Vector &Probe);

	float	VectorToPitch( const Vector &vec);
	float	FlPitchDiff( void );
	float	ChangePitch( int speed );

	Vector	m_SaveVelocity;
	float	m_idealDist;

	float	m_flMaxSpeed;
	float	m_flMinSpeed;
	float	m_flMaxDist;

	Vector	m_vecAttack2;

	int		m_iSpriteTexture;

	CBeam *m_pBeam;

	int		m_iFear;	
	float m_flNextAlert;
	float m_flNextAttack;

	static const char *pIdleSounds[];
	static const char *pAlertSounds[];
	static const char *pAttackSounds[];
	static const char *pBiteSounds[];
	static const char *pDieSounds[];
	static const char *pPainSounds[];

	void IdleSound( void );
	void AlertSound( void );
	void AttackSound( void );
	void BiteSound( void );
	void DeathSound( void );
	void PainSound( void );
};

LINK_ENTITY_TO_CLASS( monster_flybee, CFlybee );

TYPEDESCRIPTION	CFlybee::m_SaveData[] = 
{
	DEFINE_FIELD( CFlybee, m_SaveVelocity, FIELD_VECTOR ),
	DEFINE_FIELD( CFlybee, m_idealDist, FIELD_FLOAT ),
	DEFINE_FIELD( CFlybee, m_flMaxSpeed, FIELD_FLOAT ),
	DEFINE_FIELD( CFlybee, m_flMinSpeed, FIELD_FLOAT ),
	DEFINE_FIELD( CFlybee, m_flMaxDist, FIELD_FLOAT ),
	DEFINE_FIELD( CFlybee, m_flNextAlert, FIELD_TIME ),
	DEFINE_FIELD( CFlybee, m_flNextAttack, FIELD_TIME ),
	DEFINE_FIELD( CFlybee, m_iFear, FIELD_INTEGER ),
	DEFINE_FIELD( CFlybee, m_vecAttack2, FIELD_VECTOR ),
};

IMPLEMENT_SAVERESTORE( CFlybee, CFlyingMonster );



class CFlyBall : public CBaseMonster
{
public :
	void Spawn( void );
	void Precache( void );
	void EXPORT AnimateThink( void );
	void EXPORT ExplodeTouch( CBaseEntity *pOther );

	int	Classify ( void );
	static CFlyBall *CreateFlyBall( Vector vecOrigin, Vector vecAngles, entvars_s *pevOwner );

	int m_iSprite;
};
LINK_ENTITY_TO_CLASS( flyball, CFlyBall );




const char *CFlybee::pIdleSounds[] = 
{
	"ichy/ichy_idle1.wav",
	"ichy/ichy_idle2.wav",
	"ichy/ichy_idle3.wav",
	"ichy/ichy_idle4.wav",
};

const char *CFlybee::pAlertSounds[] = 
{
	"ichy/ichy_alert2.wav",
	"ichy/ichy_alert3.wav",
};

const char *CFlybee::pAttackSounds[] = 
{
	"ichy/ichy_attack1.wav",
	"ichy/ichy_attack2.wav",
};

const char *CFlybee::pBiteSounds[] = 
{
	"ichy/ichy_bite1.wav",
	"ichy/ichy_bite2.wav",
};

const char *CFlybee::pPainSounds[] = 
{
	"ichy/ichy_pain2.wav",
	"ichy/ichy_pain3.wav",
	"ichy/ichy_pain5.wav",
};

const char *CFlybee::pDieSounds[] = 
{
	"ichy/ichy_die2.wav",
	"ichy/ichy_die4.wav",
};

#define EMIT_ICKY_SOUND( chan, array ) \
	EMIT_SOUND_DYN ( ENT(pev), chan , array [ RANDOM_LONG(0,ARRAYSIZE( array )-1) ], 1.0, 0.6, 0, RANDOM_LONG(95,105) ); 


void CFlybee :: IdleSound( void )	
{ 
	EMIT_ICKY_SOUND( CHAN_VOICE, pIdleSounds ); 
}

void CFlybee :: AlertSound( void ) 
{ 
	EMIT_ICKY_SOUND( CHAN_VOICE, pAlertSounds ); 
}

void CFlybee :: AttackSound( void ) 
{ 
	EMIT_ICKY_SOUND( CHAN_VOICE, pAttackSounds );
}

void CFlybee :: BiteSound( void ) 
{ 
	EMIT_ICKY_SOUND( CHAN_WEAPON, pBiteSounds );
}

void CFlybee :: DeathSound( void ) 
{ 
	EMIT_ICKY_SOUND( CHAN_VOICE, pDieSounds ); 
}

void CFlybee :: PainSound( void )	
{ 
	EMIT_ICKY_SOUND( CHAN_VOICE, pPainSounds ); 
}


//=========================================================
// Spawn
//=========================================================
void CFlybee :: Spawn()
{
	Precache( );

	SET_MODEL(ENT(pev), "models/flybee.mdl");
	UTIL_SetSize( pev, Vector( -24, -24, 0 ), Vector( 24,24,24 ) );

	pev->solid			= SOLID_BBOX;
	pev->movetype		= MOVETYPE_FLY;
	m_bloodColor		= BLOOD_COLOR_GREEN;
	pev->health			= gSkillData.ichthyosaurHealth;
	pev->view_ofs		= Vector ( 0, 0, 16 );
	m_flFieldOfView		= VIEW_FIELD_WIDE;
	m_MonsterState		= MONSTERSTATE_NONE;
	SetFlyingSpeed( FLYBEE_SPEED );
	SetFlyingMomentum( 2.5 );	// Set momentum constant

	m_afCapability		= bits_CAP_MELEE_ATTACK1 | bits_CAP_RANGE_ATTACK1 | bits_CAP_RANGE_ATTACK2 | bits_CAP_SWIM;

	MonsterInit();

	m_idealDist		= 384;
	m_flMinSpeed	= 80;
	m_flMaxSpeed	= 300;
	m_flMaxDist		= 384;

	m_iFear			= 0;
	m_flNextAttack	= 0;

	Vector Forward;
	UTIL_MakeVectorsPrivate(pev->angles, Forward, 0, 0);
	pev->velocity = m_flightSpeed * Forward.Normalize();
	m_SaveVelocity = pev->velocity;
}

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CFlybee :: Precache()
{
	PRECACHE_MODEL("models/flybee.mdl");

	PRECACHE_SOUND("zombie/claw_miss2.wav");
	PRECACHE_SOUND("debris/beamstart4.wav");
	PRECACHE_MODEL("sprites/nhth1.spr");

	PRECACHE_SOUND_ARRAY( pIdleSounds );
	PRECACHE_SOUND_ARRAY( pAlertSounds );
	PRECACHE_SOUND_ARRAY( pAttackSounds );
	PRECACHE_SOUND_ARRAY( pBiteSounds );
	PRECACHE_SOUND_ARRAY( pDieSounds );
	PRECACHE_SOUND_ARRAY( pPainSounds );

	UTIL_PrecacheOther ( "flyball" );

	m_iSpriteTexture = PRECACHE_MODEL("sprites/shockwave.spr");
}



//=========================================================
// Classify - indicates this monster's place in the 
// relationship table.
//=========================================================
int	CFlybee :: Classify ( void )
{
	return	CLASS_ALIEN_MONSTER;
}


//=========================================================
// Check Attacks
//=========================================================

BOOL CFlybee :: CheckMeleeAttack1 ( float flDot, float flDist )
{

	if ( flDist <= 64  )
	{
		return TRUE;
	}
	return FALSE;
}


BOOL CFlybee :: CheckRangeAttack1 ( float flDot, float flDist )
{
	if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && flDot > -0.7 && m_iFear <= HATE_LEVEL )
	{
		return TRUE;
	}

	return FALSE;
}

BOOL CFlybee :: CheckRangeAttack2 ( float flDot, float flDist )
{
	if ( !HasConditions( bits_COND_ENEMY_OCCLUDED ) && m_iFear >= FEAR_LEVEL && m_flNextAttack < gpGlobals->time )
	{
		return TRUE;
	}

	return FALSE;
}


//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CFlybee :: SetYawSpeed ( void )
{
	pev->yaw_speed = 100;
}



//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CFlybee :: HandleAnimEvent( MonsterEvent_t *pEvent )
{

	switch( pEvent->event )
	{
	case FLYBEE_AE_HIT:
		{
			TraceResult tr;

			UTIL_MakeVectors( pev->angles );
			Vector vecStart = pev->origin;
			vecStart.z += pev->size.z * 0.5;
			Vector vecEnd = vecStart + (gpGlobals->v_forward * 70);

			UTIL_TraceHull( vecStart, vecEnd, dont_ignore_monsters, head_hull, ENT(pev), &tr );
			
			if ( tr.pHit )
			{
				CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
				pEntity->pev->punchangle.z = 25;
				pEntity->TakeDamage( pev, pev,20, DMG_CLUB );
			}
			break;
		}

	case FLYBEE_AE_ATTACK2:
		{
			UTIL_MakeVectors( pev->angles );
			TraceResult tr;

			Vector vecStart	= pev->origin + gpGlobals->v_up * 24 + gpGlobals->v_forward * 32;
			UTIL_TraceLine ( vecStart, m_vecAttack2, dont_ignore_monsters, dont_ignore_glass, ENT(pev), &tr );

			Vector vecEnd = tr.vecEndPos;

			for ( int i = 0; i<4; i ++ )
			{
				CBeam *pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 8 );

				if ( RANDOM_LONG(0,1) )
					pBeam->SetColor( 206,118, 254 );
				else
					pBeam->SetColor( 223,224, 255 );

				pBeam->SetBrightness( 192 );

				pBeam->SetStartPos( vecStart );
				pBeam->SetEndPos ( vecEnd );
				pBeam->RelinkBeam( );

				pBeam->SetNoise ( 30 );
				pBeam->LiveForTime ( 0.35 );
			}

			CSprite *pSprite = CSprite::SpriteCreate ( "sprites/nhth1.spr", vecEnd, TRUE );
			pSprite->AnimateAndDie ( 10 );
			pSprite->SetScale ( 0.8 );
			pSprite->SetTransparency ( kRenderTransAdd, 230, 255, 230, 150, kRenderFxNone );
			pSprite->Expand ( pSprite->pev->scale, 120 );


			MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, pev->origin );

				WRITE_BYTE( TE_BEAMCYLINDER );

				WRITE_COORD( vecEnd.x);	WRITE_COORD( vecEnd.y); WRITE_COORD( vecEnd.z);
				WRITE_COORD( vecEnd.x); WRITE_COORD( vecEnd.y); WRITE_COORD( vecEnd.z + 1000 );
				WRITE_SHORT( m_iSpriteTexture );
				WRITE_BYTE( 0 ); // startframe
				WRITE_BYTE( 0 ); // framerate
				WRITE_BYTE( 2 ); // life
				WRITE_BYTE( 16 );  // width
				WRITE_BYTE( 0 );   // noise
				WRITE_BYTE( 206 ); WRITE_BYTE( 118 ); WRITE_BYTE( 255 ); WRITE_BYTE( 80 ); // rgba
				WRITE_BYTE( 0 );		// speed
			MESSAGE_END();

			RadiusDamage ( vecEnd, pev, pev, 50, CLASS_ALIEN_MONSTER, DMG_SHOCK );

			UTIL_EmitAmbientSound( ENT(pev), vecEnd, "debris/beamstart4.wav", 0.9, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) );

			break;
		}
	case FLYBEE_AE_ATTACK1:
		{
			UTIL_MakeVectors( pev->angles );

			Vector vecSrc	= pev->origin + gpGlobals->v_up * 24 + gpGlobals->v_forward * 32;
			Vector ang		= UTIL_VecToAngles( (m_hEnemy->Center() - vecSrc).Normalize() );

			CFlyBall ::CreateFlyBall ( vecSrc + gpGlobals->v_right * 30, ang, pev );
			CFlyBall ::CreateFlyBall ( vecSrc + gpGlobals->v_right * 10, ang, pev );
			CFlyBall ::CreateFlyBall ( vecSrc - gpGlobals->v_right * 30, ang, pev );
			CFlyBall ::CreateFlyBall ( vecSrc - gpGlobals->v_right * 10, ang, pev );

			break;
		}
	default:
		CFlyingMonster::HandleAnimEvent( pEvent );
		break;

	}
}

//=========================================================
// AI Schedules Specific to this monster
//=========================================================

static Task_t	tlFlybeeSwimAround[] =
{
	{ TASK_SET_ACTIVITY,			(float)ACT_WALK },
	{ TASK_FLYBEE_SWIM,				(float)0 },
};

static Schedule_t	slFlybeeSwimAround[] =
{
	{ 
		tlFlybeeSwimAround,
		ARRAYSIZE(tlFlybeeSwimAround), 
		bits_COND_LIGHT_DAMAGE	|
		bits_COND_HEAVY_DAMAGE	|
		bits_COND_SEE_ENEMY		|
		bits_COND_NEW_ENEMY		|
		bits_COND_HEAR_SOUND,
		bits_SOUND_PLAYER |
		bits_SOUND_COMBAT,
		"SwimAround"
	},
};

static Task_t	tlFlybeeSwimAgitated[] =
{
	{ TASK_STOP_MOVING,				(float) 0 },
	{ TASK_SET_ACTIVITY,			(float)ACT_WALK },
	{ TASK_WAIT,					(float)2.0 },
};

static Schedule_t	slFlybeeSwimAgitated[] =
{
	{ 
		tlFlybeeSwimAgitated,
		ARRAYSIZE(tlFlybeeSwimAgitated), 
		0, 
		0, 
		"SwimAgitated"
	},
};


static Task_t	tlFlybeeCircleEnemy[] =
{
	{ TASK_SET_ACTIVITY,			(float)ACT_WALK },
	{ TASK_FLYBEE_CIRCLE_ENEMY, 0.0 },
};

static Schedule_t	slFlybeeCircleEnemy[] =
{
	{ 
		tlFlybeeCircleEnemy,
		ARRAYSIZE(tlFlybeeCircleEnemy), 
		bits_COND_NEW_ENEMY		|
		bits_COND_LIGHT_DAMAGE	|
		bits_COND_HEAVY_DAMAGE	|
		bits_COND_CAN_MELEE_ATTACK1 |
		bits_COND_CAN_RANGE_ATTACK1 |
		bits_COND_CAN_RANGE_ATTACK2,
		0,
		"CircleEnemy"
	},
};


Task_t tlFlybeeTwitchDie[] =
{
	{ TASK_STOP_MOVING,			0		 },
	{ TASK_SOUND_DIE,			(float)0 },
	{ TASK_DIE,					(float)0 },
};

Schedule_t slFlybeeTwitchDie[] =
{
	{
		tlFlybeeTwitchDie,
		ARRAYSIZE( tlFlybeeTwitchDie ),
		0,
		0,
		"Die"
	},
};

Task_t tlFlybeeRangeAttack1[] =
{
	{ TASK_FLYBEE_STOP_MOVING,	0				},
	{ TASK_FACE_ENEMY,			(float)0		},
	{ TASK_RANGE_ATTACK1,		(float)0		},
};

Schedule_t slFlybeeRangeAttack1[] =
{
	{
		tlFlybeeRangeAttack1,
		ARRAYSIZE( tlFlybeeRangeAttack1 ),

		bits_COND_ENEMY_DEAD		|
		bits_COND_HEAVY_DAMAGE,
		0,
		"Range attack 1"
	},
};

Task_t tlFlybeeRangeAttack2[] =
{
	{ TASK_FLYBEE_STOP_MOVING,			(float)0		},
	{ TASK_FACE_ENEMY,					(float)0		},
	{ TASK_FLYBEE_RANGE_ATTACK2,		(float)0		},
};

Schedule_t slFlybeeRangeAttack2[] =
{
	{
		tlFlybeeRangeAttack2,
		ARRAYSIZE( tlFlybeeRangeAttack2 ),

		bits_COND_ENEMY_DEAD		|
		bits_COND_HEAVY_DAMAGE,
		0,
		"Range attack 2"
	},
};

Task_t tlFlybeeRunAttack[] =
{
	{ TASK_SET_FAIL_SCHEDULE,	(float)SCHED_RANGE_ATTACK1	},
	{ TASK_FLYBEE_STOP_MOVING,	(float) 0					},
	{ TASK_FLYBEE_RUN_ATTACK,	(float)	0					},
};

Schedule_t slFlybeeRunAttack[] =
{
	{
		tlFlybeeRunAttack,
		ARRAYSIZE( tlFlybeeRunAttack ),

		bits_COND_ENEMY_DEAD		|
		bits_COND_CAN_MELEE_ATTACK1 |
		bits_COND_HEAVY_DAMAGE,
		0,
		"Range attack 2"
	},
};



Task_t	tlFlybeeMeleeAttack[] =
{
	{ TASK_FLYBEE_STOP_MOVING,	(float)0		},
	{ TASK_FACE_ENEMY,			(float)0		},
	{ TASK_MELEE_ATTACK1,		(float)0		},
};

Schedule_t	slFlybeeMeleeAttack[] =
{
	{ 
		tlFlybeeMeleeAttack,
		ARRAYSIZE ( tlFlybeeMeleeAttack ), 
		bits_COND_NEW_ENEMY			|
		bits_COND_ENEMY_DEAD,
		0,
		"Flybee Melee Attack"
	},
};

DEFINE_CUSTOM_SCHEDULES(CFlybee)
{
    slFlybeeSwimAround,
	slFlybeeSwimAgitated,
	slFlybeeCircleEnemy,
	slFlybeeTwitchDie,
	slFlybeeRangeAttack1,
	slFlybeeRangeAttack2,
	slFlybeeRunAttack,
	slFlybeeMeleeAttack,
};
IMPLEMENT_CUSTOM_SCHEDULES(CFlybee, CFlyingMonster);


//=========================================================
// GetSchedule
//=========================================================

Schedule_t* CFlybee::GetSchedule()
{
	switch(m_MonsterState)
	{
	case MONSTERSTATE_IDLE:
		m_flightSpeed = 80;
		return GetScheduleOfType( SCHED_IDLE_WALK );

	case MONSTERSTATE_ALERT:
		m_flightSpeed = 150;
		return GetScheduleOfType( SCHED_IDLE_WALK );

	case MONSTERSTATE_COMBAT:
		m_flMaxSpeed = 400;

		if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) )
		{
//			ALERT ( at_console, "MELEE1\n" );

			m_iFear = Q_max ( HATE_LEVEL, m_iFear - 5 );
			return GetScheduleOfType( SCHED_MELEE_ATTACK1 );
		}

		if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) )
		{
//			ALERT ( at_console, "ATTACK1\n" );

			m_iFear = Q_min ( FEAR_LEVEL, m_iFear + 5 );
			return GetScheduleOfType( SCHED_RANGE_ATTACK1 );
		}

		if ( HasConditions( bits_COND_CAN_RANGE_ATTACK2 ) )
		{
//			ALERT ( at_console, "ATTACK2\n" );

			m_iFear -= 10;
			m_flNextAttack = gpGlobals->time + RANDOM_FLOAT ( 3,5 );
			return GetScheduleOfType( SCHED_RANGE_ATTACK2 );
		}

		if ( HasConditions( bits_COND_HEAVY_DAMAGE ) )
		{
			m_iFear = Q_min ( FEAR_LEVEL, m_iFear + 3 );

			m_flightSpeed = Q_min ( m_flMaxSpeed, m_flightSpeed + 40 );
		}

		return GetScheduleOfType( SCHED_STANDOFF );
		
	}

	return CFlyingMonster :: GetSchedule();
}


//=========================================================
//=========================================================
Schedule_t* CFlybee :: GetScheduleOfType ( int Type ) 
{

	switch	( Type )
	{
	case SCHED_IDLE_WALK:
		return slFlybeeSwimAround;

	case SCHED_STANDOFF:
		return slFlybeeCircleEnemy;

	case SCHED_FAIL:
		return slFlybeeSwimAgitated;

	case SCHED_DIE:
		return slFlybeeTwitchDie;

	case SCHED_CHASE_ENEMY:
		AttackSound( );

	case SCHED_MELEE_ATTACK1:
		return slFlybeeMeleeAttack;

	case SCHED_RANGE_ATTACK1:
		return slFlybeeRangeAttack1;

	case SCHED_RANGE_ATTACK2:

		if ( m_hEnemy->pev->velocity.Length() <= 100 )
		{
			if ( (Center()-m_hEnemy->Center()).Length() <= 300 && fabs(pev->origin.z-m_hEnemy->pev->origin.z) <= 128 )
				return slFlybeeRunAttack;
			else
			{
				if ( RANDOM_LONG ( 0,2 ) == 0 )
					return slFlybeeRangeAttack1;
				else
					return slFlybeeRangeAttack2;
			}
		}
		else
		{
			return slFlybeeRangeAttack1;
		}

	case SCHED_BURNT:
		m_flightSpeed = m_flMaxSpeed;
		return slFlybeeSwimAround;

	}


	return CBaseMonster :: GetScheduleOfType( Type );
}



//=========================================================
// Start task - selects the correct activity and performs
// any necessary calculations to start the next task on the
// schedule.
//=========================================================

void CFlybee::StartTask(Task_t *pTask)
{
	switch (pTask->iTask)
	{
	case TASK_FLYBEE_CIRCLE_ENEMY:
		break;

	case TASK_FLYBEE_SWIM:
		break;

	case TASK_SMALL_FLINCH:
		if (m_idealDist > 128)
		{
			m_flMaxDist = 512;
			m_idealDist = 512;
		}
		CFlyingMonster::StartTask(pTask);
		break;

	case TASK_FLYBEE_STOP_MOVING:
		m_flightSpeed = 0;

		if ( m_IdealActivity == m_movementActivity )
		{
			m_IdealActivity = GetStoppedActivity();
		}

		RouteClear();
		TaskComplete();
		break;

	case TASK_FLYBEE_RUN_ATTACK:
		m_IdealActivity = ACT_CROUCH;
		m_flightSpeed = 128;
		RouteClear();
		break;

	case TASK_DIE:
		pev->movetype	= MOVETYPE_STEP;
		pev->angles.x = 0;
		UTIL_SetSize( pev, Vector( -8, -8, 0 ), Vector( 8,8,8 ) );

		CFlyingMonster::StartTask(pTask);
		break;

	case TASK_FLYBEE_RANGE_ATTACK2:
		m_vecAttack2 = m_hEnemy->Center();
		m_IdealActivity = ACT_RANGE_ATTACK2;
		break;

	default:
		CFlyingMonster::StartTask(pTask);
		break;
	}
}

void CFlybee :: RunTask ( Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_FLYBEE_CIRCLE_ENEMY:
		if (m_hEnemy == 0)
		{
			TaskComplete( );
		}
		else if (FVisible( m_hEnemy ))
		{

			// nouvelle position

			Vector vecFrom = m_hEnemy->EyePosition( );

			Vector vecDelta = (pev->origin - vecFrom).Normalize( );
			Vector vecSwim = CrossProduct( vecDelta, Vector( 0, 0, 1 ) ).Normalize( );
			
			if (DotProduct( vecSwim, m_SaveVelocity ) < 0)
				vecSwim = vecSwim * -1.0;

			Vector vecPos = vecFrom + vecDelta * m_idealDist + vecSwim * 32;

		/*	if (!m_pBeam)
			{
				m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.spr", 80 );
				m_pBeam->SetColor( 255, 180, 96 );
				m_pBeam->SetBrightness( 192 );
			}
			m_pBeam->SetStartPos( m_hEnemy->Center() );
			m_pBeam->SetEndPos ( vecPos );
			m_pBeam->RelinkBeam( );
		*/							

			// test de la position

			TraceResult tr;
		
			UTIL_TraceHull( vecFrom, vecPos, ignore_monsters, large_hull, m_hEnemy->edict(), &tr );

			if (tr.flFraction > 0.5)
				vecPos = tr.vecEndPos;

			m_SaveVelocity = m_SaveVelocity * 0.8 + 0.2 * (vecPos - pev->origin).Normalize() * m_flightSpeed;



			// console

/*			ALERT ( at_console, "%s %c%c%c%c%c%c%c%c%c%c0%c%c%c%c%c%c%c%c%c%c - %i\n",

			HasConditions( bits_COND_ENEMY_FACING_ME ) && m_hEnemy->FVisible( this ) ? "FACING " : "facing ",
			m_iFear <= -10 ? '|' : '-',	m_iFear <= -9 ? '|' : '-',	m_iFear <= -8 ? '|' : '-',
			m_iFear <= -7 ? '|' : '-',	m_iFear <= -6 ? '|' : '-',	m_iFear <= -5 ? '|' : '-',
			m_iFear <= -4 ? '|' : '-',	m_iFear <= -3 ? '|' : '-',	m_iFear <= -2 ? '|' : '-',
			m_iFear <= -1 ? '|' : '-',	m_iFear >= 1 ? '|' : '-',	m_iFear >= 2 ? '|' : '-',
			m_iFear >= 3 ? '|' : '-',	m_iFear >= 4 ? '|' : '-',	m_iFear >= 5 ? '|' : '-',
			m_iFear >= 6 ? '|' : '-',	m_iFear >= 7 ? '|' : '-',	m_iFear >= 8 ? '|' : '-',
			m_iFear >= 9 ? '|' : '-',	m_iFear >= 10 ? '|' : '-',
			m_iFear);
*/


			// cons


			if ( m_hEnemy->MyMonsterPointer()->FInViewCone ( this ) && m_hEnemy->FVisible( this ))
			{
				m_flNextAlert -= 0.1;

				if ( m_iFear < FEAR_LEVEL )
					m_iFear ++;

				if (m_idealDist < m_flMaxDist)
				{
					m_idealDist += 4;
				}
				if (m_flightSpeed < m_flMaxSpeed)
				{
					m_flightSpeed += 40;
				}
				else if (m_flightSpeed < m_flMinSpeed)
				{
					m_flightSpeed += 80;
				}
			}
			else 
			{
				m_flNextAlert += 0.1;

				if ( m_iFear > HATE_LEVEL )
					m_iFear --;


				if (m_idealDist > 128)
				{
					m_idealDist -= 4;
				}
				if (m_flightSpeed > m_flMinSpeed)
				{
					m_flightSpeed -= 3;
				}
			}

			if (m_flightSpeed < m_flMinSpeed)
			{
				m_flightSpeed = 128;
			}

			if (m_flightSpeed > m_flMaxSpeed)
			{
				m_flightSpeed = m_flMaxSpeed;
			}

			// ALERT( at_console, "%.0f\n", m_idealDist );
		}
		else
		{
			m_flNextAlert = gpGlobals->time + 0.2;
		}

		if (m_flNextAlert < gpGlobals->time)
		{
			// ALERT( at_console, "AlertSound()\n");
			AlertSound( );
			m_flNextAlert = gpGlobals->time + RANDOM_FLOAT( 3, 5 );
		}

		break;


	case TASK_FLYBEE_RUN_ATTACK:
		{
			if (m_fSequenceFinished && m_IdealActivity == ACT_CROUCH )
			{
				m_IdealActivity = ACT_RUN;
			}
			else if (m_fSequenceFinished )
				TaskComplete( );

			TraceResult tr;
			UTIL_TraceHull( pev->origin, m_hEnemy->Center(), ignore_monsters, large_hull, m_hEnemy->edict(), &tr );

			if (tr.flFraction < 0.9)
			{
//				ALERT ( at_console, "RUNATTACK fails\n" );
				TaskFail ();
				break;
			}

			m_SaveVelocity = (tr.vecEndPos - pev->origin).Normalize() * m_flightSpeed;

			m_flightSpeed = Q_min ( m_flMaxSpeed, m_flightSpeed *= 1.2 );


			break;
		}

	case TASK_FLYBEE_RANGE_ATTACK2:
		{
			if ( m_fSequenceFinished )
			{
				m_Activity = ACT_RESET;
				TaskComplete();
			}
			break;
		}



	case TASK_FLYBEE_SWIM:
		if (m_fSequenceFinished )
		{
			TaskComplete( );
		}
		break;

	case TASK_DIE:

		if ( m_fSequenceFinished && m_IdealActivity == ACT_DIESIMPLE && FBitSet( pev->flags, FL_ONGROUND) )
		{
			pev->deadflag = DEAD_DYING;
			m_IdealActivity = ACT_LAND;

			ResetSequenceInfo ();
		}
		else if ( m_fSequenceFinished && m_IdealActivity == ACT_DIESIMPLE )
		{
			pev->deadflag = DEAD_DYING;
		}
		else if ( m_fSequenceFinished && m_IdealActivity == ACT_LAND )
		{
			CFlyingMonster :: RunTask ( pTask );
			pev->deadflag = DEAD_DEAD;
		}
		break;

	default: 
		CFlyingMonster :: RunTask ( pTask );
		break;
	}
}



float CFlybee::VectorToPitch( const Vector &vec )
{
	float pitch;
	if (vec.z == 0 && vec.x == 0)
		pitch = 0;
	else
	{
		pitch = (int) (atan2(vec.z, sqrt(vec.x*vec.x+vec.y*vec.y)) * 180 / M_PI);
		if (pitch < 0)
			pitch += 360;
	}
	return pitch;
}

//=========================================================
void CFlybee::Move(float flInterval)
{
	CFlyingMonster::Move( flInterval );
}

float CFlybee::FlPitchDiff( void )
{
	float	flPitchDiff;
	float	flCurrentPitch;

	flCurrentPitch = UTIL_AngleMod( pev->angles.z );

	if ( flCurrentPitch == pev->idealpitch )
	{
		return 0;
	}

	flPitchDiff = pev->idealpitch - flCurrentPitch;

	if ( pev->idealpitch > flCurrentPitch )
	{
		if (flPitchDiff >= 180)
			flPitchDiff = flPitchDiff - 360;
	}
	else 
	{
		if (flPitchDiff <= -180)
			flPitchDiff = flPitchDiff + 360;
	}
	return flPitchDiff;
}

float CFlybee :: ChangePitch( int speed )
{
	if ( pev->movetype == MOVETYPE_FLY )
	{
		float diff = FlPitchDiff();
		float target = 0;
		if ( m_IdealActivity != GetStoppedActivity() )
		{
			if (diff < -20)
				target = 45;
			else if (diff > 20)
				target = -45;
		}
		pev->angles.x = UTIL_Approach(target, pev->angles.x, 220.0 * 0.1 );
	}
	return 0;
}

float CFlybee::ChangeYaw( int speed )
{
	if ( pev->movetype == MOVETYPE_FLY )
	{
		float diff = FlYawDiff();
		float target = 0;

		if ( m_IdealActivity != GetStoppedActivity() )
		{
			if ( diff < -20 )
				target = 20;
			else if ( diff > 20 )
				target = -20;
		}
		pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * 0.1 );
	}
	return CFlyingMonster::ChangeYaw( speed );
}


Activity CFlybee:: GetStoppedActivity( void )
{ 
	if ( pev->movetype != MOVETYPE_FLY )		// UNDONE: Ground idle here, IDLE may be something else
		return ACT_IDLE;
	return ACT_WALK;
}

void CFlybee::MoveExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flInterval )
{
	m_SaveVelocity = vecDir * m_flightSpeed;
}


void CFlybee::MonsterThink ( void )
{
	CFlyingMonster::MonsterThink( );

	if ( pev->deadflag == DEAD_NO && m_MonsterState != MONSTERSTATE_SCRIPT )
	{
		Swim( );
	}
}

void CFlybee :: Stop( void ) 
{
		m_flightSpeed = 80.0;
}

void CFlybee::Swim( )
{
	int retValue = 0;

	Vector start = pev->origin;

	Vector Angles;
	Vector Forward, Right, Up;

	if (FBitSet( pev->flags, FL_ONGROUND))
	{
		pev->angles.x = 0;
		pev->angles.y += RANDOM_FLOAT( -45, 45 );
		ClearBits( pev->flags, FL_ONGROUND );

		Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
		UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);

		pev->velocity = Forward * 200 + Up * 200;

		return;
	}

	Angles = UTIL_VecToAngles( m_SaveVelocity );
	Angles.x = -Angles.x;
	UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);

	Vector f, u, l, r, d;
	f = DoProbe(start + PROBE_LENGTH   * Forward);
	r = DoProbe(start + PROBE_LENGTH/3 * Forward+Right);
	l = DoProbe(start + PROBE_LENGTH/3 * Forward-Right);
	u = DoProbe(start + PROBE_LENGTH/3 * Forward+Up);
	d = DoProbe(start + PROBE_LENGTH/3 * Forward-Up);

	Vector SteeringVector = f+r+l+u+d;
	m_SaveVelocity = (m_SaveVelocity + SteeringVector/2).Normalize();

	Angles = Vector( -pev->angles.x, pev->angles.y, pev->angles.z );
	UTIL_MakeVectorsPrivate(Angles, Forward, Right, Up);
	// ALERT( at_console, "%f : %f\n", Angles.x, Forward.z );

	float flDot = DotProduct( Forward, m_SaveVelocity );
	if (flDot > 0.5)
		pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed;
	else if (flDot > 0)
		pev->velocity = m_SaveVelocity = m_SaveVelocity * m_flightSpeed * (flDot + 0.5);
	else
		pev->velocity = m_SaveVelocity = m_SaveVelocity * 80;

	// ALERT( at_console, "%.0f %.0f\n", m_flightSpeed, pev->velocity.Length() );


	// ALERT( at_console, "Steer %f %f %f\n", SteeringVector.x, SteeringVector.y, SteeringVector.z );

/*
	m_pBeam->SetStartPos( pev->origin + pev->velocity );
	m_pBeam->RelinkBeam( );
*/

	// ALERT( at_console, "speed %f\n", m_flightSpeed );
	
	Angles = UTIL_VecToAngles( m_SaveVelocity );

	// Smooth Pitch
	//
	if (Angles.x > 180)
		Angles.x = Angles.x - 360;
	pev->angles.x = UTIL_Approach(Angles.x, pev->angles.x, 50 * 0.1 );
	if (pev->angles.x < -80) pev->angles.x = -80;
	if (pev->angles.x >  80) pev->angles.x =  80;

	// Smooth Yaw and generate Roll
	//
	float turn = 360;
	// ALERT( at_console, "Y %.0f %.0f\n", Angles.y, pev->angles.y );

	if (fabs(Angles.y - pev->angles.y) < fabs(turn))
	{
		turn = Angles.y - pev->angles.y;
	}
	if (fabs(Angles.y - pev->angles.y + 360) < fabs(turn))
	{
		turn = Angles.y - pev->angles.y + 360;
	}
	if (fabs(Angles.y - pev->angles.y - 360) < fabs(turn))
	{
		turn = Angles.y - pev->angles.y - 360;
	}

	float speed = m_flightSpeed * 0.1;

	// ALERT( at_console, "speed %.0f %f\n", turn, speed );
	if (fabs(turn) > speed)
	{
		if (turn < 0.0)
		{
			turn = -speed;
		}
		else
		{
			turn = speed;
		}
	}
	pev->angles.y += turn;
	pev->angles.z -= turn;
	pev->angles.y = fmod((pev->angles.y + 360.0), 360.0);

	static float yaw_adj;

	yaw_adj = yaw_adj * 0.8 + turn;

	// ALERT( at_console, "yaw %f : %f\n", turn, yaw_adj );

	SetBoneController( 0, -yaw_adj / 4.0 );

	// Roll Smoothing
	//
	turn = 360;
	if (fabs(Angles.z - pev->angles.z) < fabs(turn))
	{
		turn = Angles.z - pev->angles.z;
	}
	if (fabs(Angles.z - pev->angles.z + 360) < fabs(turn))
	{
		turn = Angles.z - pev->angles.z + 360;
	}
	if (fabs(Angles.z - pev->angles.z - 360) < fabs(turn))
	{
		turn = Angles.z - pev->angles.z - 360;
	}
	speed = m_flightSpeed/2 * 0.1;
	if (fabs(turn) < speed)
	{
		pev->angles.z += turn;
	}
	else
	{
		if (turn < 0.0)
		{
			pev->angles.z -= speed;
		}
		else
		{
			pev->angles.z += speed;
		}
	}
	if (pev->angles.z < -20) pev->angles.z = -20;
	if (pev->angles.z >  20) pev->angles.z =  20;

	UTIL_MakeVectorsPrivate( Vector( -Angles.x, Angles.y, Angles.z ), Forward, Right, Up);

	// UTIL_MoveToOrigin ( ENT(pev), pev->origin + Forward * speed, speed, MOVE_STRAFE );
}


Vector CFlybee::DoProbe(const Vector &Probe)
{
	Vector WallNormal = Vector(0,0,-1); // WATER normal is Straight Down for fish.
	float frac;
	BOOL bBumpedSomething = ProbeZ(pev->origin, Probe, &frac);

	TraceResult tr;
	TRACE_MONSTER_HULL(edict(), pev->origin, Probe, dont_ignore_monsters, edict(), &tr);
	if ( tr.fAllSolid || tr.flFraction < 0.99 )
	{
		if (tr.flFraction < 0.0) tr.flFraction = 0.0;
		if (tr.flFraction > 1.0) tr.flFraction = 1.0;
		if (tr.flFraction < frac)
		{
			frac = tr.flFraction;
			bBumpedSomething = TRUE;
			WallNormal = tr.vecPlaneNormal;
		}
	}

	if (bBumpedSomething && (m_hEnemy == 0 || tr.pHit != m_hEnemy->edict()))
	{
		Vector ProbeDir = Probe - pev->origin;

		Vector NormalToProbeAndWallNormal = CrossProduct(ProbeDir, WallNormal);
		Vector SteeringVector = CrossProduct( NormalToProbeAndWallNormal, ProbeDir);

		float SteeringForce = m_flightSpeed * (1-frac) * (DotProduct(WallNormal.Normalize(), m_SaveVelocity.Normalize()));
		if (SteeringForce < 0.0)
		{
			SteeringForce = -SteeringForce;
		}
		SteeringVector = SteeringForce * SteeringVector.Normalize();
		
		return SteeringVector;
	}
	return Vector(0, 0, 0);
}




//--------------------------------------------------------------------------


CFlyBall *CFlyBall :: CreateFlyBall ( Vector vecOrigin, Vector vecAngles, entvars_s *pevOwner )
{
	CFlyBall *pBall = GetClassPtr( (CFlyBall *)NULL );

	UTIL_MakeAimVectors ( vecAngles );

	float x, y, z;
	do
	{
		x = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5);
		y = RANDOM_FLOAT(-0.5,0.5) + RANDOM_FLOAT(-0.5,0.5);
		z = x*x+y*y;
	}
	while (z > 1);

	Vector vecDir = gpGlobals->v_forward +
					x * VECTOR_CONE_6DEGREES.x * gpGlobals->v_right +
					y * VECTOR_CONE_6DEGREES.y * gpGlobals->v_up;

	pBall->pev->angles = UTIL_VecToAngles ( vecDir.Normalize() );

	pBall->pev->angles.x = -pBall->pev->angles.x;

	UTIL_SetOrigin( pBall->pev, vecOrigin );

	pBall->pev->owner = ENT ( pevOwner );

	pBall->Spawn();
	pBall->SetTouch( &CFlyBall::ExplodeTouch );
	
	return pBall;
}

void CFlyBall :: Spawn( void )
{
	Precache( );

	pev->movetype = MOVETYPE_FLY;
	pev->solid = SOLID_BBOX;
	pev->classname = MAKE_STRING("flyball");

	SET_MODEL(ENT(pev), "sprites/muz7.spr");
	pev->rendermode = kRenderTransAdd;
	pev->rendercolor.x = 255;
	pev->rendercolor.y = 255;
	pev->rendercolor.z = 255;
	pev->renderamt = 190;
	pev->scale = 0.2;

	UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0));
	UTIL_SetOrigin( pev, pev->origin );

	SetThink( &CFlyBall::AnimateThink );
	SetTouch( &CFlyBall::ExplodeTouch );

	pev->dmgtime = gpGlobals->time; // keep track of when ball spawned
	pev->nextthink = gpGlobals->time + 0.1;

	UTIL_MakeVectors ( pev->angles );
	pev->velocity = gpGlobals->v_forward * 1000;

	m_flFieldOfView = -1;
	m_hEnemy = 0;
}

int CFlyBall::Classify ( void )
{
	return CLASS_ALIEN_BIOWEAPON;
}

void CFlyBall :: Precache( void )
{
	PRECACHE_MODEL("sprites/xspark3.spr");
	PRECACHE_SOUND("weapons/electro4.wav");

	m_iSprite = PRECACHE_MODEL("sprites/muz7.spr");
}


void CFlyBall :: AnimateThink( void  )
{
	pev->nextthink = gpGlobals->time + 0.05;

	// sprite
	
	float delta = gpGlobals->time - pev->dmgtime;

	if ( delta > 5 || pev->velocity.Length() < 10)
	{
		SetTouch( NULL );
		UTIL_Remove( this );
	}

	// train

	if ( delta > 0 && delta < 0.9 )
	{
		CSprite *pTrail = CSprite::SpriteCreate ( "sprites/xspark3.spr", pev->origin, TRUE );
		pTrail->AnimateAndDie ( 22 );
		pTrail->SetScale ( 0.2 );
		pTrail->SetTransparency ( kRenderTransAdd, 230, 255, 230, 150, kRenderFxNone );
		pTrail->Expand ( pTrail->pev->scale, 120 );

	}
}


void CFlyBall::ExplodeTouch( CBaseEntity *pOther )
{
	// truc affreux pour creer une particule a travers ce message pour les decals

	TraceResult trace = UTIL_GetGlobalTrace( );
	Vector vecNorm = trace.vecPlaneNormal.Normalize();

	for ( int i = 0; i<2; i++ )
	{
		MESSAGE_BEGIN( MSG_ALL, gmsgClientDecal );

			WRITE_COORD( trace.vecEndPos.x );			// xyz source
			WRITE_COORD( trace.vecEndPos.y );
			WRITE_COORD( trace.vecEndPos.z );
			WRITE_COORD( vecNorm.x );						// xyz norme
			WRITE_COORD( vecNorm.y );
			WRITE_COORD( vecNorm.z );
			WRITE_CHAR ( 'C' );						// type de texture
			WRITE_BYTE ( 1 );						//  4 == electro-rocket

		MESSAGE_END();
	}

	if (pOther->pev->takedamage)
	{
		if ( pOther->pev != VARS ( pev->owner ) )
		{
			ClearMultiDamage( );
			pOther->TraceAttack(pev, 20, pev->velocity.Normalize(), &trace, DMG_ENERGYBEAM ); 
			ApplyMultiDamage( pev, pev );
		}
	}

	UTIL_EmitAmbientSound( ENT(pev), trace.vecEndPos, "weapons/electro4.wav", 0.3, ATTN_NORM, 0, RANDOM_LONG( 90, 99 ) );

	UTIL_Remove( this );
}