#using scripts\codescripts\struct;

#using scripts\shared\flag_shared;
#using scripts\shared\math_shared;
#using scripts\shared\sound_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\vehicle_shared;
#using scripts\shared\vehicle_ai_shared;

    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          



#namespace vehicle_death;

function autoexec __init__sytem__() {     system::register("vehicle_death",&__init__,undefined,undefined);    }

// _vehicle_death.gsc - all things related to vehicles dying

// Utility rain functions:


#using_animtree( "generic" );

function __init__()
{
	SetDvar( "debug_crash_type", -1 );
	
	//TODO T7 - function port
	/*if( IsAssetLoaded( "fx", "_debug/fx_debug_deleted_fx" ) )
	{
		//level.heli_hit_fx =
		level.heli_crash_smoke_trail_fx = "_debug/fx_debug_deleted_fx";
	}*/
}

function main()
{
	self endon( "nodeath_thread" );

	while ( isdefined( self ) )
	{
		// waittill death twice. in some cases the vehicle dies and does a bunch of stuff. then it gets deleted. which it then needs to do more stuff
		self waittill( "death", attacker, damageFromUnderneath, weapon, point, dir );
		
		if( isdefined( self.death_enter_cb ) )
			[[self.death_enter_cb]]();

		if ( isdefined( self.script_deathflag ) )
		{
			level flag::set( self.script_deathflag );
		}

		if ( !isdefined( self.delete_on_death ) )
		{
			self thread play_death_audio();
		}

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

		self death_cleanup_level_variables();

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

		if ( vehicle::is_corpse( self ) )
		{
			// Cleanup Riders
			if ( !( isdefined( self.dont_kill_riders ) && self.dont_kill_riders ) )
			{
				self death_cleanup_riders();
			}

			// kills some destructible fxs
			self notify( "delete_destructible" );

			// Done here
			return;
		}

		self vehicle::lights_off();
		
		// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
		// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -

		// Run vehicle death thread
		if ( isdefined( level.vehicle_death_thread[ self.vehicletype ] ) )
		{
			thread [[ level.vehicle_death_thread[ self.vehicletype ] ]]();
		}

		if ( !isdefined( self.delete_on_death ) )
		{
			// Do radius damage
			thread death_radius_damage();
		}

		is_aircraft = (  ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) || ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) );

		if ( !isdefined( self.destructibledef ) )
		{
			if ( !is_aircraft && !( self.vehicleType == "horse" || self.vehicleType == "horse_player" || self.vehicleType == "horse_player_low" || self.vehicleType == "horse_low" || self.vehicleType == "horse_axis" ) && isdefined( self.deathmodel ) && self.deathmodel != "" )
			{
				self thread set_death_model( self.deathmodel, self.modelswapdelay );
			}

			// Do death_fx if it has not been mantled
			if ( !isdefined( self.delete_on_death ) && ( !isdefined( self.mantled ) || !self.mantled ) && !isdefined( self.nodeathfx ) )
			{
				thread death_fx();
			}

			if ( isdefined( self.delete_on_death ) )
			{
				{wait(.05);};

				if ( self.disconnectPathOnStop === true )
				{
					self vehicle::disconnect_paths();
				}

				if ( !( isdefined( self.no_free_on_death ) && self.no_free_on_death ) )
				{
					self freevehicle();
					self.isacorpse = true;

					{wait(.05);};

					if ( isdefined( self ) )
					{
						self notify( "death_finished" );
						self delete();
					}
				}

				continue;
			}
		}

//		// makes riders blow up
//		if ( isdefined( self.riders ) && self.riders.size > 0 )
//		{
//			vehicle_aianim::blowup_riders();
//		}

		// Place a bad place cylinder if specified
		thread death_make_badplace( self.vehicletype );

		// Send vehicle type specific notify
		if ( isdefined( level.vehicle_deathnotify ) && isdefined( level.vehicle_deathnotify[ self.vehicletype ] ) )
		{
			level notify( level.vehicle_deathnotify[ self.vehicletype ], attacker );
		}

		if ( Target_IsTarget( self ) )
		{
			Target_Remove( self );
		}

		// all the vehicles get the same jolt..
		if ( self.classname == "script_vehicle" )
		{
			self thread death_jolt( self.vehicletype );
		}

		if ( do_scripted_crash() )
		{
			// Check for scripted crash
			self thread death_update_crash( point, dir );
		}

		// Clear turret target
		if ( isdefined( self.turretweapon ) && self.turretweapon != level.weaponNone)
		{
			self clearTurretTarget();
		}

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

		// Wait here until we're finished with a crash or rolling death
		self waittill_crash_done_or_stopped();

		if ( isdefined( self ) )
		{
			while ( isdefined( self ) && isdefined( self.dontfreeme ) )
			{
				wait( .05 );
			}

			// send notifies
			self notify( "stop_looping_death_fx" );
			self notify( "death_finished" );

			wait .05;

			if ( isdefined( self ) )
			{
				if ( vehicle::is_corpse( self ) )
				{
					continue;
				}

				if ( !isdefined( self ) )
				{
					continue;
				}

				// AE 2-18-09: if the player is using it, then spit them out or kill them before freeing the vehicle
				occupants = self GetVehOccupants();
				if ( isdefined( occupants ) && occupants.size )
				{
					for ( i = 0; i < occupants.size; i++ )
					{
						self usevehicle( occupants[i], 0 );
					}
				}

				if ( !( isdefined( self.no_free_on_death ) && self.no_free_on_death ) )
				{
					self freevehicle();
					self.isacorpse = true;
				}

				if ( self.modeldummyon )
				{
					self hide();
				}
			}
		}
	}
}

function do_scripted_crash()
{
	return !isdefined( self.do_scripted_crash ) || ( isdefined( self.do_scripted_crash ) && self.do_scripted_crash );
}

