/*
===========================================================================
Copyright (C) 2000 - 2013, Raven Software, Inc.
Copyright (C) 2001 - 2013, Activision, Inc.
Copyright (C) 2013 - 2015, OpenJK contributors

This file is part of the OpenJK source code.

OpenJK is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//seems to be a compiler bug, it doesn't clean out the #ifdefs between dif-compiles
//or something, so the headers spew errors on these defs from the previous compile.
//this fixes that. -rww
#ifdef _JK2MP
//get rid of all the crazy defs we added for this file
#undef currentAngles
#undef currentOrigin
#undef mins
#undef maxs
#undef legsAnimTimer
#undef torsoAnimTimer
#undef bool
#undef false
#undef true

#undef sqrtf
#undef Q_flrand

#undef MOD_EXPLOSIVE
#endif

#ifdef _JK2 //SP does not have this preprocessor for game like MP does
#ifndef _JK2MP
#define _JK2MP
#endif
#endif

#ifndef _JK2MP //if single player
#ifndef QAGAME //I don't think we have a QAGAME define
#define QAGAME //but define it cause in sp we're always in the game
#endif
#endif

#ifdef QAGAME //including game headers on cgame is FORBIDDEN ^_^
#include "g_local.h"
#elif defined _JK2MP
#include "bg_public.h"
#endif

#ifndef _JK2MP
#include "g_functions.h"
#include "g_vehicles.h"
#include "../game/wp_saber.h"
#else
#include "bg_vehicles.h"
#endif

#ifdef _JK2MP
//this is really horrible, but it works! just be sure not to use any locals or anything
//with these names (exluding bool, false, true). -rww
#define currentAngles r.currentAngles
#define currentOrigin r.currentOrigin
#define mins r.mins
#define maxs r.maxs
#define legsAnimTimer legsTimer
#define torsoAnimTimer torsoTimer
#define bool qboolean
#define false qfalse
#define true qtrue

#define sqrtf sqrt
#define Q_flrand flrand

#define MOD_EXPLOSIVE MOD_SUICIDE
#else
#define bgEntity_t gentity_t
#endif

#include "../cgame/cg_local.h"

#ifdef QAGAME //we only want a few of these functions for BG

extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
extern vmCvar_t	cg_thirdPersonAlpha;
extern vec3_t playerMins;
extern vec3_t playerMaxs;
extern cvar_t	*g_speederControlScheme;

#ifdef _JK2MP
#include "../namespace_begin.h"
#endif
extern void PM_SetAnim(pmove_t	*pm,int setAnimParts,int anim,int setAnimFlags, int blendTime);
extern int PM_AnimLength( int index, animNumber_t anim );
#ifdef _JK2MP
#include "../namespace_end.h"
#endif

#ifndef	_JK2MP
extern void CG_ChangeWeapon( int num );
#endif

extern void Vehicle_SetAnim(gentity_t *ent,int setAnimParts,int anim,int setAnimFlags, int iBlend);
extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
extern void G_VehicleTrace( trace_t *results, const vec3_t start, const vec3_t tMins, const vec3_t tMaxs, const vec3_t end, int passEntityNum, int contentmask );

// Update death sequence.
static void DeathUpdate( Vehicle_t *pVeh )
{
	if ( level.time >= pVeh->m_iDieTime )
	{
		// If the vehicle is not empty.
		if ( pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
		{
			pVeh->m_pVehicleInfo->EjectAll( pVeh );
		}
		else
		{
			// Waste this sucker.
		}

		// Die now...
/*		else
		{
			vec3_t	mins, maxs, bottom;
			trace_t	trace;

			if ( pVeh->m_pVehicleInfo->explodeFX )
			{
				G_PlayEffect( pVeh->m_pVehicleInfo->explodeFX, parent->currentOrigin );
				//trace down and place mark
				VectorCopy( parent->currentOrigin, bottom );
				bottom[2] -= 80;
				gi.trace( &trace, parent->currentOrigin, vec3_origin, vec3_origin, bottom, parent->s.number, CONTENTS_SOLID );
				if ( trace.fraction < 1.0f )
				{
					VectorCopy( trace.endpos, bottom );
					bottom[2] += 2;
					G_PlayEffect( "ships/ship_explosion_mark", trace.endpos );
				}
			}

			parent->takedamage = qfalse;//so we don't recursively damage ourselves
			if ( pVeh->m_pVehicleInfo->explosionRadius > 0 && pVeh->m_pVehicleInfo->explosionDamage > 0 )
			{
				VectorCopy( parent->mins, mins );
				mins[2] = -4;//to keep it off the ground a *little*
				VectorCopy( parent->maxs, maxs );
				VectorCopy( parent->currentOrigin, bottom );
				bottom[2] += parent->mins[2] - 32;
				gi.trace( &trace, parent->currentOrigin, mins, maxs, bottom, parent->s.number, CONTENTS_SOLID );
				G_RadiusDamage( trace.endpos, NULL, pVeh->m_pVehicleInfo->explosionDamage, pVeh->m_pVehicleInfo->explosionRadius, NULL, MOD_EXPLOSIVE );//FIXME: extern damage and radius or base on fuel
			}

			parent->e_ThinkFunc = thinkF_G_FreeEntity;
			parent->nextthink = level.time + FRAMETIME;
		}*/
	}
}

