/* 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 	
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 	

This is where all the vehicle / ai interactions happen

High level functions

	handle_attached_guys()// this is the setup for slots of guys on a vehicle threads notify handlers

	guy_runtovehicle( guy, vehicle )// this tells the guy to run to a vehicle and get in

	guy_enter( guy, vehicle, lastguy )// this puts the guy into the vehicle and tells him to idle
		
		guy_handle( guy, pos )// this handles the vehicles animation events( stand, attack, duck, turn, unload )
		
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 	
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 	
	
Lets say we want to add a "nose pick" event to the jeep passenger.
		
in _jeep  there is a thread called set_anims() where all sorts of animations and 
stuff are asigned to the positions a guy can ride in.

the element is the position where 0 is always the driver in the jeeps case 1 is the passenger.

in _jeep::set_anims() put add this

positions[ 1 ].nosepick = %jeep_passenger_nosepick;

	
in guy_handle() you have a bunch of pointers like this

	level.vehicle_aianimthread[ "idle" ] = ::guy_idle;
	level.vehicle_aianimthread[ "duck" ] = ::guy_duck;
	level.vehicle_aianimthread[ "stand" ] = ::guy_stand;

add to the list your pointer

	level.vehicle_aianimthread[ "nosepick" ] = ::guy_picknose;

then the event thread would looks something like this:

guy_picknose( guy, pos )
{
	animpos = anim_pos( self, pos );// first gets the animation struct information for the position of the guy.
	anim_endons( guy );// is the standard endons for these functions( vehicle dies, guy dies, new anim event happens )
	if ( IsDefined( animpos.nosepick ) )// from there you put a check for your animation
		animontag( guy, animpos.sittag, animpos.nosepick );
	thread guy_idle( guy, pos );
}	

*/ 

#include maps\_utility;
#include maps\_vehicle;
#include common_scripts\utility;

#using_animtree( "generic_human" );
CONST_anim_end_time	 = 0.25;// use the same number as _anim plz


guy_enter( guy, climbed_in_vehicle )
{
	AssertEx( !isSpawner( self ), "Tried to make guys enter a spawner" );
	// do stuff that should happen BEFORE _spawner auto spawn logic below this
	AssertEx( !isdefined( guy.ridingvehicle ), "ai can't ride two vehicles at the same time" );
	
	if ( !isdefined( self ) )
		return;
	if ( !isdefined( self.vehicletype ) )
		return;
	
	type = self.vehicletype;

	if ( IsSubStr( type, "snowmobile" ) )
		type = "snowmobile";

	vehicleanim = level.vehicle_aianims[ type ];
	maxpos = level.vehicle_aianims[ type ].size;

	self.attachedguys[ self.attachedguys.size ] = guy;

	// set the position
	pos = set_pos( guy, maxpos );

	if ( !isdefined( pos ) )
	{
		return;
	}

	if ( pos == 0 )
		guy.drivingVehicle = true;

	animpos = anim_pos( self, pos );

	self.usedPositions[ pos ] = true;
	guy.vehicle_position = pos;
	guy.vehicle_idling = false;

	if ( IsDefined( animpos.delay ) )
	{
		guy.delay = animpos.delay;
		if ( IsDefined( animpos.delayinc ) )
		{
			self.delayer = guy.delay;
		}
	}

	if ( IsDefined( animpos.delayinc ) )
	{
		self.delayer += animpos.delayinc;
		guy.delay = self.delayer;
	}

	guy.ridingvehicle = self;
	guy.orghealth = guy.health;
	guy.vehicle_idle = animpos.idle;			// multiple idle anims
	guy.vehicle_standattack = animpos.standattack;

	guy.deathanim = animpos.death;

	guy.deathanimscript = animpos.deathscript;
	guy.standing = 0;

	guy.allowdeath = false;
	
	
	if ( IsDefined( guy.deathanim ) && !isdefined( guy.magic_bullet_shield ) && vehicle_allows_rider_death() )
	{
		if( guy.vehicle_position != 0 || vehicle_allows_driver_death() )
		{
			guy.allowdeath = ( !isdefined( guy.script_allowdeath ) || guy.script_allowdeath );
			if( isdefined( animpos.death_no_ragdoll ) )
				guy.noragdoll = animpos.death_no_ragdoll;
		}
	}

	if( guy.classname == "script_model" )
		if ( IsDefined( animpos.death ) && guy.allowdeath && ( !isdefined( guy.script_allowdeath ) || guy.script_allowdeath ) )
			 thread guy_death( guy, animpos );

	if ( !isdefined( guy.vehicle_idle ) )
		guy.allowdeath = true;// these are the truck guys who are simply attached ai


	self.riders[ self.riders.size ] = guy;

//	if ( !isdefined( animpos.explosion_death ) )
//		thread guy_vehicle_death( guy );


	// do stuff that should happen AFTER _spawner auto spawn logic below this
	if ( guy.classname != "script_model" && spawn_failed( guy ) )
		return;

	org = self GetTagOrigin( animpos.sittag );
	angles = self GetTagAngles( animpos.sittag );

	link_to_sittag( guy, type, animpos.sittag );

	// some guys "holster" their weapons while operating a vehicle( flak88 guys ).
	// Some of the cod2 animations don't do anything with the weapon tag and
	// require script to remove the weapon, Ideally we would have guys who are riding
	// stash their gun to the sides( like in the jeep rider animations of cod2 )
	if ( IsAI( guy ) )
	{
		guy Teleport( org, angles );

		guy.a.disablelongdeath = true;
		if ( IsDefined( animpos.bHasGunWhileRiding ) && !animpos.bHasGunWhileRiding )
			guy gun_remove();

		if ( guy_should_man_turret( animpos ) )
 			thread guy_man_turret( guy, pos, climbed_in_vehicle );// assumes first turret is the only turret for now

		// changes death anim based on speed of the vehicles
	}
	else
	{
		if ( IsDefined( animpos.bHasGunWhileRiding ) && !animpos.bHasGunWhileRiding )
			detach_models_with_substr( guy, "weapon_" );// drones shouldn't have weapon.
		guy.origin = org;
		guy.angles = angles;
	}

	// let the vehicle know that it should crash because the driver is dead
	if ( pos == 0 && IsDefined( vehicleanim[ 0 ].death ) )
		thread driverdead( guy );

	self notify( "guy_entered", guy, pos );

	thread guy_handle( guy, pos );

	if ( type == "snowmobile" )
	{
       	self.steering = 0;
	    guy.onSnowMobile = true;
		return;
	}

	if ( IsDefined( animpos.getin_idle_func ) )
		thread [[ animpos.getin_idle_func ]]( guy, pos );
	else
		thread guy_idle( guy, pos );
}

vehicle_allows_driver_death()
{
	if( !isdefined( self.script_allow_driver_death ) )
		return false;
	return self.script_allow_driver_death ;
}

vehicle_allows_rider_death()
{
	if( !isdefined( self.script_allow_rider_deaths ) )
		return true;
	return self.script_allow_rider_deaths ;
}

guy_should_man_turret( animpos )
{
	if ( !IsDefined( animpos.mgturret ) )
		return false;
		
	if ( !IsDefined( self.script_nomg ) )
		return true;
	
	return !self.script_nomg;
}

handle_attached_guys()
{
	type = self.vehicletype;

	self.attachedguys = [];
	if ( !( IsDefined( level.vehicle_aianims ) && IsDefined( level.vehicle_aianims[ type ] ) ) )
		return;

	maxpos = level.vehicle_aianims[ type ].size;

	if ( IsDefined( self.script_noteworthy ) && self.script_noteworthy == "ai_wait_go" )
		thread ai_wait_go();

	self.runningtovehicle = [];
	self.usedPositions = [];
	self.getinorgs = [];
	self.delayer = 0;

	vehicleanim = level.vehicle_aianims[ type ];
	for ( i = 0; i < maxpos; i++ )
	{
		self.usedPositions[ i ] = false;
		if ( IsDefined( self.script_nomg ) && self.script_nomg && IsDefined( vehicleanim[ i ].bIsgunner ) && vehicleanim[ i ].bIsgunner )
			self.usedpositions[ 1 ] = true;// if this is a gunner position and script no mg is set then don't autoassign a guy to this position
	}
}

load_ai_goddriver( array )
{
	load_ai( array, true );
}

guy_death( guy, animpos )
{
	waittillframeend;// override _spawner set health
	
	assert( !IsAI( guy ) );
	guy setcandamage( true );
	guy endon( "death" );
	guy.allowdeath = false;
	guy.script_startinghealth = 100000;
	guy.health = 100000;
	guy endon( "jumping_out" );
	
	// if he's got magic bullet shield turned on, wait until it's done
	if( IsDefined( guy.magic_bullet_shield ) && guy.magic_bullet_shield )
	{
		while( IsDefined( guy.magic_bullet_shield ) && guy.magic_bullet_shield )
		{
			wait( 0.05 );
		}
	}
	
	guy waittill( "damage" );// fragile guy
	thread guy_deathimate_me( guy, animpos );
}