function play_death_audio()
{
	if ( isdefined ( self ) && ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
	{
		if ( !isdefined ( self.death_counter ) )
		{
			self.death_counter = 0;
		}
		if ( self.death_counter == 0 )
		{
			self.death_counter++;
			self playsound ( "exp_veh_helicopter_hit" );
		}
	}
}

function play_spinning_plane_sound()
{
	self playloopsound( "veh_drone_spin", .05 );
	level util::waittill_any( "crash_move_done", "death" );
	self stoploopsound( .02 );
}

function set_death_model( sModel, fDelay )
{
	if( !isdefined( sModel ) )
		return;
	

	if ( isdefined( fDelay ) && ( fDelay > 0 ) )
	{
		wait fDelay;
	}

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

	if ( isdefined( self.deathmodel_attached ) )
	{
		return;
	}

	eModel = vehicle::get_dummy();
	if ( !isdefined( eModel ) )
	{
		return;
	}

	if ( !isdefined( eModel.death_anim ) && ( isdefined( eModel.animtree ) ) )
	{
		eModel ClearAnim( %root, 0 );
	}
	
	// SetModel can remove the destructible so only do if if we are really trying to swap to a specified death model
	if( sModel != self.vehmodel )
	{
		eModel SetModel( sModel );
		eModel SetEnemyModel( sModel );
	}
}


function aircraft_crash( point, dir )
{
	self.crashing = true;

	if ( isdefined( self.unloading ) )
	{
		while ( isdefined( self.unloading ) )
		{
			{wait(.05);};
		}
	}

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

	self thread aircraft_crash_move( point, dir );
	self thread play_spinning_plane_sound();
}

function helicopter_crash( point, dir )
{
	self.crashing = true;
	self thread play_crashing_loop();

	if ( isdefined( self.unloading ) )
	{
		while ( isdefined( self.unloading ) )
		{
			{wait(.05);};
		}
	}

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

	//self thread helicopter_crash_move( point, dir );
	self thread helicopter_crash_movement( point, dir );

}

function helicopter_crash_movement( point, dir )
{
	self endon( "crash_done" );

	self CancelAIMove();
	self ClearVehGoalPos();

	if ( isdefined( level.heli_crash_smoke_trail_fx ) )
	{
		if ( IsSubStr( self.vehicletype, "v78" ) )
		{
			playfxontag( level.heli_crash_smoke_trail_fx, self, "tag_origin" );
		}
		else if ( self.vehicletype == "drone_firescout_axis" || self.vehicletype == "drone_firescout_isi" )
		{
			playfxontag( level.heli_crash_smoke_trail_fx, self, "tag_main_rotor" );
		}
		else
		{
			playfxontag( level.heli_crash_smoke_trail_fx, self, "tag_engine_left" );
		}
	}

	crash_zones = struct::get_array( "heli_crash_zone", "targetname" );
	if ( crash_zones.size > 0  )
	{
		best_dist = 99999;
		best_idx = -1;

		if ( isdefined( self.a_crash_zones ) )
		{
			crash_zones = self.a_crash_zones;
		}

		for ( i = 0; i < crash_zones.size; i++ )
		{
			vec_to_crash_zone = crash_zones[i].origin - self.origin;
			vec_to_crash_zone = ( vec_to_crash_zone[0], vec_to_crash_zone[1], 0 );
			dist = Length( vec_to_crash_zone );
			vec_to_crash_zone /= dist;

			veloctiy_scale = -VectorDot( self.velocity, vec_to_crash_zone );
			dist += 500 * veloctiy_scale;

			if ( dist < best_dist )
			{
				best_dist = dist;
				best_idx = i;
			}
		}

		if ( best_idx != -1 )
		{
			self.crash_zone = crash_zones[best_idx];
			self thread helicopter_crash_zone_accel( dir );
		}
	}
	else
	{
		if( isdefined( dir ) )
			dir = VectorNormalize( dir );	
		else
			dir = ( 1,0,0 );
		side_dir = VectorCross( dir, ( 0,0,1 ) );
		side_dir_mag = RandomFloatRange( -500, 500 );
		side_dir_mag += math::sign( side_dir_mag ) * 60;
		side_dir *= side_dir_mag;
		side_dir += ( 0,0,150 );

		self SetPhysAcceleration( ( RandomIntRange( -500, 500 ), RandomIntRange( -500, 500 ), -1000 ) );
		self SetVehVelocity( self.velocity + side_dir );
		self thread helicopter_crash_accel();
		if( isdefined( point ) )
			self thread helicopter_crash_rotation( point, dir );
		else
			self thread helicopter_crash_rotation( self.origin, dir );
	}

	//self thread helicopter_collision();
	self thread crash_collision_test();

	wait 15;

	// failsafe notify
	if ( IsDefined( self ) )
	{
		self notify( "crash_done" );
	}
}

function helicopter_crash_accel()
{
	self endon( "crash_done" );
	self endon( "crash_move_done" );
	self endon( "death" );

	if ( !isdefined( self.crash_accel ) )
	{
		self.crash_accel = RandomFloatRange( 50, 80 );
	}

	while ( isdefined( self ) )
	{
		self SetVehVelocity( self.velocity + AnglesToUp( self.angles ) * self.crash_accel );
		wait 0.1;
	}
}

function helicopter_crash_rotation( point, dir )
{
	self endon( "crash_done" );
	self endon( "crash_move_done" );
	self endon( "death" );

	start_angles = self.angles;
	start_angles = (start_angles[0] + 10, start_angles[1], start_angles[2]);	//RandomIntRange( -5, 5 ) );
	start_angles = (start_angles[0], start_angles[1], start_angles[2] + 10);	//RandomIntRange( -5, 5 ) );

	ang_vel = self GetAngularVelocity();
	ang_vel = ( 0, ang_vel[1] * RandomFloatRange( 2, 3 ), 0 );
	self SetAngularVelocity( ang_vel );

	point_2d = ( point[0], point[1], self.origin[2] );

	//DebugStar( self.origin, 1000, ( 1, 1, 1 ) );
	//DebugStar( point_2d, 1000, ( 1, 0, 0 ) );

	torque = ( 0, RandomIntRange( 90, 180 ), 0 );
	if ( self GetAngularVelocity()[1] < 0 )
	{
		torque *= -1;
	}

	if ( Distance( self.origin, point_2d ) > 5 )
	{
		local_hit_point = point_2d - self.origin;

		dir_2d = ( dir[0], dir[1], 0 );
		if ( Length( dir_2d ) > 0.01 )
		{
			dir_2d = VectorNormalize( dir_2D );
			torque = VectorCross( VectorNormalize( local_hit_point ), dir );
			torque = ( 0, 0, torque[2] );
			torque = VectorNormalize( torque );
			torque = ( 0, torque[2] * 180, 0 );
		}
	}

	while ( 1 )
	{
		ang_vel = self GetAngularVelocity();
		ang_vel += torque * 0.05;

		const max_angluar_vel = 360;
		if ( ang_vel[1] < max_angluar_vel * -1 )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
		}
		else if ( ang_vel[1] > max_angluar_vel )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
		}

		self SetAngularVelocity( ang_vel );

		{wait(.05);};
	}
}