// Like a think or move command, this updates various vehicle properties.
static bool Update( Vehicle_t *pVeh, const usercmd_t *pUcmd )
{
	return g_vehicleInfo[VEHICLE_BASE].Update( pVeh, pUcmd );
}
#endif //QAGAME

#ifdef _JK2MP
#include "../namespace_begin.h"
#endif

//MP RULE - ALL PROCESSMOVECOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
//If you really need to violate this rule for SP, then use ifdefs.
//By BG-compatible, I mean no use of game-specific data - ONLY use
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
//as a gentity, but the MP-compatible access restrictions are based
//on the bgEntity structure in the MP codebase) -rww
// ProcessMoveCommands the Vehicle.
static void ProcessMoveCommands( Vehicle_t *pVeh )
{
	/************************************************************************************/
	/*	BEGIN	Here is where we move the vehicle (forward or back or whatever). BEGIN	*/
	/************************************************************************************/

	//Client sets ucmds and such for speed alterations
	float speedInc, speedIdleDec, speedIdle, /*speedIdleAccel, */speedMin, speedMax;
	float fWalkSpeedMax;
	int		curTime;
	bgEntity_t *parent = pVeh->m_pParentEntity;
#ifdef _JK2MP
	playerState_t *parentPS = parent->playerState;
#else
	playerState_t *parentPS = &parent->client->ps;
#endif

#ifndef _JK2MP//SP
	curTime = level.time;
#elif defined QAGAME//MP GAME
	curTime = level.time;
#elif defined CGAME//MP CGAME
	//FIXME: pass in ucmd?  Not sure if this is reliable...
	curTime = pm->cmd.serverTime;
#endif


#ifndef _JK2MP //bad for prediction - fixme
	// Bucking so we can't do anything.
	if ( pVeh->m_ulFlags & VEH_BUCKING || pVeh->m_ulFlags & VEH_FLYING || pVeh->m_ulFlags & VEH_CRASHING )
	{
//#ifdef QAGAME //this was in Update above
//		((gentity_t *)parent)->client->ps.speed = 0;
//#endif
		parentPS->speed = 0;
		return;
	}
#endif
	speedIdleDec = pVeh->m_pVehicleInfo->decelIdle * pVeh->m_fTimeModifier;
	speedMax = pVeh->m_pVehicleInfo->speedMax;

	speedIdle = pVeh->m_pVehicleInfo->speedIdle;
	//speedIdleAccel = pVeh->m_pVehicleInfo->accelIdle * pVeh->m_fTimeModifier;
	speedMin = pVeh->m_pVehicleInfo->speedMin;



	if ( pVeh->m_pPilot /*&& (pilotPS->weapon == WP_NONE || pilotPS->weapon == WP_MELEE )*/ &&
		(pVeh->m_ucmd.buttons & BUTTON_ALT_ATTACK) && pVeh->m_pVehicleInfo->turboSpeed )
	{
		if ((curTime - pVeh->m_iTurboTime)>pVeh->m_pVehicleInfo->turboRecharge)
		{
			pVeh->m_iTurboTime = (curTime + pVeh->m_pVehicleInfo->turboDuration);
#ifndef _JK2MP //kill me now
			if (pVeh->m_pVehicleInfo->soundTurbo)
			{
				G_SoundIndexOnEnt(pVeh->m_pParentEntity, CHAN_AUTO, pVeh->m_pVehicleInfo->soundTurbo);
			}
#endif
			parentPS->speed = pVeh->m_pVehicleInfo->turboSpeed;	// Instantly Jump To Turbo Speed
		}
	}

	if ( curTime < pVeh->m_iTurboTime )
	{
		speedMax = pVeh->m_pVehicleInfo->turboSpeed;
	}
	else
	{
		speedMax = pVeh->m_pVehicleInfo->speedMax;
	}

#ifdef _JK2MP
	if ( !parentPS->m_iVehicleNum  )
#else
	if ( !pVeh->m_pVehicleInfo->Inhabited( pVeh ) )
#endif
	{//drifts to a stop
		speedInc = speedIdle * pVeh->m_fTimeModifier;
		VectorClear( parentPS->moveDir );
		//m_ucmd.forwardmove = 127;
		parentPS->speed = 0;
	}
	else
	{
		speedInc = pVeh->m_pVehicleInfo->acceleration * pVeh->m_fTimeModifier;
	}

	if ( parentPS->speed || parentPS->groundEntityNum == ENTITYNUM_NONE  ||
		 pVeh->m_ucmd.forwardmove || pVeh->m_ucmd.upmove > 0 )
	{
		if ( pVeh->m_ucmd.forwardmove > 0 && speedInc )
		{
			parentPS->speed += speedInc;
		}
		else if ( pVeh->m_ucmd.forwardmove < 0 )
		{
			if ( parentPS->speed > speedIdle )
			{
				parentPS->speed -= speedInc;
			}
			else if ( parentPS->speed > speedMin )
			{
				parentPS->speed -= speedIdleDec;
			}
		}
		// No input, so coast to stop.
		else if ( parentPS->speed > 0.0f )
		{
			parentPS->speed -= speedIdleDec;
			if ( parentPS->speed < 0.0f )
			{
				parentPS->speed = 0.0f;
			}
		}
		else if ( parentPS->speed < 0.0f )
		{
			parentPS->speed += speedIdleDec;
			if ( parentPS->speed > 0.0f )
			{
				parentPS->speed = 0.0f;
			}
		}
	}
	else
	{
		if ( pVeh->m_ucmd.forwardmove < 0 )
		{
			pVeh->m_ucmd.forwardmove = 0;
		}
		if ( pVeh->m_ucmd.upmove < 0 )
		{
			pVeh->m_ucmd.upmove = 0;
		}

		//pVeh->m_ucmd.rightmove = 0;

		/*if ( !pVeh->m_pVehicleInfo->strafePerc
			|| (!g_speederControlScheme->value && !parent->s.number) )
		{//if in a strafe-capable vehicle, clear strafing unless using alternate control scheme
			pVeh->m_ucmd.rightmove = 0;
		}*/
	}

	fWalkSpeedMax = speedMax * 0.275f;
	if ( curTime>pVeh->m_iTurboTime && (pVeh->m_ucmd.buttons & BUTTON_WALKING) && parentPS->speed > fWalkSpeedMax )
	{
		parentPS->speed = fWalkSpeedMax;
	}
	else if ( parentPS->speed > speedMax )
	{
		parentPS->speed = speedMax;
	}
	else if ( parentPS->speed < speedMin )
	{
		parentPS->speed = speedMin;
	}

	/********************************************************************************/
	/*	END Here is where we move the vehicle (forward or back or whatever). END	*/
	/********************************************************************************/
}