guy_deathimate_me( guy, animpos )
{
	animtimer = GetTime() + ( GetAnimLength( animpos.death ) * 1000 );
	angles = guy.angles;
	origin = guy.origin;
	guy = convert_guy_to_drone( guy );
	[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", origin );
	detach_models_with_substr( guy, "weapon_" );
//	guy LinkTo( self, animpos.sittag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
	guy LinkTo( self );
	guy NotSolid();
	guy setanim(  animpos.death );
//	thread animontag( guy, animpos.sittag, animpos.death );
	if( isai( guy ) )
		guy animscripts\shared::DropAllAIWeapons();
	else
		detach_models_with_substr( guy, "weapon_" );// drones shouldn't have weapon.
		
		
	if ( isdefined( animpos.death_delayed_ragdoll ) )
	{
		guy Unlink();
		guy StartRagdoll();
		wait animpos.death_delayed_ragdoll;
		guy Delete();
		return;
	}
	
//	guy Unlink();
//	if ( GetDvar( "ragdoll_enable" ) == "0" )
//	{
//		guy Delete();
//		return;
//	}

//	while ( GetTime() < animtimer && !guy IsRagdoll() )
//	{
//		guy StartRagdoll();
//		wait .05;
//	}
//	if ( !guy IsRagdoll() )
//		guy Delete();// better gone than doing random crap
}


load_ai( array, bGoddriver, group )
{
	if ( !isdefined( bGoddriver ) )
		bGoddriver = false;

	if ( !isdefined( array ) )
	{
		array = vehicle_get_riders();
	}


	ent_flag_clear( "unloaded" );
	ent_flag_clear( "loaded" );
	array_levelthread( array, ::get_in_vehicle, bGoddriver, group );
}



is_rider( guy )
{
	for ( i = 0; i < self.riders.size; i++ )
	{
		if ( self.riders[ i ] == guy )
		{
			return true;
		}
	}
	return false;
}

vehicle_get_riders()
{
	// get the AI that are assigned to this vehicle, so either were riding in it or are riding in it
	array = [];

	ai = GetAIArray( self.script_team );
	for ( i = 0; i < ai.size; i++ )
	{
		guy = ai[ i ];
		if ( !isdefined( guy.script_vehicleride ) )
			continue;

		if ( guy.script_vehicleride != self.script_vehicleride )
			continue;

		array[ array.size ] = guy;
	}

	return array;
}

get_my_vehicleride()
{
	// get the AI that are assigned to this vehicle, so either were riding in it or are riding in it
	array = [];

	AssertEx( IsDefined( self.script_vehicleride ), "Tried to get my ride but I have no .script_vehicleride" );

	vehicles = GetEntArray( "script_vehicle", "code_classname" );
	for ( i = 0; i < vehicles.size; i++ )
	{
		vehicle = vehicles[ i ];

		if ( !isdefined( vehicle.script_vehicleride ) )
			continue;

		if ( vehicle.script_vehicleride != self.script_vehicleride )
			continue;

		array[ array.size ] = vehicle;
	}

	AssertEx( array.size == 1, "Tried to get my ride but there was zero or multiple rides to choose from" );
	return array[ 0 ];
}

get_in_vehicle( guy, bGoddriver, group )
{
	if ( is_rider( guy ) )
	{
		// this guy is already riding!
		return;
	}

	if ( !handle_detached_guys_check() )
	{
		// No more spots available!
		return;
	}

	AssertEx( IsAlive( guy ), "tried to load a vehicle with dead guy, check your AI count to assure spawnability of ai's" );

	//TODO, next game: this is very similar to anim_reach but was done around the same time or before I knew such thing existed.  
	guy_runtovehicle( guy, self, bGoddriver, group );
}

handle_detached_guys_check()
{
	if ( vehicle_hasavailablespots() )
		return true;

	AssertMsg( "script sent too many ai to vehicle( max is: " + level.vehicle_aianims[ self.vehicletype ].size + " )" );
}

vehicle_hasavailablespots()
{
	// spots available - spots being run to by ai
	// simple check  This could get a lot more complicated
	if ( level.vehicle_aianims[ self.vehicletype ].size - self.runningtovehicle.size )
		return true;
	else
		return false;
}

guy_runtovehicle_loaded( guy, vehicle )
{
	vehicle endon( "death" );
	vehicle endon( "stop_loading" );

	msg = guy waittill_any_return( "long_death", "death", "enteredvehicle" );
	if ( msg != "enteredvehicle" && IsDefined( guy.forced_startingposition ) )
	{
		vehicle.usedpositions[ guy.forced_startingposition ] = false;// clear the position so someone else can take it
	}
	vehicle.runningtovehicle = array_remove( vehicle.runningtovehicle, guy );
	vehicle_loaded_if_full( vehicle );
}

vehicle_loaded_if_full( vehicle )
{
	if ( ( IsDefined( vehicle.vehicletype ) ) && ( vehicle.vehicletype == "littlebird" ) )
	{
		if ( vehicle.riders.size == 6 )
			vehicle ent_flag_set( "loaded" );
	}

	else if ( !vehicle.runningtovehicle.size && vehicle.riders.size )
	{
		if ( vehicle.usedpositions[ 0 ] )
			vehicle ent_flag_set( "loaded" );
		else
			vehicle thread vehicle_reload();// vehicle is loaded but the driver died so reload them all.  Might not look the best but what would you do if your driver was shot?
	}
}

vehicle_reload()
{
	Assert( self.riders.size );
	riders = self.riders;
	self vehicle_unload();
	self ent_flag_wait( "unloaded" );
 	riders = array_removeDead( riders );
	self thread vehicle_load_ai( riders );
}

remove_magic_bullet_shield_from_guy_on_unload_or_death( guy )
{
	// TODO: don't do this. 
	self waittill_any( "unload", "death" );
	guy stop_magic_bullet_shield();
}

guy_runtovehicle( guy, vehicle, bGoddriver, group )
{
	vehicle endon( "stop_loading" );
	climbed_in_vehicle = true;

	if ( !isdefined( bGoddriver ) )
		bGoddriver = false;
	
	vehicleanim = level.vehicle_aianims[ vehicle.vehicletype ];
	if ( IsDefined( vehicle.runtovehicleoverride ) )
	{
		vehicle thread [[ vehicle.runtovehicleoverride ]]( guy );
		return;
	}
	
	vehicle endon( "death" );
	guy endon( "death" );
	vehicle.runningtovehicle[ vehicle.runningtovehicle.size ] = guy;
	thread guy_runtovehicle_loaded( guy, vehicle );
	availablepositions = [];
	chosenorg = undefined;
	origin = 0;

	// check for get in animations and simply stuff the guy into the vehiclee if non exist
	bIsgettin = false;
	for ( i = 0; i < vehicleanim.size; i++ )
	{
		if ( IsDefined( vehicleanim[ i ].getin ) )
			bIsgettin = true;
	}

	if ( !bIsgettin )
	{
		guy notify( "enteredvehicle" );
		vehicle guy_enter( guy, climbed_in_vehicle );
		return;
	}

	if ( !isdefined( guy.get_in_moving_vehicle ) )
	{
		while ( vehicle Vehicle_GetSpeed() > 1 )
		{
			wait( 0.05 );
		}
	}

	positions = vehicle get_availablepositions( group );

	if ( IsDefined( guy.script_startingposition ) )
	{
		chosenorg = vehicle vehicle_getInstart( guy.script_startingposition );
	}
	else 
	if ( !vehicle.usedPositions[ 0 ] )
	{
		chosenorg = vehicle vehicle_getInstart( 0 );// driver first!
		if ( bGoddriver )
		{
			AssertEx( !isdefined( guy.magic_bullet_shield ), "magic_bullet_shield guy told to god mode drive a vehicle, you should simply load_ai without the god function for this guy" );
			guy thread magic_bullet_shield();
			thread remove_magic_bullet_shield_from_guy_on_unload_or_death( guy );
		}
	}
	else 
	if ( positions.availablepositions.size )
	{
		chosenorg = getClosest( guy.origin, positions.availablepositions );
	}
	else
	{
		chosenorg = undefined;
	}

	if ( !positions.availablepositions.size && positions.nonanimatedpositions.size )
	{
		guy notify( "enteredvehicle" );
		vehicle guy_enter( guy, climbed_in_vehicle );
		return;
	}
	else 
	if ( !isdefined( chosenorg ) )
	{
		return;// nothing available
	}

	origin = chosenorg.origin;// + vector_multiply( VectorNormalize( chosenorg.origin - vehicle.origin ), 15 );// move the origin out a bit sometimes the start position of the animation is just inside the colision
	angles = chosenorg.angles;

	guy.forced_startingposition = chosenorg.vehicle_position;
	// flag it so no others use it
	vehicle.usedpositions[ chosenorg.vehicle_position ] = true;


	// short circuit any _spawner auto destination behavior
	guy.script_moveoverride = true;
	guy notify( "stop_going_to_node" );

	guy set_forcegoal();
	guy disable_arrivals();
	guy.goalradius = 16;
	guy SetGoalPos( origin );
	guy waittill( "goal" );
	guy enable_arrivals();
	guy unset_forcegoal();
	guy notify( "boarding_vehicle" );
	
	
	animpos = anim_pos( vehicle, chosenorg.vehicle_position );
	if ( IsDefined( animpos.delay ) )
	{
		guy.delay = animpos.delay;
		if ( IsDefined( animpos.delayinc ) )
		{
			self.delayer = guy.delay;
		}
	}

	if ( IsDefined( animpos.delayinc ) )
	{
		self.delayer += animpos.delayinc;
		guy.delay = self.delayer;
	}

	vehicle link_to_sittag( guy, vehicle.vehicletype, animpos.sittag );
	
	
	
	
	


	guy.allowdeath = false;// they will get the allowdeath back when they get out or get it turned on if there is a death animation.

	animpos = vehicleanim[ chosenorg.vehicle_position ];
	if ( IsDefined( chosenorg ) )
	{
		if ( IsDefined( animpos.vehicle_getinanim ) )
		{
			if ( IsDefined( animpos.vehicle_getoutanim ) )
			{
				vehicle ClearAnim( animpos.vehicle_getoutanim, 0 );
			}
			vehicle = vehicle getanimatemodel();
			vehicle thread setanimrestart_once( animpos.vehicle_getinanim, animpos.vehicle_getinanim_clear );
			//thread maps\_anim::animscriptDoNoteTracksThread( vehicle, "vehicle_anim_flag" );
			level thread maps\_anim::start_notetrack_wait( vehicle, "vehicle_anim_flag" );
		}

		if ( IsDefined( animpos.vehicle_getinsoundtag ) )
			origin = 	vehicle GetTagOrigin( animpos.vehicle_getinsoundtag );
		else
			origin = vehicle.origin;
		if ( IsDefined( animpos.vehicle_getinsound ) )
			thread play_sound_in_space( animpos.vehicle_getinsound, origin );

//		if ( IsDefined( animpos.vehicle_getinsound ) )
//		{
//			animatemodel = vehicle getanimatemodel();
//			animatemodel thread play_sound_on_entity( animpos.vehicle_getinsound );
//		}

		getintags = undefined;
		getinthreads = undefined;
		if ( IsDefined( animpos.getin_enteredvehicletrack ) )
		{
			getintags = [];
			getintags[ 0 ] = animpos.getin_enteredvehicletrack;
			getinthreads = [];
			getinthreads[ 0 ] = ::entered_vehicle_notify;
			vehicle link_to_sittag( guy, vehicle.vehicletype, animpos.sittag );// link them, normally they don't link for whatever reason I don't want tof ind otu.
		}

		vehicle animontag( guy, animpos.sittag, animpos.getin, getintags, getinthreads );
	}
	guy notify( "enteredvehicle" );
	vehicle guy_enter( guy, climbed_in_vehicle );
}

entered_vehicle_notify()
{
	self notify( "enteredvehicle" );
}

driverdead( guy )
{
	if ( maps\_vehicle::isHelicopter() )
		return;

	self.driver = guy;
	self endon( "death" );
	guy waittill( "death" );

	if ( isdefined( self.vehicle_keeps_going_after_driver_dies ) )
		return;

	self notify( "driver dead" );
	self.deaddriver = true;// vehiclechase crash
	self SetWaitSpeed( 0 );
	self Vehicle_SetSpeed( 0, 10 );// nothin fancy here.
	self waittill( "reached_wait_speed" );
	self vehicle_unload();
}

copy_cat()
{
	model = Spawn( "script_model", self.origin );
	model SetModel( self.model );
	size = self GetAttachSize();
	for ( i = 0; i < size; i++ )
		model Attach( self GetAttachModelName( i ) );
	return model;
}

guy_becomes_real_ai( guy, pos )
{
	if ( IsAI( guy ) )
		return guy;

	if ( guy.drone_delete_on_unload == true )
	{
		guy Delete();
		return;
	}

	guy = makerealai( guy );

	type = self.vehicletype;
	maxpos = level.vehicle_aianims[ type ].size;
	animpos = anim_pos( self, pos );
	link_to_sittag( guy, type, animpos.sittag );
	guy.vehicle_idle = animpos.idle;
	thread guy_idle( guy, pos );
	return guy;
}

link_to_sittag( guy, type, tag )
{
	if ( type == "snowmobile" )
	    guy LinkToBlendToTag( self, tag, false );
	else
	    guy _linkto( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
}

anim_pos( vehicle, pos )
{
	return level.vehicle_aianims[ vehicle.vehicletype ][ pos ];
}

guy_deathhandle( guy, pos )
{
// 	self endon( "death" );
	guy waittill( "death" );
	if ( !isdefined( self ) )
		return;
	self.riders = array_remove( self.riders, guy );
	self.usedPositions[ pos ] = false;
}

setup_aianimthreads()
{
	if ( !isdefined( level.vehicle_aianimthread ) )
		level.vehicle_aianimthread = [];

	if ( !isdefined( level.vehicle_aianimcheck ) )
		level.vehicle_aianimcheck = [];

	level.vehicle_aianimthread[ "idle" ] = ::guy_idle;
	level.vehicle_aianimthread[ "duck" ] = ::guy_duck;

	level.vehicle_aianimthread[ "duck_once" ] = ::guy_duck_once;
	level.vehicle_aianimcheck[ "duck_once" ] = ::guy_duck_once_check;

	level.vehicle_aianimthread[ "weave" ] = ::guy_weave;
	level.vehicle_aianimcheck[ "weave" ] = ::guy_weave_check;

	level.vehicle_aianimthread[ "stand" ] = ::guy_stand;

	level.vehicle_aianimthread[ "turn_right" ] = ::guy_turn_right;
	level.vehicle_aianimcheck[ "turn_right" ] = ::guy_turn_right_check;

	level.vehicle_aianimthread[ "turn_left" ] = ::guy_turn_left;
	level.vehicle_aianimcheck[ "turn_left" ] = ::guy_turn_right_check;

	level.vehicle_aianimthread[ "turn_hardright" ] = ::guy_turn_hardright;

	level.vehicle_aianimthread[ "turn_hardleft" ] = ::guy_turn_hardleft;
	level.vehicle_aianimthread[ "turret_fire" ] = ::guy_turret_fire;
	level.vehicle_aianimthread[ "turret_turnleft" ] = ::guy_turret_turnleft;
	level.vehicle_aianimthread[ "turret_turnright" ] = ::guy_turret_turnright;
	level.vehicle_aianimthread[ "unload" ] = ::guy_unload;

	level.vehicle_aianimthread[ "pre_unload" ] = ::guy_pre_unload;
	level.vehicle_aianimcheck[ "pre_unload" ] = ::guy_pre_unload_check;

	level.vehicle_aianimthread[ "idle_alert" ] = ::guy_idle_alert;
	level.vehicle_aianimcheck[ "idle_alert" ] = ::guy_idle_alert_check;

	level.vehicle_aianimthread[ "idle_alert_to_casual" ] = ::guy_idle_alert_to_casual;
	level.vehicle_aianimcheck[ "idle_alert_to_casual" ] = ::guy_idle_alert_to_casual_check;

	level.vehicle_aianimthread[ "reaction" ] = ::guy_turret_turnright;
}

guy_handle( guy, pos )
{
	guy.vehicle_idling = true;
	thread guy_deathhandle( guy, pos );
}

// old kubelwagons..
guy_stand( guy, pos )
{
	animpos = anim_pos( self, pos );
	vehicleanim = level.vehicle_aianims[ self.vehicletype ];
	if ( !isdefined( animpos.standup ) )
		return;
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animontag( guy, animpos.sittag, animpos.standup );
	guy_stand_attack( guy, pos );
}

guy_stand_attack( guy, pos )
{
	animpos = anim_pos( self, pos );
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	guy.standing = 1;
	mintime = 0;
	while ( 1 )
	{
		timer2 = GetTime() + 2000;
		while ( GetTime() < timer2 && IsDefined( guy.enemy ) )
			animontag( guy, animpos.sittag, guy.vehicle_standattack, undefined, undefined, "firing" );
		rnum = RandomInt( 5 ) + 10;
		for ( i = 0; i < rnum; i++ )
			animontag( guy, animpos.sittag, animpos.standidle );
	}
}

guy_stand_down( guy, pos )
{
	animpos = anim_pos( self, pos );
	if ( !isdefined( animpos.standdown ) )
	{
		thread guy_stand_attack( guy, pos );
		return;
	}
	animontag( guy, animpos.sittag, animpos.standdown );
	guy.standing = 0;
	thread guy_idle( guy, pos );
}

driver_idle_speed( driver, pos )
{
	driver endon( "newanim" );
	self endon( "death" );
	driver endon( "death" );

	animpos = anim_pos( self, pos );
	while ( 1 )
	{
		if ( self Vehicle_GetSpeed() == 0 )
			driver.vehicle_idle = animpos.idle_animstop;
		else
			driver.vehicle_idle = animpos.idle_anim;
		wait .25;
	}
}

guy_reaction( guy, pos )
{
	animpos = anim_pos( self, pos );
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	if ( IsDefined( animpos.reaction ) )
		animontag( guy, animpos.sittag, animpos.reaction );
	thread guy_idle( guy, pos );
}

guy_turret_turnleft( guy, pos )
{
	animpos = anim_pos( self, pos );
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	while ( 1 )
		animontag( guy, animpos.sittag, guy.turret_turnleft );
}

guy_turret_turnright( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animpos = anim_pos( self, pos );
	while ( 1 )
		animontag( guy, animpos.sittag, guy.turret_turnleft );
}

guy_turret_fire( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.turret_fire ) )
		animontag( guy, animpos.sittag, animpos.turret_fire );
	thread guy_idle( guy, pos );
}