function helicopter_crash_zone_accel( dir )
{
	self endon( "crash_done" );
	self endon( "crash_move_done" );

	torque = ( 0, RandomIntRange( 90, 150 ), 0 );
	ang_vel = self GetAngularVelocity();
	//ang_vel = ( 0, ang_vel[1], 0 );	// get rid of the pitch and roll
	//self SetAngularVelocity( ang_vel );
	torque *= math::sign( ang_vel[1] );

	/#
	if ( isdefined( self.crash_zone.height ) )
	{
		self.crash_zone.height = 0;
	}
#/

	// if you don't have any roll give it a little
	if ( Abs( self.angles[2] ) < 3.0 )
	{
		self.angles = ( self.angles[0], self.angles[1], RandomIntRange( 3, 6 ) * math::sign( self.angles[2] ) );
	}

	is_vtol = IsSubStr( self.vehicletype, "v78" );

	if ( is_vtol )
	{
		torque *= 0.3;
	}

	while ( isdefined( self ) )
	{
		assert( isdefined( self.crash_zone ) );

		dist = Distance2D( self.origin, self.crash_zone.origin );
		if ( dist < self.crash_zone.radius )
		{
			//if ( isdefined( self.crash_zone.angles ) )
			//	self.crash_vel = Length( self.velocity ) * AnglesToForward( self.crash_zone.angles );

			self SetPhysAcceleration( ( 0, 0, -400 ) );
			/#Circle( self.crash_zone.origin + ( 0, 0, self.crash_zone.height ), self.crash_zone.radius, ( 0, 1, 0 ), false, 2000 );#/
			self.crash_accel = 0;
		}
		else
		{
			self SetPhysAcceleration( ( 0, 0, -50 ) );
			/#Circle( self.crash_zone.origin + ( 0, 0, self.crash_zone.height ), self.crash_zone.radius, ( 1, 0, 0 ), false, 2 );#/
		}

		self.crash_vel = self.crash_zone.origin - self.origin;
		self.crash_vel = ( self.crash_vel[0], self.crash_vel[1], 0 );
		self.crash_vel = VectorNormalize( self.crash_vel );
		self.crash_vel *= self GetMaxSpeed() * 0.5;

		if ( is_vtol )
		{
			self.crash_vel *= 0.5;
		}

		crash_vel_forward = AnglesToUp( self.angles ) * self GetMaxSpeed() * 2;
		crash_vel_forward  = ( crash_vel_forward[0], crash_vel_forward[1], 0 );
		self.crash_vel += crash_vel_forward;

		vel_x = DiffTrack( self.crash_vel[0], self.velocity[0], 1, 0.1 );
		vel_y = DiffTrack( self.crash_vel[1], self.velocity[1], 1, 0.1 );
		vel_z = DiffTrack( self.crash_vel[2], self.velocity[2], 1, 0.1 );

		self SetVehVelocity( ( vel_x, vel_y, vel_z ) );

		ang_vel = self GetAngularVelocity();
		ang_vel = ( 0, ang_vel[1], 0 );
		ang_vel += torque * 0.1;

		max_angluar_vel = 200;
		if ( is_vtol )
		{
			max_angluar_vel = 100;
		}

		if ( ang_vel[1] < max_angluar_vel * -1 )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
		}
		else if ( ang_vel[1] > max_angluar_vel )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
		}

		self SetAngularVelocity( ang_vel );

		wait( 0.1 );
	}
}

function helicopter_collision()
{
	self endon( "crash_done" );

	while ( 1 )
	{
		self waittill( "veh_collision", velocity, normal );

		ang_vel = self GetAngularVelocity() * 0.5;
		self SetAngularVelocity( ang_vel );

		// bounce off walls
		if ( normal[2] < 0.7 )
		{
			self SetVehVelocity( self.velocity + normal * 70 );
		}
		else
		{
			//self.crash_accel *= 0.5;
			//self SetVehVelocity( self.velocity * 0.8 );
			//TODO T7 - function port
			//CreateDynEntAndLaunch( self.deathmodel, self.origin, self.angles, self.origin, self.velocity * 0.03, self.deathfx );

			self notify( "crash_done" );
		}
	}
}

function play_crashing_loop()
{
	ent = Spawn ( "script_origin", self.origin );
	ent linkto ( self );
	ent playloopsound ( "exp_heli_crash_loop" );
	self util::waittill_any ( "death", "snd_impact" );
	ent delete();

}
function helicopter_explode( delete_me )
{
	self endon( "death" );

	self vehicle::do_death_fx();

	if ( isdefined( delete_me ) && delete_me == true )
	{
		self Delete();
	}

	self thread set_death_model( self.deathmodel, self.modelswapdelay );
}

