// ----------------------------------------------------------------------- //
//
// MODULE  : LOFFlyingAI.cpp
//
// PURPOSE : LOFFlyingAI Flying Enemy - Implementation
//
// CREATED : 9/2/99
//
// ----------------------------------------------------------------------- //
#include "LOFFlyingAI.h"

#define AI_UPDATE_DELTA				0.01f
#define FLYING_SLOW_VEL				125.0f
#define FLYING_FAST_VEL				200.0f // flies much faster than standard unit
#define FLYING_SWOOP_VEL			500.0f
#define FLYING_MOVE_ACCEL			2000.0f

#define FLYING_FLYFAST_RANGE		1024.0f
#define FLYING_ENGAGE_DIST			2048.0f

// animation names
#define FLYINGAI_FLY_ANI			"FLY"
#define FLYINGAI_FLYATTACK_ANI		"FLY_ATTACK"

#define NUM_WAYPOINTS				8

#define MIN_NEXT_ATTACK_TIME		3.0f
#define MAX_NEXT_ATTACK_TIME		6.0f

//#define _EVDEBUG 1

BEGIN_CLASS( FlyingAI )
	ADD_LONGINTPROP( State, BaseAI::AGGRESSIVE )
	ADD_LONGINTPROP( WeaponId, GUN_TOW_ID )
	ADD_LONGINTPROP( FAIType,  FlyingAI::FASWOOP )
END_CLASS_DEFAULT( FlyingAI, BaseAI, NULL, NULL )

// function prototypes for saving our waypoint information
// these functions are defined in PlayerObj.cpp
DBOOL SaveVectorPtrFn(HMESSAGEWRITE hWrite, void* pPtDataItem);
DBOOL LoadVectorPtrFn(HMESSAGEREAD hRead, void* pPtDataItem);

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::FlyingAI()
//
//	PURPOSE:	Constructor
//
// ----------------------------------------------------------------------- //

