#using scripts\codescripts\struct;

#using scripts\shared\audio_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\challenges_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\killstreaks_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\util_shared;
#using scripts\shared\_oob;
#using scripts\shared\popups_shared;
#using scripts\shared\vehicle_shared;
#using scripts\shared\weapons\_heatseekingmissile;
#using scripts\shared\weapons\_weaponobjects;
#using scripts\shared\weapons\_hacker_tool;
#using scripts\shared\visionset_mgr_shared;

#using scripts\mp\_util;
#using scripts\mp\gametypes\_globallogic;
#using scripts\mp\gametypes\_globallogic_audio;
#using scripts\mp\gametypes\_spawning;
#using scripts\mp\killstreaks\_airsupport;
#using scripts\mp\killstreaks\_helicopter;
#using scripts\mp\killstreaks\_killstreak_bundles;
#using scripts\mp\killstreaks\_killstreak_detect;
#using scripts\mp\killstreaks\_killstreak_hacking;
#using scripts\mp\killstreaks\_killstreakrules;
#using scripts\mp\killstreaks\_killstreaks;
#using scripts\mp\teams\_teams;
#using scripts\mp\killstreaks\_remote_weapons;

  
                                                                                                   	  	                                                                    	                     	                 	                                                                         	                       	                                                      	                                                      	                                                       	                                                                                                  	                                                                                                                 	                                                   	                	                                          	                                      	       	  	             	                                                     	                                                                             	                         	                                                                	                                                             	                                                             	                                                                                      	                                                         	                                                         	                                                               	                                                               	                                                   	                                                                	                                                     	                                                	                                                	                                                        	                                                                                                  	                                                                             	                                            	                                                                                    	                                          	                     	                                              	                                        	                 	   
                                             
    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	   	                                                                                                                                                                                                                                  	                                                                                                                                                                                                                                                                                                                                                                                                                                                   

#precache( "string", "KILLSTREAK_EARNED_HELICOPTER_GUNNER" );
#precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_NOT_AVAILABLE" );
#precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_INBOUND" );
#precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_HACKED" );
#precache( "string", "KILLSTREAK_DESTROYED_HELICOPTER_GUNNER" );
#precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" );
#precache( "eventstring", "mpl_killstreak_osprey_strt" );

#namespace helicopter_gunner;




	


	

	
function init()
{
	killstreaks::register( "helicopter_gunner", "helicopter_player_gunner", "killstreak_helicopter_player_gunner", "helicopter_used", &ActivateMainGunner, true );
	killstreaks::register_strings( "helicopter_gunner", &"KILLSTREAK_EARNED_HELICOPTER_GUNNER", &"KILLSTREAK_HELICOPTER_GUNNER_NOT_AVAILABLE", &"KILLSTREAK_HELICOPTER_GUNNER_INBOUND", undefined, &"KILLSTREAK_HELICOPTER_GUNNER_HACKED" );
	killstreaks::register_dialog( "helicopter_gunner", "mpl_killstreak_osprey_strt", "helicopterGunnerDialogBundle", "helicopterGunnerPilotDialogBundle", "friendlyHelicopterGunner", "enemyHelicopterGunner", "enemyHelicopterGunnerMultiple", "friendlyHelicopterGunnerHacked", "enemyHelecopterGunnerHacked", "requestHelicopterGunner", "threatHelicopterGunner" );
	killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_rockets" );
	killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_primary" );
	killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_secondary" );
	killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_tertiary" );
	killstreaks::set_team_kill_penalty_scale( "helicopter_gunner", level.teamKillReducedPenalty );
	killstreaks::devgui_scorestreak_command( "helicopter_gunner", "Debug Paths", "toggle scr_devHeliPathsDebugDraw 1 0");
	
	killstreaks::register( "helicopter_gunner_assistant", "helicopter_gunner_assistant", "killstreak_" + "helicopter_gunner_assistant", "helicopter_used", &ActivateSupportGunner, true, undefined, false, false );
	killstreaks::register_strings( "helicopter_gunner_assistant", &"KILLSTREAK_EARNED_HELICOPTER_GUNNER", &"KILLSTREAK_HELICOPTER_GUNNER_NOT_AVAILABLE", &"KILLSTREAK_HELICOPTER_GUNNER_INBOUND", undefined, &"KILLSTREAK_HELICOPTER_GUNNER_HACKED" );
	killstreaks::register_dialog( "helicopter_gunner_assistant", "mpl_killstreak_osprey_strt", "helicopterGunnerDialogBundle", "helicopterGunnerPilotDialogBundle", "friendlyHelicopterGunner", "enemyHelicopterGunner", "enemyHelicopterGunnerMultiple", "friendlyHelicopterGunnerHacked", "enemyHelecopterGunnerHacked", "requestHelicopterGunner", "threatHelicopterGunner" );
	killstreaks::set_team_kill_penalty_scale( "helicopter_gunner_assistant", level.teamKillReducedPenalty );

	// TODO: Move to killstreak data
	level.killstreaks["helicopter_gunner"].threatOnKill = true;
	
	callback::on_connect( &OnPlayerConnect );
	callback::on_spawned( &UpdatePlayerState );
	callback::on_joined_team( &UpdatePlayerState );
	callback::on_joined_spectate( &UpdatePlayerState );
	callback::on_disconnect( &UpdatePlayerState );
	callback::on_player_killed( &UpdatePlayerState );
	
	clientfield::register( "vehicle", "vtol_turret_destroyed_0", 1, 1, "int" );
	clientfield::register( "vehicle", "vtol_turret_destroyed_1", 1, 1, "int" );
	clientfield::register( "vehicle", "mothership", 1, 1, "int" );
	clientfield::register( "toplayer", "vtol_update_client", 1, 1, "counter" );
	clientfield::register( "toplayer", "fog_bank_2", 1, 1, "int" );

	visionset_mgr::register_info( "visionset", "mothership_visionset", 1, 70, 16, true, &visionset_mgr::ramp_in_out_thread_per_player_death_shutdown, false  );
		
	level thread WaitForGameEndThread();
	
	level.vtol = undefined;
}

function OnPlayerConnect()
{
	if( !isdefined( self.entNum ) )
	{
		self.entNum = self getEntityNumber();
	}
}

function UpdatePlayerState()
{
	player = self;
	UpdateAllKillstreakInventory();
}

function UpdateAllKillstreakInventory()
{
	foreach( player in level.players )
	{
		if( isdefined( player.sessionstate ) && player.sessionstate == "playing" )
			UpdateKillstreakInventory( player );
	}
}
	