function aircraft_crash_move( point, dir )
{
	self endon( "crash_move_done" );
	self endon( "death" );

	self thread crash_collision_test();
	self ClearVehGoalPos();
	self CancelAIMove();
	self SetRotorSpeed( 0.2 );

	// swap to deathmodel here
	if ( isdefined( self ) && isdefined( self.vehicletype ) )
	{
		b_custom_deathmodel_setup = true;
		switch ( self.vehicletype )
		{

		default:
			b_custom_deathmodel_setup = false;
			break;
		}

		if ( b_custom_deathmodel_setup )
		{
			self.deathmodel_attached = true;  // this will not add another deathmodel
		}
	}

	ang_vel = self GetAngularVelocity();
	ang_vel = ( 0, 0, 0 );

	self SetAngularVelocity( ang_vel );

	nodes = self GetVehicleAvoidanceNodes( 10000 );
	closest_index = -1;
	best_dist = 999999;
	if ( nodes.size > 0 )
	{
		for ( i = 0; i < nodes.size; i++ )
		{
			dir = VectorNormalize( nodes[i] - self.origin );
			forward = AnglesToForward( self.angles );
			dot = VectorDot( dir, forward );
			if ( dot < 0.0 )
			{
				continue;
			}

			dist = Distance2D( self.origin, nodes[i] );
			if ( dist < best_dist )
			{
				best_dist = dist;
				closest_index = i;
			}
		}

		if ( closest_index >= 0 )
		{
			o = nodes[closest_index];
			o = ( o[0], o[1], self.origin[2] );

			//Line( self.origin, o, ( 1, 1, 1 ), false, 10000 );
			//Circle( o, 2000, ( 1, 1, 0 ), true, 10000 );

			dir = VectorNormalize( o - self.origin );

			self SetVehVelocity( self.velocity + dir * 2000 );
		}
		else
		{
			self SetVehVelocity( self.velocity + AnglesToRight( self.angles ) * RandomIntRange( -1000, 1000 ) + ( 0, 0, RandomIntRange( 0, 1500 ) ) );
		}
	}
	else
	{
		self SetVehVelocity( self.velocity + AnglesToRight( self.angles ) * RandomIntRange( -1000, 1000 ) + ( 0, 0, RandomIntRange( 0, 1500 ) ) );
	}

	//self SetVehVelocity( self.velocity + AnglesToRight( self.angles ) * RandomIntRange( -1000, 1000 ) + ( 0, 0, RandomIntRange( 0, 1500 ) ) );
	self thread delay_set_gravity( RandomFloatRange( 1.5, 3 ) );

	torque = ( 0, RandomIntRange( -90, 90 ), RandomIntRange( 90, 720 ) );
	if ( RandomInt( 100 ) < 50 )
	{
		torque = ( torque[0], torque[1], -torque[2] );
	}

	while ( isdefined( self ) )
	{
		ang_vel = self GetAngularVelocity();
		ang_vel += torque * 0.05;

		const max_angluar_vel = 500;
		if ( ang_vel[2] < max_angluar_vel * -1 )
		{
			ang_vel = ( ang_vel[0], ang_vel[1],  max_angluar_vel * -1 );
		}
		else if ( ang_vel[2] > max_angluar_vel )
		{
			ang_vel = ( ang_vel[0], ang_vel[1],  max_angluar_vel );
		}

		self SetAngularVelocity( ang_vel );

		{wait(.05);};
	}
}

function delay_set_gravity( delay )
{
	self endon( "crash_move_done" );
	self endon( "death" );

	wait( delay );

	self SetPhysAcceleration( ( RandomIntRange( -1600, 1600 ), RandomIntRange( -1600, 1600 ), -1600 ) );
}

function helicopter_crash_move( point, dir )
{
	self endon( "crash_move_done" );
	self endon( "death" );

	self thread crash_collision_test();
	self CancelAIMove();
	self ClearVehGoalPos();
	self SetTurningAbility( 0 );


	self SetPhysAcceleration( ( 0, 0, -800 ) );

	vel = self.velocity;
	dir = VectorNormalize( dir );

	ang_vel = self GetAngularVelocity();
	ang_vel = ( 0, ang_vel[1] * RandomFloatRange( 1, 3 ), 0 );
	self SetAngularVelocity( ang_vel );

	//	if ( Length( vel ) < 10 )
	//	{
	//		random_yaw = RandomIntRange( -180, 180 );
	//		launch_dir = AnglesToForward( ( 0, random_yaw, 0 ) );
	//		vel = launch_dir * RandomIntRange( 500, 1000 );
	//	}
	//	else
	//	{
	//		vel += dir * 1000.0;
	//	}
	//
	//	self SetVehVelocity( vel );

	point_2d = ( point[0], point[1], self.origin[2] );

	//DebugStar( self.origin, 1000, ( 1, 1, 1 ) );
	//DebugStar( point_2d, 1000, ( 1, 0, 0 ) );

	torque = ( 0, 720, 0 );
	if ( Distance( self.origin, point_2d ) > 5 )
	{
		local_hit_point = point_2d - self.origin;

		dir_2d = ( dir[0], dir[1], 0 );
		if ( Length( dir_2d ) > 0.01 )
		{
			dir_2d = VectorNormalize( dir_2D );
			torque = VectorCross( VectorNormalize( local_hit_point ), dir );
			torque = ( 0, 0, torque[2] );
			torque = VectorNormalize( torque );
			torque = ( 0, torque[2] * 180, 0 );
		}
	}

	while ( 1 )
	{
		ang_vel = self GetAngularVelocity();
		ang_vel += torque * 0.05;

		const max_angluar_vel = 360;
		if ( ang_vel[1] < max_angluar_vel * -1 )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
		}
		else if ( ang_vel[1] > max_angluar_vel )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
		}

		self SetAngularVelocity( ang_vel );

		{wait(.05);};
	}
}


function boat_crash( point, dir )
{
	self.crashing = true;
	//	self thread play_crashing_loop();

	if ( isdefined( self.unloading ) )
	{
		while ( isdefined( self.unloading ) )
		{
			{wait(.05);};
		}
	}

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

	self thread boat_crash_movement( point, dir );
}

function boat_crash_movement( point, dir )
{
	self endon( "crash_move_done" );
	self endon( "death" );

	//	self thread crash_collision_test();
	self CancelAIMove();
	self ClearVehGoalPos();

	self SetPhysAcceleration( ( 0, 0, -50 ) );

	vel = self.velocity;
	dir = VectorNormalize( dir );
	///	self SetVehVelocity( ( 0, 0, 0 ) );

	ang_vel = self GetAngularVelocity();
	ang_vel = ( 0, 0, 0 );
	self SetAngularVelocity( ang_vel );

	torque = ( RandomIntRange( -5, -3 ), 0, ( RandomIntRange( 0, 100 ) < 50 ? -5 : 5 ) );

	self thread boat_crash_monitor( point, dir, 4 );

	while ( 1 )
	{
		ang_vel = self GetAngularVelocity();
		ang_vel += torque * 0.05;

		const max_angluar_vel = 360;
		if ( ang_vel[1] < max_angluar_vel * -1 )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
		}
		else if ( ang_vel[1] > max_angluar_vel )
		{
			ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
		}

		self SetAngularVelocity( ang_vel );

		velocity = self.velocity;
		velocity = (velocity[0] * 0.975, velocity[1], velocity[2]);
		velocity = (velocity[0], velocity[1] * 0.975, velocity[2]);
		self SetVehVelocity( velocity );

		{wait(.05);};
	}
}

function boat_crash_monitor( point, dir, crash_time )
{
	self endon( "death" );

	wait( crash_time );

	self notify( "crash_move_done" );
	self crash_stop();
	self notify( "crash_done" );
}