guy_idle( guy, pos, ignoredeath )
{
	guy endon( "newanim" );
	if ( !isdefined( ignoredeath ) )
		self endon( "death" );
	guy endon( "death" );
	guy.vehicle_idling = true;
	guy notify( "gotime" );

	if ( !isdefined( guy.vehicle_idle ) )
	{
//		if ( IsDefined( level.whackamolethread ) )
//			thread [[ level.whackamolethread ]]( guy );
		return;// truck guys just stand there linked.. hack for Halftrack guys
	}

	animpos = anim_pos( self, pos );

	if ( IsDefined( animpos.mgturret ) )
		return;// mggunners don't idle.
	if ( IsDefined( animpos.hideidle ) && animpos.hideidle )
		guy Hide();
	if ( IsDefined( animpos.idle_animstop ) && IsDefined( animpos.idle_anim ) )// idle alternates between stopping and going
		thread driver_idle_speed( guy, pos );

	while ( 1 )
	{
		guy notify( "idle" );
		self play_new_idle( guy, animpos );
	}
}

play_new_idle( guy, animpos )
{
	// self is the vehicle
		if ( IsDefined( guy.vehicle_idle_override ) )
	{
		self animontag( guy, animpos.sittag, guy.vehicle_idle_override );
		return;
	}

	if ( IsDefined( animpos.idleoccurrence ) )// kubelwagons have random idles like guy driver pointing forward
	{
		theanim = randomoccurrance( guy, animpos.idleoccurrence );
		self animontag( guy, animpos.sittag, guy.vehicle_idle[ theanim ] );
		return;
	}

	if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_idle ) )
	{
		self animontag( guy, animpos.sittag, animpos.player_idle );
		return;
	}

	// animate the vehicle with this guy.( IE: driver with stearing wheel )
	if ( IsDefined( animpos.vehicle_idle ) )
		self thread setanimrestart_once( animpos.vehicle_idle );
	self animontag( guy, animpos.sittag, guy.vehicle_idle );
}