//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!!
//If you really need to violate this rule for SP, then use ifdefs.
//By BG-compatible, I mean no use of game-specific data - ONLY use
//stuff available in the MP bgEntity (in SP, the bgEntity is #defined
//as a gentity, but the MP-compatible access restrictions are based
//on the bgEntity structure in the MP codebase) -rww
// ProcessOrientCommands the Vehicle.
static void ProcessOrientCommands( Vehicle_t *pVeh )
{
	/********************************************************************************/
	/*	BEGIN	Here is where make sure the vehicle is properly oriented.	BEGIN	*/
	/********************************************************************************/
	bgEntity_t *parent = pVeh->m_pParentEntity;
	playerState_t /**parentPS, */*riderPS;

#ifdef _JK2MP
	bgEntity_t *rider = NULL;
	if (parent->s.owner != ENTITYNUM_NONE)
	{
		rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum];
	}
#else
	gentity_t *rider = parent->owner;
#endif

	// Bucking so we can't do anything.
#ifndef _JK2MP //bad for prediction - fixme
	if ( pVeh->m_ulFlags & VEH_BUCKING || pVeh->m_ulFlags & VEH_FLYING || pVeh->m_ulFlags & VEH_CRASHING )
	{
		return;
	}
#endif

#ifdef _JK2MP
	if ( !rider )
#else
	if ( !rider || !rider->client )
#endif
	{
		rider = parent;
	}



#ifdef _JK2MP
	parentPS = parent->playerState;
	riderPS = rider->playerState;
#else
	//parentPS = &parent->client->ps;
	riderPS = &rider->client->ps;
#endif

	if (rider)
	{
#ifdef _JK2MP
	float angDif = AngleSubtract(pVeh->m_vOrientation[YAW], riderPS->viewangles[YAW]);
	if (parentPS && parentPS->speed)
	{
		float s = parentPS->speed;
		float maxDif = pVeh->m_pVehicleInfo->turningSpeed*4.0f; //magic number hackery
		if (s < 0.0f)
		{
			s = -s;
		}
		angDif *= s/pVeh->m_pVehicleInfo->speedMax;
		if (angDif > maxDif)
		{
			angDif = maxDif;
		}
		else if (angDif < -maxDif)
		{
			angDif = -maxDif;
		}
		pVeh->m_vOrientation[YAW] = AngleNormalize180(pVeh->m_vOrientation[YAW] - angDif*(pVeh->m_fTimeModifier*0.2f));
	}
#else
		pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
#endif
	}


