//-----------------------------------------------------------------------------
//
//  $Logfile:: /EF2/Code/DLLs/game/vehicle.cpp                               $
// $Revision:: 34                                                             $
//   $Author:: Singlis                                                        $
//     $Date:: 9/26/03 2:36p                                                  $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
//
// DESCRIPTION:
// Script controlled Vehicles.
//

#include "_pch_cpp.h"
#include "scriptslave.h"
#include "vehicle.h"
#include "player.h"
#include "specialfx.h"
#include "explosion.h"
#include "earthquake.h"
#include "gibs.h"
#include "player.h"

Event EV_Vehicle_Start
(
	"start",
	EV_DEFAULT,
	NULL,
	NULL,
	"Initialize the vehicle."
);
Event EV_Vehicle_Enter
(
	"enter",
	EV_CODEONLY,
	"eS",
	"vehicle driver_anim",
	"Called when someone gets into a vehicle."
);
Event EV_Vehicle_Exit
(
	"exit",
	EV_DEFAULT,
	"e",
	"vehicle",
	"Called when driver gets out of the vehicle."
);
Event EV_Vehicle_Drivable
(
	"drivable",
	EV_DEFAULT,
	NULL,
	NULL,
	"Make the vehicle drivable"
);
Event EV_Vehicle_UnDrivable
(
	"undriveable",
	EV_DEFAULT,
	NULL,
	NULL,
	"Make the vehicle undrivable"
);
Event EV_Vehicle_Jumpable
(
	"canjump",
	EV_DEFAULT,
	"b",
	"jumpable",
	"Sets whether or not the vehicle can jump"
);
Event EV_Vehicle_Lock
(
	"lock",
	EV_DEFAULT,
	NULL,
	NULL,
	"Sets the vehicle to be locked"
);
Event EV_Vehicle_UnLock
(
	"unlock",
	EV_DEFAULT,
	NULL,
	NULL,
	"Sets the vehicle to be unlocked"
);
Event EV_Vehicle_SeatAnglesOffset
(
	"seatanglesoffset",
	EV_DEFAULT,
	"v",
	"angles",
	"Set the angles offset of the seat"
);
Event EV_Vehicle_SeatOffset
(
	"seatoffset",
	EV_DEFAULT,
	"v",
	"offset",
	"Set the offset of the seat"
);
Event EV_Vehicle_DriverAnimation
(
	"driveranim",
	EV_DEFAULT,
	"s",
	"animation",
	"Set the animation of the driver to use when the driver is in the vehicle"
);
Event EV_Vehicle_SetWeapon
(
	"setweapon",
	EV_DEFAULT,
	"s",
	"weaponname",
	"Set the weapon for the vehicle"
);
Event EV_Vehicle_SetSpeed
(
	"vehiclespeed",
	EV_DEFAULT,
	"f",
	"speed",
	"Set the speed of the vehicle"
);
Event EV_Vehicle_SetTurnRate
(
	"turnrate",
	EV_DEFAULT,
	"f",
	"rate",
	"Set the turning rate of the vehicle"
);
Event EV_Vehicle_SteerInPlace
(
	"steerinplace",
	EV_DEFAULT,
	NULL,
	NULL,
	"Set the vehicle to turn in place"
);
Event EV_Vehicle_ShowWeapon
(
	"showweapon",
	EV_DEFAULT,
	NULL,
	NULL,
	"Set the weapon to be show in the view"
);
Event EV_Vehicle_RestrictPitch
(
	"restrictpitch",
	EV_DEFAULT,
	"f",
	"pitchDelta",
	"The max and minimum pitch of the driver"
);
Event EV_Vehicle_RestrictRotation
(
	"restrictrotation",
	EV_DEFAULT,
	"f",
	"rotateDelta",
	"The max and min rotation of the driver"
);
Event EV_Vehicle_NoPrediction
(
	"noprediction",
	EV_DEFAULT,
	"b",
	"bool",
	"Turns no prediction on or off"
);

Event EV_Vehicle_DisableInventory
(
	"disableinventory",
	EV_DEFAULT,
	NULL,
	NULL,
	"Disables the inventory when the player uses this vehicle"
);

extern Event EV_Player_DisableUseWeapon;
extern Event EV_Player_PutawayWeapon;

CLASS_DECLARATION( ScriptModel, VehicleBase, NULL )
{
	{ NULL, NULL }
};

VehicleBase::VehicleBase()
{
	if ( LoadingSavegame )
	{
		// Archive function will setup all necessary data
		return;
	}
	
	takedamage = DAMAGE_NO;
	showModel();
	setSolidType( SOLID_NOT );
	setMoveType( MOVETYPE_NONE );
	setOrigin( GetLocalOrigin() + Vector( "0 0 30") );
	
	//
	// we want the bounds of this model auto-rotated
	//
	flags |= FL_ROTATEDBOUNDS;
	
	//
	// rotate the mins and maxs for the model
	//
	setSize( mins, maxs );
	
	vlink = NULL;
	offset = Vector(0, 0, 0);
	
	PostEvent( EV_BecomeNonSolid, EV_POSTSPAWN );
}

CLASS_DECLARATION( VehicleBase, BackWheels, "script_wheelsback" )
{
	{ NULL, NULL }
};

CLASS_DECLARATION( VehicleBase, FrontWheels, "script_wheelsfront" )
{
	{ NULL, NULL }
};

CLASS_DECLARATION( VehicleBase, Vehicle, "script_vehicle" )
{
	{ &EV_Blocked,					    &Vehicle::VehicleBlocked	},
	{ &EV_Touch,					    &Vehicle::VehicleTouched	},
	{ &EV_Use,						    &Vehicle::DriverUse		},
	{ &EV_Vehicle_Start,			    &Vehicle::VehicleStart	},
	{ &EV_Vehicle_Drivable,		        &Vehicle::Drivable		},
	{ &EV_Vehicle_UnDrivable,	        &Vehicle::UnDrivable		},
	{ &EV_Vehicle_Jumpable,	            &Vehicle::Jumpable		},
	{ &EV_Vehicle_SeatAnglesOffset,     &Vehicle::SeatAnglesOffset},
	{ &EV_Vehicle_SeatOffset,           &Vehicle::SeatOffset		},
	{ &EV_Vehicle_Lock,		            &Vehicle::Lock			},
	{ &EV_Vehicle_UnLock,	            &Vehicle::UnLock			},
	{ &EV_Vehicle_SetWeapon,            &Vehicle::SetWeapon		},
	{ &EV_Vehicle_DriverAnimation,      &Vehicle::DriverAnimation },
	{ &EV_Vehicle_SetSpeed,             &Vehicle::SetSpeed		},
	{ &EV_Vehicle_SetTurnRate,          &Vehicle::SetTurnRate		},
	{ &EV_Vehicle_SteerInPlace,         &Vehicle::SteerInPlace	},
	{ &EV_Vehicle_ShowWeapon,           &Vehicle::ShowWeaponEvent },
	{ &EV_Vehicle_RestrictPitch,		&Vehicle::RestrictPitch	},
	{ &EV_Vehicle_RestrictRotation,		&Vehicle::RestrictRotation},
	{ &EV_Vehicle_NoPrediction,			&Vehicle::SetNoPrediction },
	{ &EV_Vehicle_DisableInventory,		&Vehicle::DisableInventory},
	{ NULL, NULL }
};

Vehicle::Vehicle()
{
	if ( LoadingSavegame )
	{
		// Archive function will setup all necessary data
		return;
	}
	takedamage = DAMAGE_YES;
	seatangles = vec_zero;
	driveroffset = vec_zero;
	seatoffset = vec_zero;
	driver = 0;
	lastdriver = 0;
	currentspeed = 0;
	turnangle = 0;
	turnimpulse = 0;
	moveimpulse = 0;
	jumpimpulse = 0;
	conesize = 75;
	hasweapon = false;
	locked = false;
	steerinplace = false;
	drivable = true;
	jumpable = false;
	showweapon = false;
	flags |= FL_DIE_EXPLODE;
	// touch triggers by default
	flags |= FL_TOUCH_TRIGGERS;
	gravity = 1;
	mass = size.length() * 10.0f;
	
	health      = 1000;
	speed       = 1200;
	maxturnrate = 40.0f;
	
	usetime     = 0.0f;
	
	maxtracedist = 0;
	
	
	_restrictPitch = false;
	_restrictYaw = false;
	_noPrediction = false;
	
	_disableInventory = false;

	PostEvent( EV_Vehicle_Start, FRAMETIME );
}