function crash_stop()
{
	self endon( "death" );

	self SetPhysAcceleration( ( 0, 0, 0 ) );
	self SetRotorSpeed( 0 );

	speed = self GetSpeedMPH();
	while ( speed > 2 )
	{
		velocity = self.velocity;
		velocity *= 0.9;
		self SetVehVelocity( velocity );

		angular_velocity = self GetAngularVelocity();
		angular_velocity *= 0.9;
		self SetAngularVelocity( angular_velocity );

		speed = self GetSpeedMPH();

		{wait(.05);};
	}

	self SetVehVelocity( ( 0, 0, 0 ) );
	self SetAngularVelocity( ( 0, 0, 0 ) );

	self vehicle::toggle_tread_fx( false );
	self vehicle::toggle_exhaust_fx( false );
	self vehicle::toggle_sounds( false );
}

function crash_collision_test()
{
	self endon( "death" );

	self waittill( "veh_collision", velocity, normal );

	self helicopter_explode();
	self notify( "crash_move_done" );

	if ( normal[2] > 0.7 )
	{
		forward = AnglesToForward( self.angles );
		right = VectorCross( normal, forward );
		desired_forward = VectorCross( right, normal );
		self SetPhysAngles( VectorToAngles( desired_forward ) );

		self crash_stop();
		self notify( "crash_done" );
	}
	else
	{
		{wait(.05);};
		self Delete();
	}
}

function crash_path_check( node )
{
	// find a crashnode on the current path
	// this only works on ground info_vehicle_node vheicles. not dynamic helicopter script_origin paths. they have their own dynamic crashing.
	targ = node;
	search_depth = 5;

	while ( isdefined( targ ) && search_depth >= 0 )
	{
		if ( ( isdefined( targ.detoured ) ) && ( targ.detoured == 0 ) )
		{
			detourpath = vehicle::path_detour_get_detourpath( getvehiclenode( targ.target, "targetname" ) );
			if ( isdefined( detourpath ) && isdefined( detourpath.script_crashtype ) )
			{
				return true;
			}
		}
		if ( isdefined( targ.target ) )
		{
			// Edited for the case of nodes targetting eachother for looping paths 1/30/08 TFlame
			targ1 = getvehiclenode( targ.target, "targetname" );
			if ( isdefined( targ1 ) && isdefined( targ1.target ) && isdefined( targ.targetname ) && targ1.target == targ.targetname )
			{
				return false;
			}
			else if ( isdefined( targ1 ) && targ1 == node ) // circular case -AP 1/15/09
			{
				return false;
			}
			else
			{
				targ = targ1;
			}

		}
		else
		{
			targ = undefined;
		}

		search_depth--;
	}
	return false;

}

function death_firesound( sound )
{
	self thread sound::loop_on_tag( sound, undefined, false );
	self util::waittill_any( "fire_extinguish", "stop_crash_loop_sound" );
	if ( !isdefined( self ) )
	{
		return;
	}
	self notify( "stop sound" + sound );
}

function death_fx()
{
	// going to use vehicletypes for identifying a vehicles association with effects.
	// will add new vehicle types if vehicle is different enough that it needs to use
	// different effect. also handles the sound
	if ( self vehicle::is_destructible() )
	{
		return;
	}

	self util::explode_notify_wrapper();

	if(isdefined(self.do_death_fx))
	{
		self [[self.do_death_fx]]();
	}
	else
	{
		self vehicle::do_death_fx();
	}
}


function death_make_badplace( type )
{
	if ( !isdefined( level.vehicle_death_badplace[ type ] ) )
	{
		return;
	}

	struct = level.vehicle_death_badplace[ type ];
	if ( isdefined( struct.delay ) )
	{
		wait struct.delay;
	}

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

	badplace_box( "vehicle_kill_badplace", struct.duration, self.origin, struct.radius, "all" );
}

function death_jolt( type )
{
	self endon( "death" );

	if ( ( isdefined( self.ignore_death_jolt ) && self.ignore_death_jolt ) )
	{
		return;
	}

	self JoltBody( ( self.origin + ( 23, 33, 64 ) ), 3 );

	if ( isdefined( self.death_anim ) )
	{
		self AnimScripted( "death_anim", self.origin, self.angles, self.death_anim, "normal", %root, 1, 0 );
		self waittillmatch( "death_anim", "end" );
	}
	else // if ( !isdefined( self.destructibledef ) )
	{
		if ( self.isphysicsvehicle )
		{
			num_launch_multiplier = 1;

			if ( isdefined( self.physicslaunchdeathscale ) )
			{
				num_launch_multiplier = self.physicslaunchdeathscale;
			}
			
			self LaunchVehicle( (0, 0, 180) * num_launch_multiplier, (RandomFloatRange(5, 10), RandomFloatRange(-5, 5), 0), true, false, true );
		}
	}
}

function deathrollon()
{
	if ( self.health > 0 )
	{
		self.rollingdeath = 1;
	}
}

function deathrolloff()
{
	self.rollingdeath = undefined;
	self notify( "deathrolloff" );
}

function loop_fx_on_vehicle_tag( effect, loopTime, tag )
{
	assert( isdefined( effect ) );
	assert( isdefined( tag ) );
	assert( isdefined( loopTime ) );

	self endon( "stop_looping_death_fx" );

	while ( isdefined( self ) )
	{
		playfxontag( effect, deathfx_ent(), tag );
		wait loopTime;
	}
}

function deathfx_ent()
{
	if ( !isdefined( self.deathfx_ent ) )
	{
		ent = Spawn( "script_model", ( 0, 0, 0 ) );
		emodel = vehicle::get_dummy();
		ent setmodel( self.model );
		ent.origin = emodel.origin;
		ent.angles = emodel.angles;
		ent notsolid();
		ent hide();
		ent linkto( emodel );
		self.deathfx_ent = ent;
	}
	else
	{
		self.deathfx_ent setmodel( self.model );
	}
	return self.deathfx_ent;
}