FlyingAI::FlyingAI() : BaseAI()
{
	m_bIsMecha				= DTRUE;
	m_nWeaponId				= GUN_TOW_ID;

	m_pIdleSound			= "Sounds\\Enemies\\MCA\\FlyingAI\\Idle.wav";

	m_bCreateHandHeldWeapon	= DFALSE;
	m_bChangeAnimation		= DFALSE;
	m_bAllowMovement		= DTRUE;
	m_bSpawnWeapon			= DFALSE;

	m_cc					= ALIEN;
	m_dwFlags				&= ~FLAG_GRAVITY; // definitely don't want gravity!

	m_hFlyAni				= INVALID_ANI;
	m_hFlyAttackAni			= INVALID_ANI;

	m_fFlyVel				= FLYING_SLOW_VEL;
	m_fFlyFastVel			= FLYING_FAST_VEL;
	m_fSwoopVel				= FLYING_SWOOP_VEL;
	m_fBaseMoveAccel		= FLYING_MOVE_ACCEL;

	m_eFAIType				= FASWOOP;

	m_nUpCount				= 0;

	m_fNextAttackTime		= MAX_NEXT_ATTACK_TIME;

	m_pWayPoints.Init(DTRUE);
	VEC_INIT(m_vStartPoint);
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::~FlyingAI()
//
//	PURPOSE:	Destructor
//
// ----------------------------------------------------------------------- //

FlyingAI::~FlyingAI()
{	
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE) return;

	if (m_hIdleSound)
	{
		pServerDE->KillSound(m_hIdleSound);
		m_hIdleSound = DNULL;
	}
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::SetAnimationIndexes()
//
//	PURPOSE:	Initialize model animation indexes
//
// ----------------------------------------------------------------------- //
	
void FlyingAI::SetAnimationIndexes()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return;
//debug ev
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::SetAnimationIndexes() ----------------");
#endif

	BaseAI::SetAnimationIndexes();
	m_hFlyAni		= pServerDE->GetAnimIndex(m_hObject, FLYINGAI_FLY_ANI);
	m_hFlyAttackAni	= pServerDE->GetAnimIndex(m_hObject, FLYINGAI_FLYATTACK_ANI);
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::UpdateWeapon()
//
//	PURPOSE:	Update the our weapon
//
// ----------------------------------------------------------------------- //

void FlyingAI::UpdateWeapon()
{

	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return;
//debug ev
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateWeapon() ----------------");
#endif
	m_bChangeAnimation = DFALSE;

	// See if we are firing...

	if (IsFiring())
	{
		// If we just started firing, figure out what weapon/animation to use...

		if (!(m_dwLastAction & AI_AFLG_FIRE))
		{
			m_bChangeAnimation = DTRUE;
		}
	}

	BaseAI::UpdateWeapon();
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::EngineMessageFn
//
//	PURPOSE:	Handle engine messages
//
// ----------------------------------------------------------------------- //

DDWORD FlyingAI::EngineMessageFn(DDWORD messageID, void *pData, DFLOAT fData)
{
//debug ev
#if _EVDEBUG
CServerDE* pServerDE = GetServerDE();
pServerDE->CPrint("FlyingAI::EngineMessageFn() %d", messageID);
#endif

	switch(messageID)
	{
		case MID_INITIALUPDATE:
		{
			if (fData != INITIALUPDATE_SAVEGAME)
			{
				InitialUpdate();
			}

			CacheFiles();
			break;
		}

		case MID_PRECREATE:
		{
			DDWORD dwRet = BaseAI::EngineMessageFn(messageID, pData, fData);
			
			int nInfo = (int)fData;
			if (nInfo == PRECREATE_WORLDFILE || nInfo == PRECREATE_STRINGPROP)
			{
				ReadProp((ObjectCreateStruct*)pData);
			}
			return dwRet;
			break;
		}

		case MID_SAVEOBJECT:
		{
			Save((HMESSAGEWRITE)pData, (DDWORD)fData);
		}
		break;

		case MID_LOADOBJECT:
		{
			Load((HMESSAGEREAD)pData, (DDWORD)fData);
		}
		break;
		
		default : break;
	}

	return BaseAI::EngineMessageFn(messageID, pData, fData);
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::ReadProp
//
//	PURPOSE:	Set property value
//
// ----------------------------------------------------------------------- //

DBOOL FlyingAI::ReadProp(ObjectCreateStruct *pData)
{
	GenericProp genProp;
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !pData) return DFALSE;

	if ( pServerDE->GetPropGeneric( "FAIType", &genProp ) == DE_OK )
	{
#if _EVDEBUG
pServerDE->CPrint("got propgeneric FAIType %d", genProp.m_Long);
#endif
		m_eFAIType = (FAIMoveType)genProp.m_Long;
	}

	return DTRUE;
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::Save
//
//	PURPOSE:	Save the object
//
// ----------------------------------------------------------------------- //

void FlyingAI::Save(HMESSAGEWRITE hWrite, DDWORD dwSaveFlags)
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !hWrite) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::Save() ----------------");
#endif

	// this function (SaveVectorPtrFn) is defined in PlayerObj.cpp
	m_pWayPoints.Save(hWrite, SaveVectorPtrFn);
	pServerDE->WriteToMessageVector(hWrite, &m_vStartPoint);

	pServerDE->WriteToMessageDWord (hWrite, (DDWORD)m_eFAIType);
	pServerDE->WriteToMessageDWord (hWrite, m_nUpCount);

	pServerDE->WriteToMessageDWord (hWrite, m_hFlyAni);
	pServerDE->WriteToMessageDWord (hWrite, m_hFlyAttackAni);
	pServerDE->WriteToMessageByte  (hWrite, m_bChangeAnimation);
	pServerDE->WriteToMessageFloat (hWrite, m_fNextAttackTime);
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::Load
//
//	PURPOSE:	Load the object
//
// ----------------------------------------------------------------------- //

void FlyingAI::Load(HMESSAGEREAD hRead, DDWORD dwLoadFlags)
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !hRead) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::Load() ----------------");
#endif

	// this function (SaveVectorPtrFn) is defined in PlayerObj.cpp
	m_pWayPoints.Load(hRead, LoadVectorPtrFn);
	pServerDE->ReadFromMessageVector (hRead, &m_vStartPoint);

	m_eFAIType =(FAIMoveType) pServerDE->ReadFromMessageDWord (hRead);
	m_nUpCount				= pServerDE->ReadFromMessageDWord (hRead);
	m_hFlyAni				= pServerDE->ReadFromMessageDWord (hRead);
	m_hFlyAttackAni			= pServerDE->ReadFromMessageDWord (hRead);
	m_bChangeAnimation		= pServerDE->ReadFromMessageByte  (hRead);
	m_fNextAttackTime		= pServerDE->ReadFromMessageFloat (hRead);
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::InitialUpdate()
//
//	PURPOSE:	Handle initial update
//
// ----------------------------------------------------------------------- //

void FlyingAI::InitialUpdate()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::InitialUpdate() ----------------");
#endif
			
	DFLOAT fRadius = 2000.0f;
	fRadius *= (m_eModelSize == MS_SMALL ? 0.2f : (m_eModelSize == MS_LARGE ? 5.0f : 1.0f));

	// Play idle sound as long as we're alive...

	m_hIdleSound = PlaySoundFromObject(m_hObject, m_pIdleSound, fRadius, 
									   m_nBasePriority, 
									   DTRUE, DTRUE);
	DVector *pPos = new DVector;
	pServerDE->GetObjectPos(m_hObject, pPos);
	VEC_COPY(m_vStartPoint, *pPos);
	m_pWayPoints.AddTail(pPos);
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::CacheFiles
//
//	PURPOSE:	Cache resources used by this the object
//
// ----------------------------------------------------------------------- //

void FlyingAI::CacheFiles()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::CacheFiles() ----------------");
#endif

	if (m_pIdleSound)
	{
		pServerDE->CacheFile(FT_SOUND, m_pIdleSound);
	}
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::UpdateMovement
//
//	PURPOSE:	Update AI movement
//
// ----------------------------------------------------------------------- //

void FlyingAI::UpdateMovement()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateMovement() - %d", m_hObject);
#endif

	if (m_eFAIType == FASHOGO || (m_eState == SCRIPT))
	{
#if _EVDEBUG
if (m_eState == SCRIPT)
pServerDE->CPrint("FlyingAI::UpdateMovement() ***** USING SCRIPT *******");
else
pServerDE->CPrint("FlyingAI::UpdateMovement() ***** USING SHOGO AI! *****");
#endif
		BaseAI::UpdateMovement();
		return;
	}
	//UpdateOnGround();

	// The engine might apply an acceleration to us, make sure 
	// it doesn't last...(keep gravity however...)

	DVector vAccel;
	pServerDE->GetAcceleration(m_hObject, &vAccel);
	vAccel.x = vAccel.z = 0.0f;
	if (vAccel.y > 0.0f) vAccel.y = 0.0f;

	pServerDE->SetAcceleration(m_hObject, &vAccel);


	// Retrieve object vectors for current frame..

	DRotation rRot;
	pServerDE->GetObjectRotation(m_hObject, &rRot);
	pServerDE->GetRotationVectors(&rRot, &m_vUp, &m_vRight, &m_vForward);

	UpdateThinking();

	// If we are playing a scripted animation, don't update the movement...

	if ((m_eState == SCRIPT) && (m_curScriptCmd.command == AI_SCMD_PLAYANIMATION))
	{
		return;
	}


	// If we're moving (and not scripted), see if we are stuck...

	if ((m_eState != SCRIPT) && (m_dwControlFlags & BC_CFLG_MOVING))
	{
		// If we're stuck on something, see if turing will help us continue...

		if (m_bStuckOnSomething)
		{
			m_bStuckOnSomething = AvoidObstacle();
			if (!m_bStuckOnSomething) 
			{
				UpdateControlFlags();
			}
		}


		// If we are okay to move, check for ledges...

		if (!m_bStuckOnSomething)
		{
			m_bStuckOnSomething = !CanMoveDir(m_vForward);
		}
		

		// Make us stand still...

		if (m_bStuckOnSomething)
		{
			SetActionFlag(AI_AFLG_STAND);
			UpdateControlFlags();
		}
	}

	// Do real work...

	NewUpdateMovement();
	

	// Update current animation...

	UpdateAnimation();
}


// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::NewUpdateMovement
//
//	PURPOSE:	Update AI movement using MoveObject
//
// ----------------------------------------------------------------------- //

void FlyingAI::NewUpdateMovement()
{
	CServerDE* pServerDE = GetServerDE();

	if (!pServerDE || m_damage.IsDead() || !m_bAllowMovement) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NewUpdateMovement() - %d MOVETYPE = %d", m_hObject,  m_eFAIType);
pServerDE->CPrint("FlyingAI::NewUpdateMovement() - moving? %d", ((m_dwControlFlags & BC_CFLG_MOVING)));
pServerDE->CPrint("FlyingAI::NewUpdateMovement() - NUM WP  %d", m_pWayPoints.GetLength());
#endif

	m_bStuckOnSomething = DFALSE;

	if ( !m_hTarget )
	{
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NewUpdateMovement() NO TARGET SO RETURN ASAP!");
#endif
		return;

	}
//------------------------
	DFLOAT fTimeDelta = pServerDE->GetFrameTime();

	DVector vNewPos;
	VEC_COPY(vNewPos, m_vPos);
	if (m_pWayPoints.GetLength() == 0)
	{
		// new ev - 15/3/99 we must create a new DVector to add to the tail, otherwise
		// DList does not allocate memory for it
		DVector *vPanicPos = new DVector;
		GetPanicPos(vPanicPos);
		m_pWayPoints.AddTail(vPanicPos);
		UpdateThinking();
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() vPanicPos %.2f %.2f %.2f", VEC_EXPAND(*vPanicPos));
pServerDE->CPrint("FlyingAI::NUM() LIST IS EMPTY!");
#endif

		return;
	}
	DVector *pPos = *m_pWayPoints.GetItem(TLIT_FIRST);
	if (!pPos) return;
	DVector vNextWP;
	vNextWP.x = pPos->x;
	vNextWP.y = pPos->y;
	vNextWP.z = pPos->z;

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() vNextWP %.2f %.2f %.2f", VEC_EXPAND(vNextWP));
#endif

	DFLOAT fDist = VEC_DIST(vNewPos, vNextWP);
	m_fLastDistTraveled = VEC_DIST(m_vPos, m_vLastPos);
	if (m_dwLastFrameCtlFlgs & BC_CFLG_MOVING)
	{
		if (m_fLastDistTraveled < m_fPredTravelDist * 0.95f)
		{
#if _EVDEBUG
pServerDE->CPrint("*******************************");
pServerDE->CPrint("****** STUCK ******************");
pServerDE->CPrint("*******************************");
#endif
			m_bStuckOnSomething = DTRUE;
		}
	}
	if (fDist < 16.0f || m_bStuckOnSomething)
	{
		if (m_pWayPoints.GetLength() > 0)
		{
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() STUCK AND REMOVING THE HEAD NODE!");
			pPos = *m_pWayPoints.GetItem(TLIT_FIRST);
pServerDE->CPrint("FlyingAI::NUM() TOP POS %.2f %.2f %.2f", VEC_EXPAND(*pPos));
#endif
			m_pWayPoints.RemoveHead();
		}
		if (m_pWayPoints.GetLength() == 0)
		{
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() STUCK AND GETTING PANIC POS!");
#endif
		// new ev - 15/3/99 we must create a new DVector to add to the tail, otherwise
		// DList does not allocate memory for it
			DVector *vPanicPos = new DVector;
			GetPanicPos(vPanicPos);
			m_pWayPoints.AddTail(vPanicPos);
			UpdateThinking();

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() vPanicPos %.2f %.2f %.2f", VEC_EXPAND(*vPanicPos));
pServerDE->CPrint("FlyingAI::NUM() LIST IS EMPTY2!");
#endif
			return;
		}
		else
		{
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() STUCK AND GETTING NEXT AVAIBLE POS!");
#endif
			pPos = *m_pWayPoints.GetItem(TLIT_FIRST);
			if (!pPos) return;
			vNextWP.x = pPos->x;
			vNextWP.y = pPos->y;
			vNextWP.z = pPos->z;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() vNextWP %.2f %.2f %.2f", VEC_EXPAND(vNextWP));
#endif
		}
	}
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() dist = %.4f", fDist);
#endif
	DVector vDist;
	VEC_SUB(vDist, vNextWP, vNewPos);
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() vDist      %.2f %.2f %.2f", VEC_EXPAND(vDist));
#endif

VEC_NORM(vDist);
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() vDist NORM %.2f %.2f %.2f", VEC_EXPAND(vDist));
pServerDE->CPrint("FlyingAI::NUM() m_vForward %.2f %.2f %.2f", VEC_EXPAND(m_vForward));
#endif


	DFLOAT fMoveVel;
	if (m_nUpCount > 0)
	{
		fMoveVel = m_fSwoopVel * pServerDE->Random(0.75f, 0.9f);
#if _EVDEBUG
		pServerDE->CPrint("FlyingAI::NUM() SLOW  fMoveVel : %.3f", fMoveVel);
#endif
	}
	else if (m_eFAIType == FASWOOP)
	{
		fMoveVel = m_fSwoopVel;// * pServerDE->Random(0.75f, 1.25f);
#if _EVDEBUG
		pServerDE->CPrint("FlyingAI::NUM() SWOOP fMoveVel : %.3f", fMoveVel);
#endif
	}
	else
	{
		fMoveVel = m_fFlyFastVel * pServerDE->Random(0.75f, 1.25f);
#if _EVDEBUG
		pServerDE->CPrint("FlyingAI::NUM() FAST  fMoveVel : %.3f", fMoveVel);
#endif
	}
	DFLOAT fVal		= (m_dwControlFlags & BC_CFLG_REVERSE) ? -1.0f : 1.0f;

	fMoveVel *= (fTimeDelta * fVal);
	DVector vF;
	//VEC_COPY(vF, m_vForward);
	VEC_COPY(vF, vDist);

	// Don't limit movement in any axis (ev)

	VEC_MULSCALAR(vF, vF, fMoveVel);
	VEC_ADD(vNewPos, vNewPos, vF);
	VEC_COPY(m_vLastPos, m_vPos);
	m_fPredTravelDist = VEC_DIST(m_vLastPos, vNewPos);
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::NUM() lastpos  %.2f %.2f %.2f", VEC_EXPAND(m_vLastPos));
pServerDE->CPrint("FlyingAI::NUM() predtraveldist %.3f", m_fPredTravelDist);
pServerDE->CPrint("FlyingAI::NUM()  newpos  %.2f %.2f %.2f", VEC_EXPAND(vNewPos));
#endif
	pServerDE->MoveObject(m_hObject, &vNewPos);
//------------------------
return;
/*
	// Make sure we're trying to move...
	if ( !(m_dwControlFlags & BC_CFLG_MOVING) ) return;

	DFLOAT fTimeDelta = pServerDE->GetFrameTime();

	DVector vNewPos;
	VEC_COPY(vNewPos, m_vPos);

	
	// See if we are stuck on something.  Can only happen if we were
	// moving on the last frame...

	if (m_dwLastFrameCtlFlgs & BC_CFLG_MOVING)
	{
		m_fLastDistTraveled = VEC_DIST(m_vPos, m_vLastPos);

		if (m_fLastDistTraveled < m_fPredTravelDist * 0.95f)
		{
			m_bStuckOnSomething = DTRUE;
		}
	}


	DFLOAT fMoveVel = m_fFlyVel;
	DFLOAT fVal		= (m_dwControlFlags & BC_CFLG_REVERSE) ? -1.0f : 1.0f;

	// Check for running...
m_dwControlFlags |= BC_CFLG_RUN;	

	if ((m_dwControlFlags & BC_CFLG_RUN) && m_bAllowRun)
	{
		fMoveVel = m_fFlyFastVel;
	}

pServerDE->CPrint("FlyingAI::NewUpdateMovement() moveVel %.3f", fMoveVel);
	fMoveVel *= (fTimeDelta * fVal);
pServerDE->CPrint("FlyingAI::NewUpdateMovement() moveVel*timedelta %.3f", fMoveVel);
m_dwControlFlags |= BC_CFLG_FORWARD;	
pServerDE->CPrint("FAI::NUM() BCF %d BCB %d BCSL %d BCFR %d",
				  (m_dwControlFlags & BC_CFLG_FORWARD),
				  (m_dwControlFlags & BC_CFLG_REVERSE),
				  (m_dwControlFlags & BC_CFLG_STRAFE_RIGHT),
				  (m_dwControlFlags & BC_CFLG_STRAFE_LEFT));
	
	// Move us forward/backward...

	if ((m_dwControlFlags & BC_CFLG_FORWARD) || 
		(m_dwControlFlags & BC_CFLG_REVERSE))
	{
		DVector vF;
		VEC_COPY(vF, m_vForward);

		// Limit movement to x and z...

		vF.y = 0.0;
		VEC_MULSCALAR(vF, vF, fMoveVel);
		VEC_ADD(vNewPos, vNewPos, vF);
	}
	
	
	// See if we should strafe...

	DVector vDims, vR;
	pServerDE->GetObjectDims(m_hObject, &vDims);

	if (m_dwControlFlags & BC_CFLG_STRAFE_RIGHT)
	{
		// Make sure new position isn't off a cliff (or into a wall)...

		if (CanMoveDir(m_vRight))
		{
			VEC_MULSCALAR(vR, m_vRight, fMoveVel);
			VEC_ADD(vNewPos, vNewPos, vR);
		}
	}
	else if (m_dwControlFlags & BC_CFLG_STRAFE_LEFT)
	{
		DVector vLeft;
		VEC_COPY(vLeft, m_vRight);
		VEC_MULSCALAR(vLeft, vLeft, -1.0f); // point left

		// Make sure new position isn't off a cliff (or into a wall)...

		if (CanMoveDir(vLeft))
		{
			VEC_MULSCALAR(vR, m_vRight, fMoveVel);
			VEC_SUB(vNewPos, vNewPos, vR);
		}
	}


	// Save our last position...

	VEC_COPY(m_vLastPos, m_vPos);
	m_fPredTravelDist = VEC_DIST(m_vLastPos, vNewPos);
pServerDE->CPrint("FlyingAI::NUM() lastpos  %.2f %.2f %.2f", VEC_EXPAND(m_vLastPos));
pServerDE->CPrint("FlyingAI::NUM() predtraveldist %.3f", m_fPredTravelDist);
pServerDE->CPrint("FlyingAI::NUM()  newpos  %.2f %.2f %.2f", VEC_EXPAND(vNewPos));
	pServerDE->MoveObject(m_hObject, &vNewPos);
*/
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::GetPanicPos
//
//	PURPOSE:	Walk / Run at the target based on distance
//
// ----------------------------------------------------------------------- //

void FlyingAI::GetPanicPos(DVector *vPos)
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return;
	if (m_vStartPoint.x == 0.0f && 
		m_vStartPoint.y == 0.0f && 
		m_vStartPoint.z == 0.0f )
	{
		pServerDE->GetObjectPos(m_hObject, vPos);
	}
	else
	{
		VEC_COPY(*vPos, m_vStartPoint);
	}
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::GetPanicPos()  %.2f %.2f %.2f", VEC_EXPAND(*vPos));
#endif
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::ApproachTarget
//
//	PURPOSE:	Walk / Run at the target based on distance
//
// ----------------------------------------------------------------------- //

void FlyingAI::ApproachTarget()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject || !m_hTarget) return;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::ApproachTarget()  MOVETYPE = %d", m_eFAIType);
#endif

	CWeapon* pWeapon = m_weapons.GetCurWeapon();
	if (!pWeapon) return;

	// Distance to target...

	DFLOAT fDist = DistanceToObject(m_hTarget);
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::ApproachTarget() DIST TO TARGET %.3f", fDist);
#endif

	// If we can't see our target, see if we can find him...

	if (!IsObjectVisibleToAI(m_hTarget))
	{
		if (IsPlayer(m_hTarget))
		{
			if (m_bLostPlayer)
			{
				SetNewTarget(DNULL);
				m_bSpottedPlayer = DFALSE;
			}
			else
			{
				SearchForPlayer(m_hTarget);
			}
			return;
		}
	}

	if (fDist > FLYING_ENGAGE_DIST)
	{
		ClearActionFlag(AI_AFLG_FIRE);
		m_bChangeAnimation = DFALSE;
		if (fDist > FLYING_FLYFAST_RANGE)
		{
			RunForward();
		}
		else
		{
			WalkForward();
		}
	}
	else
	{
		if (!m_bChangeAnimation) 
		{
			m_bChangeAnimation = DTRUE;
		}
		SetActionFlag(AI_AFLG_STAND);
	}
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::UpdateAggressive()
//
//	PURPOSE:	Implement the attacking actions
//
// ----------------------------------------------------------------------- //
/*
void FlyingAI::UpdateAggressive()
{
	// If we don't have a target try to aquire one...

	if (!ChooseTarget()) return;
	
	// Try to shoot target.....

	ShootTarget();

	
	SetActionFlag(AI_AFLG_AIMING);
	SetActionFlag(AI_AFLG_EVASIVE);

	// Always try to get a better shot...

	ApproachTarget();
}
*/
// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::UpdateAnimation()
//
//	PURPOSE:	Update the current animation
//
// ----------------------------------------------------------------------- //