void Vehicle::VehicleStart( Event *ev )
{
	Entity *ent;
	VehicleBase *last;
	Vector   drivemins, drivemaxs;
	float    max;
	float    width,height;
	orientation_t orn;
	
	// become solid
	setSolidType( SOLID_BBOX );
	
	last = this;
	
	for( ent = G_NextEntity( NULL ); ent != NULL; ent = G_NextEntity( ent ) )
	{
		if ( ( ent != this ) && ( ent->isSubclassOf( VehicleBase ) ) )
		{
			if ( ( ent->absmax.x >= absmin.x ) && ( ent->absmax.y >= absmin.y ) && ( ent->absmax.z >= absmin.z ) &&
				( ent->absmin.x <= absmax.x ) && ( ent->absmin.y <= absmax.y ) && ( ent->absmin.z <= absmax.z ) )
			{
				last->vlink = ( VehicleBase * )ent;
				last = ( VehicleBase * )ent;
				last->offset = last->origin - origin;
				last->offset = getLocalVector( last->offset );
				last->edict->s.scale *= edict->s.scale;
			}
		}
	}
	
	last->vlink = NULL;
	
	//
	// get the seat offset
	//
	if ( GetRawTag( "tag_rider", &orn ) )
	{
		driveroffset = Vector( orn.origin );
	}
	driveroffset += seatoffset * edict->s.scale;
	
	SetDriverAngles( angles + seatangles );
	
	
	
	max_health = health;
	
	//
	// calculate drive mins and maxs
	//
	max = 0;
	if ( fabs( mins[ 0 ] ) > max )
		max = fabs( mins[ 0 ] );
	if ( fabs( maxs[ 0 ] ) > max )
		max = fabs( maxs[ 0 ] );
	if ( fabs( mins[ 1 ] ) > max )
		max = fabs( mins[ 1 ] );
	if ( fabs( maxs[ 1 ] ) > max )
		max = fabs( maxs[ 1 ] );
	drivemins = Vector( -max, -max, mins[ 2 ] ) * edict->s.scale;
	drivemaxs = Vector( max, max, maxs[ 2 ] ) * edict->s.scale;
	
	width = maxs[ 1 ] - mins[ 1 ];
	height = maxs[ 0 ] - mins[ 0 ];
	
	maxtracedist = height;
	
	Corners[ 0 ][ 0 ] = -( width / 4.0f );
	Corners[ 0 ][ 1 ] = ( height / 4.0f );
	Corners[ 0 ][ 2 ] = 0.0f;
	
	Corners[ 1 ][ 0 ] = ( width / 4.0f );
	Corners[ 1 ][ 1 ] = ( height / 4.0f );
	Corners[ 1 ][ 2 ] = 0.0f;
	
	Corners[ 2 ][ 0 ] = -( width / 4.0f );
	Corners[ 2 ][ 1 ] = -( height / 4.0f );
	Corners[ 2 ][ 2 ] = 0.0f;
	
	Corners[ 3 ][ 0 ] = ( width / 4.0f );
	Corners[ 3 ][ 1 ] = -( height / 4.0f );
	Corners[ 3 ][ 2 ] = 0.0f;
	if ( drivable )
	{
		// drop everything back to the floor
		//droptofloor( 250.0f );
		Postthink();
	}
	last_origin = origin;
	setSize( drivemins, drivemaxs );
}

void Vehicle::Drivable( Event *ev )
{
	setMoveType( MOVETYPE_NONE );
	drivable = true;
}

void Vehicle::UnDrivable( Event *ev )
{
	setMoveType( MOVETYPE_PUSH );
	drivable = false;
}

void Vehicle::Jumpable( Event *ev )
{
	jumpable = true;
}

void Vehicle::Lock( Event *ev )
{
	locked = true;
}

void Vehicle::UnLock( Event *ev )
{
	locked = false;
}

void Vehicle::SteerInPlace( Event *ev )
{
	steerinplace = true;
}

void Vehicle::SeatAnglesOffset( Event *ev )
{
	seatangles = ev->GetVector( 1 );
}

void Vehicle::SeatOffset( Event *ev )
{
	seatoffset = ev->GetVector( 1 );
}

void Vehicle::SetWeapon( Event *ev )
{
	showweapon = true;
	hasweapon = true;
	weaponName = ev->GetString( 1 );
}

void Vehicle::ShowWeaponEvent( Event *ev )
{
	showweapon = true;
}

//-----------------------------------------------------
//
// Name:		RestrictPitch
// Class:		Vehicle
//
// Description:	Restricts the drivers pitch while in the vehicle. This calculates the pitchSeam. 
//				A seam is the location where the normalized angle range boundary. For example
//				on a normalized range of -180, 180, the seam is located at -180/180. To solve the seam
//				problem, the seam is always positioned directly (-180 degrees) behind the starting pitch.
//
// Parameters:	event - event containing the pitch restrictions
//
// Returns:		None
//-----------------------------------------------------
void Vehicle::RestrictPitch(Event* event)
{
	_startPitch = angles[PITCH];
	_pitchSeam = _startPitch - 180.0f;

	_minimumPitch = AngleNormalizeArbitrary( _startPitch - event->GetFloat(1), _pitchSeam);
	_maximumPitch = AngleNormalizeArbitrary( _startPitch + event->GetFloat(1), _pitchSeam);

	_restrictPitch = true;
}


//-----------------------------------------------------
//
// Name:		RestrictRotation
// Class:		Vehicle
//
// Description:	Restricts the drivers rotation while in the vehicle.This calculates the yawSeam. 
//				A seam is the location where the normalized angle range boundary. For example
//				on a normalized range of -180, 180, the seam is located at -180/180. To solve the seam
//				problem, the seam is always positioned directly (-180 degrees) behind the starting yaw.
//
// Parameters:	event - the event containing the rotation restrictions
//
// Returns:		None
//-----------------------------------------------------
void Vehicle::RestrictRotation(Event* event)
{
	_startYaw = angles[YAW];
	_yawSeam = _startYaw - 180.0f;
	
	_minimumYaw = AngleNormalizeArbitrary( _startYaw - event->GetFloat(1), _yawSeam);
	_maximumYaw = AngleNormalizeArbitrary( _startYaw + event->GetFloat(1), _yawSeam);

	_restrictYaw = true;
}


//-----------------------------------------------------
//
// Name:		SetNoPrediction
// Class:		Vehicle
//
// Description:	Turns the no prediction flag
//
// Parameters:	event - the event that turns no prediction on or off
//
// Returns:		None
//-----------------------------------------------------
void Vehicle::SetNoPrediction(Event* event)
{
	_noPrediction = event->GetBoolean(1);
}


void Vehicle::DisableInventory(Event* event)
{
	_disableInventory = true;
}


void Vehicle::DriverAnimation( Event *ev )
{
	driveranim = ev->GetString( 1 );
}

qboolean Vehicle::HasWeapon( void )
{
	return hasweapon;
}

qboolean Vehicle::ShowWeapon( void )
{
	return showweapon;
}

void Vehicle::SetDriverAngles( const Vector &angles )
{
	int i;
	
	if ( !driver )
		return;
	
	for( i = 0; i < 3; i++ )
	{
		driver->client->ps.delta_angles[ i ] = ANGLE2SHORT( angles[ i ] - driver->client->cmd_angles[ i ] );
	}
}

void Vehicle::HandleEvent( Event *ev )
{

}

/*
=============
CheckWater
=============
*/
void Vehicle::CheckWater( void )
{
	Vector  point;
	int	  cont;
	int	  sample1;
	int	  sample2;
	VehicleBase *v;
	
	unlink();
	v = this;
	while( v->vlink )
	{
		v = v->vlink;
		v->unlink();
	}
	
	if ( driver )
	{
		driver->unlink();
	}
	
	//
	// get waterlevel
	//
	waterlevel = 0;
	watertype = 0;
	
	sample2 = maxs[ 2 ] - mins[ 2 ];
	sample1 = sample2 / 2;
	
	point = origin;
	point[ 2 ] += mins[ 2 ];
	cont = gi.pointcontents( point, 0 );
	
	if ( cont & MASK_WATER )
	{
		watertype = cont;
		waterlevel = 1;
		point[ 2 ] = origin[ 2 ] + mins[ 2 ] + sample1;
		cont = gi.pointcontents( point, 0 );
		if ( cont & MASK_WATER )
		{
			waterlevel = 2;
			point[ 2 ] = origin[ 2 ] + mins[ 2 ] + sample2;
			cont = gi.pointcontents( point, 0 );
			if ( cont & MASK_WATER )
            {
				waterlevel = 3;
            }
		}
	}
	
	link();
	v = this;
	while( v->vlink )
	{
		v = v->vlink;
		v->link();
	}
	
	if ( driver )
	{
		driver->link();
		driver->waterlevel = waterlevel;
		driver->watertype = watertype;
	}
}