randomoccurrance( guy, occurrences )
{
	range = [];
	totaloccurrance = 0;
	for ( i = 0; i < occurrences.size; i++ )
	{
		totaloccurrance += occurrences[ i ];
		range[ i ] = totaloccurrance;
	}
	pick = RandomInt( totaloccurrance );
	for ( i = 0; i < occurrences.size; i++ )
		if ( pick < range[ i ] )
			return i;
}


guy_duck_once_check( guy, pos )
{
	return IsDefined( 	anim_pos( self, pos ).duck_once );
}

guy_duck_once( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );
	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.duck_once ) )
	{
		if ( IsDefined( animpos.vehicle_duck_once ) )
			self thread setanimrestart_once( animpos.vehicle_duck_once );
		animontag( guy, animpos.sittag, animpos.duck_once );
	}
	thread guy_idle( guy, pos );
}

guy_weave_check( guy, pos )
{
	return IsDefined( 	anim_pos( self, pos ).weave );
}

guy_weave( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );
	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.weave ) )
	{
		if ( IsDefined( animpos.vehicle_weave ) )
			self thread setanimrestart_once( animpos.vehicle_weave );
		animontag( guy, animpos.sittag, animpos.weave );
	}
	thread guy_idle( guy, pos );
}

guy_duck( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.duckin ) )
		animontag( guy, animpos.sittag, animpos.duckin );
	thread guy_duck_idle( guy, pos );
}

guy_duck_idle( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animpos = anim_pos( self, pos );
	theanim = randomoccurrance( guy, animpos.duckidleoccurrence );
	while ( 1 )
		animontag( guy, animpos.sittag, animpos.duckidle[ theanim ] );
}

guy_duck_out( guy, pos )
{
	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.ducking ) && guy.ducking )
	{
		animontag( guy, animpos.sittag, animpos.duckout );
		guy.ducking = false;
	}
	thread guy_idle( guy, pos );
}

guy_unload_que( guy )
{
	self endon( "death" );
	self.unloadque = array_add( self.unloadque, guy );
	guy waittill_any( "death", "jumpedout" );
	self.unloadque = array_remove( self.unloadque, guy );
	if ( !self.unloadque.size )
	{
		self ent_flag_set( "unloaded" );
		self.unload_group = "default";
	}
}

riders_unloadable( unload_group )
{
	if ( ! self.riders.size )
		return false;
	for ( i = 0; i < self.riders.size; i++ )
	{	
		if( !isalive( self.riders[ i ] ) )
		{
			continue;
		}
		Assert( IsDefined( self.riders[ i ].vehicle_position ) );
		if ( check_unloadgroup( self.riders[ i ].vehicle_position, unload_group ) )
			return true;
	}
	return false;
}


get_unload_group()
{
	// make the unload group into a more useful array
	group = [];
	unloadgroups = [];
	if ( IsDefined( self.unload_group ) )
	{
		unloadgroups = level.vehicle_unloadgroups[ self.vehicletype ][ self.unload_group ];
	}

	foreach ( pos in unloadgroups )
	{
		group[ pos ] = pos;
	}

	return group;
}

check_unloadgroup( pos, unload_group )
{
	if ( !IsDefined( unload_group ) )
		unload_group = self.unload_group;

	type = self.vehicletype;
	if ( !isdefined( level.vehicle_unloadgroups[ type ] ) )
		return true;// just unloads everybody

	if ( !isdefined( level.vehicle_unloadgroups[ type ][ unload_group ] ) )
	{
		PrintLn( "Invalid Unload group on node at origin: " + self.currentnode.origin + " with group:( \"" + unload_group + "\" )" );
		PrintLn( "Unloading everybody" );
		return true;
	}

	group = level.vehicle_unloadgroups[ type ][ unload_group ];
	for ( i = 0; i < group.size; i++ )
	{
		if ( pos == group[ i ] )
			return true;
	}
	return false;
}


getoutrig_model_idle( model, tag, animation )
{
	self endon( "unloading" );
	while ( 1 )
		animontag( model, tag, animation );
}

getoutrig_model( animpos, model, tag, animation, bIdletillunload )
{

	type = self.vehicletype;
	if ( bIdletillunload )
	{
		thread getoutrig_model_idle( model, tag, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].idleanim );
		self waittill( "unloading" );
	}

	self.unloadque = array_add( self.unloadque, model );

	self thread getoutrig_abort( model, tag, animation );
	if ( !isdefined( self.crashing ) )
		animontag( model, tag, animation );

	model Unlink();

	// looks like somebody deleted the helicopter while that animation was playing.  and errored so I'm throwing in this defensive fix!.
	if ( !isdefined( self ) )
	{
		model Delete();
		return;
	}

	Assert( IsDefined( self.unloadque ) );

	self.unloadque = array_remove( self.unloadque, model );
	if ( !self.unloadque.size )
		self notify( "unloaded" );
	self.fastroperig[ animpos.fastroperig ] = undefined;
	wait 10;
	model Delete();// possibly do something to delete when the player is not looking at it.
}

getoutrig_disable_abort_notify_after_riders_out()
{
	wait .05;
	while ( IsAlive( self ) && self.unloadque.size > 2 )
		wait .05;// 1 unloadque will be there for the rope.
	if ( ! IsAlive( self ) || ( IsDefined( self.crashing ) && self.crashing ) )
		return;
	self notify( "getoutrig_disable_abort" );
}


getoutrig_abort_while_deploying()
{
	self endon( "end_getoutrig_abort_while_deploying" );
	while ( !isdefined( self.crashing ) )
		wait 0.05;
		
	updatedRiders = [];
		
	foreach( rider in self.riders )
	{
		if( isAlive( rider ) )	
		{
			add_to_array( updatedRiders , rider );
		}
	}
	
	array_levelthread( updatedRiders, ::deleteent );
	self notify( "crashed_while_deploying" );
	
	updatedRiders = undefined;
}