void FlyingAI::UpdateAnimation()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return;

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateAnimation() - START  MOVETYPE = %d", m_eFAIType);
#endif

// See if we are currently firing...

DDWORD bAttacking = (m_dwControlFlags & BC_CFLG_FIRING);

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateAnimation() - ATTACKING   %d", bAttacking);
pServerDE->CPrint("FlyingAI::UpdateAnimation() - CHANGINGAni %d", m_bChangeAnimation);
pServerDE->CPrint("FlyingAI::UpdateAnimation() - MOVING      %d", (m_dwControlFlags & BC_CFLG_MOVING));
#endif


	if (m_damage.IsDead())
	{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetDeathAnimation");
#endif
		SetDeathAnimation();
	}
	else if (m_bLanding)
	{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetLandingAnimation");
#endif
		//SetLandingAnimation();
	}
	else if (IsMecha() && (m_bTransforming || !m_bBipedal || (m_bBipedal != m_bBipedalLastFrame)))
	{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetTransformAnimation");
#endif
		//SetTransformAnimation();
	}
	else if (m_bCrying)
	{
#if _EVDEBUG
pServerDE->CPrint("FAI::UpdateTearsAnimation");
#endif

		//UpdateTearsAnimation();
	}
	else if (m_bRecoiling)
	{
#if _EVDEBUG
pServerDE->CPrint("FAI::UpdateRecoilAnimation");
#endif
		//UpdateRecoilAnimation();
	}
	else if (m_bAllowMovement)
	{
		if (IsLiquid(m_eContainerCode))
		{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetSwimAnimation");
#endif
		//	SetSwimAnimation();
		}
		else if ((m_dwControlFlags & BC_CFLG_DUCK) && !m_bBodyOnLadder)
		{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetCrouchAnimation");
#endif
		//	SetCrouchAnimation();
		}
		else if ((m_dwControlFlags & BC_CFLG_MOVING))
		{
			if ((m_dwControlFlags & BC_CFLG_RUN)) 
			{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetRunAnimation");
#endif
		//		SetRunAnimation();
			}
			else 
			{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetWalkAnimation");
#endif
		//		SetWalkAnimation();
			}
		}
		else
		{
#if _EVDEBUG
pServerDE->CPrint("FAI::SetStandAnimation");
#endif
		//	SetStandAnimation();
		}
		
#if _EVDEBUG
pServerDE->CPrint("FAI::UpdateInAir");
#endif
		UpdateInAir();
	}

	HMODELANIM hAni;
	if (bAttacking)
	{
		hAni = m_hFlyAttackAni;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateAnimation() - FlyAttackAni %d", hAni);
#endif
		
		CWeapon* pWeapon = m_weapons.GetCurWeapon();
		if (pWeapon)
		{
			if ((m_fLastFiredTime + m_fNextAttackTime) < pServerDE->GetTime())
			{
				DVector vBogus;
				VEC_INIT(vBogus);

				// Make sure we can see our target...

				if (GetTargetPos(vBogus))
				{
					pWeapon->Fire();
				}
				m_fLastFiredTime	= pServerDE->GetTime();
				m_fNextAttackTime	= pServerDE->Random(MIN_NEXT_ATTACK_TIME, MAX_NEXT_ATTACK_TIME);

#if _EVDEBUG
pServerDE->CPrint("FAI::UA() - Told weapon to fire, curr %.4f", m_fLastFiredTime);
#endif
			}
			else
			{
#if _EVDEBUG
pServerDE->CPrint("FAI::UA() lastf %.4f currtime %.4f", (m_fLastFiredTime + 1.0f), pServerDE->GetTime());
#endif
			}
		}

	}
	else
	{
		hAni = m_hFlyAni;
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateAnimation() - FlyAni %d", hAni);
#endif
	}
	SetAnimation(hAni, DFALSE);