/*
=============
WorldEffects
=============
*/
void Vehicle::WorldEffects( void )
{
	//
	// Check for earthquakes
	//
	/* if ( groundentity && ( level.earthquake > level.time ) )
	{
		velocity += Vector
			(
			level.earthquake_magnitude * EARTHQUAKE_STRENGTH * G_CRandom(),
			level.earthquake_magnitude * EARTHQUAKE_STRENGTH * G_CRandom(),
			level.earthquake_magnitude * 1.5f * G_Random()
			);
	} */
	
	//
	// check for lava
	//
	if ( watertype & CONTENTS_LAVA )
	{
		Damage( world, world, 20.0f * waterlevel, origin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_LAVA );
	}
}

void Vehicle::DriverUse( Event *ev )
{
	Event *event;
	Entity *other;
	
	other = ev->GetEntity( 1 );
	if ( level.time < ( usetime + 1.0f ) )
		return;
	else
		usetime = level.time;
	
	if ( !other || !other->isSubclassOf( Sentient ) )
	{
		return;
	}
	
	if ( driver )
	{
//		int height;
//		int ang;
		Vector angles;
		Vector forward;
		Vector pos;
//		float ofs;
//		trace_t trace;
		
		if ( other != driver )
		{
			return;
		}
		
		if ( locked )
			return;
		/*
		//
		// place the driver on the ground
		//
		ofs = size.length() * 0.5f;
		for ( height = 0; height < 100; height += 16 )
		{
			for ( ang = 0; ang < 360; ang += 30 )
            {
				angles[ 1 ] = driver->angles[ 1 ] + ang + 90.0f;
				angles.AngleVectors( &forward, NULL, NULL );
				pos = origin + (forward * ofs);
				pos[2] += height;
				trace = G_Trace( pos, driver->mins, driver->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 1" );
				if ( !trace.startsolid && !trace.allsolid )
				{
					Vector end;
					
					end = pos;
					end[ 2 ] -= 128.0f;
					trace = G_Trace( pos, driver->mins, driver->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 2" );
					if ( trace.fraction < 1.0f )
					{
						driver->setOrigin( pos );
						goto foundpos;
					}
				}
            }
		}
		*/

//		return;
		
//foundpos:
		
		turnimpulse = 0;
		moveimpulse = 0;
		jumpimpulse = 0;
		
		event = new Event( EV_Vehicle_Exit );
		event->AddEntity( this );
		driver->ProcessEvent( event );

		if ( drivable )
		{
			StopLoopSound();
			Sound( "snd_dooropen", CHAN_BODY );
			Sound( "snd_stop", CHAN_VOICE );
			driver->setSolidType( SOLID_BBOX );
		}

		driver->setOrigin(_oldOrigin);

		enableDriverInventory();

		if( hasweapon )
		{
			driver->DeactivateWeapon( WEAPON_DUAL );
			driver->takeItem( weaponName.c_str() );
		}

		driver = NULL;

	}
	else
	{
		driver = ( Sentient * )other;
		VectorCopy(driver->edict->client->ps.origin, _oldOrigin);
		
		lastdriver = driver;
		
		
		if ( drivable )
			setMoveType( MOVETYPE_VEHICLE );

		if ( hasweapon )
		{
			driver->giveItem( weaponName.c_str() );
			driver->useWeapon(weaponName.c_str(), WEAPON_DUAL);
		}

		disableDriverInventory();
		
		if ( drivable )
		{
			Sound( "snd_doorclose", CHAN_BODY );
			Sound( "snd_start", CHAN_VOICE );
			driver->setSolidType( SOLID_NOT );
		}
		
		event = new Event( EV_Vehicle_Enter );
		event->AddEntity( this );
		if ( driveranim.length() )
			event->AddString( driveranim );
		driver->ProcessEvent( event );
		
		offset = other->origin - origin;
		
		flags	|= FL_POSTTHINK;
		SetDriverAngles( angles + seatangles );
	}	
}


//-----------------------------------------------------
//
// Name:		
// Class:		
//
// Description:	
//
// Parameters:	
//
// Returns:		
//-----------------------------------------------------
void Vehicle::disableDriverInventory( void )
{
	if( _disableInventory == false )
		return;

	if(driver != 0 && driver->isSubclassOf(Player))
	{
		((Player*)driver.Pointer())->disableInventory();
	}
}


//-----------------------------------------------------
//
// Name:		
// Class:		
//
// Description:	
//
// Parameters:	
//
// Returns:		
//-----------------------------------------------------
void Vehicle::enableDriverInventory( void )
{
	if( driver != 0 && driver->isSubclassOf(Player))
	{
		((Player*)driver.Pointer())->enableInventory();
	}
}

qboolean Vehicle::Drive( usercmd_t *ucmd )
{
	Vector i, j, k;
	
	if ( !driver || !driver->isClient() )
	{
		return false;
	}
	
	if ( !drivable )
	{
		driver->client->ps.pm_flags |= PMF_FROZEN;
		ucmd->forwardmove = 0;
		ucmd->rightmove = 0;
		return false;
	}
	
	if(_noPrediction )
	{
		driver->client->ps.pm_flags |= PMF_NO_PREDICTION;
	}
	
	moveimpulse = ( ( float )ucmd->forwardmove ) * 3.0f;
	//turnimpulse = ( ( float )-ucmd->rightmove ) * 3.0f;
	jumpimpulse = ( ( float )ucmd->upmove * gravity ) / 350.0f;

	ucmd->lean = 0;
	
	if ( ( jumpimpulse < 0.0f ) || ( !jumpable ) )
		jumpimpulse = 0.0f;
	
	turnimpulse = 10.0f * angledist( SHORT2ANGLE( ucmd->angles[ 1 ] ) - driver->client->cmd_angles[ 1 ] );
	
	return true;
}