getoutrig_abort( model, tag, animation )
{

	totalAnimTime = GetAnimLength( animation );
	ropesFallAnimTime = totalAnimTime - 1.0;
	if ( self.vehicletype == "mi17" )
		ropesFallAnimTime = totalAnimTime - .5;// go go ghetto numbers

	ropesDeployedAnimTime = 2.5;

	Assert( totalAnimTime > ropesDeployedAnimTime );
	Assert( ropesFallAnimTime - ropesDeployedAnimTime > 0 );

	self endon( "getoutrig_disable_abort" );

	thread getoutrig_disable_abort_notify_after_riders_out();
//	self thread notify_delay( "getoutrig_disable_abort", ropesFallAnimTime - ropesDeployedAnimTime );

	thread 	getoutrig_abort_while_deploying();

	waittill_notify_or_timeout( "crashed_while_deploying", ropesDeployedAnimTime );

	self notify( "end_getoutrig_abort_while_deploying" );

	// ropes are deployed, wait for a chopper death if it isn't dead already
	while ( !isdefined( self.crashing ) )
		wait 0.05;

	// make the rope fall by jumping to the end of it's animation where it falls
	thread animontag( model, tag, animation );
	waittillframeend;
	model SetAnimTime( animation, ropesFallAnimTime / totalAnimTime );

	attacker = self;
	if( isdefined( self.achievement_attacker ) )
		attacker = self.achievement_attacker;
	
	// all the guys on the rope must fall off too
	for ( i = 0; i < self.riders.size; i++ )
	{
		if ( !isdefined( self.riders[ i ] ) )
			continue;
		if ( !isdefined( self.riders[ i ].ragdoll_getout_death ) )
			continue;
		if ( self.riders[ i ].ragdoll_getout_death != 1 )
			continue;
		if ( !isdefined( self.riders[ i ].ridingvehicle ) )
			continue;
		// thread animontag_ragdoll_death( self.riders[ i ] );
		self.riders[ i ].forcefallthroughonropes = 1; // I found a case where the "damage" was registering on the
//		self.riders[ i ] DoDamage( 100, self.riders[ i ] geteye(), self.riders[ i ].ridingvehicle );
		if( isalive( self.riders[ i ] ) )
			thread animontag_ragdoll_death_fall( self.riders[ i ], self, attacker );
//		self.riders[ i ] notify( "damage", 100, self.riders[ i ].ridingvehicle );
	}
}

setanimrestart_once( vehicle_anim, bClearAnim )
{
	self endon( "death" );
	self endon( "dont_clear_anim" );
	
	if ( !isdefined( bClearAnim ) )
	{
		bClearAnim = true;
	}
		
	cycletime = GetAnimLength( vehicle_anim );
	self SetFlaggedAnimRestart( "vehicle_anim_flag", vehicle_anim );
	wait( cycletime );
	
	if ( bClearAnim )
	{
		self ClearAnim( vehicle_anim, 0 );
	}
}


getout_rigspawn( animatemodel, pos, bIdletillunload )
{
	if ( !isdefined( bIdletillunload ) )
		bIdletillunload = true;
	type = self.vehicletype;
	animpos = anim_pos( self, pos );

	if ( IsDefined( self.attach_model_override ) && IsDefined( self.attach_model_override[ animpos.fastroperig ] ) )
		overrridegetoutrig = true;
	else
		overrridegetoutrig = false;
	if ( !isdefined( animpos.fastroperig ) || IsDefined( self.fastroperig[ animpos.fastroperig ] ) || overrridegetoutrig )
		return;// already one in place
	origin =  animatemodel GetTagOrigin( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag );
	angles =  animatemodel GetTagAngles( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag );

	self.fastroperiganimating[ animpos.fastroperig ] = true;

	getoutrig_model = Spawn( "script_model", origin );
	getoutrig_model.angles = angles;
	getoutrig_model.origin = origin;
	getoutrig_model SetModel( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].model );

	self.fastroperig[ animpos.fastroperig ] = getoutrig_model;// flag this model as out

	getoutrig_model UseAnimTree( #animtree );
// 			getoutrig_model UseAnimTree( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].animtree );

	getoutrig_model LinkTo( animatemodel, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
	thread getoutrig_model( animpos, getoutrig_model, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].dropanim, bIdletillunload );
	return getoutrig_model;
}

check_sound_tag_dupe( soundtag )
{
	// long day. this is probably 10 times more complicated than it needs to be.

	if ( !isdefined( self.sound_tag_dupe ) )
		self.sound_tag_dupe = [];

	duped = false;

	if ( !isdefined( self.sound_tag_dupe[ soundtag ] ) )
		self.sound_tag_dupe[ soundtag ] = true;
	else
		duped = true;

	thread check_sound_tag_dupe_reset( soundtag );

	return duped;
}

check_sound_tag_dupe_reset( soundtag )
{
	wait .05;
	if ( ! IsDefined( self ) )
		return;
	self.sound_tag_dupe[ soundtag ] = false;

	keys = GetArrayKeys( self.sound_tag_dupe );

	for ( i = 0; i < keys.size; i++ )
		if ( self.sound_tag_dupe[ keys[ i ] ] )
			return;

	self.sound_tag_dupe = undefined;

}