#if _EVDEBUG
DVector** pCur = DNULL;
DVector*  pPos = DNULL;
pCur = m_pWayPoints.GetItem(TLIT_FIRST);
int i = 0;
while (pCur)
{
	pPos = *pCur;
pServerDE->CPrint("UPDATE ANIMATION POS %d: %.2f %.2f %.2f", i++, pPos->x, pPos->y, pPos->z);
	pCur = m_pWayPoints.GetItem(TLIT_NEXT);
}
#endif

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateAnimation() - END-----------------");
#endif
}

void FlyingAI::UpdateThinking()
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return;
	if (m_pWayPoints.GetLength() >= NUM_WAYPOINTS/* || m_pWayPoints.GetLength() == 0*/) return;

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateThinking() %d - NUM WP %d  MOVETYPE = %d", m_hObject, m_pWayPoints.GetLength(), m_eFAIType);
#endif

	if (!m_hTarget)
	{
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateThinking() NO TARGET-> SO SLEEP");
#endif
	return;
	}

	// Loop through all our waypoints to get the latest point (where we are headed to)
	DVector** pCur = DNULL;
	DVector*  pPos = DNULL;
	pCur = m_pWayPoints.GetItem(TLIT_FIRST);
	while (pCur)
	{
		pPos = *pCur;
		pCur = m_pWayPoints.GetItem(TLIT_NEXT);
	}
	if (!pPos) return;