void Vehicle::Postthink( void )
{
	float			turn;
	Vector		i, j, k;
	Vector      normalsum;
	Vector      temp;
	Vector      pitch;
	Vector      roll;
	VehicleBase *v;
	VehicleBase *last;
	float       drivespeed;
	

	if ( drivable )
	{
		currentspeed = moveimpulse / 4.0f;
		
		turnangle = turnangle * 0.25f + turnimpulse;
		if ( turnangle > maxturnrate )
		{
			turnangle = maxturnrate;
		}
		else if ( turnangle < -maxturnrate )
		{
			turnangle = -maxturnrate;
		}
		else if ( fabs( turnangle ) < 2.0f )
		{
			turnangle = 0;
		}
		
		CalculateOrientation();		
		
		turn = turnangle * ( 1.0f / 200.0f );
		
		if ( groundentity )
		{
			float dot;
			Vector newvel;
			Vector flatvel;
			
			velocity[ 0 ] *= 0.925f;
			velocity[ 1 ] *= 0.925f;
			flatvel = Vector( orientation[ 0 ] );
			velocity += flatvel * currentspeed;
			flatvel[ 2 ] = 0;
			dot = velocity * flatvel;
			if ( dot > speed )
			{
				dot = speed;
			}
			else if ( dot < -speed )
			{
				dot = -speed;
			}
			else if ( fabs( dot ) < 20.0f )
			{
				dot = 0;
			}
			newvel = flatvel * dot;
			velocity[ 0 ] = newvel[ 0 ];
			velocity[ 1 ] = newvel[ 1 ];
			velocity[ 2 ] += dot * jumpimpulse;
			
			avelocity *= 0.05f;
			if ( steerinplace )
            {
				if ( dot < 350.0f )
					dot = 350.0f;
				avelocity.y += turn * dot;
            }
			else
            {
				avelocity.y += turn * dot;
            }
		}
		else
		{
			avelocity *= 0.1f;
		}
		angles += avelocity * level.frametime;
		setAngles( angles );
	}
	
	drivespeed = velocity * Vector( orientation[ 0 ] );
	
	if ( drivable && driver )
	{
		str sound_name;
		
		if ( currentspeed > 0.0f )
			sound_name = "snd_forward";
		else if ( currentspeed < 0.0f )
			sound_name = "snd_backward";
		else
			sound_name = "snd_idle";
		
		LoopSound( sound_name.c_str() );
	}
	
	i = Vector( orientation[ 0 ] );
	j = Vector( orientation[ 1 ] );
	k = Vector( orientation[ 2 ] );
	
	if ( driver )
	{
		Player * player;
		
		if ( driver->isSubclassOf ( Player ) )
		{
			player = ( Player * )( Sentient * )driver;
			player->setOrigin( origin + ( i * driveroffset[0] ) + ( j * driveroffset[1] ) + ( k * driveroffset[2] ) );
			if ( drivable )
			{
				player->velocity = vec_zero;
				Vector playerAngles = player->v_angle;
				Vector viewAngles = angles;
				
				if( _restrictPitch )
				{
					if(viewAngles[PITCH] > _maximumPitch)	
						viewAngles[PITCH] = _maximumPitch;
					else if(viewAngles[PITCH] < _minimumPitch)	
						viewAngles[PITCH] = _minimumPitch;
				}
				
				if( _restrictYaw )
				{
					if(viewAngles[YAW] > _maximumYaw)	
						viewAngles[YAW] = _maximumYaw;
					else if(viewAngles[YAW] <= _minimumYaw)	
						viewAngles[YAW] = _minimumYaw;
				}
				
				angles = viewAngles;
				playerAngles.y = angles.y;
				playerAngles.z = angles.z;
				player->SetViewAngles(playerAngles);
			}
		}
	}
	
	last = this;
	while( last->vlink )
	{
		v = last->vlink;
		v->setOrigin( origin + ( i * v->offset.x ) + ( j * v->offset.y ) + ( k * v->offset.z ) );
		v->avelocity = avelocity;
		v->velocity = velocity;
		v->angles[ ROLL ] = angles[ ROLL ];
		v->angles[ YAW ] = angles[ YAW ];
		v->angles[ PITCH ] = (float)( (int)( v->angles[ PITCH ] + (drivespeed/4) ) % 360 );
		
		if ( v->isSubclassOf( FrontWheels ) )
		{
			v->angles += Vector( 0.0f, turnangle, 0.0f );
		}
		v->setAngles( v->angles );
		
		last = v;
	}
	
	//CheckWater();
	WorldEffects();
	
	// save off last origin
	last_origin = origin;
	
	if ( !driver && !velocity.length() && groundentity && !( watertype & CONTENTS_LAVA ) )
	{
		flags &= ~FL_POSTTHINK;
		if ( drivable )
			setMoveType( MOVETYPE_NONE );
	}
}

/////////////////////////////////////////////////////////
//
// Name:  CalculateOrientation()
//
// Description: Calculates the orientation of the vehicle
// by getting the normals of all the poly's underneath 
// each corner of the vehicle's bounding box, this
// allows the vehicle to pitch appropriately
//
/////////////////////////////////////////////////////////

void Vehicle::CalculateOrientation( void )
{
	Vector      temp, pitch, normalsum;
	Vector      i, j, k;
	int         numnormals;
	trace_t     trace;
	
	temp[ PITCH ] = 0;
	temp[ YAW ] = angles[ YAW ];
	temp[ ROLL ] = 0;
	temp.AngleVectors( &i, &j, &k );
	j = vec_zero - j;
	
	//
	// figure out what our orientation is
	//
	
	numnormals = 0;
	for ( int index = 0; index < 4; index++ )
	{
		Vector start, end;
		Vector boxoffset;
		
		boxoffset = Corners[ index ];
		start = origin + ( i * boxoffset[0] ) + ( j * boxoffset[1] ) + ( k * boxoffset[2] );
		end = start;
		end[ 2 ] -= maxtracedist;
		trace = G_Trace( start, vec_zero, vec_zero, end, NULL, MASK_SOLID, false, "Vehicle::PostThink Corners" );
		
		if ( ( trace.fraction > 0.0f ) && ( trace.fraction != 1.0f ) && !trace.startsolid )
		{
			normalsum += Vector( trace.plane.normal );
			numnormals++;
		}
	}
	
	if ( numnormals > 1 )
	{
		temp = normalsum * ( 1.0f/ numnormals );
		temp.normalize();
		i = temp.CrossProduct( temp, j );
		pitch = i;
		
		// determine pitch
		angles[ 0 ] = -(pitch.toPitch());
	}
}

void Vehicle::VehicleTouched( Event *ev )
{
	Entity	*other;
	float		speed;
	Vector   delta;
	Vector	dir;
	
	other = ev->GetEntity( 1 );
	if ( other == driver )
	{
		return;
	}
	
	if ( other == world )
	{
		return;
	}
	
	if ( drivable && !driver )
	{
		return;
	}
	
	delta = origin - last_origin;
	speed = delta.length();
	if ( speed > 2 )
	{
		Sound( "vehicle_crash", true );
		dir = delta * ( 1.0f / speed );
		other->Damage( this, lastdriver, speed * 8.0f, origin, dir, vec_zero, speed * 15, 0, MOD_VEHICLE );
	}
	
}

void Vehicle::VehicleBlocked( Event *ev )
{
	return;
	/*
	Entity	*other;
	float		speed;
	float    damage;
	Vector   delta;
	Vector   newvel;
	Vector	dir;
	
	if ( !velocity[0] && !velocity[1] )
		return;
	  
	other = ev->GetEntity( 1 );
	if ( other == driver )
	{
		return;
	}
	if ( other->isSubclassOf( VehicleBase ) )
	{
		delta = other->origin - origin;
		delta.normalize();
		
		newvel = vec_zero - ( velocity) + ( other->velocity * 0.25 );
		if ( newvel * delta < 0 )
		{
			velocity = newvel;
			delta = velocity - other->velocity;
			damage = delta.length()/4;
		}
		else
		{
			return;
		}
	}
	else if ( ( velocity.length() < 350 ) )
	{
		other->velocity += velocity*1.25f;
		other->velocity[ 2 ] += 100;
		damage = velocity.length()/4;
	}
	else
	{
		damage = other->health + 1000;
	}
		  
	// Gib 'em outright
	speed = fabs( velocity.length() );
	dir = velocity * ( 1 / speed );
	other->Damage( this, lastdriver, damage, origin, dir, vec_zero, speed, 0, MOD_VEHICLE, -1, -1, 1.0f );
	*/
}

Sentient *Vehicle::Driver( void )
{
	return driver;
}

qboolean Vehicle::IsDrivable( void )
{
	return drivable;
}

void Vehicle::SetSpeed( Event *ev )
{
	speed = ev->GetFloat( 1 );
}

void Vehicle::SetTurnRate( Event *ev )
{
	maxturnrate = ev->GetFloat( 1 );
}


CLASS_DECLARATION( Vehicle, DrivableVehicle, "script_drivablevehicle" )
{
	{ &EV_Damage,				   &Entity::DamageEvent },
	{ &EV_Killed,				   &DrivableVehicle::Killed },
	{ NULL, NULL }
};

DrivableVehicle::DrivableVehicle()
{
	if ( LoadingSavegame )
	{
		// Archive function will setup all necessary data
		return;
	}
	
	drivable = true;
	flags |= FL_DIE_EXPLODE;
}