function death_cleanup_level_variables()
{
	script_linkname = self.script_linkname;
	targetname = self.targetname;

	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
	// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
	if ( isdefined( script_linkname ) )
	{
		ArrayRemoveValue( level.vehicle_link[ script_linkname ], self );
	}

	if ( isdefined( self.script_VehicleSpawngroup ) )
	{
		if ( isdefined( level.vehicle_SpawnGroup[ self.script_VehicleSpawngroup ] ) )
		{
			ArrayRemoveValue( level.vehicle_SpawnGroup[ self.script_VehicleSpawngroup ], self );
			ArrayRemoveValue( level.vehicle_SpawnGroup[ self.script_VehicleSpawngroup ], undefined );
		}
	}

	if ( isdefined( self.script_VehicleStartMove ) )
	{
		ArrayRemoveValue( level.vehicle_StartMoveGroup[ self.script_VehicleStartMove ], self );
	}

	if ( isdefined( self.script_vehicleGroupDelete ) )
	{
		ArrayRemoveValue( level.vehicle_DeleteGroup[ self.script_vehicleGroupDelete ], self );
	}
}

function death_cleanup_riders()
{
	// if vehicle is gone then delete the ai here.
	if ( isdefined( self.riders ) )
	{
		for ( j = 0; j < self.riders.size; j++ )
		{
			if ( isdefined( self.riders[ j ] ) )
			{
				self.riders[ j ] delete();
			}
		}
	}

	if ( vehicle::is_corpse( self ) )
	{
		self.riders = [];
	}
}

function death_radius_damage( meansOfDamage = "MOD_EXPLOSIVE" )
{
	self endon( "death" );

	if ( !isdefined( self ) || self.abandoned === true || self.damage_on_death === false || self.radiusdamageradius <= 0 )
	{
		return;
	}

	position = self.origin + ( 0,0,15 );
	radius = self.radiusdamageradius;
	damageMax = self.radiusdamagemax;
	damageMin = self.radiusdamagemin;
	attacker = self;

	{wait(.05);};

	if ( isdefined( self ) )
	{
		self RadiusDamage( position, radius, damageMax, damageMin, attacker, meansOfDamage );
	}
}


function death_update_crash( point, dir )
{
	if ( !isdefined( self.destructibledef ) )
	{
		if ( isdefined( self.script_crashtypeoverride ) )
		{
			crashtype = self.script_crashtypeoverride;
		}
		else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) )
		{
			crashtype = "aircraft";
		}
		else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
		{
			crashtype = "helicopter";
		}
		else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "boat" ) ) )
		{
			crashtype = "boat";
		}
		else if ( isdefined( self.currentnode ) && crash_path_check( self.currentnode ) )
		{
			crashtype = "none";
		}
		else
		{
			crashtype = "tank";  // tanks used to be the only vehicle that would stop. legacy nonsense from CoD1
		}

		if ( crashtype == "aircraft" )
		{
			self thread aircraft_crash( point, dir );
		}
		else if ( crashtype == "helicopter" )
		{
			if ( isdefined( self.script_nocorpse ) )
			{
				// GLocke - Does not drop a physics script model on death
				self thread helicopter_explode();
			}
			else
			{
				self thread helicopter_crash( point, dir );
			}
		}
		else if ( crashtype == "boat" )
		{
			self thread boat_crash( point, dir );
		}
		else if ( crashtype == "tank" )
		{
			if ( !isdefined( self.rollingdeath ) )
			{
				self vehicle::set_speed( 0, 25, "Dead" );
			}
			else
			{
				// dpg 3/12/08 removed this. if you need it for something, let someone know
				//self vehicle::set_speed( 8, 25, "Dead rolling out of path intersection" );
				self waittill( "deathrolloff" );
				self vehicle::set_speed( 0, 25, "Dead, finished path intersection" );
			}

			wait .4;

			if ( isdefined( self ) && !vehicle::is_corpse( self ) )
			{
				self vehicle::set_speed( 0, 10000, "deadstop" );

				self notify( "deadstop" );
				if ( self.disconnectPathOnStop === true )
				{
					self vehicle::disconnect_paths();
				}

				if ( ( isdefined( self.tankgetout ) ) && ( self.tankgetout > 0 ) )
				{
					// tankgetout will never get notified if there are no guys getting out
					self waittill( "animsdone" );
				}
			}
		}
	}
}

function waittill_crash_done_or_stopped()
{
	self endon( "death" );

	if ( isdefined( self ) && ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) || ( isdefined(self.vehicleclass) && (self.vehicleclass == "boat" ) ) ) )
	{
		if ( ( isdefined( self.crashing ) ) && ( self.crashing == true ) )
		{
			self waittill( "crash_done" );
		}
	}
	else
	{
		wait 0.2;	// don't check the velocity right away because we might have been launched

		if ( self.isphysicsvehicle )
		{
			self ClearVehGoalPos();
			self CancelAIMove();
			//GLocke 2/16/10 - just wait for physics vehicles to get close to 0
			stable_count = 0;
			while( stable_count < 3 )
			{
				if ( isdefined( self.velocity ) && LengthSquared( self.velocity ) > 1.0 )
				{
					stable_count = 0;
				}
				else
				{
					stable_count++;
				}
				wait( 0.3 );
			}
			self vehicle::disconnect_paths();
		}
		else
		{
			while ( isdefined( self ) && self GetSpeedMPH() > 0 )
			{
				wait( 0.3 );
			}
		}
	}
}


function vehicle_damage_filter_damage_watcher( driver, heavy_damage_threshold )
{
	self endon( "death" );
	self endon( "exit_vehicle" );
	self endon( "end_damage_filter" );

	if ( !isdefined( heavy_damage_threshold ) )
	{
		heavy_damage_threshold = 100;
	}

	while ( 1 )
	{
		self waittill( "damage",  damage, attacker, direction, point, type, tagName, modelName, partname, weapon );

		//IPrintLn( "damage: " + damage );
		//self ClearDamageIndicator();
		Earthquake( 0.25, 0.15, self.origin, 512, self );
		driver PlayRumbleOnEntity( "damage_light" );

		time = GetTime();
		if ( ( time - level.n_last_damage_time ) > 500 )
		{
			level.n_hud_damage = true;

			if ( damage > heavy_damage_threshold )
			{
				//rpc( "scripts/_vehicle", "damage_filter_heavy" );
				driver playsound ( "veh_damage_filter_heavy" );
			}
			else
			{
				//rpc( "scripts/_vehicle", "damage_filter_light" );
				driver playsound ( "veh_damage_filter_light" );
			}

			level.n_last_damage_time = GetTime();
		}
	}
}