#if _EVDEBUG
pServerDE->CPrint("UTHINKING FINAL POS VWP %d: %.2f %.2f %.2f", i, pPos->x, pPos->y, pPos->z);
#endif

	DVector vPos;
	VEC_COPY(vPos, *pPos);

	DVector vRandDir;
	
	// Now calculate a direction to head to (based upon our current Flying AI type)
	CalculateNextMoveDir(&vRandDir, m_eFAIType);

#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateThinking() - RAND vect %.2f %.2f %.2f", VEC_EXPAND(vRandDir));
#endif

	if (!CheckForFlyingCollision2(vPos, vRandDir/*m_vForward*/, 128.0f))
	{

		DVector vTo;
		//VEC_ADD(vTo, vPos, vDir);
		if (m_eFAIType == FASTART)
		{
			VEC_MULSCALAR(vRandDir, vRandDir, 128.0f);
			VEC_ADD(vTo, m_vStartPoint, vRandDir);
#if _EVDEBUG
pServerDE->CPrint("FASTART FlyingAI::UT() - vRandDir %.2f %.2f %.2f", VEC_EXPAND(vRandDir));
#endif
		}
		else if (m_eFAIType == FASWOOP)
		{


			DFLOAT fDist = VEC_DIST(vPos, m_vTargetPos);
			DFLOAT fHeightDiff = vPos.y - m_vTargetPos.y;
			if (fHeightDiff < 48.0f && m_nUpCount == 0)
			{
				m_nUpCount = 10;
			}
#if _EVDEBUG
pServerDE->CPrint("FASWOOP FlyingAI::UT() - vRandDir %.2f %.2f %.2f", VEC_EXPAND(vRandDir));
#endif
			if (m_nUpCount > 0)
			{
				vRandDir.y = ((DFLOAT)m_nUpCount--) / 10.0f;
#if _EVDEBUG
pServerDE->CPrint("FASWOOP FlyingAI::UT() - vRandDir %.2f %.2f %.2f", VEC_EXPAND(vRandDir));
#endif
			}
#if _EVDEBUG
pServerDE->CPrint("FASWOOP fdist %.2f fHeightDiff %.2f", fDist, fHeightDiff);
#endif
			VEC_MULSCALAR(vRandDir, vRandDir, 80.0f);
			VEC_ADD(vTo, vPos, vRandDir);
		}
		else
		{
			VEC_MULSCALAR(vRandDir, vRandDir, 128.0f);
			VEC_ADD(vTo, vPos, vRandDir);
		}


	DVector vR;
	DDWORD nDir = (DDWORD)pServerDE->Random(0,8);
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateThinking() -nDir %d vTo  %.2f %.2f %.2f", nDir, VEC_EXPAND(vTo));
#endif

		if (nDir == 0)
		{
			// Make sure new position isn't off a cliff (or into a wall)...

			VEC_MULSCALAR(vR, m_vRight, 96.0f);
			VEC_ADD(vTo, vTo, vR);
	#if _EVDEBUG
	pServerDE->CPrint("FASWOOP STRAFERIGHT FlyingAI::UT() - vTo %.2f %.2f %.2f", VEC_EXPAND(vTo));
	#endif
		}
		else if (nDir == 1)
		{
			DVector vLeft;
			VEC_COPY(vLeft, m_vRight);
			VEC_MULSCALAR(vLeft, vLeft, -1.0f); // point left

			VEC_MULSCALAR(vR, m_vRight, 96.0f);
			VEC_SUB(vTo, vTo, vR);
	#if _EVDEBUG
	pServerDE->CPrint("FASWOOP STRAFELEFT FlyingAI::UT() - vTo %.2f %.2f %.2f", VEC_EXPAND(vTo));
	#endif
		}

//pServerDE->CPrint("FlyingAI::UpdateThinking() - vTo  %.2f %.2f %.2f", VEC_EXPAND(vTo));

		// new ev - 15/3/99 we must create a new DVector to add to the tail, otherwise
		// DList does not allocate memory for it
		DVector *pPosNew = new DVector;
		pPosNew->x = vTo.x;
		pPosNew->y = vTo.y;
		pPosNew->z = vTo.z;
//pServerDE->CPrint("FlyingAI::UpdateThinking() - pPos %.2f %.2f %.2f", pPos->x, pPos->y, pPos->z);
		m_pWayPoints.AddTail(pPosNew);
		
	} else
	{
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UpdateThinking() collision");
pServerDE->CPrint("FlyingAI::UT() - vRand  %.2f %.2f %.2f", VEC_EXPAND(vRandDir));
#endif
VEC_NEGATE(vRandDir, vRandDir);
#if _EVDEBUG
pServerDE->CPrint("FlyingAI::UT() - !vRand %.2f %.2f %.2f", VEC_EXPAND(vRandDir));
#endif
		VEC_MULSCALAR(vRandDir, vRandDir, 128.0f);

		DVector vTo;
		//VEC_ADD(vTo, vPos, vDir);
		if (m_eFAIType == FASTART)
		{
			VEC_ADD(vTo, m_vStartPoint, vRandDir);
		}
		else
		{
			VEC_ADD(vTo, vPos, vRandDir);
		}

		DVector *pPosNew = new DVector;
		pPosNew->x = vTo.x;
		pPosNew->y = vTo.y;
		pPosNew->z = vTo.z;
//pServerDE->CPrint("FlyingAI::UpdateThinking() - pPos %.2f %.2f %.2f", pPos->x, pPos->y, pPos->z);
		m_pWayPoints.AddTail(pPosNew);
	}

}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::CheckForCollision()
//
//	PURPOSE:	See if there is anything fDist away in the vDir
//
// ----------------------------------------------------------------------- //