/*	speed = VectorLength( parentPS->velocity );

	// If the player is the rider...
	if ( rider->s.number < MAX_CLIENTS )
	{//FIXME: use the vehicle's turning stat in this calc
		pVeh->m_vOrientation[YAW] = riderPS->viewangles[YAW];
	}
	else
	{
		float turnSpeed = pVeh->m_pVehicleInfo->turningSpeed;
		if ( !pVeh->m_pVehicleInfo->turnWhenStopped
			&& !parentPS->speed )//FIXME: or !pVeh->m_ucmd.forwardmove?
		{//can't turn when not moving
			//FIXME: or ramp up to max turnSpeed?
			turnSpeed = 0.0f;
		}
#ifdef _JK2MP
		if (rider->s.eType == ET_NPC)
#else
		if ( !rider || rider->NPC )
#endif
		{//help NPCs out some
			turnSpeed *= 2.0f;
#ifdef _JK2MP
			if (parentPS->speed > 200.0f)
#else
			if ( parent->client->ps.speed > 200.0f )
#endif
			{
				turnSpeed += turnSpeed * parentPS->speed/200.0f*0.05f;
			}
		}
		turnSpeed *= pVeh->m_fTimeModifier;

		//default control scheme: strafing turns, mouselook aims
		if ( pVeh->m_ucmd.rightmove < 0 )
		{
			pVeh->m_vOrientation[YAW] += turnSpeed;
		}
		else if ( pVeh->m_ucmd.rightmove > 0 )
		{
			pVeh->m_vOrientation[YAW] -= turnSpeed;
		}

		if ( pVeh->m_pVehicleInfo->malfunctionArmorLevel && pVeh->m_iArmor <= pVeh->m_pVehicleInfo->malfunctionArmorLevel )
		{//damaged badly
		}
	}*/

	/********************************************************************************/
	/*	END	Here is where make sure the vehicle is properly oriented.	END			*/
	/********************************************************************************/
}

#ifdef _JK2MP //temp hack til mp speeder controls are sorted -rww
void AnimalProcessOri(Vehicle_t *pVeh)
{
	ProcessOrientCommands(pVeh);
}
#endif