function vehicle_damage_filter_exit_watcher( driver )
{
	self util::waittill_any( "exit_vehicle", "death", "end_damage_filter" );

	//rpc( "scripts/_vehicle", "damage_filter_disable" );
	//rpc( "scripts/_vehicle", "damage_filter_off" );

	//self.damage_filter_init = undefined;

	//if ( isdefined( driver.save_visionset ) )
	//{
	//	//TODO T7 - convert to use manager
	//	driver VisionSetNaked( driver.save_visionset, 0 );
	//	#endif
	//}
}

function vehicle_damage_filter( vision_set, heavy_damage_threshold, filterid = 0, b_use_player_damage = false )
{
	self endon( "death" );
	self endon( "exit_vehicle" );
	self endon( "end_damage_filter" );
	
	driver = self GetSeatOccupant( 0 );

	if ( !isdefined( self.damage_filter_init ) )
	{
		//rpc( "scripts/_vehicle", "init_damage_filter", filterid );
		self.damage_filter_init = true;
	}
	else
	{
		//rpc( "scripts/_vehicle", "damage_filter_enable", 0, filterid );
	}

	if ( isdefined( vision_set ) )
	{
		//TODO T7 - convert to use manager
		/*level.player.save_visionset = level.player GetVisionSetNaked();
		level.player VisionSetNaked( vision_set, 0.5 );*/
	}

	level.n_hud_damage = false;
	level.n_last_damage_time = GetTime();

	damagee = ( ( isdefined( b_use_player_damage ) && b_use_player_damage ) ? driver : self );

	damagee thread vehicle_damage_filter_damage_watcher( driver, heavy_damage_threshold );
	damagee thread vehicle_damage_filter_exit_watcher( driver );

	while ( 1 )
	{
		if ( ( isdefined( level.n_hud_damage ) && level.n_hud_damage ) )
		{
			time = GetTime();
			if ( ( time - level.n_last_damage_time ) > 500 )
			{
				//rpc( "scripts/_vehicle", "damage_filter_off" );
				level.n_hud_damage = false;
			}
		}

		{wait(.05);};
	}
}

// self == vehicle
function flipping_shooting_death( attacker, hitDir )
{
	// do we need this?
	if( isdefined( self.delete_on_death ) )
	{
		if ( isdefined( self ) )
		{
			self delete();
		}
		
		return;
	}
	
	if ( !isdefined( self ) )
	{
		return;
	}
	
	self endon( "death" ); //quit thread if deleted
	
	self vehicle_death::death_cleanup_level_variables();			
	
	self DisableAimAssist();
	
	self vehicle_death::death_fx();
	self thread vehicle_death::death_radius_damage();
	self thread vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
	
	self vehicle::toggle_tread_fx( false );
	self vehicle::toggle_exhaust_fx( false );
	self vehicle::toggle_sounds( false );
	self vehicle::lights_off();
	self thread flipping_shooting_crash_movement( attacker, hitDir );
	
	self waittill( "crash_done" );
	
	while( ( isdefined( self.controlled ) && self.controlled ) )
		{wait(.05);};
	
	// A dynEnt will be spawned in the collision thread when it hits the ground and "crash_done" notify will be sent
	self delete();
}

function plane_crash()
{
	self endon( "death" );

	self SetPhysAcceleration( ( 0, 0, -1000 ) );
	self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify

	forward = AnglesToForward( self.angles );
	forward_mag = RandomFloatRange( 0, 300 );
	forward_mag += math::sign( forward_mag ) * 400;
	forward *= forward_mag;

	new_vel = forward + (self.velocity * 0.2);

	ang_vel = self GetAngularVelocity();

	yaw_vel = RandomFloatRange( 0, 130 ) * math::sign( ang_vel[1] );
	yaw_vel += math::sign( yaw_vel ) * 20;

	ang_vel = ( RandomFloatRange( -1, 1 ), yaw_vel, 0 );

	// setup the roll to look correct
	roll_amount = ( abs( ang_vel[1] ) / 150.0 ) * 30.0;
	if( ang_vel[1] > 0 )
	{
		roll_amount = -roll_amount;
	}

	self.angles = ( self.angles[0], self.angles[1], roll_amount );
	ang_vel = ( ang_vel[0], ang_vel[1], roll_amount * 0.9 );

	// set how much of the forward velocity to rotate when the vehicle rotates, more like a plane
	self.velocity_rotation_frac = 1.0;//RandomFloatRange( 0.95, 0.99 );

	self.crash_accel = RandomFloatRange( 65, 90 );

	set_movement_and_accel( new_vel, ang_vel );
}

function barrel_rolling_crash()
{
	self endon( "death" );

	self SetPhysAcceleration( ( 0, 0, -1000 ) );
	self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify

	forward = AnglesToForward( self.angles );
	forward_mag = RandomFloatRange( 0, 250 );
	forward_mag += math::sign( forward_mag ) * 300;
	forward *= forward_mag;

	new_vel = forward + (0,0,70);

	ang_vel = self GetAngularVelocity();
	yaw_vel = RandomFloatRange( 0, 60 ) * math::sign( ang_vel[1] );
	yaw_vel += math::sign( yaw_vel ) * 30;

	roll_vel = RandomFloatRange( -200, 200 );
	roll_vel += math::sign( roll_vel ) * 300;
	ang_vel = ( RandomFloatRange( -5, 5 ), yaw_vel, roll_vel );

	// set how much of the forward velocity to rotate when the vehicle rotates, more like a plane
	self.velocity_rotation_frac = 1.0;//RandomFloatRange( 0.95, 0.99 );

	self.crash_accel = RandomFloatRange( 145, 210 );

	self SetPhysAcceleration( ( 0, 0, -250 ) );

	set_movement_and_accel( new_vel, ang_vel );
}

function random_crash( hitdir )
{
	self endon( "death" );

	self SetPhysAcceleration( ( 0, 0, -1000 ) );
	self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify

	if( !isdefined( hitdir ) )
	{
		hitdir = (1,0,0);
	}
	hitdir = VectorNormalize( hitdir );

	side_dir = VectorCross( hitdir, (0,0,1) );
	side_dir_mag = RandomFloatRange( -280, 280 );
	side_dir_mag += math::sign( side_dir_mag ) * 150;
	side_dir *= side_dir_mag;

	forward = AnglesToForward( self.angles );
	forward_mag = RandomFloatRange( 0, 300 );
	forward_mag += math::sign( forward_mag ) * 30;
	forward *= forward_mag;

	new_vel = (self.velocity * 1.2) + forward + side_dir + (0,0,50);

	ang_vel = self GetAngularVelocity();
	ang_vel = ( ang_vel[0] * 0.3, ang_vel[1], ang_vel[2] * 1.2 );

	yaw_vel = RandomFloatRange( 0, 130 ) * math::sign( ang_vel[1] );
	yaw_vel += math::sign( yaw_vel ) * 50;

	ang_vel += ( RandomFloatRange( -5, 5 ), yaw_vel, RandomFloatRange( -18, 18 ) );

	// set how much of the forward velocity to rotate when the vehicle rotates, more like a plane
	self.velocity_rotation_frac = RandomFloatRange( 0.3, 0.99 );

	self.crash_accel = RandomFloatRange( 65, 90 );

	set_movement_and_accel( new_vel, ang_vel );
}