DBOOL FlyingAI::CheckForFlyingCollision(DVector vDir, DFLOAT fDist)
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return DFALSE;

	DVector vPos;
	pServerDE->GetObjectPos(m_hObject, &vPos);

	VEC_NORM(vDir);
	VEC_MULSCALAR(vDir, vDir, fDist);

	DVector vTo;
	VEC_ADD(vTo, vPos, vDir);

	IntersectQuery IQuery;
	IntersectInfo IInfo;

	VEC_COPY(IQuery.m_From, vPos);
	VEC_COPY(IQuery.m_To, vTo);
	IQuery.m_Flags	  = INTERSECT_OBJECTS | IGNORE_NONSOLID;
	IQuery.m_FilterFn = DNULL;

	//s_nNumCallsToIntersectSegment++;
	if (pServerDE->IntersectSegment(&IQuery, &IInfo))
	{
		return DTRUE;
	}

	return DFALSE;
}

// ----------------------------------------------------------------------- //
//
//	ROUTINE:	FlyingAI::CheckForCollision2()
//
//	PURPOSE:	See if there is anything fDist away in the vDir from pos vPos
//
// ----------------------------------------------------------------------- //

DBOOL FlyingAI::CheckForFlyingCollision2(DVector vPos, DVector vDir, DFLOAT fDist)
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE || !m_hObject) return DFALSE;

	VEC_NORM(vDir);
	VEC_MULSCALAR(vDir, vDir, fDist);

	DVector vTo;
	VEC_ADD(vTo, vPos, vDir);

	IntersectQuery IQuery;
	IntersectInfo IInfo;

	VEC_COPY(IQuery.m_From, vPos);
	VEC_COPY(IQuery.m_To, vTo);
	IQuery.m_Flags	  = INTERSECT_OBJECTS | IGNORE_NONSOLID;
	IQuery.m_FilterFn = DNULL;

	//s_nNumCallsToIntersectSegment++;
	if (pServerDE->IntersectSegment(&IQuery, &IInfo))
	{
		return DTRUE;
	}

	return DFALSE;
}