guy_unload( guy, pos )
{
	animpos = anim_pos( self, pos );
	type = self.vehicletype;
	// check to see if this guy is in the unload group if not then go to idle and ignore the unload call
	if ( !check_unloadgroup( pos ) )
	{
		 thread guy_idle( guy, pos );
		 return;
	}
	// no getout for this guy.
	if ( !isdefined( animpos.getout ) )
	{
		thread guy_idle( guy, pos );
		return;
	}

	if ( IsDefined( animpos.hideidle ) && animpos.hideidle )
		guy Show();// bleh. hacking out nonexitant idle animations on seaknight

	thread guy_unload_que( guy );

	self endon( "death" );
	if ( IsAI( guy ) && IsAlive( guy ) )
		guy endon( "death" );


	if ( IsDefined( guy.onSnowMobile ) )
	{
		guy gun_recall();
		guy.onSnowMobile = undefined;
		if ( IsDefined( guy.getOffVehicleFunc ) )
			guy [[ guy.getOffVehicleFunc ]]();
	}

	if ( IsDefined( guy.onRotatingVehicleTurret ) )
	{
		guy.onRotatingVehicleTurret = undefined;
		if ( IsDefined( guy.getOffVehicleFunc ) )
		{
			guy [[ guy.getOffVehicleFunc ]]();
		}
	}

	animatemodel = getanimatemodel();

	if ( IsDefined( animpos.vehicle_getoutanim ) )
	{
		animatemodel thread setanimrestart_once( animpos.vehicle_getoutanim, animpos.vehicle_getoutanim_clear );
		sound_tag_dupped = false;
		if ( IsDefined( animpos.vehicle_getoutsoundtag ) )
		{
			sound_tag_dupped = check_sound_tag_dupe( animpos.vehicle_getoutsoundtag );
			origin = 	animatemodel GetTagOrigin( animpos.vehicle_getoutsoundtag );
		}
		else
			origin = animatemodel.origin;

		if ( IsDefined( animpos.vehicle_getoutsound ) && ! sound_tag_dupped )
			thread play_sound_in_space( animpos.vehicle_getoutsound, origin );

		sound_tag_dupped = undefined;
	}

	delay = 0;

	if ( IsDefined( animpos.getout_timed_anim ) )
		delay += GetAnimLength( animpos.getout_timed_anim );
	if ( IsDefined( animpos.delay ) )
		delay += animpos.delay;
	if ( IsDefined( guy.delay ) )
		delay += guy.delay;
	if ( delay > 0 )
	{
		thread guy_idle( guy, pos );
		wait delay;
	}

	// handle those guys who are standing when a vehicle unloads
	hascombatjumpout = IsDefined( animpos.getout_combat );
	if ( !hascombatjumpout && guy.standing )
		guy_stand_down( guy, pos );
	else if ( !hascombatjumpout && !guy.vehicle_idling && IsDefined( guy.vehicle_idle ) )
		guy waittill( "idle" );

	guy.deathanim = undefined;
	guy.deathanimscript = undefined;

	guy notify( "newanim" );
	
	if ( IsDefined( animpos.bHasGunWhileRiding ) && !animpos.bHasGunWhileRiding )
		guy gun_recall();
	

	if ( IsAI( guy ) )
		guy PushPlayer( true );
	// some vehicles don't require an unload animation like the flak88 where all the guys are animating on the ground
	// some guys don't unload at all and stick to the vehicle till death!

	bNoanimUnload = false;
	if ( IsDefined( animpos.bNoanimUnload ) )
		bNoanimUnload = true;
	else if ( 	!isdefined( animpos.getout ) ||
				( !isdefined( self.script_unloadmgguy ) && ( IsDefined( animpos.bIsgunner ) && animpos.bIsgunner ) ) ||
				IsDefined( self.script_keepdriver ) && pos == 0 )
	{
		self thread guy_idle( guy, pos );
		return;
	}

	if ( guy should_give_orghealth() )
	{
		guy.health = guy.orghealth;
	}

	guy.orghealth = undefined;
	if ( IsAI( guy ) && IsAlive( guy ) )
		guy endon( "death" );
	guy.allowdeath = false;// nobody should die during the transition

	// some exits all happen at a special tag the halftrack guys all use the same tag to exit but a different tag to sit at.
	if ( IsDefined( animpos.exittag ) )
		tag = animpos.exittag;
	else
		tag = animpos.sittag;

	if ( hascombatjumpout && guy.standing )
		animation = animpos.getout_combat;
	else if ( IsDefined( guy.get_out_override ) )
		animation = guy.get_out_override;
	else if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout ) )
		animation = animpos.player_getout;
	else
		animation = animpos.getout;


	if ( !bNoanimUnload )
	{
		thread guy_unlink_on_death( guy );

		// throw out the rope before unloading
		if ( IsDefined( animpos.fastroperig ) )
		{
			if ( ! IsDefined( self.fastroperig[ animpos.fastroperig ] ) )
			{
				thread guy_idle( guy, pos );// idle while rope is deploying
				getoutrig_model = self getout_rigspawn( animatemodel, guy.vehicle_position, false );
				// animontag( getoutrig_model, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].idleanim );
			}
		}

		if ( IsDefined( animpos.getoutsnd ) )
			guy thread play_sound_on_tag( animpos.getoutsnd, "J_Wrist_RI", true );

		if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound ) )
			guy thread play_sound_on_entity( animpos.player_getout_sound );

		if ( IsDefined( animpos.getoutloopsnd ) )
			guy thread play_loop_sound_on_tag( animpos.getoutloopsnd );

		if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound_loop ) )
			level.player thread play_loop_sound_on_entity( animpos.player_getout_sound_loop );

		guy notify( "newanim" );
		guy notify( "jumping_out" );

		// testing, default to this while unloading. should fix drones that die on an exploding vehicle.
		

		add_new_spawned_ai = false;
		if( !IsAI( guy ) )
			add_new_spawned_ai = true;
			
		guy = guy_becomes_real_ai( guy, pos );
		if ( !isalive( guy ) )
			return;
		
		guy.ragdoll_getout_death = true;
		
		if ( isdefined( animpos.rappel_kill_achievement ) )
			guy enable_achievement_harder_they_fall();
			
		if ( IsDefined( animpos.ragdoll_getout_death ) )
		{
			guy.ragdoll_getout_death = true;
			if ( IsDefined( animpos.ragdoll_fall_anim ) )	
				guy.ragdoll_fall_anim = animpos.ragdoll_fall_anim;
		}
		
		if( add_new_spawned_ai ) 
		{
			// need to re-add the drone guy that became a real ai earlier.
			self.riders = array_add( self.riders , guy);  
			thread guy_deathhandle( guy, pos );
			thread guy_unload_que( guy );
			guy.ridingvehicle = self; 

		}

		if ( IsAI( guy ) )
			guy endon( "death" );

		// notify these again because it's a different entity now and this will kill its new idle.
		guy notify( "newanim" );
		guy notify( "jumping_out" );

		if ( IsDefined( animpos.littlebirde_getout_unlinks ) && animpos.littlebirde_getout_unlinks )
		{
			self thread stable_unlink( guy );
		}

		//this is for a secondary bm21 exit animation
		if ( IsDefined( animpos.getout_secondary ) )
		{
			animontag( guy, tag, animation );
			secondaryunloadtag = tag;
			if ( IsDefined( animpos.getout_secondary_tag ) )
				secondaryunloadtag = animpos.getout_secondary_tag;
			animontag( guy, secondaryunloadtag, animpos.getout_secondary );
		}
		else
		{
			guy.anim_end_early = true;// cut off the anim .25 early so it blends nicely into AI.
			animontag( guy, tag, animation );
		}

		// end all the loop sounds
		if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound_loop ) )
			level.player thread stop_loop_sound_on_entity( animpos.player_getout_sound_loop );

		if ( IsDefined( animpos.getoutloopsnd ) )
			guy thread stop_loop_sound_on_entity( animpos.getoutloopsnd );



		if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound_end ) )
			level.player thread play_sound_on_entity( animpos.player_getout_sound_end );
	}
	else
	{
		if ( !isai( guy ) )
		{
			if ( guy.drone_delete_on_unload == true )
			{
				guy Delete();
				return;
			}

			guy = makerealai( guy );
		}

	}

	self.riders = array_remove( self.riders, guy );
	self.usedPositions[ pos ] = false;
	
	guy.ridingvehicle = undefined;
	guy.drivingVehicle = undefined;

	if ( !isalive( self ) && !isdefined( animpos.unload_ondeath ) )
	{
		guy Delete();
		return;
	}

	guy Unlink();
	if ( !isdefined( guy.magic_bullet_shield ) )
		guy.allowdeath = true;// nobody should die during the transition


	if ( IsAlive( guy ) )
	{
		guy.a.disablelongdeath = !( guy IsBadGuy() );
		guy.forced_startingposition = undefined;
		guy notify( "jumpedout" );
		guy disable_achievement_harder_they_fall();
//		guy Unlink();

		if ( IsDefined( animpos.getoutstance ) )
		{
			guy.desired_anim_pose = animpos.getoutstance;
			guy AllowedStances( "crouch" );
			guy thread animscripts\utility::UpdateAnimPose();
			guy AllowedStances( "stand", "crouch", "prone" );
		}

		guy PushPlayer( false );

		// if he doesn't target a node make his new goal position his current position
		if ( guy_resets_goalpos( guy ) )
		{
			guy.goalradius = 600;
			guy SetGoalPos( guy.origin );
		}
	}

	if ( IsDefined( animpos.getout_delete ) && animpos.getout_delete )
	{
		guy Delete();
		return;
	}

	guy guy_cleanup_vehiclevars();
}

guy_resets_goalpos( guy )
{
	if ( IsDefined( guy.script_delayed_playerseek )  )
		return false;
		 
	if ( guy has_color() )
		return false;
	
	if ( IsDefined( guy.qSetGoalPos ) )
		return false;
		
	if ( !isdefined( guy.target ) )
		return true;
	
	// does the guy target nodes?
	targetedNodes = GetNodeArray( guy.target, "targetname" );
	return !targetedNodes.size;
}


animontag( guy, tag, animation, notetracks, sthreads, flag )
{
	guy notify( "animontag_thread" );
	guy endon( "animontag_thread" );

	if ( !isdefined( flag ) )
		flag = "animontagdone";

	if ( IsDefined( self.modeldummy ) )
		animatemodel = self.modeldummy;
	else
		animatemodel = self;

	if ( !isdefined( tag ) )
	{
		org = guy.origin;
		angles = guy.angles;
	}
	else
	{
		org = animatemodel GetTagOrigin( tag );
		angles = animatemodel GetTagAngles( tag );
	}

	if ( IsDefined( guy.ragdoll_getout_death ) )
		level thread animontag_ragdoll_death( guy, self );

	guy AnimScripted( flag, org, angles, animation );

	// todo: make doNotetracks work on ai
	if ( IsAI( guy ) )
		thread DoNoteTracks( guy, animatemodel, flag );

	if ( IsDefined( guy.anim_end_early ) )
	{
		guy.anim_end_early = undefined;
		animWait = GetAnimLength( animation ) - CONST_anim_end_time;
		if ( animWait > 0 )
			wait( animWait );

		guy StopAnimScripted();
		guy.interval = 0;
		guy thread recover_interval();
	}
	else
	{
		if ( IsDefined( notetracks ) )
		{
			for ( i = 0; i < notetracks.size; i++ )
			{
				guy waittillmatch( flag, notetracks[ i ] );
				guy thread [[ sthreads[ i ] ]]();
			}
		}

		guy waittillmatch( flag, "end" );
	}

	guy notify( "anim_on_tag_done" );

	guy.ragdoll_getout_death = undefined;
}

recover_interval()
{
	self endon( "death" );
	wait( 2 );
	if ( self.interval == 0 )
		self.interval = 80;
}