#ifdef QAGAME //back to our game-only functions
// This function makes sure that the vehicle is properly animated.
/*
static void AnimalTailSwipe(Vehicle_t* pVeh, gentity_t *parent, gentity_t *pilot)
{
	trace_t	trace;
	vec3_t angles;
	vec3_t vRoot, vTail;
	vec3_t	lMins, lMaxs;
	mdxaBone_t	boltMatrix;
	int iRootBone;
	int iRootTail;

	VectorSet(angles, 0, parent->currentAngles[YAW], 0);
	VectorSet(lMins, parent->mins[0]-1, parent->mins[1]-1, 0);
	VectorSet(lMaxs, parent->maxs[0]+1, parent->maxs[1]+1, 1);
#ifdef _JK2MP
	iRootBone = trap_G2API_AddBolt( parent->ghoul2, 0, "tail_01" );
	iRootTail = trap_G2API_AddBolt( parent->ghoul2, 0, "tail_04" );

	// Get the positions of the root of the tail and the tail end of it.
	trap_G2API_GetBoltMatrix( parent->ghoul2, 0, iRootBone,
				&boltMatrix, angles, parent->currentOrigin, level.time,
				NULL, parent->modelScale );
	BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, vRoot );

	trap_G2API_GetBoltMatrix( parent->ghoul2, 0, iRootTail,
				&boltMatrix, angles, parent->currentOrigin, level.time,
				NULL, parent->modelScale );
	BG_GiveMeVectorFromMatrix( &boltMatrix, ORIGIN, vTail );
#else
	iRootBone = gi.G2API_GetBoneIndex( &parent->ghoul2[parent->playerModel], "tail_01", qtrue );
	iRootTail = gi.G2API_GetBoneIndex( &parent->ghoul2[parent->playerModel], "tail_04", qtrue );

	// Get the positions of the root of the tail and the tail end of it.
	gi.G2API_GetBoltMatrix( parent->ghoul2, parent->playerModel, iRootBone,
				&boltMatrix, angles, parent->currentOrigin, (cg.time?cg.time:level.time),
				NULL, parent->s.modelScale );
	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, vRoot );

	gi.G2API_GetBoltMatrix( parent->ghoul2, parent->playerModel, iRootTail,
				&boltMatrix, angles, parent->currentOrigin, (cg.time?cg.time:level.time),
				NULL, parent->s.modelScale );
	gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, vTail );
#endif

	// Trace from the root of the tail to the very end.
	G_VehicleTrace( &trace, vRoot, lMins, lMaxs, vTail, parent->s.number, MASK_NPCSOLID );
	if ( trace.fraction < 1.0f )
	{
		if ( ENTITYNUM_NONE != trace.entityNum && g_entities[trace.entityNum].client &&
#ifndef _JK2MP //no rancor in jk2mp (at least not currently)
			g_entities[trace.entityNum].client->NPC_class != CLASS_RANCOR &&
#else //and in mp want to check inuse
			g_entities[trace.entityNum].inuse &&
#endif
			g_entities[trace.entityNum].client->NPC_class != CLASS_VEHICLE )
		{
			vec3_t pushDir;
			vec3_t angs;
			int iDamage = 10;
			// Get the direction we're facing.
			VectorCopy( parent->client->ps.viewangles, angs );
			// Add some fudge.
			angs[YAW] += Q_flrand( 5, 15 );
			angs[PITCH] = Q_flrand( -20, -10 );
			AngleVectors( angs, pushDir, NULL, NULL );
			// Reverse direction.
			pushDir[YAW] = -pushDir[YAW];

			// Smack this ho down.
#ifdef _JK2MP
			G_Sound( &g_entities[trace.entityNum], CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
#else
			G_Sound( &g_entities[trace.entityNum], G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) );
#endif
			G_Throw( &g_entities[trace.entityNum], pushDir, 50 );

			if ( g_entities[trace.entityNum].health > 0 )
			{
				// Knock down and dish out some hurt.
				gentity_t *hit = &g_entities[trace.entityNum];
#ifdef _JK2MP
				if (BG_KnockDownable(&hit->client->ps))
				{
					hit->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
					hit->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100;
					hit->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim

					hit->client->ps.otherKiller = pilot->s.number;
					hit->client->ps.otherKillerTime = level.time + 5000;
					hit->client->ps.otherKillerDebounceTime = level.time + 100;

					hit->client->ps.velocity[0] = pushDir[0]*80;
					hit->client->ps.velocity[1] = pushDir[1]*80;
					hit->client->ps.velocity[2] = 100;
				}
#else
				G_Knockdown( hit, parent, pushDir, 300, qtrue );
#endif
				G_Damage( hit, parent, parent, NULL, NULL, iDamage, DAMAGE_NO_KNOCKBACK | DAMAGE_IGNORE_TEAM, MOD_MELEE );
				//G_PlayEffect( pVeh->m_pVehicleInfo->explodeFX, parent->currentOrigin );
			}// Not Dead
		}// Not Rancor & In USe
	}// Trace Hit Anything?
}
*/
static void AnimateVehicle( Vehicle_t *pVeh )
{
	animNumber_t	Anim = BOTH_VT_IDLE;
	int				iFlags = SETANIM_FLAG_NORMAL, iBlend = 300;
	gentity_t *		pilot = (gentity_t *)pVeh->m_pPilot;
	gentity_t *		parent = (gentity_t *)pVeh->m_pParentEntity;
	//playerState_t *	pilotPS;
	//playerState_t *	parentPS;
	float			fSpeedPercToMax;

#ifdef _JK2MP
	pilotPS = (pilot)?(pilot->playerState):(0);
	parentPS = parent->playerState;
#else
	//pilotPS = (pilot)?(&pilot->client->ps):(0);
	//parentPS = &parent->client->ps;
#endif

	// We're dead (boarding is reused here so I don't have to make another variable :-).
	if ( parent->health <= 0 )
	{
		if ( pVeh->m_iBoarding != -999 )	// Animate the death just once!
		{
			pVeh->m_iBoarding = -999;
			iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;

			// FIXME! Why do you keep repeating over and over!!?!?!? Bastard!
			//Vehicle_SetAnim( parent, SETANIM_LEGS, BOTH_VT_DEATH1, iFlags, iBlend );
		}
		return;
	}

	// If they're bucking, play the animation and leave...
	if ( parent->client->ps.legsAnim == BOTH_VT_BUCK )
	{
		// Done with animation? Erase the flag.
		if ( parent->client->ps.legsAnimTimer <= 0 )
		{
			pVeh->m_ulFlags &= ~VEH_BUCKING;
		}
		else
		{
			return;
		}
	}
	else if ( pVeh->m_ulFlags & VEH_BUCKING )
	{
		iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;
		Anim = BOTH_VT_BUCK;
		iBlend = 500;
		Vehicle_SetAnim( parent, SETANIM_LEGS, BOTH_VT_BUCK, iFlags, iBlend );
		return;
	}

	// Boarding animation.
	if ( pVeh->m_iBoarding != 0 )
	{
		// We've just started boarding, set the amount of time it will take to finish boarding.
		if ( pVeh->m_iBoarding < 0 )
		{
			int iAnimLen;

			// Boarding from left...
			if ( pVeh->m_iBoarding == -1 )
			{
				Anim = BOTH_VT_MOUNT_L;
			}
			else if ( pVeh->m_iBoarding == -2 )
			{
				Anim = BOTH_VT_MOUNT_R;
			}
			else if ( pVeh->m_iBoarding == -3 )
			{
				Anim = BOTH_VT_MOUNT_B;
			}

			// Set the delay time (which happens to be the time it takes for the animation to complete).
			// NOTE: Here I made it so the delay is actually 70% (0.7f) of the animation time.
#ifdef _JK2MP
			iAnimLen = BG_AnimLength( parent->localAnimIndex, Anim ) * 0.7f;
#else
			iAnimLen = PM_AnimLength( parent->client->clientInfo.animFileIndex, Anim ) * 0.7f;
#endif
			pVeh->m_iBoarding = level.time + iAnimLen;

			// Set the animation, which won't be interrupted until it's completed.
			// TODO: But what if he's killed? Should the animation remain persistant???
			iFlags = SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD;

			Vehicle_SetAnim( parent, SETANIM_LEGS, Anim, iFlags, iBlend );
			if (pilot)
			{
				Vehicle_SetAnim(pilot, SETANIM_BOTH, Anim, iFlags, iBlend );
			}
			return;
		}
		// Otherwise we're done.
		else if ( pVeh->m_iBoarding <= level.time )
		{
			pVeh->m_iBoarding = 0;
		}
	}

	// Percentage of maximum speed relative to current speed.
	//float fSpeed = VectorLength( client->ps.velocity );
	fSpeedPercToMax = parent->client->ps.speed / pVeh->m_pVehicleInfo->speedMax;


	// Going in reverse...
	if ( fSpeedPercToMax < -0.01f )
	{
		Anim = BOTH_VT_WALK_REV;
		iBlend = 600;
	}
	else
	{
		bool		Turbo		= (fSpeedPercToMax>0.0f && level.time<pVeh->m_iTurboTime);
		bool		Walking		= (fSpeedPercToMax>0.0f && ((pVeh->m_ucmd.buttons&BUTTON_WALKING) || fSpeedPercToMax<=0.275f));
		bool		Running		= (fSpeedPercToMax>0.275f);


		// Remove Crashing Flag
		//----------------------
		pVeh->m_ulFlags &= ~VEH_CRASHING;

		if (Turbo)
		{// Kicked In Turbo
			iBlend	= 50;
			iFlags	= SETANIM_FLAG_OVERRIDE;
			Anim	= BOTH_VT_TURBO;
		}
		else
		{// No Special Moves
			iBlend	= 300;
 			iFlags	= SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS;
			Anim	= (Walking)?(BOTH_VT_WALK_FWD  ):((Running)?(BOTH_VT_RUN_FWD  ):(BOTH_VT_IDLE1));
		}
	}
	Vehicle_SetAnim( parent, SETANIM_LEGS, Anim, iFlags, iBlend );
}