void DrivableVehicle::Killed(Event *ev)
{
	Entity * ent;
	Entity * attacker;
	Vector dir;
	Event * event;
	const char * name;
	VehicleBase *last;
	
	takedamage = DAMAGE_NO;
	setSolidType( SOLID_NOT );
	hideModel();
	
	attacker		= ev->GetEntity( 1 );
	
	//
	// kill the driver
	//
	if ( driver )
	{
		Vector dir;
		SentientPtr sent;
		Event * event;
		
		velocity = vec_zero;
		sent = driver;
		event = new Event( EV_Use );
		event->AddEntity( sent );
		ProcessEvent( event );
		dir = sent->origin - origin;
		dir[ 2 ] += 64.0f;
		dir.normalize();
		sent->Damage( this, this, sent->health * 2.0f, origin, dir, vec_zero, 50, 0, MOD_VEHICLE  );
	}
	
	if (flags & FL_DIE_EXPLODE)
	{
		CreateExplosion( origin, 150.0f * edict->s.scale, this, this, this );
	}
	
	if (flags & FL_DIE_GIBS)
	{
		setSolidType( SOLID_NOT );
		hideModel();
		
		CreateGibs( this, -150.0f, edict->s.scale, 3 );
	}
	//
	// kill all my wheels
	//
	last = this;
	while( last->vlink )
	{
		last->vlink->PostEvent( EV_Remove, 0.0f );
		last = last->vlink;
	}
	
	
	//
	// kill the killtargets
	//
	name = KillTarget();
	if ( name && strcmp( name, "" ) )
	{
		ent = NULL;
		do
		{
			ent = G_FindTarget( ent, name );
			if ( !ent )
			{
				break;
			}
			ent->PostEvent( EV_Remove, 0.0f );
		}
		while ( 1 );
	}
	
	//
	// fire targets
	//
	name = Target();
	if ( name && strcmp( name, "" ) )
	{
		ent = NULL;
		do
		{
			ent = G_FindTarget( ent, name );
			if ( !ent )
			{
				break;
			}
			
			event = new Event( EV_Activate );
			event->AddEntity( attacker );
			ent->ProcessEvent( event );
		}
		while ( 1 );
	}
	
	PostEvent( EV_Remove, 0.0f );
}


//===============================================================
// Horse Vehicle Class
// New style of vehicle -- Allows the player to drive the vehicle
// but still mouselook and fire ( turret style )
// different control configurations ( ie Strafe Only ) etc can 
// be subclassed from movemode and plugged in easily
// 
//===============================================================

Event EV_HorseVehicle_SetSpeed
(
	"sethorsespeed",
	EV_SCRIPTONLY,
	"f", 
	"speed",
	"Set the speed of the horse"
);
Event EV_HorseVehicle_SetMoveMode
(
	"setmovemode",
	EV_SCRIPTONLY,
	"sF",
	"mode angles",
	"Mode ( standard , strafe, or locked ) and a Vector describing the view Angle )"
);
Event EV_HorseVehicle_SetForcedForward
(
	"forceforwardspeed",
	EV_SCRIPTONLY,
	"f",
	"speed",
	"Forces the vehicle to move forward at the specified speed"
);
Event EV_Vehicle_AnimDone
(
	"animdone",
	EV_CODEONLY,
	NULL,
	NULL,
	"animdoneevent"
);
Event EV_Driver_AnimDone
(
	"driveranimdone",
	EV_CODEONLY,
	NULL,
	NULL,
	"driveranimdoneevent"
);

CLASS_DECLARATION( Vehicle, HorseVehicle, "script_horsevehicle" )
{
	{ &EV_HorseVehicle_SetSpeed ,			&HorseVehicle::SetSpeed	},
	{ &EV_HorseVehicle_SetMoveMode ,		&HorseVehicle::SetVehicleMoveMode		},
	{ &EV_HorseVehicle_SetForcedForward,	&HorseVehicle::SetForcedForwardSpeed		},
	{ &EV_Vehicle_AnimDone,                 &HorseVehicle::AnimDone	},
	{ &EV_Driver_AnimDone,                  &HorseVehicle::DriverAnimDone	},


	//Move Mode Events
	{ &EV_FollowPath_SetWayPointName,		&HorseVehicle::PassToMoveMode		},
	{ NULL, NULL }
};


/////////////////////////////////////////////////////////
//
// Name:  HorseVehicle()
//
// Description: Constructor
// 
/////////////////////////////////////////////////////////

HorseVehicle::HorseVehicle()
{
	if ( LoadingSavegame )
	{
		// Archive function will setup all necessary data
		return;
	}
	
	drivable = true;
	jumpable = true;
	driver = NULL;
	currentspeed = 300;
	
	_baseYaw = 0;
	_driverYaw = 0;
	_lastYaw = 0;
	
	_driverPitch = 0;
	_lastPitch = 0;
	
	_forcedForwardSpeed = 0;
	_jumpflag = false;
	_jumped = false;
	_jumpSpeed = 0;
	
	_ducked = false;
	
	_minYawThreshold = 90;
	_maxYawThreshold = 90;
	
	_minPitchThreshold = 90;
	_maxPitchThreshold = 90;
	
	_moveMode = NULL;
	_SetMoveMode( "standard" );
	
	_currentCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
	_newCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
	_jumpmode = JUMPMODE_DONE;
	_duckmode = DUCKMODE_DONE;
	_animDone = false;
	_driverAnimDone = false;
	
	_ducked = false;
	_duckheld = false;
	
	_DriverBBoxMaxs   = vec_zero;
	_DriverBBoxMins   = vec_zero;
	_originalBBoxMaxs = vec_zero;
	_originalBBoxMins = vec_zero;
	
	//Create the Animate Object so that we can set the
	//animation on the vehicle
	if ( !animate )
		animate = new Animate;
	
}

HorseVehicle::~HorseVehicle()
{
	if ( _moveMode )
	{
		delete _moveMode;
		_moveMode = 0;
		
	}
}

/////////////////////////////////////////////////////////
//
// Name:  PassToMoveMode()
//
// Description: Hands off the event to the move mode for
// it to handle -- allows the vehicle to remain dumb in 
// regards to movement specific events
// 
/////////////////////////////////////////////////////////

void HorseVehicle::PassToMoveMode( Event *ev )
{
	
	if ( _moveMode )
		_moveMode->HandleEvent( ev );
	
}

/////////////////////////////////////////////////////////
//
// Name:  Drive()
//
// Description: Called from Player->ClientThink
// Player passes it a ucmd pointer, which points to the
// latest input data from the user.  Drive() uses this
// data to set up the movement impulses that help control
// how the vehicle moves
// 
/////////////////////////////////////////////////////////

qboolean HorseVehicle::Drive(	usercmd_t *ucmd )
{
	Vector i, j, k;
	
	if ( !driver || !driver->isClient() )
		return false;
	
	if ( !drivable )
	{
		driver->client->ps.pm_flags |= PMF_FROZEN;
		ucmd->forwardmove = 0;
		ucmd->rightmove = 0;
		return false;
	}
	
	//Turn off Client Prediction
	//driver->client->ps.pm_flags |= PMF_NO_PREDICTION;
	driver->client->ps.in_vehicle = true;
	driver->client->ps.vehicleoffset[0] = origin[0];
	driver->client->ps.vehicleoffset[1] = origin[1];
	driver->client->ps.vehicleoffset[2] = origin[2] + 160.0f;
	
	driver->velocity = velocity;
	
	//jumpimpulse is used to calculate a jump value
	//moveimpulse is used to calculate forward and back movement
	//turnimpulse is used to calculate strafe movement
	jumpimpulse = ( ( float )ucmd->upmove * gravity ) / 350.0f;
	moveimpulse = ucmd->forwardmove;
	turnimpulse = ucmd->rightmove;
	
	/*
	if ( ( jumpimpulse < 0 ) || ( !jumpable ) )
	jumpimpulse = 0;
	*/
	
	return true;
}

/////////////////////////////////////////////////////////
//
// Name:  Postthink()
//
// Description: Think Function ( called every frame )
// Postthink is the master function for the vehicle.
// it delegates out movement, animation, and FX to 
// subfunctions.
/////////////////////////////////////////////////////////

void HorseVehicle::Postthink ( void	)
{
	
	if ( drivable )
	{
		CalculateOrientation();
		
		_moveMode->Move( this );
		
		if (driver)
		{
			_PlayMovementSound();
			
			if ( !_jumpflag)
				_AnimateVehicle("run");			
			
			if ( !_duckflag )
				_PositionDriverModel();			
			
			if ( groundentity && ( jumpimpulse > 0 ) && ( _jumpmode == JUMPMODE_DONE ) && ( _duckmode == DUCKMODE_DONE ) )
				_InitializeJump();		
			
			if ( _jumpflag )
				_HandleJump();
			
			if ( groundentity && ( jumpimpulse < 0 ) && ( _duckmode == DUCKMODE_DONE ) && ( _jumpmode == JUMPMODE_DONE ) && !_duckflag )
				_InitializeDuck();
			
			//Released the duck key
			if ( _duckflag && ( jumpimpulse >= 0 ) )
				_ducked = false;
			
			if ( _duckflag )
				_HandleDuck();
			
		}
		
	}
	
	//Do World Effects ( for earthquakes and the like )
	WorldEffects();
	
	Vector flatvel;
	if ( _forcedForwardSpeed )
	{
		flatvel = Vector( orientation[ 0 ] );
		velocity += flatvel * _forcedForwardSpeed;
	}
	
	
	// Turn off think if we aren't being used
	if ( !driver && !velocity.length() && groundentity && !( watertype & CONTENTS_LAVA ) )
	{
		flags &= ~FL_POSTTHINK;
		if ( drivable )
			setMoveType( MOVETYPE_NONE );
	}
}