void FlyingAI::CalculateNextMoveDir(DVector *vDir, FAIMoveType eType)
{
	CServerDE* pServerDE = GetServerDE();
	if (!pServerDE)	return;

	switch (eType)
	{
		case FASTART:
		{
#if _EVDEBUG
pServerDE->CPrint("FASTART eType = %d", eType);
#endif
			vDir->x = pServerDE->Random(-1.0f, 1.0f);
			vDir->y = pServerDE->Random(-1.0f, 1.0f);
			vDir->z = pServerDE->Random(-1.0f, 1.0f);
		}
		break;
		case FASWOOP:
		{
#if _EVDEBUG
pServerDE->CPrint("FASWOOP eType = %d", eType);
#endif
			if (m_hTarget)
			{
#if _EVDEBUG
pServerDE->CPrint("FASWOOP WE HAVE A TARGET");
#endif
				VEC_SUB(*vDir, m_vTargetPos, m_vPos);
				VEC_NORM(*vDir);
#if _EVDEBUG
			pServerDE->CPrint("FASWOOP vDir %.2f %.2f %.2f", VEC_EXPAND(*vDir));
#endif
			}
			else 
			{
				VEC_COPY(*vDir, m_vForward);
#if _EVDEBUG
pServerDE->CPrint("FASWOOP vDir %.2f %.2f %.2f", VEC_EXPAND(*vDir));
#endif
			}
		}
		break;
		case FACONSTRAINED:
		case FASHOGO:	
		case FARAND:
		default:
		{
#if _EVDEBUG
pServerDE->CPrint("FAOTHER eType = %d", eType);
#endif
			vDir->x = pServerDE->Random(-1.0f, 1.0f);
			vDir->y = pServerDE->Random(-0.5f, 0.5f);
			vDir->z = pServerDE->Random(-1.0f, 1.0f);
		}
		break;
	}
}