function UpdateKillstreakInventory( player )
{
	if( !isdefined( player ) )
		return;
	
	heli_team = undefined;
	if( isdefined( level.vtol ) && isdefined( level.vtol.owner ) && !level.vtol.shuttingDown && ( level.vtol.totalRocketHits < ( 6 ) ) )
		heli_team = level.vtol.owner.team;
	
	if( isdefined( heli_team ) && ( player.team == heli_team ) )
	{
		if( ( GetFirstAvailableSeat( player ) != -1 ) && !isdefined( level.vtol.usage[player.entNum] ) )
		{
			if( !player killstreaks::has_killstreak( "inventory_helicopter_gunner_assistant" ) )
				player killstreaks::give( "inventory_helicopter_gunner_assistant", undefined, undefined, true, true );
			return;
		}
	}

	if( player killstreaks::has_killstreak( "inventory_helicopter_gunner_assistant" ) )
		player killstreaks::take( "inventory_helicopter_gunner_assistant" );
}

function ActivateMainGunner( killstreakType )
{
	player = self;
	
	while( isdefined( level.vtol ) && level.vtol.shuttingdown )
	{
		if( !player killstreakrules::isKillstreakAllowed( "helicopter_gunner", player.team ) )
			return false;
	}

	player util::freeze_player_controls( true );
	
	result = player SpawnHeliGunner();
	
	player util::freeze_player_controls( false );
	
	if( level.gameEnded )
		return true;	
	
	if( !isdefined( result ) )
		return false;

	return result;
}

function ActivateSupportGunner( killstreakType )
{
	player = self;
	
	if( isdefined( level.vtol ) && level.vtol.shuttingdown )
		return false;

	if( isdefined( level.vtol.usage[player.entNum] ) )
		return false;
	
	player util::freeze_player_controls( true );	

	result = player EnterHelicopter( false );
	
	player util::freeze_player_controls( false );	

	return result;
}
	
function GetFirstAvailableSeat( player )
{
	if( isdefined( level.vtol ) && ( !level.vtol.shuttingDown ) && ( level.vtol.team == player.team ) && ( level.vtol.owner != player ) )
	{
		for( i = 0; i < ( 2 ); i++ )
		{
			if( !isdefined( level.vtol.assistants[i].occupant ) && !level.vtol.assistants[i].destroyed )
			{
				return i;
			}
		}
	}
	
	return -1;
}