function set_movement_and_accel( new_vel, ang_vel )
{
	self death_fx();
	self thread death_radius_damage();

	self SetVehVelocity( new_vel );
	self SetAngularVelocity( ang_vel );
	
	if( !isdefined( self.off ) )
	{
		self thread flipping_shooting_crash_accel();
	}
	self thread vehicle_ai::nudge_collision();
	
	//drone death sounds JM - play 1 shot hit, turn off main loop, thread dmg loop
	self playsound("veh_wasp_dmg_hit");
	self vehicle::toggle_sounds( 0 );
	
	if( !isdefined( self.off ) )
	{
		self thread flipping_shooting_dmg_snd();
	}

	wait 0.1;
	
	if( RandomInt( 100 ) < 40 && !isdefined( self.off ) && self.variant !== "rocket" )
	{
		self thread vehicle_ai::fire_for_time( RandomFloatRange( 0.7, 2.0 ) );
	}
	
	result = self util::waittill_any_timeout( 15, "crash_done" );

	if ( result === "crash_done" )
	{
		self vehicle::do_death_dynents();
		self vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
	}
	else
	{
		// failsafe notify
		self notify( "crash_done" );
	}
}

function flipping_shooting_crash_movement( attacker, hitdir )
{
	self endon( "crash_done" );
	self endon( "death" );
	
	self CancelAIMove();
	self ClearVehGoalPos();	
	self ClearLookAtEnt();
	
	self SetPhysAcceleration( ( 0, 0, -1000 ) );
	self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify
	
	if( !isdefined( hitdir ) )
	{
		hitdir = (1,0,0);
	}
	
	hitdir = VectorNormalize( hitdir );
	
	new_vel = self.velocity;
	
	self.crash_style = GetDvarInt( "debug_crash_type" );
	if( self.crash_style == -1 )
	{
		self.crash_style = RandomInt( 3 );
	}
	
	switch( self.crash_style )
	{
	case 0: barrel_rolling_crash(); break;
	case 1: plane_crash(); break;
	default: random_crash( hitdir );
	}
}

function flipping_shooting_dmg_snd()
{
	dmg_ent = Spawn("script_origin", self.origin);
	dmg_ent linkto (self);
	dmg_ent PlayLoopSound ("veh_wasp_dmg_loop");
	self util::waittill_any("crash_done", "death");
	dmg_ent stoploopsound(1);
	wait (2);
	dmg_ent delete();
}

function flipping_shooting_crash_accel()
{
	self endon( "crash_done" );
	self endon( "death" );
	
	count = 0;
	
	prev_forward = AnglesToForward( self.angles );
	prev_forward_vel = VectorDot( self.velocity, prev_forward ) * self.velocity_rotation_frac;
	if( prev_forward_vel < 0 )
	{
		prev_forward_vel = 0;
	}
	
	while( 1 )
	{
		self SetVehVelocity( self.velocity + AnglesToUp( self.angles ) * self.crash_accel );
		self.crash_accel *= 0.98;
		
		// Rotate part of the velocity
		new_velocity = self.velocity;
		new_velocity -= prev_forward * prev_forward_vel;
		
		forward = AnglesToForward( self.angles );
		new_velocity += forward * prev_forward_vel;// * 0.98;
		
		prev_forward = forward;
		prev_forward_vel = VectorDot( new_velocity, prev_forward ) * self.velocity_rotation_frac;
		if( prev_forward_vel < 10 )
		{
			new_velocity += forward * 40;
			prev_forward_vel = 0;
		}
		
		self SetVehVelocity( new_velocity );
		
		wait 0.1;
		
		count++;
		if ( count % 8 == 0 && RandomInt( 100 ) > 40 )
		{
			if ( self.velocity[2] > 130.0 )
			{
				self.crash_accel *= 0.75;
			}
			else if ( self.velocity[2] < 40.0 && count < 60 )
			{
				if ( Abs( self.angles[0] ) > 35 || Abs( self.angles[2] ) > 35 ) // tilted
				{
					self.crash_accel = RandomFloatRange( 100, 150 );
				}
				else
				{
					self.crash_accel = RandomFloatRange( 45, 70 );
				}
			}
			
			//ang_vel = self GetAngularVelocity();
			//ang_vel += ( RandomFloatRange( -10, 10 ), RandomFloatRange( -35, 35 ), RandomFloatRange( -1, 1 ) );
			//self SetAngularVelocity( ang_vel );
		}
	}
}

function death_fire_loop_audio()
{
	sound_ent = Spawn( "script_origin", self.origin );
	sound_ent PlayLoopSound( "veh_qrdrone_death_fire_loop" , .1 );
	wait 11;
	sound_ent StopLoopSound( 1 );
	sound_ent delete();
}

function FreeWhenSafe( time = 4 )
{
	self thread DelayedRemove_thread( time, false );
}

function DeleteWhenSafe( time = 4 )
{
	self thread DelayedRemove_thread( time, true );
}

function DelayedRemove_thread( time, shouldDelete )
{
	if ( !isdefined( self ) )
	{
		return;
	}

	self endon ( "death" );
	self endon ( "free_vehicle" );

	if ( shouldDelete === true )
	{
		self SetVehVelocity( ( 0, 0, 0 ) );
		self Ghost();
		self NotSolid();
	}

	util::waitForTimeAndNetworkFrame( time );

	if ( shouldDelete === true )
	{
		self Delete();
	}
	else
	{
		self FreeVehicle();
	}
}

function CleanUp()
{
	if ( isdefined( self.cleanup_after_time ) )
	{
		wait self.cleanup_after_time;
		if ( isdefined( self ) )
		{
			self Delete();
		}
	}
}