//rwwFIXMEFIXME: This is all going to have to be predicted I think, or it will feel awful
//and lagged
// This function makes sure that the rider's in this vehicle are properly animated.
static void AnimateRiders( Vehicle_t *pVeh )
{
	animNumber_t Anim = BOTH_VT_IDLE;
	int iFlags = SETANIM_FLAG_NORMAL, iBlend = 500;
	gentity_t *pilot = (gentity_t *)pVeh->m_pPilot;
	gentity_t *parent = (gentity_t *)pVeh->m_pParentEntity;
	playerState_t *pilotPS;
	//playerState_t *parentPS;
	float fSpeedPercToMax;

#ifdef _JK2MP
	pilotPS = pVeh->m_pPilot->playerState;
	//parentPS = pVeh->m_pPilot->playerState;
#else
	pilotPS = &pVeh->m_pPilot->client->ps;
	//parentPS = &pVeh->m_pParentEntity->client->ps;
#endif

	// Boarding animation.
	if ( pVeh->m_iBoarding != 0 )
	{
		return;
	}

	// Percentage of maximum speed relative to current speed.
	fSpeedPercToMax = parent->client->ps.speed / pVeh->m_pVehicleInfo->speedMax;

/*	// Going in reverse...
#ifdef _JK2MP //handled in pmove in mp
	if (0)
#else
	if ( fSpeedPercToMax < -0.01f )
#endif
	{
		Anim = BOTH_VT_WALK_REV;
		iBlend = 600;
		bool		HasWeapon	= ((pilotPS->weapon != WP_NONE) && (pilotPS->weapon != WP_MELEE));
		if (HasWeapon)
		{
			if (pVeh->m_pPilot->s.number<MAX_CLIENTS)
			{
				CG_ChangeWeapon(WP_NONE);
			}

			pVeh->m_pPilot->client->ps.weapon = WP_NONE;
			G_RemoveWeaponModels(pVeh->m_pPilot);
		}
	}
	else
*/
	{
		bool		HasWeapon	= ((pilotPS->weapon != WP_NONE) && (pilotPS->weapon != WP_MELEE));
		bool		Attacking	= (HasWeapon && !!(pVeh->m_ucmd.buttons&BUTTON_ATTACK));
		bool		Right		= (pVeh->m_ucmd.rightmove>0);
		bool		Left		= (pVeh->m_ucmd.rightmove<0);
		bool		Turbo		= (fSpeedPercToMax>0.0f && level.time<pVeh->m_iTurboTime);
		bool		Walking		= (fSpeedPercToMax>0.0f && ((pVeh->m_ucmd.buttons&BUTTON_WALKING) || fSpeedPercToMax<=0.275f));
		bool		Running		= (fSpeedPercToMax>0.275f);
		EWeaponPose	WeaponPose	= WPOSE_NONE;


		// Remove Crashing Flag
		//----------------------
		pVeh->m_ulFlags &= ~VEH_CRASHING;


		// Put Away Saber When It Is Not Active
		//--------------------------------------
#ifndef _JK2MP
		if (HasWeapon &&
			(pVeh->m_pPilot->s.number>=MAX_CLIENTS || (cg.weaponSelectTime+500)<cg.time) &&
			(pilotPS->weapon==WP_SABER && (Turbo || !pilotPS->SaberActive())))
		{
			if (pVeh->m_pPilot->s.number<MAX_CLIENTS)
			{
				CG_ChangeWeapon(WP_NONE);
			}

			pVeh->m_pPilot->client->ps.weapon = WP_NONE;
			G_RemoveWeaponModels(pVeh->m_pPilot);
		}
#endif

		// Don't Interrupt Attack Anims
		//------------------------------
#ifdef _JK2MP
		if (pilotPS->weaponTime>0)
		{
			return;
		}
#else
		if (pilotPS->torsoAnim>=BOTH_VT_ATL_S && pilotPS->torsoAnim<=BOTH_VT_ATF_G)
		{
			float		bodyCurrent	  = 0.0f;
			int			bodyEnd		  = 0;
			if (!!gi.G2API_GetBoneAnimIndex(&pVeh->m_pPilot->ghoul2[pVeh->m_pPilot->playerModel], pVeh->m_pPilot->rootBone, level.time, &bodyCurrent, NULL, &bodyEnd, NULL, NULL, NULL))
			{
				if (bodyCurrent<=((float)(bodyEnd)-1.5f))
				{
					return;
				}
			}
		}
#endif

		// Compute The Weapon Pose
		//--------------------------
		if (pilotPS->weapon==WP_BLASTER)
		{
			WeaponPose = WPOSE_BLASTER;
		}
		else if (pilotPS->weapon==WP_SABER)
		{
			if ( (pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VT_ATL_TO_R_S)
			{
				pVeh->m_ulFlags	&= ~VEH_SABERINLEFTHAND;
			}
			if (!(pVeh->m_ulFlags&VEH_SABERINLEFTHAND) && pilotPS->torsoAnim==BOTH_VT_ATR_TO_L_S)
			{
				pVeh->m_ulFlags	|=  VEH_SABERINLEFTHAND;
			}
			WeaponPose = (pVeh->m_ulFlags&VEH_SABERINLEFTHAND)?(WPOSE_SABERLEFT):(WPOSE_SABERRIGHT);
		}


 		if (Attacking && WeaponPose)
		{// Attack!
			iBlend	= 100;
 			iFlags	= SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART;

			if (Turbo)
			{
				Right = true;
				Left = false;
			}

			// Auto Aiming
			//===============================================
			if (!Left && !Right)		// Allow player strafe keys to override
			{
#ifndef _JK2MP
				if (pVeh->m_pPilot->enemy)
				{
					vec3_t	toEnemy;
					//float	toEnemyDistance;
					vec3_t	actorRight;
					float	actorRightDot;

					VectorSubtract(pVeh->m_pPilot->currentOrigin, pVeh->m_pPilot->enemy->currentOrigin, toEnemy);
					/*toEnemyDistance = */VectorNormalize(toEnemy);

					AngleVectors(pVeh->m_pParentEntity->currentAngles, 0, actorRight, 0);
					actorRightDot = DotProduct(toEnemy, actorRight);

	 				if (fabsf(actorRightDot)>0.5f || pilotPS->weapon==WP_SABER)
					{
						Left	= (actorRightDot>0.0f);
						Right	= !Left;
					}
					else
					{
						Right = Left = false;
					}
				}
				else
#endif
				if (pilotPS->weapon==WP_SABER && !Left && !Right)
				{
					Left = (WeaponPose==WPOSE_SABERLEFT);
					Right	= !Left;
				}
			}


			if (Left)
			{// Attack Left
				switch(WeaponPose)
				{
				case WPOSE_BLASTER:		Anim = BOTH_VT_ATL_G;		break;
				case WPOSE_SABERLEFT:	Anim = BOTH_VT_ATL_S;		break;
				case WPOSE_SABERRIGHT:	Anim = BOTH_VT_ATR_TO_L_S;	break;
				default:				assert(0);
				}
			}
			else if (Right)
			{// Attack Right
				switch(WeaponPose)
				{
				case WPOSE_BLASTER:		Anim = BOTH_VT_ATR_G;		break;
				case WPOSE_SABERLEFT:	Anim = BOTH_VT_ATL_TO_R_S;	break;
				case WPOSE_SABERRIGHT:	Anim = BOTH_VT_ATR_S;		break;
				default:				assert(0);
				}
			}
			else
			{// Attack Ahead
				switch(WeaponPose)
				{
				case WPOSE_BLASTER:		Anim = BOTH_VT_ATF_G;		break;
				default:				assert(0);
				}
			}
		}
		else if (Turbo)
		{// Kicked In Turbo
			iBlend	= 50;
			iFlags	= SETANIM_FLAG_OVERRIDE;
			Anim	= BOTH_VT_TURBO;
		}
		else
		{// No Special Moves
			iBlend	= 300;
 			iFlags	= SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLDLESS;

			if (WeaponPose==WPOSE_NONE)
			{
				if (Walking)
				{
					Anim = BOTH_VT_WALK_FWD;
				}
				else if (Running)
				{
					Anim = BOTH_VT_RUN_FWD;
				}
				else
				{
					Anim = BOTH_VT_IDLE1;//(Q_irand(0,1)==0)?(BOTH_VT_IDLE):(BOTH_VT_IDLE1);
				}
			}
			else
			{
				switch(WeaponPose)
				{
				case WPOSE_BLASTER:		Anim = BOTH_VT_IDLE_G;			break;
				case WPOSE_SABERLEFT:	Anim = BOTH_VT_IDLE_SL;			break;
				case WPOSE_SABERRIGHT:	Anim = BOTH_VT_IDLE_SR;			break;
				default:				assert(0);
				}
			}
		}// No Special Moves
	}

	Vehicle_SetAnim( pilot, SETANIM_BOTH, Anim, iFlags, iBlend );
}
#endif //QAGAME

#ifndef QAGAME
void AttachRidersGeneric( Vehicle_t *pVeh );
#endif

//on the client this function will only set up the process command funcs
void G_SetAnimalVehicleFunctions( vehicleInfo_t *pVehInfo )
{
#ifdef QAGAME
	pVehInfo->AnimateVehicle			=		AnimateVehicle;
	pVehInfo->AnimateRiders				=		AnimateRiders;
//	pVehInfo->ValidateBoard				=		ValidateBoard;
//	pVehInfo->SetParent					=		SetParent;
//	pVehInfo->SetPilot					=		SetPilot;
//	pVehInfo->AddPassenger				=		AddPassenger;
//	pVehInfo->Animate					=		Animate;
//	pVehInfo->Board						=		Board;
//	pVehInfo->Eject						=		Eject;
//	pVehInfo->EjectAll					=		EjectAll;
//	pVehInfo->StartDeathDelay			=		StartDeathDelay;
	pVehInfo->DeathUpdate				=		DeathUpdate;
//	pVehInfo->RegisterAssets			=		RegisterAssets;
//	pVehInfo->Initialize				=		Initialize;
	pVehInfo->Update					=		Update;
//	pVehInfo->UpdateRider				=		UpdateRider;
#endif //QAGAME
	pVehInfo->ProcessMoveCommands		=		ProcessMoveCommands;
	pVehInfo->ProcessOrientCommands		=		ProcessOrientCommands;

#ifndef QAGAME //cgame prediction attachment func
	pVehInfo->AttachRiders				=		AttachRidersGeneric;
#endif
//	pVehInfo->AttachRiders				=		AttachRiders;
//	pVehInfo->Ghost						=		Ghost;
//	pVehInfo->UnGhost					=		UnGhost;
//	pVehInfo->Inhabited					=		Inhabited;
}

// Following is only in game, not in namespace
#ifdef _JK2MP
#include "../namespace_end.h"
#endif

#ifdef QAGAME
extern void G_AllocateVehicleObject(Vehicle_t **pVeh);
#endif

#ifdef _JK2MP
#include "../namespace_begin.h"
#endif

// Create/Allocate a new Animal Vehicle (initializing it as well).
//this is a BG function too in MP so don't un-bg-compatibilify it -rww
void G_CreateAnimalNPC( Vehicle_t **pVeh, const char *strAnimalType )
{
	// Allocate the Vehicle.
#ifdef _JK2MP
#ifdef QAGAME
	//these will remain on entities on the client once allocated because the pointer is
	//never stomped. on the server, however, when an ent is freed, the entity struct is
	//memset to 0, so this memory would be lost..
    G_AllocateVehicleObject(pVeh);
#else
	if (!*pVeh)
	{ //only allocate a new one if we really have to
		(*pVeh) = (Vehicle_t *) BG_Alloc( sizeof(Vehicle_t) );
	}
#endif
	memset(*pVeh, 0, sizeof(Vehicle_t));
	(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )];
#else
	(*pVeh) = (Vehicle_t *) gi.Malloc( sizeof(Vehicle_t), TAG_G_ALLOC, qtrue );
	(*pVeh)->m_pVehicleInfo = &g_vehicleInfo[BG_VehicleGetIndex( strAnimalType )];
#endif
}

#ifdef _JK2MP

#include "../namespace_end.h"

//get rid of all the crazy defs we added for this file
#undef currentAngles
#undef currentOrigin
#undef mins
#undef maxs
#undef legsAnimTimer
#undef torsoAnimTimer
#undef bool
#undef false
#undef true

#undef sqrtf
#undef Q_flrand

#undef MOD_EXPLOSIVE
#endif