function InitHelicopterSeat( index, destroyTag )
{
	level.vtol.assistants[index] = SpawnStruct();
	assistant = level.vtol.assistants[index];
	
	assistant.occupant = undefined;
	assistant.destroyed = false;
	assistant.rocketHits = 0;
	assistant.targetTag = destroyTag;
	
	assistant.targetEnt = spawn( "script_model", ( 0, 0, 0 ) );
	assistant.targetEnt.useVTOLTime = true;	
	assistant.targetEnt SetModel( "p7_dogtags_enemy" ); // hack to send ent to clients for targeting
	assistant.targetEnt LinkTo( level.vtol, assistant.targetTag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
	assistant.targetEnt.team = level.vtol.team;
	Target_Set( assistant.targetEnt, ( 0, 0, 0 ) );
	Target_SetAllowHighSteering( assistant.targetEnt, true );
	assistant.targetEnt.parent = level.vtol;
	
	level.vtol vehicle::add_to_target_group( assistant.targetEnt );
}

function HackedPreFunction( hacker )
{
	heliGunner = self;
	heliGunner.owner unlink();
	level.vtol clientfield::set( "vehicletransition", 0 );
	visionset_mgr::deactivate( "visionset", "mothership_visionset", heliGunner.owner );
	heliGunner.owner SetModelLodBias( 0 );
	heliGunner.owner clientfield::set_to_player( "fog_bank_2", 0 );
	heliGunner.owner clientfield::set_to_player( "toggle_flir_postfx", 0 );
	heliGunner.owner notify( "gunner_left" );	
	heliGunner.owner killstreaks::clear_using_remote();
	heliGunner.owner killstreaks::unhide_compass();
	heliGunner.owner vehicle::stop_monitor_missiles_locked_on_to_me();
	heliGunner.owner vehicle::stop_monitor_damage_as_occupant();

	foreach( assistant in heliGunner.assistants )
	{
		if( isdefined( assistant.occupant ) )
			assistant.occupant iPrintLnBold( &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" );
		LeaveHelicopter( assistant.occupant, false );
	}
		
	heliGunner MakeVehicleUnusable();
}

function HackedPostFunction( hacker )
{
	heliGunner = self;
	heliGunner clientfield::set( "enemyvehicle", 2 );
	heliGunner MakeVehicleUsable();
	heliGunner UseVehicle( hacker, 0 );
	level.vtol clientfield::set( "vehicletransition", 1 );
	heliGunner thread vehicle::monitor_missiles_locked_on_to_me( hacker );
	heliGunner thread vehicle::monitor_damage_as_occupant( hacker );

	hacker thread WatchVisionSwitchThread();
	hacker killstreaks::hide_compass();
	heliGunner thread WatchPlayerExitRequestThread( hacker );
	visionset_mgr::activate( "visionset", "mothership_visionset", hacker, 1, heliGunner killstreak_hacking::get_hacked_timeout_duration_ms(), 1 );
	hacker SetModelLodBias( (isdefined(level.mothership_lod_bias)?level.mothership_lod_bias:8) );
	heliGunner.owner GiveDedicatedShadow( level.vtol );
	heliGunner.owner clientfield::set_to_player( "fog_bank_2", 1 );
	
	hacker thread WatchPlayerTeamChangeThread( heliGunner );
	hacker killstreaks::set_killstreak_delay_killcam( "helicopter_gunner" );
	
	if ( heliGunner.killstreak_timer_started )
	{
		heliGunner.killstreak_duration = heliGunner killstreak_hacking::get_hacked_timeout_duration_ms();
		heliGunner.killstreak_end_time = hacker killstreak_hacking::set_vehicle_drivable_time_starting_now( heliGunner );
		heliGunner.killstreakEndTime = int( heliGunner.killstreak_end_time );
	}
	else
	{
		heliGunner.killstreak_timer_start_using_hacked_time = true;
	}
}
	
function SpawnHeliGunner()
{
	player = self;
	player endon( "disconnect" );
	level endon( "game_ended" );
	
	if( !isdefined( level.heli_paths ) || !level.heli_paths.size )
		return false;

	if( !isdefined( level.Heli_primary_path ) || !level.heli_primary_path.size )
		return false;

	if( ( isdefined( player.isPlanting ) && player.isPlanting ) || ( isdefined( player.isDefusing ) && player.isDefusing ) || player util::isUsingRemote() || player IsWallRunning() || player oob::IsOutOfBounds() )
		return false;
	
	killstreak_id = player killstreakrules::killstreakStart( "helicopter_gunner", player.team, undefined, true );
	if( killstreak_id == (-1) )
		return false;
		
	startNode = level.heli_primary_path[0];
	
	level.vtol = SpawnVehicle( "veh_bo3_mil_gunship_mp", startnode.origin, startnode.angles, "dynamic_spawn_ai" );
	level.vtol killstreaks::configure_team( "helicopter_gunner", killstreak_id, player, "helicopter" );
	level.vtol killstreak_hacking::enable_hacking( "helicopter_gunner", &HackedPreFunction, &HackedPostFunction );
	level.vtol.killstreak_id = killstreak_id;
	level.vtol.destroyFunc = &DeleteHelicopterCallback;
	level.vtol.hardpointType = "helicopter_gunner";
	level.vtol clientfield::set( "enemyvehicle", 1 );
	level.vtol clientfield::set( "vtol_turret_destroyed_0", 0 );
	level.vtol clientfield::set( "vtol_turret_destroyed_1", 0 );	
	level.vtol clientfield::set( "mothership", 1 );	
	level.vtol vehicle::init_target_group();
	level.vtol.killstreak_timer_started = false;
	level.vtol.allowdeath = false;
	
	level.vtol.playerMovedRecently = false;
	level.vtol.soundmod = "default_loud";
	level.vtol hacker_tool::registerwithhackertool(( 50 ), ( 10000 ) );
	
	level.vtol.assistants = [];
	level.vtol.usage = [];
	
	InitHelicopterSeat( 0, "tag_gunner_barrel1" );
	InitHelicopterSeat( 1, "tag_gunner_barrel2");
	
	level.destructible_callbacks["turret_destroyed"] = &VTOLDestructibleCallback;
	level.destructible_callbacks["turret1_destroyed"] = &VTOLDestructibleCallback;
	level.destructible_callbacks["turret2_destroyed"] = &VTOLDestructibleCallback;
	
	level.vtol.shuttingDown = false;
	level.vtol thread PlayLockOnSoundsThread( player, level.vtol );
	
	level.vtol thread helicopter::wait_for_killed();
	level.vtol thread wait_for_bda_dialog();
	
	level.vtol.maxhealth = ( 15000 );
	tableHealth = killstreak_bundles::get_max_health( "helicopter_gunner" );
	
	if ( isdefined( tableHealth ) )
	{
		level.vtol.maxhealth = tableHealth;
	}	
	level.vtol.original_health = level.vtol.maxhealth;
	level.vtol.health = level.vtol.maxhealth;
	
	level.vtol SetCanDamage( true );
	level.vtol thread heatseekingmissile::MissileTarget_ProximityDetonateIncomingMissile( "death" );
	level.vtol thread WatchMissilesThread();

	attack_nodes = GetEntArray( "heli_attack_area", "targetname" );
	if( attack_nodes.size )
	{
		level.vtol thread HelicopterThinkThread( startNode, attack_nodes );
		player thread WatchLocationChangeThread( attack_nodes );
	}
	else
	{
		level.vtol thread helicopter::heli_fly( startNode, 0.0, "helicopter_gunner" );
	}
	
	level.vtol.totalRocketHits = 0;
	level.vtol.turretRocketHits = 0;
	level.vtol.targetEnt = undefined;
	
	level.vtol.overrideVehicleDamage = &HelicopterGunnerDamageOverride;	
	level.vtol.hackedHealthUpdateCallback = &HelicopterGunner_hacked_health_callback;
	level.vtol.DetonateViaEMP = &helicopteDetonateViaEMP;
	
	player thread killstreaks::play_killstreak_start_dialog( "helicopter_gunner", player.team, killstreak_id );
	level.vtol killstreaks::play_pilot_dialog_on_owner( "arrive", "helicopter_gunner", killstreak_id );
	
	player AddWeaponStat( GetWeapon( "helicopter_player_gunner" ), "used", 1 );
		
	level.vtol thread WaitForVTOLShutdownThread();

	result = player EnterHelicopter( true );
	
	return result;
}

function HelicopterGunner_hacked_health_callback()
{
	helicopter = self;
	
	if ( helicopter.shuttingDown == true ) 
	{
		return;
	}
	
// Not sure what design wants here.  
//	for( seatIndex = 0; seatIndex < HELICOPTER_GUNNER_ASSISTANT_SEAT_COUNT; seatIndex++ )
//	{
//		assistant = helicopter.assistants[seatIndex];
//		if( !assistant.destroyed )
//		{
//			damage = 1000;
//			helicopter.noDamageFeedback = 1;
//			helicopter DoDamage( damage, assistant.targetEnt.origin, undefined, undefined, undefined, "MOD_UNKNOWN", 0, undefined, seatIndex + 8 );
//			helicopter.noDamageFeedback = 0;
//			
//			SupportTurretDestroyed( helicopter, seatIndex );
//		}
//	}
//	helicopter AllowMainTurretLockon();
	
	hackedHealth = killstreak_bundles::get_hacked_health( "helicopter_gunner" );
	assert( isdefined( hackedHealth ) );
	if ( helicopter.health > hackedhealth )
	{
		helicopter.health = hackedhealth;	
	}
}


function WaitForGameEndThread()
{
	level waittill( "game_ended" );
	if( isdefined( level.vtol ) && isdefined( level.vtol.owner ))
		LeaveHelicopter( level.vtol.owner, true );
}

function WaitForVTOLShutdownThread()
{
	helicopter = self;
	helicopter waittill( "vtol_shutdown", attacker );
	
	if( isdefined( attacker ) )
	{
		LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_HELICOPTER_GUNNER", attacker.entnum );
	}
	
	if( isdefined( helicopter.targetEnt ) )
	{
		Target_Remove( helicopter.targetEnt );
		helicopter.targetEnt Delete();
		helicopter.targetEnt = undefined;
	}
	
	for( seatIndex = 0; seatIndex < ( 2 ); seatIndex++ )
	{
		assistant = level.vtol.assistants[seatIndex];
		if( isdefined( assistant.targetEnt ) )
		{
			Target_Remove( assistant.targetEnt );
			assistant.targetEnt Delete();
			assistant.targetEnt = undefined;
		}
	}	
	
	killstreakrules::killstreakStop( "helicopter_gunner", helicopter.originalTeam, helicopter.killstreak_id );
	
	LeaveHelicopter( level.vtol.owner, true );	
	
	level.vtol = undefined;
	
	helicopter delete();
}

function DeleteHelicopterCallback()
{
	helicopter = self;
	helicopter notify( "vtol_shutdown", undefined );
}

function OnTimeoutCallback()
{
	for( i = 0; i < ( 2 ); i++ )
	{
		if( isdefined(level.vtol.assistants[i].occupant )  )
		{
			level.vtol.assistants[i].occupant killstreaks::play_pilot_dialog( "timeout", "helicopter_gunner", undefined, level.vtol.killstreak_id );
		}
	}
	
	LeaveHelicopter( level.vtol.owner, true );
}

function WatchPlayerTeamChangeThread( helicopter )
{
	helicopter notify( "mothership_team_change" );
	helicopter endon ( "mothership_team_change" );
	
	assert( IsPlayer( self ) );
	player = self;
	
	player endon( "gunner_left" );
	
	player util::waittill_any( "joined_team", "disconnect", "joined_spectators" );
	
	ownerLeft = helicopter.ownerEntNum == player.entNum;
	
	player thread LeaveHelicopter( player, ownerLeft ); // need to thread to prevent endon( "gunner_left" ) to terminate the LeaveHelicopter
	
	if( ownerLeft )
		helicopter notify( "vtol_shutdown", undefined );	
}

function WatchPlayerExitRequestThread( player )
{
	player notify( "WatchPlayerExitRequestThread_singleton" );
	player endon ( "WatchPlayerExitRequestThread_singleton" );
	assert( IsPlayer( player ) );
	mothership = self;
	
	level endon( "game_ended" );
	player endon( "disconnect" );
	player endon( "gunner_left" );

	owner = mothership.ownerEntNum == player.entNum;
	
	while( true )
	{
		timeUsed = 0;
		while( player UseButtonPressed() )
		{
			timeUsed += 0.05;
			if( timeUsed > 0.25 )
			{
				mothership killstreaks::play_pilot_dialog_on_owner( "remoteOperatorRemoved", "helicopter_gunner", level.vtol.killstreak_id );
				player thread LeaveHelicopter( player, owner ); // need to thread this so that endon( "gunner_left" ) does not self termniate in LeaveHelicopter()
				return;
			}
			{wait(.05);};
		}
		{wait(.05);};
	}	
}

function EnterHelicopter( isOwner )
{
	assert( IsPlayer( self ) );
	player = self;
	
	seatIndex = -1;
	if( !isOwner )
	{
		seatIndex = GetFirstAvailableSeat( player );
		if( seatIndex == -1 )
		{
			return false;
		}
		level.vtol.assistants[ seatIndex ].occupant = player;
	}
	
	level.vtol.occupied = true; // needed for killcam to function properly

	player util::setUsingRemote( "helicopter_gunner" );
	player.ignoreEMPJammed = true;
	result = player killstreaks::init_ride_killstreak( "helicopter_gunner" );	
	player.ignoreEMPJammed = false;
	if( result != "success" )
	{
		if( result != "disconnect" )
		{
			player killstreaks::clear_using_remote();
		}
		
		if( !isOwner )
			level.vtol.assistants[ seatIndex ].occupant = undefined;
		
		if( isOwner )
		{
			level.vtol.failed2enter = true;
			level.vtol notify( "vtol_shutdown" );
		}

		return false;
	}
	
	if( isOwner )
	{
		level.vtol UseVehicle( player, 0 );
		level.vtol clientfield::set( "vehicletransition", 1 );
	}
	else
	{
		if( level.vtol.shuttingdown )
		{
			player killstreaks::clear_using_remote();
			return false;
		}
		level.vtol UseVehicle( player, seatIndex + ( 1 ) );
		level.vtol clientfield::set( "vehicletransition", 1 );
		
		level.vtol killstreaks::play_pilot_dialog_on_owner( "remoteOperatorAdd", "helicopter_gunner", level.vtol.killstreak_id );
	}

	killcament = spawn( "script_model", ( 0, 0, 0 ) );
	killcament SetModel( "tag_origin" );
	killcament.angles = ( 0, 0, 0 );
	killcament SetWeapon( GetWeapon( "helicopter_gunner_turret_primary" ) );
	killcament linkto( level.vtol, "tag_barrel", ( 370, 0, 25 ), ( 0, 0, 0 ) );		
	level.vtol.killcament = killcament;
	
	level.vtol.usage[player.entNum] = 1;
	
	level.vtol thread audio::sndUpdateVehicleContext(true);
	
	level.vtol thread vehicle::monitor_missiles_locked_on_to_me( player );
	level.vtol thread vehicle::monitor_damage_as_occupant( player );
	
	if ( level.vtol.killstreak_timer_started )
	{
		player vehicle::set_vehicle_drivable_time( level.vtol.killstreak_duration, level.vtol.killstreak_end_time );
	}
	else
	{
		player vehicle::set_vehicle_drivable_time( 9009009, GetTime() + 9009009 );
	}
	
	update_client_for_player( player );
	
	UpdateAllKillstreakInventory();
	
	player thread WatchVisionSwitchThread();
	level.vtol thread WatchPlayerExitRequestThread( player );
	player thread WatchPlayerTeamChangeThread( level.vtol );
	
	visionset_mgr::activate( "visionset", "mothership_visionset", player, 1, ( 60000 ), 1 );
	player SetModelLodBias( (isdefined(level.mothership_lod_bias)?level.mothership_lod_bias:8) );
	player GiveDedicatedShadow( level.vtol );
	player clientfield::set_to_player( "fog_bank_2", 1 );
	
	if ( true )
	{
		player thread HideCompassAfterWait( 0.1 ); // need to do this due to the way this scorestreak starts up
	}
	
	return true;
}

function HideCompassAfterWait( waittime )
{
	self endon( "death" );
	self endon( "disconnect" );
	
	wait waittime;
	
	self killstreaks::hide_compass();
}

function MainTurretDestroyed( helicopter, eAttacker, weapon )
{
	helicopter.owner iPrintLnBold( &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" );
		
	if ( isdefined(helicopter.targetEnt))
    {
		Target_Remove( helicopter.targetEnt );
		helicopter.targetEnt Delete();
		helicopter.targetEnt = undefined;
    }
	helicopter.shuttingDown = true;
	UpdateAllKillstreakInventory();
	
	eAttacker = self [[ level.figure_out_attacker ]]( eAttacker );
	if( !isdefined( helicopter.destroyScoreEventGiven ) && isdefined( eAttacker ) && ( !isdefined( helicopter.owner ) || helicopter.owner util::IsEnemyPlayer( eAttacker ) ) )
	{
		LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED", eAttacker.entnum );
		challenges::destroyedAircraft( eAttacker, weapon, true );
		eAttacker challenges::addFlySwatterStat( weapon, helicopter );
		scoreevents::processScoreEvent( "destroyed_vtol_mothership", eAttacker, helicopter.owner, weapon );
		helicopter killstreaks::play_destroyed_dialog_on_owner( "helicopter_gunner", helicopter.killstreak_id );
		helicopter.destroyScoreEventGiven = 1;
	}

	helicopter thread PerformLeaveHelicopterFromDamage();
}

function wait_for_bda_dialog( killstreakId )
{
	self endon( "vtol_shutdown" );
	
	while(true)
	{
		self waittill( "bda_dialog", dialogKey );
		
		for( i = 0; i < ( 2 ); i++ )
		{
			if( isdefined( level.vtol.assistants[i].occupant )  )
			{
				level.vtol.assistants[i].occupant killstreaks::play_pilot_dialog( dialogKey, "helicopter_gunner", killstreakId, self.pilotIndex );
			}
		}
	}
}

function SupportTurretDestroyed( helicopter, seatIndex )
{
	assistant = helicopter.assistants[seatIndex];
	if( !assistant.destroyed )
	{
		Target_Remove( assistant.targetEnt );
		level.vtol vehicle::remove_from_target_group();

		assistant.targetEnt Delete();
		assistant.targetEnt = undefined;
		assistant.destroyed = true;
		
		if ( isdefined( helicopter.owner ) && isdefined( helicopter.hardpointType ) )
		{
			helicopter killstreaks::play_pilot_dialog_on_owner( "weaponDestroyed", helicopter.hardpointType, helicopter.killstreak_id );
		}
		
		if( isdefined( assistant.occupant ) )
		{
			assistant.occupant globallogic_audio::flush_killstreak_dialog_on_player( helicopter.killstreak_id );
			assistant.occupant killstreaks::play_pilot_dialog( "weaponDestroyed", helicopter.hardpointType, undefined, helicopter.pilotIndex );
			wait 2.0;
			LeaveHelicopter( assistant.occupant, false );
		}
	}
	
	// update destroyed states
	
	if ( seatIndex == 0 )
	{
		level.vtol clientfield::set( "vtol_turret_destroyed_0", 1 );
		level.vtol update_client_for_driver_and_occupants();
	}
	else if ( seatIndex == 1 )
	{
		level.vtol clientfield::set( "vtol_turret_destroyed_1", 1 );
		level.vtol update_client_for_driver_and_occupants();
	}
}

function update_client_for_driver_and_occupants() // self == vtol
{
	vtol = self;
	
	update_client_for_player( vtol.owner );

	foreach( assistant in vtol.assistants )
	{
		update_client_for_player( assistant.occupant );
	}
}

function update_client_for_player( player )
{
	if ( isdefined( player ) )
	{
		player clientfield::increment_to_player( "vtol_update_client", 1 );
	}
}

function VTOLDestructibleCallback( brokenNotify, eAttacker, weapon )
{
	helicopter = self;
	
	helicopter endon ( "delete" );
	helicopter endon ( "vtol_shutdown" );
	
	notifies = [];
	notifies[0] = "turret1_destroyed";
	notifies[1] = "turret2_destroyed";
	
	for( seatIndex = 0; seatIndex < ( 2 ); seatIndex++ )
	{
		if( brokenNotify == notifies[seatIndex] )
		{
			SupportTurretDestroyed( helicopter, seatIndex );
			break;
		}
	}
	
	if( brokenNotify == "turret_destroyed" )
	{
		MainTurretDestroyed( helicopter, eAttacker, weapon );
		return;
	}
	
	helicopter AllowMainTurretLockon();
}

function AllowMainTurretLockon()
{	
	helicopter = self;
	
	// allow lockon on the main turrets
	if( helicopter.assistants[0].destroyed && helicopter.assistants[1].destroyed )
	{
		if( !isdefined( helicopter.targetEnt ) )
		{
			helicopter.targetEnt = spawn( "script_model", ( 0, 0, 0 ) );
			helicopter.targetEnt SetModel( "p7_dogtags_enemy" ); // hack to send ent to clients for targeting
			helicopter.targetEnt LinkTo( level.vtol, "tag_barrel", ( 0, 0, 0 ), ( 0, 0, 0 ) );
			helicopter.targetEnt.parent = level.vtol;
			helicopter.targetEnt.team = level.vtol.team;
			Target_Set( helicopter.targetEnt, ( 0, 0, 0 ) );	
			helicopter.targetEnt.useVTOLTime = true;
			Target_SetAllowHighSteering( helicopter.targetEnt, true );
			
			level.vtol vehicle::add_to_target_group( helicopter.targetEnt );
		} 
	}
}

function LeaveHelicopter( player, ownerLeft )
{
	if( !isdefined( level.vtol ) || level.vtol.completely_shutdown === true )
		return;

	if( isdefined( player ) )
	{
		player vehicle::stop_monitor_missiles_locked_on_to_me();
		player vehicle::stop_monitor_damage_as_occupant();
	}
	
	if( isdefined( player ) && isdefined( level.vtol ) && isdefined( level.vtol.owner ) )
	{
		if( isdefined( player.usingvehicle ) && player.usingvehicle )
		{
			player unlink();
			level.vtol clientfield::set( "vehicletransition", 0 );
			
			if( ownerLeft )
				player killstreaks::take( "helicopter_gunner" );
			else
				player killstreaks::take( "inventory_helicopter_gunner_assistant" );
		}
	}
	
	if( ownerLeft )
	{
		level.vtol.shuttingDown = true;
		foreach( assistant in level.vtol.assistants )
		{
			if( isdefined( assistant.occupant ) )
			{
				assistant.occupant iPrintLnBold( &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" );
				LeaveHelicopter( assistant.occupant, false );
			}
		}
		
		level.vtol.occupied = false;		
		level.vtol.hardpointType = "helicopter_gunner";
		level.vtol thread helicopter::heli_leave();
		level.vtol thread audio::sndUpdateVehicleContext(false);
	}
	else
	{
		if( isdefined( player ) )
		{
			player globallogic_audio::flush_killstreak_dialog_on_player( level.vtol.killstreak_id );
			
			foreach( assistant in level.vtol.assistants )
			{
				if( isdefined( assistant.occupant ) && assistant.occupant == player )
				{
					assistant.occupant = undefined;
					break;
				}		
			}		
		}
	}
	
	if( isdefined( player ) )
	{
		player clientfield::set_to_player( "toggle_flir_postfx", 0 );
		visionset_mgr::deactivate( "visionset", "mothership_visionset", player );
		player SetModelLodBias( 0 );
		player GiveDedicatedShadow( player );

		player clientfield::set_to_player( "fog_bank_2", 0 );
		player killstreaks::unhide_compass();
		
		player notify( "gunner_left" );	
		
		player killstreaks::clear_using_remote();
		
		if( level.gameEnded )
			player util::freeze_player_controls( true );
	}
	
	UpdateAllKillstreakInventory();

	if ( ownerLeft )
		level.vtol.completely_shutdown = true;
}

function vtol_shake()
{
	if( isdefined( level.vtol ) && isdefined( level.vtol.owner ) )
	{
		org = level.vtol GetTagOrigin( "tag_barrel" );

		magnitude = 0.3;
		duration = 2;
		radius = 500;
		v_pos = self.origin;
		Earthquake( magnitude, duration, org, 500 );
	}
}


function HelicopterGunnerDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal )
{
	helicopter = self;
	
	if( sMeansOfDeath == "MOD_TRIGGER_HURT" )
		return 0;
	
	if( helicopter.shuttingDown )
		return 0;

	iDamage = self killstreaks::OnDamagePerWeapon( "helicopter_gunner", eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, level.vtol.maxhealth, undefined, level.vtol.maxhealth*0.4, undefined, 0, undefined, true, 1.0 );
	if( iDamage == 0 )
		return 0;
	
	// handle rocket damage
	handleAsRocketDamage = ( ( sMeansOfDeath == "MOD_PROJECTILE" ) || ( sMeansOfDeath == "MOD_EXPLOSIVE" ) );
	if ( weapon.statIndex == level.weaponShotgunEnergy.statIndex || weapon.statIndex == level.weaponPistolEnergy.statIndex || weapon.statIndex == level.weaponSmgNailGun.statIndex )
		handleAsRocketDamage = false;

	if( handleAsRocketDamage )
	{
		updateInventory = 1;
		
		missileTarget = eInflictor missile_gettarget();
		
		vtol_shake();
		
		rocketHit = 1.0;
		if ( weapon.statIndex == level.weaponLauncherMulti.statIndex )
			rocketHit = 0.5;
					
		helicopter.totalRocketHits += rocketHit;
		
		if( isdefined( missileTarget ) ) 
		{
			// handle rocket damage to the support turrets
			for( seatIndex = 0; seatIndex < ( 2 ); seatIndex++ )
			{
				assistant = helicopter.assistants[seatIndex];
				
				if( !assistant.destroyed && ( assistant.targetEnt == missileTarget ) )
				{
					assistant.rocketHits += rocketHit;
					
					if ( assistant.rocketHits >= 2 )
					{
						helicopter DoDamage( iDamage, assistant.targetEnt.origin, eAttacker, eInflictor, sHitLoc, "MOD_UNKNOWN", 0, weapon, seatIndex + 8 );
						iDamage = 0;
						SupportTurretDestroyed( helicopter, seatIndex );
					}
				}
			}	
	
			// handle rocket damage to the main turrets
			if( isdefined( helicopter.targetEnt ) && ( helicopter.targetEnt == missileTarget ) )
			{
				helicopter.turretRocketHits += rocketHit;
				
				// main turret need 2 rockets
				if( helicopter.turretRocketHits >= 2 )
				{
					Target_Remove( helicopter.targetEnt );
					helicopter.targetEnt Delete();
					helicopter.targetEnt = undefined;			
				}
			}
		}
		
		// allow lockon on the main turret
		if( helicopter.assistants[0].destroyed && helicopter.assistants[1].destroyed && ( !isdefined( helicopter.targetEnt ) ) )
		{
			helicopter.targetEnt = spawn( "script_model", ( 0, 0, 0 ) );
			helicopter.targetEnt SetModel( "p7_dogtags_enemy" ); // hack to send ent to clients for targeting
			helicopter.targetEnt LinkTo( level.vtol, "tag_barrel", ( 0, 0, 0 ), ( 0, 0, 0 ) );
			helicopter.targetEnt.parent = level.vtol;
			helicopter.targetEnt.team = level.vtol.team;
			Target_Set( helicopter.targetEnt, ( 0, 0, 0 ) );	
			helicopter.targetEnt.useVTOLTime = true;
			Target_SetAllowHighSteering( helicopter.targetEnt, true );
		}
		
		if( helicopter.totalRocketHits >= ( 6 ) )
		{
			MainTurretDestroyed( helicopter, eAttacker, weapon );
			updateInventory = 0;
		}
		
		if ( updateInventory )
			UpdateAllKillstreakInventory();
	}
	
	if( iDamage >= level.vtol.health && !helicopter.shuttingDown )
	{
		helicopter.shuttingDown = true;
		UpdateAllKillstreakInventory();
		
		if ( !isdefined( helicopter.destroyScoreEventGiven ) && isdefined( eAttacker ) && ( !isdefined( helicopter.owner ) || helicopter.owner util::IsEnemyPlayer( eAttacker ) ) )
		{
			eAttacker = self [[ level.figure_out_attacker ]]( eAttacker );
			LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED", eAttacker.entnum );
			scoreevents::processScoreEvent( "destroyed_vtol_mothership", eAttacker, helicopter.owner, weapon );
			helicopter killstreaks::play_destroyed_dialog_on_owner( "helicopter_gunner", helicopter.killstreak_id );
			helicopter.destroyScoreEventGiven = 1;
		}
		
		helicopter thread PerformLeaveHelicopterFromDamage();
	}
	
	if( helicopter.shuttingDown )
	{
		if( iDamage >= helicopter.health )
			iDamage = helicopter.health - 1; // keep it alive. We want it to go away not explode
	}

	///#iprintln( partName + " health:" + helicopter.health + " damage:" + iDamage );#/
	return iDamage;
}

function PerformLeaveHelicopterFromDamage()
{
	helicopter = self;
	helicopter endon( "death" );

	if ( self.leave_by_damage_initiated === true )
		return;

	self.leave_by_damage_initiated = true;

	helicopter thread remote_weapons::do_static_fx();
	failsafe_timeout = 5.0;
	helicopter util::waittill_any_timeout( failsafe_timeout, "static_fx_done" );
	LeaveHelicopter( helicopter.owner, true );
}

function helicopteDetonateViaEMP( attacker, weapon )
{
	MainTurretDestroyed( level.vtol, attacker, weapon );
}

function MissileCleanupThread( missile )
{
	targetEnt = self;
	
	targetEnt endon( "delete" );
	targetEnt endon( "death" );
		
	missile util::waittill_any( "death", "delete" );
	
	targetEnt Delete();
}

function WatchMissilesThread()
{
	helicopter = self;
	player = helicopter.owner;
	
	player endon( "disconnect" );
	player endon( "gunner_left" );
	
	heliMissile = GetWeapon( "helicopter_gunner_turret_rockets" );
	
	while( true )
	{
		player waittill( "missile_fire", missile );

		trace_origin = level.vtol GetTagOrigin( "tag_flash" );
		trace_direction = level.vtol GetTagAngles( "tag_barrel" );
		trace_direction = AnglesToForward( trace_direction ) * 8000;
		trace = BulletTrace( trace_origin, trace_origin + trace_direction, false, level.vtol );
		end_origin = trace["position"];
		
		missiles  = getentarray( "rocket", "classname" );
		
		/#
		//Box( end_origin, (-4, -4, 0 ), ( 4, 4, 1000 ), 0, ( 0, 0.7, 0 ), 0.6, false, 9999999 );
		#/
			
		foreach( missile in missiles )
		{
			if( missile.item == heliMissile )
			{
				targetEnt = Spawn( "script_model", end_origin );
				missile Missile_SetTarget( targetEnt );
				targetEnt thread MissileCleanupThread( missile );
			}
		}
		
		// setup the "reload" time for the player's vehicle HUD
		weapon_wait_duration_ms = Int( heliMissile.fireTime * 1000 );
		player SetVehicleWeaponWaitDuration( weapon_wait_duration_ms );
		player SetVehicleWeaponWaitEndTime( GetTime() + weapon_wait_duration_ms );
	}
}

function WatchVisionSwitchThread()
{
	assert( IsPlayer( self ) );
	player = self;
	
	player endon( "disconnect" );
	player endon( "gunner_left" );
	
	inverted = false;
	player clientfield::set_to_player( "toggle_flir_postfx", 2 );

	while( true )
	{
		if( player JumpButtonPressed() )
		{
			if( inverted )
			{
				player clientfield::set_to_player( "toggle_flir_postfx", 2 );
				player PlaySoundToPlayer( "mpl_cgunner_flir_off", player );
			}
			else
			{
				player clientfield::set_to_player( "toggle_flir_postfx", 1 );
				player PlaySoundToPlayer( "mpl_cgunner_flir_on", player );
			}

			inverted = !inverted;
			
			while( player JumpButtonPressed() )
				{wait(.05);};
		}
		
		{wait(.05);};
	}
}

function PlayLockOnSoundsThread( player, heli )
{
	player endon( "disconnect" );
	player endon( "gunner_left" );
	
	heli endon( "death" );
	heli endon ( "crashing" );
	heli endon ( "leaving" );
	
	heli.lockSounds = spawn( "script_model", heli.origin );
	wait ( 0.1 );
	heli.lockSounds LinkTo( heli, "tag_player" );
	
	while( true )
	{
		heli waittill( "locking on" );
		
		while( true )
		{
			if( EnemyIsLocking( heli ) )
			{
				heli.lockSounds PlaySoundToPlayer( "uin_alert_lockon", player );
				wait ( 0.125 );
			}
			
			if( EnemyLockedOn( heli ) )
			{
				heli.lockSounds PlaySoundToPlayer( "uin_alert_lockon", player );
				wait ( 0.125 );
			}
			
			if( !EnemyIsLocking( heli ) && !EnemyLockedOn( heli ) )
			{
				heli.lockSounds StopSounds();
				break;
			}			
		}
	}
}

function EnemyIsLocking( heli )
{
	return ( isdefined( heli.locking_on ) && heli.locking_on );
}

function EnemyLockedOn( heli )
{
	return ( isdefined( heli.locked_on ) && heli.locked_on );
}

function HelicopterThinkThread( startNode, destNodes )
{
	self notify( "flying");
	self endon( "flying" );

	self endon ( "death" );
	self endon ( "crashing" );
	self endon ( "leaving" );

	nextnode = getent( startNode.target, "targetname" );
	assert( isdefined( nextnode ), "Next node in path is undefined, but has targetname" );
	self SetSpeed( 150, 80 );	
	self setvehgoalpos( nextnode.origin + ( 0, 0, ( 2000 ) ), 1 );
	self waittill( "near_goal" );
	
	firstpass = true;
	//while( true )
	{
		if( !self.playerMovedRecently )
		{
			node = self UpdateAreaNodes( destNodes, false );
			level.vtol.currentNode = node;
			targetNode = getEnt( node.target, "targetname" );
		
			TravelToNode( targetNode );
			
			if( isdefined( targetNode.script_airspeed ) && isdefined( targetNode.script_accel ) )
			{
				heli_speed = targetNode.script_airspeed;
				heli_accel = targetNode.script_accel;
			}
			else
			{
				heli_speed = 150+randomInt(20);
				heli_accel = 40+randomInt(10);
			}
			
			self SetSpeed( heli_speed, heli_accel );	
			self setvehgoalpos( targetNode.origin + ( 0, 0, ( 2000 ) ), 1 );
			self setgoalyaw( targetNode.angles[ 1 ] + ( 0 ) );	
		}

		if( ( 0 ) != 0 )
		{
			self waittill( "near_goal" );
			waitTime = ( 0 );
		}
		else if( !isdefined( targetNode.script_delay ) )
		{
			self waittill( "near_goal" );
			waitTime = 10 + randomInt( 5 );
		}
		else
		{				
			self waittillmatch( "goal" );				
			waitTime = targetNode.script_delay;
		}
		
		if( firstpass )
		{
			self.killstreak_duration = ( ( self.killstreak_timer_start_using_hacked_time === true ) ? self killstreak_hacking::get_hacked_timeout_duration_ms() : ( 60000 ) );
			self.killstreak_end_time = GetTime() + self.killstreak_duration;
			self.killstreakEndTime = int( self.killstreak_end_time );
			self thread killstreaks::WaitForTimeout( "helicopter_gunner", self.killstreak_duration, &OnTimeoutCallback, "delete", "death" );
			self.killstreak_timer_started = true;
			self UpdateDrivableTimeForAllOccupants( self.killstreak_duration, self.killstreak_end_time );
	
			firstpass = false;
		}

		wait( waitTime );
	}
}

function UpdateDrivableTimeForAllOccupants( duration_ms, end_time_ms ) // self == vtol
{
	if ( isdefined( self.owner ) )
	{
		self.owner vehicle::set_vehicle_drivable_time( duration_ms, end_time_ms );
	}
	
	for( i = 0; i < ( 2 ); i++ )
	{
		if( isdefined( self.assistants[i].occupant ) && !self.assistants[i].destroyed )
		{
			 self.assistants[i].occupant vehicle::set_vehicle_drivable_time( duration_ms, end_time_ms );
		}
	}
}

function WatchLocationChangeThread( destNodes )
{
	player = self;
	
	player endon( "disconnect" );
	player endon( "gunner_left" );

	helicopter = level.vtol;
	
	helicopter endon ( "delete" );
	helicopter endon ( "vtol_shutdown" );
	
	player.moves = 0;
	helicopter waittill ( "near_goal" );
	helicopter waittill ( "goal" );
	
	while( true )
	{
		if( self SecondaryOffhandButtonPressed() )
		{
			player.moves++;
			player thread SetPlayerMovedRecentlyThread();
			node = self UpdateAreaNodes( destNodes, true );
			helicopter.currentNode = node;
			targetNode = getEnt( node.target, "targetname" );
			
			player playlocalsound ( "mpl_cgunner_nav" );
			helicopter TravelToNode( targetNode );
			
			if( isdefined( targetNode.script_airspeed ) && isdefined( targetNode.script_accel ) )
			{
				heli_speed = targetNode.script_airspeed;
				heli_accel = targetNode.script_accel;
			}
			else
			{
				heli_speed = 80+randomInt(20);
				heli_accel = 40+randomInt(10);
			}
			
			helicopter SetSpeed( heli_speed, heli_accel );	
			helicopter setvehgoalpos( targetNode.origin + ( 0, 0, ( 2000 ) ), 1 );
			helicopter setgoalyaw( targetNode.angles[ 1 ] + ( 0 ) );
	
			helicopter waittill( "goal" );
						
			// wait for the button to release:
			while ( self SecondaryOffhandButtonPressed() )
			{
				{wait(.05);};
			}
		}
		
		{wait(.05);};
	}
}

function SetPlayerMovedRecentlyThread()
{
	player = self;
	
	player endon( "disconnect" );
	player endon( "gunner_left" );

	helicopter = level.vtol;
	
	helicopter endon ( "delete" );
	helicopter endon ( "vtol_shutdown" );
	
	myMove = self.moves;
	level.vtol.playerMovedRecently = true;
	wait ( 100 );
	
	//only remove the flag if I am still the most recent move
	if( myMove == self.moves && isdefined( level.vtol ) )
	{
		level.vtol.playerMovedRecently = false;
	}
}

function UpdateAreaNodes( areaNodes, forceMove )
{
	validEnemies = [];

	foreach( node in areaNodes )
	{
		node.validPlayers = [];
		node.nodeScore = 0;
	}
	
	foreach( player in level.players )
	{
		if( !isAlive( player ) )
		{
			continue;
		}

		if( player.team == self.team )
		{
			continue;
		}
		
		foreach( node in areaNodes )
		{
			if( distanceSquared( player.origin, node.origin ) > 1048576 )
			{
				continue;
			}
								
			node.validPlayers[node.validPlayers.size] = player;
		}
	}
	
	bestNode = undefined;
	foreach ( node in areaNodes )
	{
		if( isdefined( level.vtol.currentNode ) && ( node == level.vtol.currentNode ) )
		{
			continue;
		}
		
		heliNode = getEnt( node.target, "targetname" );
		foreach( player in node.validPlayers )
		{
			node.nodeScore += 1;
			
			if( bulletTracePassed( player.origin + (0,0,32), heliNode.origin, false, player ) )
			{
				node.nodeScore += 3;
			}
		}
				
		if( forceMove && ( distance( level.vtol.origin, heliNode.origin ) < 200 ) )
		{
			node.nodeScore = -1;
		}
		
		if( !isdefined( bestNode ) || ( node.nodeScore > bestNode.nodeScore ) )
		{
			bestNode = node;
		}
	}
	
	return bestNode;
}

function TravelToNode( goalNode )
{
	originOffets = GetOriginOffsets( goalNode );
	
	if( originOffets["start"] != self.origin )
	{
		if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) )
		{
			heli_speed = goalNode.script_airspeed;
			heli_accel = goalNode.script_accel;
		}
		else
		{
			heli_speed = 30 + randomInt(20);
			heli_accel = 15 + randomInt(15);
		}
		
		self SetSpeed( heli_speed, heli_accel );
		self setvehgoalpos( originOffets["start"] + (0,0,30), 0 );
		self setgoalyaw( goalNode.angles[ 1 ] + ( 0 ) );
		
		self waittill ( "goal" );
	}
	
	if( originOffets["end"] != goalNode.origin )
	{
		if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) )
		{
			heli_speed = goalNode.script_airspeed;
			heli_accel = goalNode.script_accel;
		}
		else
		{
			heli_speed = 30+randomInt(20);
			heli_accel = 15+randomInt(15);
		}
		
		self SetSpeed( heli_speed, heli_accel );
		self setvehgoalpos( originOffets["end"] + (0,0,30), 0 );
		self setgoalyaw( goalNode.angles[ 1 ] + ( 0 ) );
		
		self waittill ( "goal" );
	}
}

function GetOriginOffsets( goalNode )
{
	startOrigin = self.origin;
	endOrigin = goalNode.origin;
	
	numTraces = 0;
	maxTraces = 40;
	
	traceOffset = (0,0,-196);
	
	traceOrigin = BulletTrace( startOrigin+traceOffset, endOrigin+traceOffset, false, self );

	while( DistanceSquared( traceOrigin[ "position" ], endOrigin+traceOffset ) > 10 && numTraces < maxTraces )
	{	
		/#println( "trace failed: " + DistanceSquared( traceOrigin[ "position" ], endOrigin+traceOffset ) );#/
			
		if( startOrigin[2] < endOrigin[2] )
		{
			startOrigin += (0,0,128);
		}
		else if( startOrigin[2] > endOrigin[2] )
		{
			endOrigin += (0,0,128);
		}
		else
		{	
			startOrigin += (0,0,128);
			endOrigin += (0,0,128);
		}

		numTraces++;
		traceOrigin = BulletTrace( startOrigin+traceOffset, endOrigin+traceOffset, false, self );
	}
	
	offsets = [];
	offsets["start"] = startOrigin;
	offsets["end"] = endOrigin;
	return offsets;
}