/////////////////////////////////////////////////////////
//
// Name:  DriverUse()
//
// Description: Function is called when the player "uses"
// the vehicle.  It handles putting the driver in and 
// taking the driver out of the vehicle.
//
// Some sort of use-delay mechanism must be in place or
// it will be very difficult for the player to get on and
// off the vehicle, because the use events are stepping
// on each other
// 
/////////////////////////////////////////////////////////

void HorseVehicle::DriverUse( Event *ev )
{
	
	Event *event;
	Entity *other;
	
	other = ev->GetEntity( 1 );
	if ( level.time < usetime + 1.0f )
		return;
	else
		usetime = level.time;
	
	if ( !other || !other->isSubclassOf( Sentient ) )
	{
		return;
	}
	
	if ( driver )
	{
		int height;
		int ang;
		Vector angles;
		Vector forward;
		Vector pos;
		float ofs;
		trace_t trace;
		
		if ( other != driver )
		{
			return;
		}
		
		if ( locked )
			return;
		
		//
		// place the driver on the ground
		//
		driver->detach();
		_AnimateDriver( "idle" );
		_AnimateVehicle( "idle" );
		driver->client->ps.in_vehicle = false;
		ofs = size.length() * 0.5f;
		for ( height = 0; height < 100; height += 16 )
		{
			for ( ang = 0; ang < 360; ang += 30 )
            {
				angles[ 1 ] = driver->angles[ 1 ] + ang + 90.0f;
				angles.AngleVectors( &forward, NULL, NULL );
				pos = origin + (forward * ofs);
				pos[2] += height;
				trace = G_Trace( pos, driver->mins, driver->maxs, pos, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 1" );
				if ( !trace.startsolid && !trace.allsolid )
				{
					Vector end;
					
					end = pos;
					end[ 2 ] -= 128.0f;
					trace = G_Trace( pos, driver->mins, driver->maxs, end, NULL, MASK_PLAYERSOLID, false, "Vehicle::DriverUse 2" );
					if ( trace.fraction < 1.0f )
					{
						driver->setOrigin( pos );
						goto foundpos;
					}
				}
            }
		}
		return;
		
foundpos:
		
		turnimpulse = 0;
		moveimpulse = 0;
		jumpimpulse = 0;
		
		event = new Event( EV_Vehicle_Exit );
		event->AddEntity( this );
		driver->ProcessEvent( event );
		if ( hasweapon )
		{
			driver->takeItem( weaponName.c_str() );
		}
		if ( drivable )
		{
			StopLoopSound();
			Sound( "snd_dooropen", CHAN_BODY );
			Sound( "snd_stop", CHAN_VOICE );
			driver->setSolidType( SOLID_BBOX );
		}
		
		driver = NULL;
	}
	else
	{
		driver = ( Sentient * )other;
		
		lastdriver = driver;
		
		
		if ( drivable )
			setMoveType( MOVETYPE_VEHICLE );
		
		if ( drivable )
		{
			Sound( "snd_doorclose", CHAN_BODY );
			Sound( "snd_start", CHAN_VOICE );
			driver->setSolidType( SOLID_NOT );
		}
		
		event = new Event( EV_Vehicle_Enter );
		event->AddEntity( this );
		if ( driveranim.length() )
			event->AddString( driveranim );
		driver->ProcessEvent( event );
		
		offset = other->origin - origin;
		
		flags	|= FL_POSTTHINK;
		SetDriverAngles( angles + seatangles );
		
		int tagnum = gi.Tag_NumForName( this->edict->s.modelindex, "tag_rider" );
		
		
		driver->attach(this->entnum , tagnum , false , seatoffset );
		
		if ( driver->isSubclassOf( Player ) )
		{
			Player* player;
			player = ( Player * )( Sentient * )driver;
			player->v_angle = angles;
			
			_lastYaw = player->client->cmd_angles[YAW];
			_DriverBBoxMaxs = player->maxs;
			_DriverBBoxMins = player->mins;
		}
		
		_driverYaw = angles[YAW];
		_driverPitch = angles[PITCH];		
		
		_originalBBoxMaxs = _DriverBBoxMaxs;
		_originalBBoxMins = _DriverBBoxMins;
	}
}

/////////////////////////////////////////////////////////
//
// Name:  _PlayMovementSound()
//
// Description: Plays a sound based on movement
//
/////////////////////////////////////////////////////////

void HorseVehicle::_PlayMovementSound()
{
	str sound_name;
	
	if ( currentspeed > 0.0f )
		sound_name = "snd_forward";
	else if ( currentspeed < 0.0f )
		sound_name = "snd_backward";
	else
		sound_name = "snd_idle";
	
	LoopSound( sound_name.c_str() );	
}

/////////////////////////////////////////////////////////
//
// Name:  _AnimateVehicle()
//
// Description: Sets the animation on the vehicle
//
/////////////////////////////////////////////////////////

void HorseVehicle::_AnimateVehicle(const str &anim , qboolean useEvent )
{
	if ( animate )
	{
		int anim_num = gi.Anim_Random ( this->edict->s.modelindex, anim.c_str() );
		if ( anim_num != -1 )
		{
			if ( animate->CurrentAnim() != anim_num )
			{
				animate->ClearLegsAnim();
				animate->ClearTorsoAnim();
				animate->NewAnim( anim_num );	
				
				if ( useEvent )
				{
					Event *ev = new Event( EV_Vehicle_AnimDone );
					animate->SetAnimDoneEvent( ev );
					_animDone = false;
				}
			}
			
		}
	}	
}

/////////////////////////////////////////////////////////
//
// Name:  _AnimateDriver()
//
// Description: Sets the animation on the vehicle
//
/////////////////////////////////////////////////////////

void HorseVehicle::_AnimateDriver(const str &anim , qboolean useEvent )
{
	if ( !driver )
		return;
	
	Player *player;
	player = ( Player * )( Sentient * )driver;
	
	
	if ( player->animate )
	{
		int anim_num = gi.Anim_Random ( player->edict->s.modelindex, anim.c_str() );
		if ( anim_num != -1 )
		{
			if ( player->animate->CurrentAnim() != anim_num )
			{
				player->animate->ClearLegsAnim();
				player->animate->ClearTorsoAnim();
				player->animate->NewAnim( anim_num );	
				
				if ( useEvent )
				{
					Event *ev = new Event( EV_Driver_AnimDone );
					player->animate->SetAnimDoneEvent( ev );
					_driverAnimDone = false;
				}
			}
			
		}
	}	
}


/////////////////////////////////////////////////////////
//
// Name:  _PositionDriverModel()
//
// Description: Places the driver model in the correct position
//
/////////////////////////////////////////////////////////

void HorseVehicle::_PositionDriverModel()
{
	Vector		i, j, k;
	
	i = Vector( orientation[ 0 ] );
	j = Vector( orientation[ 1 ] );
	k = Vector( orientation[ 2 ] );
	
	if ( driver )
	{
		Player *player;
		player = ( Player * )( Sentient * )driver;
		
		if ( drivable )
		{
			player->velocity = vec_zero;
			player->setAngles( angles );
			
			float delta;
			delta = AngleDelta(_lastYaw , player->client->cmd_angles[YAW]  );
			
			
			_driverYaw += ( delta * -1.0f );
			_lastYaw = player->client->cmd_angles[YAW];
			_newCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
			
			if ( _driverYaw >= ( angles[YAW] + _maxYawThreshold ) )
			{
				_driverYaw = angles[YAW] + _maxYawThreshold;
				_newCrosshairMode = CROSSHAIR_MODE_RIGHT;
				
			}
			
			if ( _driverYaw <= ( angles[YAW] - _minYawThreshold ) )
			{
				_driverYaw = angles[YAW] - _minYawThreshold;
				_newCrosshairMode = CROSSHAIR_MODE_LEFT;
			}
			
			delta = AngleDelta(_lastPitch , player->client->cmd_angles[PITCH] );
			
			_driverPitch += ( delta * -1.0f );
			_lastPitch = player->client->cmd_angles[PITCH];
			
			if ( _driverPitch > ( angles[PITCH] + _maxPitchThreshold ) )
				_driverPitch = angles[PITCH] + _maxPitchThreshold;
			
			if ( _driverPitch < ( angles[PITCH] - _minPitchThreshold ) )
				_driverPitch = angles[PITCH] - _minPitchThreshold;
			
			_SetCrossHairMode();
			player->v_angle = player->client->cmd_angles;
			player->v_angle[YAW] = _driverYaw;
			player->v_angle[PITCH] = _driverPitch;
			
			if ( !_duckflag )
				player->SetAnim( "ride" , legs );
			
		}
	}
}

/////////////////////////////////////////////////////////
//
// Name:  _SetSpeed()
//
// Description: Specifies the speed that the vehicle will move
//
/////////////////////////////////////////////////////////

void HorseVehicle::_SetSpeed ( Event *ev )
{
	currentspeed = ev->GetFloat( 1 );
}
   
/////////////////////////////////////////////////////////
//
// Name:  SetVehicleMoveMode()
//
// Description: Script Interface to set the moveMode object
//
/////////////////////////////////////////////////////////

void HorseVehicle::SetVehicleMoveMode( Event *ev )
{
	str modeName = ev->GetString( 1 );
	_SetMoveMode( modeName );
}

/////////////////////////////////////////////////////////
//
// Name:  SetForcedForwardSpeed()
//
// Description: Script interface for setting the 
//              _forcedForwardSpeed
//
/////////////////////////////////////////////////////////

void HorseVehicle::SetForcedForwardSpeed( Event *ev )
{
	_forcedForwardSpeed = ev->GetFloat( 1 );
}

/////////////////////////////////////////////////////////
//
// Name:  _HandleJump()
//
// Description: Takes care of jumping
//
/////////////////////////////////////////////////////////

void HorseVehicle::_HandleJump()
{
	
	if ( groundentity && _jumped && ( _jumpmode == JUMPMODE_DONE ) )
	{
		velocity[2] = 0;
		_jumpflag = false;		
		_jumped = false;
		_jumpSpeed = 0;
		return;
		
	}
	
	switch ( _jumpmode )
	{
	case JUMPMODE_START:
		flags |= FL_FLY;
		_jumpSpeed += 100.0f;
		if ( _jumpSpeed > 600.0f )
			_jumpSpeed = 600.0f;		
		
		velocity[2] = _jumpSpeed;
		_AnimateVehicle ( "jump_to_rise" , true );
		
		if ( _animDone )
		{
			_jumpSpeed = 0;
			_animDone = false;
			_jumpmode = JUMPMODE_HOLD;
		}
		
		break;
		
	case JUMPMODE_HOLD:
		if ( level.time < _holdtime )
		{
			_jumpSpeed += 10.0f;
			velocity[2] = _jumpSpeed;
			_AnimateVehicle("rise" , true);
		}
		else
		{
			_jumpSpeed = 0;
			_animDone = false;
			_jumpmode = JUMPMODE_LAND;		
		}
		
		break;
		
	case JUMPMODE_LAND:
		_jumpSpeed -= 100.0f;
		if ( _jumpSpeed < -600.0f )
			_jumpSpeed = -600.0f;
		
		velocity[2] = _jumpSpeed;
		_AnimateVehicle( "rise_to_land" , true );				
		
		if ( _animDone )
		{
			flags &= ~FL_FLY;
			_jumpmode = JUMPMODE_DONE;
			_AnimateVehicle( "run" );					
		}
		
		break;
		
		
	default:
		//Should never get here, but you know... It's code.
		break;
		
	}
	
	_jumped = true;
	
}

/////////////////////////////////////////////////////////
//
// Name:  _InitializeDuck()
//
// Description: Starts Jump
//
/////////////////////////////////////////////////////////
void HorseVehicle::_InitializeDuck()
{
	if ( !driver )
		return;
	
	
	_duckflag = true;
	_duckmode = DUCKMODE_START;
	_driverAnimDone = false;
	_ducked = true;
	
	//Player *player;
	//player = ( Player * )( Sentient * )driver;
	
	_DriverBBoxMaxs[2] -= 64.0f;
}

/////////////////////////////////////////////////////////
//
// Name:  _HandleDuck()
//
// Description: Starts Jump
//
/////////////////////////////////////////////////////////
void HorseVehicle::_HandleDuck()
{
	//Going to position torso Forward
	//_driverYaw = 0;
	//_lastYaw = 0;
	
	//_driverPitch = 0;
	//_lastPitch = 0;
	
	if ( !driver )
		return;
	
	Player *player;
	player = ( Player * )( Sentient * )driver;
	player->v_angle = angles;
	player->setAngles( angles );
	
	_driverYaw = angles[YAW];
	_lastYaw = player->client->cmd_angles[YAW];
	
	Vector tempMins;
	Vector tempMaxs;
	
	switch ( _duckmode )
	{
	case DUCKMODE_START:
		_AnimateDriver("ride_to_duck" , true );
		
		if ( _driverAnimDone )
			_duckmode = DUCKMODE_HOLD;		
		break;
		
	case DUCKMODE_HOLD:
		if ( _ducked )
			_AnimateDriver("ride_duck_hold" , true );
		else
			_duckmode = DUCKMODE_FINISH;
		break;
		
	case DUCKMODE_FINISH:
		_AnimateDriver("duck_to_ride" , true );
		if ( _driverAnimDone )
			_duckmode = DUCKMODE_DONE;				
		break;
		
	case DUCKMODE_DONE:	
		_AnimateDriver( "ride" );
		
		tempMaxs[2] +=64.0f;
		
		player->setSize(_DriverBBoxMaxs, _DriverBBoxMaxs );
		_DriverBBoxMaxs = _originalBBoxMaxs;
		_DriverBBoxMins = _originalBBoxMins;
		_duckflag = false;
		break;
	}
}

/////////////////////////////////////////////////////////
//
// Name:  _InitializeJump()
//
// Description: Starts Jump
//
/////////////////////////////////////////////////////////

void HorseVehicle::_InitializeJump()
{
	_jumptime = level.time + .25f;
	_holdtime = _jumptime + .15f;
	
	_jumpflag = true;
	_jumpmode = JUMPMODE_START;
	_animDone = false;
	
}

/////////////////////////////////////////////////////////
//
// Name:  _SetMoveMode()
//
// Description: _moveMode Factory
//
/////////////////////////////////////////////////////////

void HorseVehicle::_SetMoveMode( const str &modeName )
{
	if ( _moveMode )
	{
		delete _moveMode;
		_moveMode = 0;
	}
	
	if ( !Q_stricmp(modeName.c_str() , "standard" ) )
		_moveMode = new HVMoveMode_Standard;
	
	else if ( !Q_stricmp(modeName.c_str() , "strafe" ) )
		_moveMode = new HVMoveMode_Strafe;
	
	else if ( !Q_stricmp(modeName.c_str() , "locked" ) )
		_moveMode = new HVMoveMode_Locked;
	
	else if ( !Q_stricmp(modeName.c_str() , "waypoint" ) )
		_moveMode = new HVMoveMode_FollowPath;
}

void HorseVehicle::_SetCrossHairMode()
{
	if ( _newCrosshairMode == _currentCrosshairMode )
		return;
	
	switch ( _newCrosshairMode )
	{
	case CROSSHAIR_MODE_STRAIGHT:
		gi.cvar_set("cl_chright", "100" );
		gi.cvar_set("cl_chleft" , "-100" );
		_currentCrosshairMode = CROSSHAIR_MODE_STRAIGHT;
		break;
		
	case CROSSHAIR_MODE_LEFT:
		gi.cvar_set("cl_chright", "310" );
		gi.cvar_set("cl_chleft" , "-100" );
		_currentCrosshairMode = CROSSHAIR_MODE_LEFT;
		break;
		
	case CROSSHAIR_MODE_RIGHT:
		gi.cvar_set("cl_chright", "100" );
		gi.cvar_set("cl_chleft" , "-310" );
		_currentCrosshairMode = CROSSHAIR_MODE_RIGHT;
		break;
		
	default:
		return;
	}
	
}

void HorseVehicle::AnimDone( Event *ev )
{
	_animDone = true;
}

void HorseVehicle::DriverAnimDone( Event *ev )
{
	_driverAnimDone = true;
}

void HorseVehicle::HandleEvent( Event *ev )
{
	Event *new_event;
	new_event = new Event( ev );
	ProcessEvent(new_event);
}

void HorseVehicle::_SetBaseYaw()
{
	if ( !driver )
		return;
	
	
	Player *player;
	player = ( Player * )( Sentient * )driver;
	_baseYaw = player->client->cmd_angles[YAW];
	_lastYaw = _baseYaw;
	
}

//===============================================================================
//
// Vehicle Move Mode Classes:
// These classes act as movement handling strategies for vehicles.  This way
// we can specify multiple different ways a single vehicle reacts, based on
// the context of the game.
//
//===============================================================================
CLASS_DECLARATION( Listener, VehicleMoveMode, "MoveModeBaseClass" )
{
	{ NULL, NULL }
};

VehicleMoveMode::VehicleMoveMode()
{

}

void VehicleMoveMode::Move( Vehicle *vehicle )
{

}

void VehicleMoveMode::HandleEvent( Event *ev )
{

}

//==============================================================================
//
// HVMoveMode_Standard
//
// Standard Move Strategy for the Horse Vehicle.  This is the strategy that is
// instaniated by default
//
//===============================================================================
HVMoveMode_Standard::HVMoveMode_Standard()
{

}

void HVMoveMode_Standard::Move( Vehicle *base_vehicle )
{
	HorseVehicle *vehicle;
	vehicle = (HorseVehicle*)base_vehicle;
	
	Vector flatvel;
	
	//Zero Out our Velocity		
	vehicle->velocity = vec_zero;
	
	if ( vehicle->moveimpulse > 0.0f )
	{
		if ( vehicle->currentspeed < 0.0f )
			vehicle->currentspeed *= -1.0f;
		
		flatvel = Vector( vehicle->orientation[ 0 ] );
		vehicle->velocity += flatvel * vehicle->currentspeed;
	}
	
	if ( vehicle->moveimpulse < 0.0f )
	{
		if ( vehicle->currentspeed > 0.0f )
			vehicle->currentspeed *= -1.0f;
		
		flatvel = Vector( vehicle->orientation[ 0 ] );
		vehicle->velocity += flatvel * vehicle->currentspeed;
	}
	
	if ( vehicle->turnimpulse > 0.0f )
	{
		if ( vehicle->currentspeed > 0.0f )
			vehicle->currentspeed *= -1.0f;
		
		flatvel = Vector( vehicle->orientation[ 1 ] );
		vehicle->velocity += flatvel * vehicle->currentspeed;
	}
	
	if ( vehicle->turnimpulse < 0.0f )
	{
		if ( vehicle->currentspeed < 0.0f )
			vehicle->currentspeed *= -1.0f;
		
		flatvel = Vector( vehicle->orientation[ 1 ] );
		vehicle->velocity += flatvel * vehicle->currentspeed;
	}
}

//==============================================================================
//
// HVMoveMode_Strafe
//
// The only movement allowed by the horse is strafing left and right
//
//===============================================================================
HVMoveMode_Strafe::HVMoveMode_Strafe()
{
	
}

void HVMoveMode_Strafe::Move( Vehicle *base_vehicle )
{
	HorseVehicle *vehicle;
	vehicle = (HorseVehicle*)base_vehicle;
	
	Vector flatvel;
	
	//Zero Out our Velocity	
	vehicle->velocity = vec_zero;
	
	if ( vehicle->turnimpulse > 0)
	{
		if ( vehicle->currentspeed > 0 )
			vehicle->currentspeed*=-1;
		
		flatvel = Vector( vehicle->orientation[ 1 ] );
		vehicle->velocity += flatvel * vehicle->currentspeed;
	}
	
	if ( vehicle->turnimpulse < 0)
	{
		if ( vehicle->currentspeed < 0 )
			vehicle->currentspeed*=-1;
		
		flatvel = Vector( vehicle->orientation[ 1 ] );
		vehicle->velocity += flatvel * vehicle->currentspeed;
	}	
}

//==============================================================================
//
// HVMoveMode_Locked
//
// A bit strange... Allows No Movement at all
//
//===============================================================================
HVMoveMode_Locked::HVMoveMode_Locked()
{
	
}

void HVMoveMode_Locked::Move( Vehicle *base_vehicle )
{
	
}

//==============================================================================
//
// HVMoveMode_FollowPath
//
// Makes the Vehicle Follow WayPointNodes -- There is currently NO collision
// avoidance here
//
//===============================================================================
Event EV_FollowPath_SetWayPointName
(
	"waypointname",
	EV_SCRIPTONLY,
	"s",
	"waypoint_target_name",
	"Set the waypoint node name to go to first"
);

CLASS_DECLARATION( VehicleMoveMode, HVMoveMode_FollowPath, "FollowPath_MoveModeStrategy" )
{
	{ &EV_FollowPath_SetWayPointName,  &HVMoveMode_FollowPath::SetWaypointName },
	{ NULL, NULL }
};

HVMoveMode_FollowPath::HVMoveMode_FollowPath()
{
	_pathcompleted = false;
	_currentWaypoint = NULL;
}

void HVMoveMode_FollowPath::Move( Vehicle *base_vehicle )
{
	HorseVehicle *vehicle;
	vehicle = (HorseVehicle*)base_vehicle;
	
	
	//Zero Out our Velocity	
	vehicle->velocity = vec_zero;
	if ( vehicle->currentspeed < 0.0f )
		vehicle->currentspeed *= -1.0f;
	
	//First Check if we are at a way point;
	Vector dest;
	Vector check;
	
	if ( _pathcompleted )
		return;
	
	if (!_currentWaypoint)
	{
		_SetWayPoint( _currentWaypointName );
		if (!_currentWaypoint)
			return;
	}
	
	//Paranoia check, as it keeps crashing one particular box
	if ( !_currentWaypoint )
		return;
	
	dest = _currentWaypoint->origin;
	check = dest - vehicle->origin;
	
	// Take care of Z - Differences
	check[2] = 0;
	
	// Check if we're close enough
	if ( check.length() < 25.0f )
	{
		// Run Our Thread
		str waypointThread;
		waypointThread = _currentWaypoint->GetThread();
		
		if (waypointThread.length() )
			_RunThread( waypointThread );
		
		// See if we have another point to go to
		if ( _currentWaypoint->target.length() == 0 )
		{
			vehicle->velocity = vec_zero;
			_currentWaypoint = NULL;
			_pathcompleted = true;
			return;			
		}
		
		// Go To the Next Point
		_currentWaypointName = _currentWaypoint->target;
		_SetWayPoint( _currentWaypointName );		
		
		if (!_currentWaypoint)
			return;	
	}
	
	if ( _currentWaypoint )
	{
		vehicle->velocity = check;	
		vehicle->velocity.normalize();
		vehicle->velocity *= vehicle->currentspeed;
	}	
}

void HVMoveMode_FollowPath::SetWaypointName( Event *ev )
{
	_currentWaypointName = ev->GetString( 1 );
	_pathcompleted = false;
}

void HVMoveMode_FollowPath::_SetWayPoint( const str& name )
{
	Entity* ent_in_range;
	gentity_t *ed;
	
	for ( int i = 0; i < MAX_GENTITIES; i++ )
	{
		ed = &g_entities[i];
		
		if ( !ed->inuse || !ed->entity )
		{
			continue;
		}
		
		
		ent_in_range = g_entities[i].entity;
		
		if( ent_in_range->isSubclassOf( WayPointNode ) )
		{
			if (!Q_stricmp(ent_in_range->targetname.c_str() , name.c_str() ))
			{
				_currentWaypoint = (WayPointNode*)ent_in_range;
				return;
			}
		}
	}
	
	_currentWaypoint =  NULL;
}

void HVMoveMode_FollowPath::_RunThread( const str &thread_name	)
{
	if ( thread_name.length() <= 0 )
		return;
	
	CThread *thread;
	
	thread = Director.CreateThread( thread_name );
	
	if ( thread )
		thread->DelayedStart( 0.0f );
}
                            
void HVMoveMode_FollowPath::HandleEvent( Event *ev )
{
	Event *new_event;
	new_event = new Event( ev );
	ProcessEvent(new_event);
}