animontag_ragdoll_death( guy, vehicle )
{
	// thread draw_line_from_ent_to_ent_until_notify( level.player, guy, 1, 0, 0, guy, "anim_on_tag_done" );
	if ( IsDefined( guy.magic_bullet_shield ) && guy.magic_bullet_shield )
		return;
	if ( !isAI( guy ) )
		guy SetCanDamage( true );

	guy endon( "anim_on_tag_done" );


	damage = undefined;
	attacker = undefined;
	vehicleallreadydead = vehicle.health <= 0;
	while ( true )
	{
		if ( !vehicleallreadydead && !( IsDefined( vehicle ) && vehicle.health > 0 ) )
			break;
		guy waittill( "damage", damage, attacker );
		if( isdefined( guy.forcefallthroughonropes ) )
			break;
		if ( !isdefined( damage ) )
			continue;
		if ( damage < 1 )
			continue;
		if ( !isdefined( attacker ) )
			continue;
		if ( ( IsPlayer( attacker ) ) )
			break;
	}

	if ( !isalive( guy ) )
		return;// guy was deleted between "damage" and the "fastrope_fall" notetrack.

	thread arcadeMode_kill( guy.origin, "rifle", 300 );

	thread animontag_ragdoll_death_fall( guy, vehicle, attacker );

}

animontag_ragdoll_death_fall( guy, vehicle, attacker )
{

	guy.deathanim = undefined;
	guy.deathFunction = undefined;
	guy.anim_disablePain = true;

	if ( IsDefined( guy.ragdoll_fall_anim ) )
	{
		// only do fall animation if the guy is high enough to not fall through the ground
		moveDelta = GetMoveDelta( guy.ragdoll_fall_anim, 0, 1 );
		groundPos = PhysicsTrace( guy.origin + ( 0, 0, 16 ), guy.origin - ( 0, 0, 10000 ) );

		distanceFromGround = Distance( guy.origin + ( 0, 0, 16 ), groundPos );
		if ( abs( moveDelta[ 2 ] + 16 ) <= abs( distanceFromGround ) )
		{
			guy thread play_sound_on_entity( "generic_death_falling" );
			guy AnimScripted( "fastrope_fall", guy.origin, guy.angles, guy.ragdoll_fall_anim );
			guy waittillmatch( "fastrope_fall", "start_ragdoll" );
		}
	}
	if ( !isdefined( guy ) )
		return;// guy was deleted between "damage" and the "fastrope_fall" notetrack.
	guy.deathanim = undefined;
	guy.deathFunction = undefined;
	guy.anim_disablePain = true;
	
	guy notify( "rope_death", attacker );
	guy Kill( attacker.origin, attacker );
	
	guy animscripts\shared::DropAllAIWeapons();
	guy StartRagdoll();
}

// applies endons to donotetracks
DoNoteTracks( guy, vehicle, flag )
{
	guy endon( "newanim" );
	vehicle endon( "death" );
	guy endon( "death" );
	guy animscripts\shared::DoNoteTracks( flag );
}

animatemoveintoplace( guy, org, angles, movetospotanim )
{
	guy AnimScripted( "movetospot", org, angles, movetospotanim );
	guy waittillmatch( "movetospot", "end" );
}

guy_vehicle_death( guy, attacker, type )
{
	if( !isalive( guy ) )
		return;
	
	animpos = anim_pos( self, guy.vehicle_position );

	guy.vehicle_attacker = attacker;
	
	if ( IsDefined( animpos.explosion_death ) )
		return guy_blowup( guy );

	if ( IsDefined( animpos.unload_ondeath ) && IsDefined( self ) )
	{
		thread guy_idle( guy, guy.vehicle_position, true );// hack, idle gets canceled out by the death;
	 	wait animpos.unload_ondeath;
	 	if ( IsDefined( guy ) && IsDefined( self ) )
		{
 			self.groupedanim_pos = guy.vehicle_position;
			self vehicle_ai_event( "unload" );
		}
		return;
	}

	if ( IsDefined( guy ) )
	{
		if( isdefined( guy.ragdoll_getout_death ) && type != "bm21_troops" )
		{
		 	return;
		}

	
		origin = guy.origin;

		/#
		assertex( !isdefined( guy.magic_bullet_shield ), "Vehicle script is trying to delete magic bullet shield guy " + guy getentnum() );
		#/

		[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", origin );


		// Fix for the driver and front passenger not dying when they are getting out and blown up by an explosion
		// Look below for possibly more appropiate fix for all vehicles.
		if ( type == "bm21_troops" )
		{
			guy.allowdeath = true;
			guy Kill();
			return;
		}

		guy Delete();

// This is probably the more appropiate fix, but since we are so late in the project we'll just isolate the fix to the
// bm21 vehicletype. If we do decide to go with this. Be sure to pass in is_helicopter from vehicle_kill() and
// remove the above if( isdefined( guy.ragdoll_getout_death ) && type != "bm21_troops" )
//		if ( IsDefined( is_helicopter ) && is_helicopter )
//		{
//			if( isdefined( guy.ragdoll_getout_death ) )
//			{
//			 	return;
//			}
//			else
//			{
//				guy Delete();
//			}
//		}
//		else
//		{
//			guy Kill();
//		}
	}
}

guy_turn_right_check( guy, pos )
{
	return IsDefined( 	anim_pos( self, pos ).turn_right );
}

guy_turn_right( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );
	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.vehicle_turn_right ) )
		thread setanimrestart_once( animpos.vehicle_turn_right );
	animontag( guy, animpos.sittag, animpos.turn_right );
	thread guy_idle( guy, pos );
}

guy_turn_left( guy, pos )
{
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );
	animpos = anim_pos( self, pos );
	if ( IsDefined( animpos.vehicle_turn_left ) )
		self thread setanimrestart_once( animpos.vehicle_turn_left );
	animontag( guy, animpos.sittag, animpos.turn_left );
	thread guy_idle( guy, pos );
}

guy_turn_left_check( guy, pos )
{
	return IsDefined( 	anim_pos( self, pos ).turn_left );
}


guy_turn_hardright( guy, pos )
{
	animpos = level.vehicle_aianims[ self.vehicletype ][ pos ];
	if ( IsDefined( animpos.idle_hardright ) )
		guy.vehicle_idle_override = animpos.idle_hardright;
}

guy_turn_hardleft( guy, pos )
{
	animpos = level.vehicle_aianims[ self.vehicletype ][ pos ];
	if ( IsDefined( animpos.idle_hardleft ) )
		guy.vehicle_idle_override = animpos.idle_hardleft;
}

ai_wait_go()
{
	self endon( "death" );
	self waittill( "loaded" );
	maps\_vehicle::gopath( self );
}

set_pos( guy, maxpos )
{
	pos = guy.script_startingposition;

	/#
	if ( IsDefined( pos ) )
	{
		AssertEx( ( pos < maxpos ) && ( pos >= 0 ), "script_startingposition on a vehicle rider must be between " + maxpos + " and 0" );
	}
	#/

	if ( IsDefined( guy.forced_startingposition ) )
	{
		pos = guy.forced_startingposition;
	}

	if ( IsDefined( pos ) )
	{
		return pos;
	}

	AssertEx( !isdefined( pos ), "Illegal starting position" );

	// if there isn't one then set it to the lowest unused spot
	for ( j = 0; j < self.usedPositions.size; j++ )
	{
		if ( self.usedPositions[ j ] )
			continue;

		return j;
	}

	if ( IsDefined( guy.script_vehicleride ) )
		AssertMsg( "can't find vehicle rider position , likely too many guys assigned to ride a vehicle ( copy paste in radiant? ) rider number: ", guy.script_vehicleride );

	AssertMsg( "All spots on this vehicle were used up, too many AI trying to ride." );
}

guy_man_turret( guy, pos, climbed_in_vehicle )
{
	animpos = anim_pos( self, pos );
	turret = self.mgturret[ animpos.mgturret ];

	if( !isalive( guy ) )
		return;
	turret endon( "death" );
	guy endon( "death" );

	if ( isdefined( climbed_in_vehicle ) && isdefined( animpos.passenger_2_turret_func ) )
		[[ animpos.passenger_2_turret_func ]]( self, guy, pos, turret );
	
	set_turret_team( turret );
	turret SetDefaultDropPitch( 0 );

	wait( 0.1 );
	guy endon( "guy_man_turret_stop" );
	level thread maps\_mgturret::mg42_setdifficulty( turret, getDifficulty() );
	//turret SetMode( "auto_ai" );
	turret SetTurretIgnoreGoals( true );

	while ( 1 )
	{
		if ( !isdefined( guy GetTurret() ) )
			guy UseTurret( turret );
		wait 1;
	}
}

guy_unlink_on_death( guy )
{
	guy endon( "jumpedout" );
	guy waittill( "death" );
	if ( IsDefined( guy ) )
		guy Unlink();
}


guy_blowup( guy )
{
	if ( ! IsDefined( guy.vehicle_position ) )
		return;
	pos = guy.vehicle_position;
	anim_pos = anim_pos( self, pos );
	if ( !isdefined( anim_pos.explosion_death ) )
		return;

	[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", guy.origin );

	guy.deathanim = anim_pos.explosion_death;
// 	guy.allowdeath = true;
	angles = self.angles;
	origin = guy.origin;

	// I think there's a better way to to dthis but I'm lazy
	if ( IsDefined( anim_pos.explosion_death_offset ) )
	{
		origin += vector_multiply( AnglesToForward( angles ), anim_pos.explosion_death_offset[ 0 ] );
		origin += vector_multiply( AnglesToRight( angles ), anim_pos.explosion_death_offset[ 1 ] );
		origin += vector_multiply( AnglesToUp( angles ), anim_pos.explosion_death_offset[ 2 ] );
	}
	guy = convert_guy_to_drone( guy );
	detach_models_with_substr( guy, "weapon_" );
	guy NotSolid();
	guy.origin = origin;
	guy.angles = angles;

	guy AnimScripted( "deathanim", origin, angles, anim_pos.explosion_death );
	fraction = .3;
	if ( IsDefined( anim_pos.explosion_death_ragdollfraction ) )
		fraction = anim_pos.explosion_death_ragdollfraction;
	animlength = GetAnimLength( anim_pos.explosion_death );
	timer = GetTime() + ( animlength * 1000 );
	wait animlength * fraction;

	force = ( 0, 0, 1 );
	org = guy.origin;

	if ( GetDvar( "ragdoll_enable" ) == "0" )
	{
		guy Delete();
		return;
	}

	if( isai( guy ) )
		guy animscripts\shared::DropAllAIWeapons();
	else
		detach_models_with_substr( guy, "weapon_" );// drones shouldn't have weapon.

	while ( ! guy IsRagdoll() && GetTime() < timer )
	{
		org = guy.origin;
		wait .05;
		force = guy.origin - org;
		guy StartRagdoll();

	}
	wait .05;
	force = vector_multiply( force, 20000 );
	for ( i = 0; i < 3; i++ )
	{
		if ( IsDefined( guy ) )
			org = guy.origin;
//		PhysicsJolt( org, 250, 250, force );
		wait( 0.05 );
	}
	if ( !guy IsRagdoll() )
		guy Delete();

}

// maybe I should make a utility out of this?. could be slow
convert_guy_to_drone( guy, bKeepguy )
{
	if ( !isdefined( bKeepguy ) )
		bKeepguy = false;
	model = Spawn( "script_model", guy.origin );
	model.angles = guy.angles;
	model SetModel( guy.model );
	size = guy GetAttachSize();
	for ( i = 0; i < size; i++ )
	{
		model Attach( guy GetAttachModelName( i ), guy GetAttachTagName( i ) );
// 		struct.attachedtags[ i ] = guy GetAttachTagName( i );
	}
	model UseAnimTree( #animtree );
	if ( IsDefined( guy.team ) )
		model.team = guy.team;
	if ( !bKeepguy )
		guy Delete();
	model MakeFakeAI();
	return model;
}

vehicle_animate( animation, animtree )
{
	self UseAnimTree( animtree );
	self SetAnim( animation );
}

vehicle_getInstart( pos )
{
	animpos = anim_pos( self, pos );
	Assert( IsDefined( animpos ) );
	Assert( IsDefined( animpos.sittag ) );
	Assert( IsDefined( animpos.getin ) );
	return vehicle_getanimstart( animpos.getin, animpos.sittag, pos );
}

//TODO: anim_reach is the new and cool way.
vehicle_getanimstart( animation, tag, pos )
{
	struct = SpawnStruct();

	origin = undefined;
	angles = undefined;
	Assert( IsDefined( animation ) );
	org = self GetTagOrigin( tag );
	ang = self GetTagAngles( tag );
	origin = GetStartOrigin( org, ang, animation );
	angles = GetStartAngles( org, ang, animation );
	struct.origin = origin;
	struct.angles = angles;
	struct.vehicle_position = pos;
	return struct;
}

is_position_in_group( vehicle, pos, group )
{
	if ( !isdefined( group ) )
		return true;

	Assert( IsDefined( level.vehicle_unloadgroups[ vehicle.vehicletype ][ group ] ) );
	vehicles_group = level.vehicle_unloadgroups[ vehicle.vehicletype ][ group ];
	foreach ( member in vehicles_group )
	{
		if ( member == pos )
			return true;
	}
	
	return false;
}

get_availablepositions( group )
{
	vehicleanim = level.vehicle_aianims[ self.vehicletype ];
	availablepositions = [];
	nonanimatedpositions = [];
	for ( i = 0; i < self.usedPositions.size; i++ )
	{
		if ( self.usedPositions[ i ] )
			continue;

		if ( IsDefined( vehicleanim[ i ].getin ) && is_position_in_group( self, i, group ) )
			availablepositions[ availablepositions.size ] = vehicle_getInstart( i );
		else
			nonanimatedpositions[ nonanimatedpositions.size ] = i;
	}
	
	struct = SpawnStruct();
	struct.availablepositions = availablepositions;
	struct.nonanimatedpositions = nonanimatedpositions;

	return struct;
}

getanimatemodel()
{
	if ( IsDefined( self.modeldummy ) )
		return self.modeldummy;
	else
		return self;
}

animpos_override_standattack( type, pos, animation )
{
	level.vehicle_aianims[ type ][ pos ].vehicle_standattack = animation;
}

detach_models_with_substr( guy, substr )
{
	size = guy GetAttachSize();
	modelstodetach = [];
	tagsstodetach = [];
	index = 0;
	for ( i = 0; i < size; i++ )
	{
		modelname = guy GetAttachModelName( i );
		tagname = guy GetAttachTagName( i );
		if ( IsSubStr( modelname, substr ) )
		{
			modelstodetach[ index ] = modelname;
			tagsstodetach[ index ] = tagname;
		}
	}
	for ( i = 0; i < modelstodetach.size; i++ )
		guy Detach( modelstodetach[ i ], tagsstodetach[ i ] );
}

should_give_orghealth()
{
	if ( !isai( self ) )
		return false;
	if ( !isdefined( self.orghealth ) )
		return false;
	return !isdefined( self.magic_bullet_shield );
}

guy_pre_unload_check( guy, pos )
{
	return IsDefined( 	anim_pos( self, pos ).pre_unload );
}


guy_pre_unload( guy, pos )
{
	animpos = anim_pos( self, pos );
	if ( !isdefined( animpos.pre_unload ) )
		return;

	/*
	guy = guy_becomes_real_ai( guy, pos );
	if ( !isalive( guy ) )
		return;
	*/

	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animontag( guy, animpos.sittag, animpos.pre_unload );
	while ( 1 )
		animontag( guy, animpos.sittag, animpos.pre_unload_idle );
}

guy_idle_alert( guy, pos )
{
	animpos = anim_pos( self, pos );
	if ( !isdefined( animpos.idle_alert ) )
		return;

	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

//	animontag( guy, animpos.sittag, animpos.idle_alert );
	while ( 1 )
		animontag( guy, animpos.sittag, animpos.idle_alert );
}

guy_idle_alert_check( guy, pos )
{
	return IsDefined( anim_pos( self, pos ).idle_alert );
}



guy_idle_alert_to_casual( guy, pos )
{
	animpos = anim_pos( self, pos );
	if ( !isdefined( animpos.idle_alert ) )
		return;
	guy endon( "newanim" );
	self endon( "death" );
	guy endon( "death" );

	animontag( guy, animpos.sittag, animpos.idle_alert_to_casual );
	thread guy_idle( guy, pos );
}

guy_idle_alert_to_casual_check( guy, pos )
{
	return IsDefined( anim_pos( self, pos ).idle_alert_to_casual );
}




stable_unlink( guy )
{
	self waittill( "stable_for_unlink" );
	
	if( isalive( guy ) )
		guy Unlink();
}


track_entered_vehicle()
{

}

animate_guys( other )
{
	return_guys = [];
	foreach ( guy in self.riders )
	{
		if ( !isalive( guy ) )
			continue;

		if ( IsDefined( level.vehicle_aianimcheck[ other ] ) && ! [[ level.vehicle_aianimcheck[ other ] ]]( guy, guy.vehicle_position ) )
			continue;// ignore this if they have a check function and this anim doesn't exist

		if ( IsDefined( level.vehicle_aianimthread[ other ] ) )
		{
				guy notify( "newanim" );
				guy.queued_anim_threads = [];// sorry que, this animation is more important.
				thread [[ level.vehicle_aianimthread[ other ] ]]( guy, guy.vehicle_position );
				return_guys[ return_guys.size ] = guy;
		}
		else
			PrintLn( "Error: leaaaaaaaaaaaaaak", other );
	}
	return return_guys;

}

guy_cleanup_vehiclevars()
{
	self.vehicle_idling = undefined;
	self.standing = undefined;
	self.vehicle_position = undefined;
	self.delay = undefined;
}

delete_corpses_around_vehicle()
{
	centroid = self getcentroid();
	point_in_bounds = self getpointinbounds( 1,0,0 );
	dist = distance( point_in_bounds, centroid );
	corpses = getcorpsearray();
	foreach( corpse in corpses )
		if( distance( corpse.origin, centroid ) < dist )
			corpse delete();
	
}