#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;


CONST_MISSILE_HEALTH 	= 10;
CONST_HOSTILE 			= "hud_fofbox_hostile";
CONST_HOSTIL_LOCK 		= "hud_fofbox_hostile_ms_target";
CONST_SELF 				= "hud_fofbox_self";


init()
{	
	SetDvar( "missileRemoteSteerPitchRange", "60 87" );
	
	level._missile_strike_setting = [];
	
	level._missile_strike_setting[ "Particle_FX" ]			  			= SpawnStruct();
	level._missile_strike_setting[ "Particle_FX" ].Gas					= LoadFX( "vfx/unique/vfx_killstreak_missilestrike_dna" );
	level._missile_strike_setting[ "Particle_FX" ].GasFriendly			= LoadFX( "vfx/unique/vfx_killstreak_missilestrike_dna_friendly" );

	level._missile_strike_setting[ "Audio" ]			  = SpawnStruct();
	
	level._missile_strike_setting[ "Launch_Value" ]			  = SpawnStruct();
	mapname = getDvar( "mapname" );
	if ( mapname == "mp_suburbia" )
	{
		level._missile_strike_setting[ "Launch_Value" ].Vert		= 7000;
		level._missile_strike_setting[ "Launch_Value" ].Horz 		= 10000;
		level._missile_strike_setting[ "Launch_Value" ].TargetDest	= 2000;
	}
	else if ( mapname == "mp_mainstreet" )
	{
		level._missile_strike_setting[ "Launch_Value" ].Vert 		= 7000;
		level._missile_strike_setting[ "Launch_Value" ].Horz 		= 10000;
		level._missile_strike_setting[ "Launch_Value" ].TargetDest 	= 2000;
	}
	else
	{
		level._missile_strike_setting[ "Launch_Value" ].Vert 		= 24000;
		level._missile_strike_setting[ "Launch_Value" ].Horz  		= 7000;
		level._missile_strike_setting[ "Launch_Value" ].TargetDest 	= 1500;

	}	
	
	level.rockets = [];
	level.missile_strike_gas_clouds = [];
	
	level.killstreakFuncs[ "missile_strike" ] = ::tryUseMissileStrike;
	
	level.killstreakWieldWeapons["remotemissile_projectile_mp"]	= "missile_strike";
	level.killstreakWieldWeapons["remotemissile_projectile_gas_mp"] = "missile_strike";
	level.killstreakWieldWeapons["remotemissile_projectile_cluster_parent_mp"] = "missile_strike";
	level.killstreakWieldWeapons["remotemissile_projectile_cluster_child_mp"] = "missile_strike";
	level.killstreakWieldWeapons["remotemissile_projectile_cluster_child_hellfire_mp"] = "missile_strike";
	level.killstreakWieldWeapons["remotemissile_projectile_secondary_mp"] = "missile_strike";
	level.killstreakWieldWeapons["killstreak_strike_missile_gas_mp"] = "missile_strike";

	level.remotemissile_fx[ "explode" ] = LoadFX( "vfx/explosion/rocket_explosion_airburst" );
	
	thread OnPlayerConnect();
}


tryUseMissileStrike( lifeId, modules )
{
	result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak("missile_strike");
	if ( result != "success" )
		return false;
	
	self playerSaveAngles();
	
	self setUsingRemote( "missile_strike" );
	MissileWeapon = self BuildWeaponSettings( modules );
	
	if( IsDefined( level.isHorde ) && level.isHorde )
	{
		self.missileWeapon = MissileWeapon;
		
		//Track if this is a Missile Strike called as a built-in Demolitions scoretreak in co-op so we can start the class cool-down 
		//and also start cooldown on the owner when another player picks up the built-in scorestreak goliath
		if ( self.killstreakIndexWeapon == 1 )
			self notify ( "used_horde_missile_strike" );		
	}
	
	level thread _fire( lifeId, self, MissileWeapon );
	
	return true;
}

BuildWeaponSettings( modules )
{
	// Find Weapon Type
	weapon = spawnstruct();
	
	weapon.modules = modules;
	
	// Determine projectile type
	weapon.name 			= "remotemissile_projectile_mp";
	weapon.gasMissile 		= false;
	weapon.clusterMissile 	= false;
	weapon.clusterHellfire 	= false;
	weapon.clusterSpiral 	= false;
	
	if ( array_contains( weapon.modules, "missile_strike_hellfire" ) )
	{
		weapon.name				= "remotemissile_projectile_cluster_parent_mp";
		weapon.clusterMissile 	= true;
		weapon.clusterHellfire 	= true;
	}
	if ( array_contains( weapon.modules, "missile_strike_cluster" ) )
	{
		weapon.name 			= "remotemissile_projectile_cluster_parent_mp";
		weapon.clusterMissile 	= true;
		weapon.clusterSpiral 	= true;
		thread preSpawnClusterRotationEntities( weapon );
	}
	else if ( array_contains( weapon.modules, "missile_strike_chem" ) )
	{
		weapon.name 			= "remotemissile_projectile_gas_mp";
		weapon.gasMissile 		= true;
	}

	// Determine ammo count
	weapon.RocketAmmo = 1;
	if ( array_contains( weapon.modules, "missile_strike_extra_1" ) )
		weapon.RocketAmmo++;
	if ( array_contains( weapon.modules, "missile_strike_extra_2" ) )
		weapon.RocketAmmo++;
		
	return weapon;
}

preSpawnClusterRotationEntities( MissileWeapon )
{
	// it is expensive to spawn this many entities on one frame when the cluster missile is fired so prespawn some of them, about 2 per snapshot
	prespawnOrigin = ( 0, 0, -1000 );
	
	MissileWeapon.preSpawnedKillcamEnt = Spawn( "script_model", prespawnOrigin );
	MissileWeapon.preSpawnedKillcamEnt SetContents(0);
	MissileWeapon.preSpawnedKillcamEnt SetScriptMoverKillCam( "large explosive" );
	waitframe();
	MissileWeapon.preSpawnedRotationEnts = [];
	MissileWeapon.preSpawnedIndex = 0;
	for ( i = 0; i < 12; i++ )
	{
		ent = Spawn( "script_origin", prespawnOrigin );
		MissileWeapon.preSpawnedRotationEnts[MissileWeapon.preSpawnedRotationEnts.size] = ent;
		waitframe();
	}
}


////=================================================================================================================//
////												On CONNECT			                                    		 //
////=================================================================================================================//

OnPlayerConnect()
{	
	while( true )
	{
		level waittill("connected", player);
		player thread WaitingForSpawnDuringStreak();
	}
}

WaitingForSpawnDuringStreak()
{

	self waittill("spawned_player");
	self.MissileStrikeGasTime = 0;
	self thread CreateGasTrackingOverlay();
	self thread WaitForGasDamage();
}

getBestSpawnPoint( remoteMissileSpawnPoints )
{
	validEnemies = [];

	foreach ( spawnPoint in remoteMissileSpawnPoints )
	{
		spawnPoint.validPlayers = [];
		spawnPoint.spawnScore = 0;
	}
	
	foreach ( player in level.players )
	{
		if ( !isReallyAlive( player ) )
			continue;

		if ( player.team == self.team )
			continue;
		
		if ( player.team == "spectator" )
			continue;
		
		bestDistance = 999999999;
		bestSpawnPoint = undefined;
	
		foreach ( spawnPoint in remoteMissileSpawnPoints )
		{
			//could add a filtering component here but i dont know what it would be.
			spawnPoint.validPlayers[spawnPoint.validPlayers.size] = player;
		
			potentialBestDistance = Distance2D( spawnPoint.targetent.origin, player.origin );
			
			if ( potentialBestDistance <= bestDistance )
			{
				bestDistance = potentialBestDistance;
				bestSpawnpoint = spawnPoint;	
			}	
		}
		
		assertEx( isDefined( bestSpawnPoint ), "Closest remote-missile spawnpoint undefined for player: " + player.name );
		bestSpawnPoint.spawnScore += 2;
	}

	bestSpawn = remoteMissileSpawnPoints[0];
	foreach ( spawnPoint in remoteMissileSpawnPoints )
	{
		foreach ( player in spawnPoint.validPlayers )
		{
			spawnPoint.spawnScore += 1;
			
			if ( bulletTracePassed( player.origin + (0,0,32), spawnPoint.origin, false, player ) )
				spawnPoint.spawnScore += 3;
		
			if ( spawnPoint.spawnScore > bestSpawn.spawnScore )
			{
				bestSpawn = spawnPoint;
			}
			else if ( spawnPoint.spawnScore == bestSpawn.spawnScore ) // equal spawn weights so we toss a coin.
			{			
				if ( coinToss() )
					bestSpawn = spawnPoint;	
			}
		}
	}
	
	return ( bestSpawn );
}

////=================================================================================================================//
////												FIRE ROCKET		                                    		 	//
////=================================================================================================================//

_fire( lifeId, player, MissileWeapon ) 
{
	missileType = MissileWeapon.name;
	
	player playerAddNotifyCommands();
	
	rocket = fireOrbitalMissile( player, missileType );
	
	if( IsDefined( level.isHorde ) && level.isHorde )
		player.rocket = rocket;
	
	MissileWeapon.RocketAmmo--;
	if( MissileWeapon.RocketAmmo > 0 || MissileWeapon.clusterMissile )
		rocket DisableMissileBoosting();
	
	rocket.owner = player;
	rocket.team = player.team;	
	rocket.lifeId = lifeId;
	rocket.type = "remote";
	level.remoteMissileInProgress = true;
	
	rocket thread maps\mp\gametypes\_damage::setEntityDamageCallback( CONST_MISSILE_HEALTH, undefined, ::missileStrikeOnDeath, undefined, true );
	
	if( IsDefined( level.ishorde ) && level.ishorde )
	{
		rocket thread rocketImpact_droneKillCheck( player );
	}
	
	rocket SetMissileMinimapVisible( true );
	rocket PlaySoundToTeam( "mstrike_entry_npc", getOtherTeam(rocket.team) );
	rocket PlaySoundToTeam( "mstrike_entry_npc", rocket.team, player );
	
	MissileEyes( player, rocket, MissileWeapon );
}

rocketImpact_droneKillCheck( player )
{
	self waittill( "death" );
	
	if( isdefined( level.flying_attack_drones ) )
	{
		foreach( drone in level.flying_attack_drones )
		{
			if( drone.origin[2] > ( self.origin[2] ) && drone.origin[2] < ( self.origin[2] + 1200 ) && Distance2DSquared( drone.origin, self.origin ) < ( 40000 ))
				drone DoDamage( 350, self.origin, player, player, "MOD_PROJECTILE_SPLASH", "remotemissile_projectile_mp" );
		}
	}
}
missileStrikeOnDeath( attacker, weapon, meansOfDeath, damage )
{
	if( IsDefined( level.isHorde ) && level.isHorde )
	{
		if( IsDefined( self.owner ) )
		{
			self.owner.missileWeapon = undefined;
			self.owner.rocket = undefined;
		}
	}
	
	PlayFX( level.remotemissile_fx[ "explode" ], self.origin );

	self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "missile_strike_destroyed", undefined, undefined, false );
	
	self delete();
}

fireOrbitalMissile( player, missileType )
{
	remoteMissileSpawnArray = maps\mp\killstreaks\_aerial_utility::getEntOrStructArray( "remoteMissileSpawn" , "targetname" );
	
	foreach ( spawn in remoteMissileSpawnArray )
	{
		if ( isDefined( spawn.target ) )
			spawn.targetEnt = getEnt( spawn.target, "targetname" );	
	}
	
	if ( remoteMissileSpawnArray.size > 0 )
		remoteMissileSpawn = player getBestSpawnPoint( remoteMissileSpawnArray );
	else
		remoteMissileSpawn = undefined;
	
	startPos = undefined;
	targetPos = undefined;
	if ( isDefined( remoteMissileSpawn ) )
	{
		startPos = remoteMissileSpawn.origin;	
		targetPos = remoteMissileSpawn.targetEnt.origin;
		backDist = 24000;
		if( IsDefined( level.remote_missile_height_override ) )
			backDist = level.remote_missile_height_override;
		
		vector = vectorNormalize( startPos - targetPos );		
		startPos = ( vector * backDist ) + targetPos;
	}
	else
	{
		upVector = (0, 0, level._missile_strike_setting[ "Launch_Value" ].Vert );
		backDist = level._missile_strike_setting[ "Launch_Value" ].Horz ;
		targetDist = level._missile_strike_setting[ "Launch_Value" ].TargetDest;
	
		forward = AnglesToForward( player.angles );
		startpos = player.origin + upVector + forward * backDist * -1;
		targetPos = player.origin + forward * targetDist;
	}
	
	return MagicBullet( missileType, startpos, targetPos, player );
}


////=================================================================================================================//
////												CONTROL AND RIDE ROCKET		                                    		 	//
////=================================================================================================================//

MissileEyes( player, rocket, MissileWeapon )
{
	player endon ( "joined_team" );
	player endon ( "joined_spectators" );
	MissileWeapon endon ( "missile_strike_complete" );
	player endon ( "disconnect" );

	rocket thread Rocket_CleanupOnDeath();
	player thread Player_CleanupOnGameEnded( rocket, MissileWeapon );
	player thread Player_CleanupOnTeamChange( rocket, MissileWeapon );
	
	player.ClusterDeployed = false;
	player.MissileBoostUsed = false;
	
	player CameraLinkTo( rocket, "tag_origin" );
	player ControlsLinkTo( rocket );
	
	if ( getDvarInt( "camera_thirdPerson" ) )
		player setThirdPersonDOF( false );
	
	player thread init_hud( rocket, MissileWeapon );
			
	player thread playerWaitReset( MissileWeapon );
	
	///////////////////// FIND EXOTIC MISSILES TYPES AND DO SPECIAL STUFF /////////////////////////////////////
	/// 
	
	if ( MissileWeapon.clusterMissile )
		player thread WatchForClusterSplit( rocket, MissileWeapon );

	//
	//////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	
	if( MissileWeapon.RocketAmmo <= 0 && !MissileWeapon.clusterMissile )
	{
		// Boost available immediately
		rocket EnableMissileBoosting();
		player thread hud_watch_for_boost_active( rocket, MissileWeapon );
	}
	else
	{
		player thread WatchForExtraMissileFire( rocket, MissileWeapon );
	}
	
	player thread playerWatchForEarlyExit( MissileWeapon );
	
	rocket waittill( "death" );  ///////////////// WAIT DEATH HERE ///////////////////////////////////////////
	
	/////////////////////// AFTER DEATH CHECK AGAIN FOR MISSILE TYPES ///////////////////////////////////
	///////
	/// 

	if ( MissileWeapon.gasMissile )
	{
		if( isDefined( rocket ) ) //////// Find out if rocket failed or not.  If fired into void, it is removed and fails.
		{
			player thread ReleaseGas( rocket.origin );
		}
	}

	// missile strike needs its own stat
	// is defined check required because remote missile doesnt handle lifetime explosion gracefully
	// instantly deletes its self after an explode and death notify
	if ( isDefined(rocket) )
	{
		player maps\mp\_matchdata::logKillstreakEvent( "missile_strike", rocket.origin );
	}

	//////
	/////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	MissileWeapon notify( "missile_strike_complete" );
}

playerWatchForEarlyExit( MissileWeapon )
{
	level endon( "game_ended" );
	MissileWeapon endon( "missile_strike_complete" );
	self endon( "disconnect" );
	
	while ( true )
	{
		useHold = 0;
		while ( self UseButtonPressed() )
		{
			useHold += 0.05;
			if ( useHold > 0.5 )
			{
				MissileWeapon notify( "ms_early_exit" );
				return;
			}
			waitframe();
		}
		
		waitframe();
	}
}

playerWaitReset( MissileWeapon )
{
	MissileWeapon waittill_either( "missile_strike_complete", "ms_early_exit" );
	
	self playerReset();
}

playerReset()
{
	self endon( "disconnect" );
	
	self ControlsUnlink();
	self freezeControlsWrapper( true );
	self playerRemoveNotifyCommands();

	self stopMissileBoostSounds(); // Played by code during the boost
	
	// If a player gets the final kill with a hellfire, level.gameEnded will already be true at this point
	if ( !level.gameEnded || isDefined( self.finalKill ) )
		self maps\mp\killstreaks\_aerial_utility::playerShowFullStatic();
		
	wait( 0.5 );
	maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();

	self remove_hud();
	
	self CameraUnlink();
		
	self freezeControlsWrapper( false );
	
	if ( self isUsingRemote() )
		self clearUsingRemote();
	
	if ( getDvarInt( "camera_thirdPerson" ) )
		self setThirdPersonDOF( true );
	
	self playerRestoreAngles();
}

stopMissileBoostSounds()
{
	self StopLocalSound( "mstrike_boost_shot" );
	self StopLocalSound( "mstrike_boost_boom" );
	self StopLocalSound( "mstrike_boost_swoop" );
	self StopLocalSound( "mstrike_boost_jet" );
	self StopLocalSound( "mstrike_boost_roar" );
}

WatchForExtraMissileFire( MasterRocket, MissileWeapon )
{
	MasterRocket endon("death");
	
	wait( 0.5 );
	
	while( true )
	{
		self waittill("FireButtonPressed");
		
		if( MissileWeapon.RocketAmmo > 0 )
		{
			self thread FireBabyMissile( MasterRocket, MissileWeapon );
		}
		else
		{
			break;
		}
		
		wait( 0.1 );
	}
	
}

EnableBoost(MasterRocket)
{
	wait( 0.5 );
	MasterRocket EnableMissileBoosting();
}

FireBabyMissile( MasterRocket, MissileWeapon ) // VehiclAloc for number of vehicle slots reserved for drones
{
	MissileType = MissileWeapon.name;
	
	offset = (0,32,0);
	EvenOrOdd = MissileWeapon.RocketAmmo % 2;
	if(EvenOrOdd == 0)
	{
		offset = (0,-64,0);
	}
	BulletTraceTarget = MasterRocket.origin + (anglestoforward(MasterRocket.angles) * 32000);
	TraceArray = BulletTrace(MasterRocket.origin, BulletTraceTarget, false, MasterRocket, false, false, false, true, false);
	
	startpos = (MasterRocket.origin + offset) + (anglestoforward(MasterRocket.angles) * 750);
	targetPos = TraceArray["position"];
//	targetPos = MasterRocket.origin + (anglestoforward(MasterRocket.angles) * 2500);
		
	rocket = MagicBullet( "remotemissile_projectile_secondary_mp", startpos, targetPos, self );

	if( !IsDefined( rocket ) )
		return;
		
	MissileWeapon.RocketAmmo--;
	if( MissileWeapon.RocketAmmo <= 0 && !MissileWeapon.clusterMissile )
	{
		self hud_update_fire_text( rocket, MissileWeapon );
		self thread hud_watch_for_boost_active( MasterRocket, MissileWeapon );
		thread EnableBoost(MasterRocket);
	}
	
	if(isdefined(MissileType))
	{
		if ( IsDefined( MasterRocket.targets ) && MasterRocket.targets.size )
		{
			rocket Missile_SetTargetEnt( MasterRocket.targets[0] );
		}
	}
	
	rocket.team = self.team;
	rocket SetMissileMinimapVisible( true );
	
	// player feedback
	self PlayRumbleOnEntity( "sniper_fire" );
	Earthquake( 0.2, 0.2, MasterRocket.origin, 200 );

	rocket thread maps\mp\gametypes\_damage::setEntityDamageCallback( CONST_MISSILE_HEALTH, undefined, ::missileStrikeOnDeath, undefined, true );	
}


////////////////////////////// CLUSTER MISSILE STUFF /////////////////////////////////////////////////////
/////////////

WatchForClusterSplit( rocket, MissileWeapon )
{
	MissileWeapon endon( "missile_strike_complete" );
	self endon( "spawned_player" );
	rocket endon( "death" );
	
	// Only handle the main (final) missile
	while( MissileWeapon.RocketAmmo > 0 )
		self waittill( "FireButtonPressed" );

	wait( 0.25 );
	
	self waittill( "FireButtonPressed" );
	
	self.ClusterDeployed = true;
	
	self hud_update_fire_text( rocket, MissileWeapon );
	
	self thread split_rocket( rocket, MissileWeapon );
}

split_rocket( rocket, MissileWeapon ) // self == player
{	
	assert( MissileWeapon.clusterMissile );
	if ( MissileWeapon.clusterHellfire )
	{
		self thread split_rocket_hellfire( rocket, MissileWeapon );
	}
	else
	{
		assert( MissileWeapon.clusterSpiral );
		self thread split_rocket_spiral( rocket, MissileWeapon );
	}
}

split_rocket_hellfire( rocket, MissileWeapon ) // self == player
{
	waitFrames = 2;
	
	// Valid targets to fire at?
	targets = [];
	
	// Tracking
	if ( IsDefined( rocket.targets ) && rocket.targets.size )
	{
		targets = rocket.targets;
	}
	
	self thread fire_straight_bomblet( rocket, 0, MissileWeapon );

	foreach( target in targets )
	{
		self thread fire_targeted_bomblet( rocket, target, waitFrames, MissileWeapon );
		waitFrames++;
	}
	
	for ( i = targets.size; i <= 8; i++ )
	{
		self thread fire_random_bomblet( rocket, i % 6, waitFrames, MissileWeapon );
		waitFrames++;
	}
	
	// player feedback
	self PlayRumbleOnEntity( "sniper_fire" );
	Earthquake( 0.2, 0.2, rocket.origin, 200 );
	
	// Slow the rocket down for viewing the bomblets
	rocket SetMissileCoasting( true );
	
	// Quickly fade to white and back
	self thread fade_to_white();
	
	// Play a loop sound on the rocket for deploying the bomblets
	
	self thread bomblet_camera_waiter( rocket, MissileWeapon );
}

split_rocket_spiral( rocket, MissileWeapon ) // self == player
{
	MissileWeapon endon( "missile_strike_complete" );
	self endon( "spawned_player" );
	rocket endon( "death" );

	self thread fade_to_white();
	
	self waittill( "full_white" );
	
	self thread freezeControlsWrapper( true );
	
	if ( !IsDefined( rocket ) )
		return;
	
	self.StrikeRocketStoredPosition = rocket.origin;
	self.StrikeRocketStoredAngles = rocket.angles; 
	DistanceToScale = Distance(rocket.origin, self.origin) + 10000;
	
	RocketStoredForwardPosition = rocket.origin + (AnglesToForward(rocket.angles) * DistanceToScale);
	ClusterStartPoint = self.StrikeRocketStoredPosition;
	RotationStartPoint = rocket.origin + (AnglesToForward(rocket.angles) * 3250);

	rocket SetMissileCoasting( true );
	
	self thread bomblet_camera_waiter( rocket, MissileWeapon );
		
	self thread SpawnClusterChildren( RocketStoredForwardPosition, self.StrikeRocketStoredPosition, RotationStartPoint, MissileWeapon );

	self waittill("fade_white_over");

	wait(9);
	
	self freezeControlsWrapper(false);
}

fire_straight_bomblet( rocket, waitFrames, MissileWeapon ) // self == player
{
	MissileWeapon endon( "missile_strike_complete" );

	origin 	= rocket.origin;
	angles 	= rocket.angles;
	owner 	= rocket.owner;
	
	if ( waitFrames > 0 )
		wait ( waitFrames * 0.05 );
	
	bomblet = MagicBullet( "remotemissile_projectile_cluster_child_hellfire_mp", origin, origin + AnglesToForward( angles ) * 1000, self );
	bomblet.team = self.team;
	bomblet.killCamEnt = self;
	
	bomblet SetMissileMinimapVisible( true );
	
	bomblet thread bomblet_explosion_waiter( self, MissileWeapon );
}

fire_targeted_bomblet( rocket, target, waitFrames, MissileWeapon ) // self == player
{
	MissileWeapon endon( "missile_strike_complete" );

	// Cache info about the rocket (in case it dies)
	origin 			= rocket.origin;
	angles 			= rocket.angles;
	owner 			= rocket.owner;
	target_origin 	= target.origin;
	
	wait ( waitFrames * 0.05 );
		
	if ( IsDefined( target ) && Distance2DSquared( target.origin, target_origin ) < ( 240.0 * 240.0 ) )
		target_origin = target.origin;
	
	bomblet = MagicBullet( "remotemissile_projectile_cluster_child_hellfire_mp", origin, target_origin, self );
	bomblet.team = self.team;
	bomblet.killCamEnt = self;
	
	if ( IsDefined( target ) )
		bomblet Missile_SetTargetEnt( target );
	
	bomblet SetMissileMinimapVisible( true );
	
	bomblet thread bomblet_explosion_waiter( self, MissileWeapon );
}

fire_random_bomblet( rocket, index, waitFrames, MissileWeapon ) // self == player
{
	MissileWeapon endon( "missile_strike_complete" );

	targeting_radius = 600;
	
	// Cache info about the rocket (in case it dies)
	origin 		= rocket.origin;
	angles 		= rocket.angles;
	owner 		= rocket.owner;
	aimTarget	= rocket.aimtarget;
	
	wait ( waitFrames * 0.05 );
	
	angle = RandomIntRange( 10 + ( 60 * index ), 50 + ( 60 * index ) );
	radius = RandomIntRange( 200, targeting_radius + 100 );
	x = min( radius, targeting_radius - 50 ) * Cos( angle );
	y = min( radius, targeting_radius - 50 ) * Sin( angle );
	
	bomblet = MagicBullet( "remotemissile_projectile_cluster_child_hellfire_mp", origin, aimtarget + ( x, y, 0 ), self );
	bomblet.team = self.team;
	bomblet.killCamEnt = self;
	
	bomblet SetMissileMinimapVisible( true );
	
	bomblet thread bomblet_explosion_waiter( self, MissileWeapon );
}

bomblet_explosion_waiter( player, MissileWeapon ) // self == bomblet
{
	player endon( "disconnect" );
	MissileWeapon endon( "ms_early_exit" );
	MissileWeapon endon( "missile_strike_complete" );
	level endon( "game_ended" );
	
	self waittill( "death" );
	
	MissileWeapon notify( "bomblet_exploded" );
}

bomblet_camera_waiter( rocket, MissileWeapon ) // self == player
{
	self endon( "disconnect" );
	MissileWeapon endon( "ms_early_exit" ); 
	MissileWeapon endon( "missile_strike_complete" );
	rocket endon( "death" );
	level endon ( "game_ended" );

	MissileWeapon waittill( "bomblet_exploded" );

	wait( 1.0 );

	self thread bomblet_camera_waiter_complete( rocket, MissileWeapon );
}

bomblet_camera_waiter_complete( rocket, MissileWeapon )
{
	rocket notify( "death" );
	MissileWeapon notify( "missile_strike_complete" );
}

getPrespawnedClusterRotationEnt( MissileWeapon, newOrigin )
{
	while ( MissileWeapon.preSpawnedRotationEnts.size < (MissileWeapon.preSpawnedIndex+1) )
		waitframe();
	
	newEnt = MissileWeapon.preSpawnedRotationEnts[MissileWeapon.preSpawnedIndex];
	MissileWeapon.preSpawnedIndex++;
	if ( IsDefined( newOrigin ) )
		newEnt.origin = newOrigin;
	return newEnt;
}

SpawnClusterChildren( RocketStoredForwardPosition, ClusterStartPoint, RotationStartPoint, MissileWeapon )
{
	// ClusterStartPoint = rocket's origin
	// RotationStartPoint = rocket's origin + some forward offset
	
	///////// single middle child rocket///////////////////
	RandomX = RandomIntRange(16,64);
	if(RandomInt(100) > 50)
	{
		RandomX = RandomX *-1;
	}
	RandomY = RandomIntRange(16,64);
	if(RandomInt(100) > 50)
	{
		RandomY = RandomY *-1;
	}
	RandomPosition = ClusterStartPoint + (RandomX, RandomY,0);
	RandomTargetPosition = RotationStartPoint + (RandomX, RandomY,0);
	
	RotatingCenterEnt = getPrespawnedClusterRotationEnt( MissileWeapon, RotationStartPoint );
	RotatingCenterEnt.RotatingLinkArray = [];
	RotatingCenterEnt.RotatingLinkArray[0] = getPrespawnedClusterRotationEnt( MissileWeapon, RandomTargetPosition );
	RotatingCenterEnt.RotatingLinkArray[0] LinkToSynchronizedParent(RotatingCenterEnt);
	

	rocket = MagicBullet( "remotemissile_projectile_cluster_child_mp", RandomPosition, RandomTargetPosition, self );
	rocket Missile_SetTargetEnt(RotatingCenterEnt.RotatingLinkArray[0]);
	rocket Missile_SetFlightmodeDirect();
	rocket.owner = self;
	rocket.team = self.team;
	rocket SetMissileMinimapVisible( true );
	rocket thread bomblet_explosion_waiter( self, MissileWeapon );
	
	view_offset = RandomTargetPosition - RandomPosition;
	view_offset = VectorNormalize( (view_offset[0], view_offset[1], 0) );
	view_offset *= -30;
	view_offset += (0,0, 200);
	
	killcam = MissileWeapon.preSpawnedKillcamEnt;
	killcam.origin = RandomPosition + view_offset;
	killcam.killCamStartTime = GetTime();
	killcam LinkTo( rocket );
	killcam thread killCamRocketDeath( rocket );
	
	rocket.killCamEnt = killcam;
	
	ChildrenRockets = 10;
	MissileWeapon.RotatingLinkArrayIndex = RotatingCenterEnt.RotatingLinkArray.size;
	for(i = 0; i < ChildrenRockets; i++)
	{
		Index = RotatingCenterEnt.RotatingLinkArray.size;
		RotatingCenterEnt.RotatingLinkArray[Index] = getPrespawnedClusterRotationEnt( MissileWeapon );
	}
	
	rocket endon( "death" );
	self thread DeleteRotationEnts(RotatingCenterEnt, rocket);
	self thread RotateTargets(RotatingCenterEnt, rocket);
	self thread MoveTargets(RotatingCenterEnt, rocket);
	
	// each MagicBullet spawns 2 entities on a frame so a waitframe between each firing means 4 ents spawned per snapshot
	waitframe();
	
	////////// Random child rockets //////////////////////////////////////////////
	for(i = 1; i <= ChildrenRockets; i++)
	{
		rotateEnt = getMissilePosition( ClusterStartPoint, RotationStartPoint, RotatingCenterEnt, MissileWeapon );
		RandomTargetPosition = rotateEnt.origin;
		RandomPosition = rotateEnt.RandomPosition;
		child_rocket = MagicBullet( "remotemissile_projectile_cluster_child_mp", RandomPosition, RandomTargetPosition, self );
		child_rocket Missile_SetTargetEnt(RotatingCenterEnt.RotatingLinkArray[i]);
		child_rocket Missile_SetFlightmodeDirect();
		child_rocket.owner = self;
		child_rocket.team = self.team;
		child_rocket.killCamEnt = killcam;
		child_rocket SetMissileMinimapVisible( true );
		child_rocket thread bomblet_explosion_waiter( self, MissileWeapon );
		waitframe();
	}
}

getMissilePosition( ClusterStartPoint, RotationStartPoint, RotatingCenterEnt, MissileWeapon )
{
	rotateEnt = RotatingCenterEnt.RotatingLinkArray[MissileWeapon.RotatingLinkArrayIndex];
	MissileWeapon.RotatingLinkArrayIndex++;
	
	offset = RotatingCenterEnt.origin - RotationStartPoint;
	ClusterStartPoint = ClusterStartPoint + offset;
	
	RandomStartX = RandomIntRange(64,500);
	RandomTargetStartX = RandomStartX + 0;
	if(RandomInt(100) > 50)
	{
		RandomStartX = RandomStartX *-1;
		RandomTargetStartX = RandomStartX - 0;
	}
	
	RandomStartY = RandomIntRange(64,500);
	RandomTargetStartY = RandomStartY + 0;
	if(RandomInt(100) > 50)
	{
		RandomStartY = RandomStartY *-1;
		RandomTargetStartY = RandomStartY - 0;
	}
	RandomPosition = ClusterStartPoint + (RandomStartX, RandomStartY,0);
	RandomTargetPosition = RotatingCenterEnt.origin + (RandomTargetStartX, RandomTargetStartY,0);
	rotateEnt.origin = RandomTargetPosition;
	rotateEnt.RandomPosition = RandomPosition;
	rotateEnt LinkToSynchronizedParent( RotatingCenterEnt );
	return rotateEnt;
}

killCamRocketDeath(rocket)
{
	self endon("death");
	
	rocket waittill("death");
	
	self Unlink();
	self.origin += (0,0,50);
	wait 3;
	self Delete();
}

RotateTargets(RotatingCenterEnt, MasterRocket)
{
	MasterRocket endon("death");
	
	Time = 10;
	while(true)
	{
		RotatingCenterEnt RotateVelocity( (0,120,0), Time );
		wait(Time);
	}
}

/// MasterRocket is center child cluster rocket
// StoredAngles and StoredPosition is original parent rocket, must copy from self to new vector so doesn't get stomped if new missile fired
MoveTargets(RotatingCenterEnt, MasterRocket)
{
	MasterRocket endon("death");
	
	StoredMasterRocketPos = MasterRocket.origin;
	ConstDistanceBetween = Distance(MasterRocket.origin, RotatingCenterEnt.origin);
	StoredAngles = self.StrikeRocketStoredAngles;
	StoredPosition = self.StrikeRocketStoredPosition;
	
	while(true)
	{
		DistanceBetweenStart = Distance(MasterRocket.origin, StoredMasterRocketPos);
		NewPosition = StoredPosition + (AnglesToForward(StoredAngles) * (ConstDistanceBetween + DistanceBetweenStart));
		RotatingCenterEnt.origin = NewPosition;
		waitframe();
	}
}

DeleteRotationEnts(RotatingCenterEnt, MasterRocket)
{
	MasterRocket waittill("death");
	
	if(isdefined(RotatingCenterEnt))
	{
		foreach(Ent in RotatingCenterEnt.RotatingLinkArray)
		{
			Ent delete();
		}
		
		RotatingCenterEnt delete();
	}
}



//////
/////////////////////////////////////  END CLUSTER ROCKET   ///////////////////////////////////////////////


///////////////////////////// GAS MISSILE STUFF///////////////////////////////////////////////////
///////////
////////

ReleaseGas( LastRocketPos )
{
	GasPosition = LastRocketPos + (0,0,40);

	GasCloud = spawn("script_model", GasPosition);
	GasCloud setModel( "tag_origin" );
	killCamEnt = Spawn( "script_model", GasCloud.origin);
	GasCloud.killCamEnt = killCamEnt;
	GasCloud.killCamEnt SetScriptMoverKillCam( "explosive" );
	GasCloud.killCamEnt LinkToSynchronizedParent( GasCloud);
	
	gas_center = spawnstruct();
	gas_center.origin = GasPosition;
	gas_center.team = self.team;
	
	level.missile_strike_gas_clouds[level.missile_strike_gas_clouds.size] = gas_center;
	
	GasCloud thread showGasCloud( self );
	
	// enemy compass icon
	GasCloud.objIDEnemy = maps\mp\gametypes\_gameobjects::getNextObjID();	
	objective_add( GasCloud.objIDEnemy, "active", GasCloud.origin, "hud_gas_enemy" );
	Objective_PlayerEnemyTeam( GasCloud.objIDEnemy, self GetEntityNumber() );
	
	// friendly compass icon
	GasCloud.objIDFriendly = maps\mp\gametypes\_gameobjects::getNextObjID();	
	objective_add( GasCloud.objIDFriendly, "active", GasCloud.origin, "hud_gas_friendly" );
	Objective_PlayerTeam( GasCloud.objIDFriendly, self GetEntityNumber() );
	
	self thread ChemDamageThink( GasCloud, GasPosition, self );
			
	self waittill_any_timeout_no_endon_death( 20, "joined_team", "joined_spectators", "disconnect" );

	_objective_delete( GasCloud.objIDEnemy );
	_objective_delete( GasCloud.objIDFriendly );
	
	wait(2);
	GasCloud friendlyEnemyEffectsStop();
	GasCloud.killCamEnt delete();
	GasCloud delete();
	
	found = false;
	newarray = [];
	for ( i = 0; i < level.missile_strike_gas_clouds.size; i++ )
	{
		if ( !found && level.missile_strike_gas_clouds[i].origin == GasPosition )
		{
			found = true;
			continue;
}

		newarray[ newarray.size ] = level.missile_strike_gas_clouds[i];
	}
	assert( found );
	assert( newarray.size == level.missile_strike_gas_clouds.size - 1 );
	level.missile_strike_gas_clouds = newarray;
}

showGasCloud( owner )	
{
	friendlyFxId = level._missile_strike_setting[ "Particle_FX" ].GasFriendly;
	enemyFxId = level._missile_strike_setting[ "Particle_FX" ].Gas;
	origin = self GetTagOrigin( "tag_origin" );
	fwd = ( 1, 0, 0 );
	self.friendlyFX = spawnFXShowToTeam( friendlyFxId, owner.team, origin, fwd );
	self.enemyFX = spawnFXShowToTeam( enemyFxId, getOtherTeam(owner.team), origin, fwd );
}

friendlyEnemyEffectsStop()
{
	if(IsDefined(self.friendlyFX))
		self.friendlyFX Delete();
	if(IsDefined(self.enemyFX))
		self.enemyFX Delete();
}

CreateGasTrackingOverlay()
{
	// TODO: Cleanup HUD
	if ( !IsDefined( self.StrikeGasTrackingOverlay ) )
	{
		self.StrikeGasTrackingOverlay = newClientHudElem( self );
		self.StrikeGasTrackingOverlay.x = 0;
		self.StrikeGasTrackingOverlay.y = 0;
		self.StrikeGasTrackingOverlay setshader( "lab_gas_overlay", 640, 480 );
		self.StrikeGasTrackingOverlay.alignX = "left";
		self.StrikeGasTrackingOverlay.alignY = "top";
		self.StrikeGasTrackingOverlay.horzAlign = "fullscreen";
		self.StrikeGasTrackingOverlay.vertAlign = "fullscreen";
		self.StrikeGasTrackingOverlay.alpha = 0;
	}
}

ChemDamageThink (GasCloud, GasPosition, attacker )
{
	GasCloud endon( "death" );
	attacker endon( "joined_team" );
	attacker endon( "joined_spectators" );
	attacker endon( "disconnect" );
	
	Radius = 200;
	Damage = 20;
	
	while( true )
	{
		if( !IsDefined(attacker) )
			return;
		
		// EInflictor is self on radius damage and needs to be kill cam ent
		GasCloud.killCamEnt RadiusDamage(GasPosition, Radius, Damage, Damage, attacker, "MOD_TRIGGER_HURT", "killstreak_strike_missile_gas_mp", false);
	
		wait( 1 );
	}
}

WaitForGasDamage()
{
	while(1)
	{			
		self waittill( "damage", amount, attacker, direction, point, means_of_death, model, tag, part_name, damage_flags, weapon_name );
		{
			if( IsDefined(attacker) && (self == attacker) )
				continue;
			
			if( self _hasPerk("specialty_stun_resistance") ) 
				continue;
			
			if( IsDefined(weapon_name) && (weapon_name == "killstreak_strike_missile_gas_mp") )
			{
				self thread ShockThink();
			}
		}	
	}
}

ShockThink()
{
	if(self.MissileStrikeGasTime <= 0)
	{
		self thread fadeInOutGasTrackingOverlay();
		self thread RemoveOverlayDeath();
	}
	self.MissileStrikeGasTime = 2;
	self shellshock( "mp_lab_gas", 1);
	while(self.MissileStrikeGasTime > 0)
	{
		self.MissileStrikeGasTime--;
		wait(1);	
	}
	self notify( "missile_strike_gas_end" );
	self EndGasTrackingOverlay();
}

fadeInOutGasTrackingOverlay()
{

	level endon( "game_ended" );
	self endon( "missile_strike_gas_end" );
	self endon( "death" );
	
	if (IsDefined( self.StrikeGasTrackingOverlay ) )
	{
		while ( true )
		{
			self.StrikeGasTrackingOverlay FadeOverTime( 0.4 );
			self.StrikeGasTrackingOverlay.alpha = 1;
			wait( 0.5 );
			self.StrikeGasTrackingOverlay FadeOverTime( 0.4 );
			self.StrikeGasTrackingOverlay.alpha = 0.5;
			wait( 0.5 );
		}
	}
}

EndGasTrackingOverlay()
{
	if (IsDefined( self.StrikeGasTrackingOverlay ) )
	{
		self.StrikeGasTrackingOverlay FadeOverTime( 0.2 );
		self.StrikeGasTrackingOverlay.alpha = 0.0;
	}
}

EndGasTrackingOverlayDeath()
{
	if (IsDefined( self.StrikeGasTrackingOverlay ) )
	{
	//	self.StrikeGasTrackingOverlay FadeOverTime( 0.0 );
		self.StrikeGasTrackingOverlay.alpha = 0.0;
	}
}

RemoveOverlayDeath()
{
	self endon( "missile_strike_gas_end" );
	self waittill( "death" );
	self thread EndGasTrackingOverlayDeath();
}


/////
///////
//////////////////////////////////// END GAS MISSILE STUFF /////////////////////////////////////////////


Player_CleanupOnTeamChange( rocket, MissileWeapon )
{
	rocket endon ( "death" );
	self endon ( "disconnect" );

	self waittill_any( "joined_team", "joined_spectators" );

	MissileWeapon notify( "missile_strike_complete" );
	
	level.remoteMissileInProgress = undefined;
}


Rocket_CleanupOnDeath()
{
	entityNumber = self getEntityNumber();
	level.rockets[ entityNumber ] = self;
	self waittill( "death" );	
	
	level.rockets[ entityNumber ] = undefined;
	
	level.remoteMissileInProgress = undefined;
}


Player_CleanupOnGameEnded( rocket, MissileWeapon )
{
	rocket endon ( "death" );
	self endon ( "disconnect" );
	
	level waittill ( "game_ended" );
	
	MissileWeapon notify( "missile_strike_complete" );
}


// ----------------------------------------------------------------------------
// Targeting
// ----------------------------------------------------------------------------

player_is_valid_target( player ) // self == player
{
	if ( !IsDefined( player ) )
		return false;
	
	if ( IsAlliedSentient( player, self ) )
		return false;
	
	if ( !IsAlive( player ) )
		return false;
	
	if ( player _hasPerk( "specialty_blindeye" ) )
		return false;
	
	// 5 second ignore on spawn
	if ( isDefined( player.spawntime ) && ( ( ( GetTime() - player.spawntime ) / 1000 ) < 5 ) )
		return false;
	
	return true;
}

getEnemyTargets() // self == player
{
	enemyPlayers = [];
	
	foreach ( player in level.players )
	{
		if ( !player_is_valid_target( player ) )
			continue;
	
		enemyPlayers[enemyPlayers.size] = player;
	}
	
	return enemyPlayers;
}

getValidTargetsSorted( rocket, shouldTrace, MissileWeapon )
{
	missileType = MissileWeapon.name;
	
	targeting_radius = 600;
	targeting_radius_squared = targeting_radius * targeting_radius;
	
	targets = [];

	forward = AnglesToForward ( rocket.angles );

	rocketZ = rocket.origin[2];
	mapCenterZ = level.mapCenter[2];
	diff = mapCenterZ - rocketZ;

	ratio = diff / forward[2];

	aimTarget = rocket.origin + forward * ratio;
	rocket.aimTarget = aimTarget;

//	/#
//	circle( rocket.aimTarget, targeting_radius, (0,1,0), true, true, 2000 );
//	#/
	
	maxTargets = 0;
	
	// Baby missiles get a single lockon target
	// Cluster missiles (parents) get up to 10 lockon targets
	
	if ( MissileWeapon.RocketAmmo > 0 ) 
		maxTargets = 1;
	else if ( MissileWeapon.clusterMissile && MissileWeapon.clusterHellfire && !self.ClusterDeployed ) // Only the hellfire gets lock-on support
		maxTargets = 10;
	else
		return targets;
	
	enemies = self getEnemyTargets();
	
	foreach( player in enemies )
	{
		if ( Distance2DSquared( player.origin, aimTarget) < targeting_radius_squared )
		{
			if ( shouldTrace )
			{
				if ( BulletTracePassed( player.origin + (0,0,60), player.origin + (0,0,180), false, player) )
				{
					targets[targets.size] = player;					
				}
			}
			else
			{
				targets[targets.size] = player;
			}
		}
	}
	
	sorted_targets = get_array_of_closest( rocket.aimTarget, targets, undefined, maxTargets );
	
	return sorted_targets;
}

targeting_hud_init() // self == player
{
	self targeting_hud_destroy();
	
	max_targeting_icons = 10;
	
	self.missile_target_icons = [];
	
	for ( i = 0; i < max_targeting_icons; i++ )
	{
		newIcon = NewClientHudElem( self );
		newIcon.x = 0;
		newIcon.y = 0;
		newIcon.z = 0;
		newIcon.alpha = 0;
		newIcon.archived = false;
		newIcon.shader = CONST_HOSTILE;
		newIcon SetShader( CONST_HOSTILE, 450, 450 );
		newIcon SetWayPoint( false, false, false, false );
		newIcon SetWaypointIconFadeAtCenter( false );
		newIcon SetWaypointAerialTargeting( true );
		
		self.missile_target_icons[i] = newIcon;
	}
}

targeting_hud_destroy() // self == player
{
	if ( !IsDefined( self.missile_target_icons ) )
		return;
	
	max_targeting_icons = 10;
	
	for ( i = 0; i < max_targeting_icons; i++ )
	{
		if ( IsDefined( self.missile_target_icons[i] ) )
			self.missile_target_icons[i] Destroy();
	}
	
	self.missile_target_icons = undefined;
}

targeting_hud_think( rocket, MissileWeapon ) // self == player
{
	self endon( "disconnect" );
	MissileWeapon endon( "ms_early_exit" ); 
	MissileWeapon endon( "missile_strike_complete" );
	rocket endon("death");
	level endon ( "game_ended" );
	
	wait( 1 );
	
	missileType = MissileWeapon.name;
	rocket.targets = self getValidTargetsSorted( rocket, true, MissileWeapon );
	
	max_targeting_icons = 10;

	framesBetweenTargetScans = 5;
	framesSinceTargetScan = 0;
	player_z_offset = 47;
	
	while( true )
	{
		// Hide all of the icons
		foreach ( icon in self.missile_target_icons )
			icon.alpha = 0;
		
		framesSinceTargetScan++;

		if( framesSinceTargetScan > framesBetweenTargetScans )
		{
			rocket.targets = self getValidTargetsSorted( rocket, true, MissileWeapon );
			framesSinceTargetScan = 0;
		}
		
		// icon for the kill streak user
		HUDicon = self.missile_target_icons[0];
		HUDicon.x = self.origin[0];
		HUDicon.y = self.origin[1];
		HUDicon.z = self.origin[2];
		HUDicon.alpha = 1;
			
		if( HUDicon.shader != CONST_SELF )
		{
			HUDicon.shader = CONST_SELF;
			HUDicon SetShader( CONST_SELF, 450, 450 );
			HUDicon SetWayPoint( false, false, false, false );
			HUDicon SetWaypointIconFadeAtCenter( false );
			HUDicon SetWaypointAerialTargeting( true );
		}
		
		valid_target_index = 1;
		newTargets = 0;
		
		agents = level.agentArray;
		if ( !IsDefined( agents ) )
			agents = [];
		potentialTargets = array_combine( level.players, agents );
		
		// Cull the rocket targets from the array to be added back later (they always take priority)
		rocket.targets = array_removeUndefined( rocket.targets );
		nonTargets = array_remove_array( potentialTargets, rocket.targets );
		validNonTargets = [];
		foreach( ent in nonTargets )
		{
			if ( player_is_valid_target( ent ) )
				validNonTargets[validNonTargets.size] = ent;
		}
		
		// Sort the array of nonTargets by distance from the aim target
		sortedNonTargets = get_array_of_closest( rocket.aimTarget, validNonTargets, undefined, undefined );
		
		// Combine the arrays, but give the actual targets priority
		potentialTargets = array_combine( rocket.targets, sortedNonTargets );
		
		foreach( player in potentialTargets )
		{
			if( !IsDefined(player) )
				continue;
			
			HUDicon = self.missile_target_icons[valid_target_index];
			if ( !IsDefined( HUDicon ) )
				break;
			
			if( ( IsPlayer(player) || IsAgent ( player ) ) && player_is_valid_target(player))
			{
				HUDicon.x = player.origin[0];
				HUDicon.y = player.origin[1];
				HUDicon.z = player.origin[2];
				HUDicon.alpha = 1;
				valid_target_index++;
				
				if( array_contains( rocket.targets, player) && (HUDicon.shader == CONST_HOSTILE) )
				{
					// set lock on icon
					HUDicon.shader = CONST_HOSTIL_LOCK;
					HUDicon SetShader( CONST_HOSTIL_LOCK, 450, 450 );
					HUDicon SetWayPoint( false, false, false, false, false );
					HUDicon fadeOverTime( 0.05 );
					HUDicon SetWaypointIconFadeAtCenter( false );
					HUDicon SetWaypointAerialTargeting( true );
					newTargets++;
					
				}
				else if( !array_contains( rocket.targets, player) && (HUDicon.shader == CONST_HOSTIL_LOCK) )
				{
					HUDicon.shader = CONST_HOSTILE;
					HUDicon SetShader( CONST_HOSTILE, 450, 450 );
					HUDicon SetWayPoint( false, false, false, false );
					HUDicon fadeOverTime( 0.05 );
					HUDicon SetWaypointIconFadeAtCenter( false );
					HUDicon SetWaypointAerialTargeting( true );
				}
			}
		}
	
		if( newTargets == 1 )
			rocket PlaySoundToPlayer( "mstrike_locked_on_single", rocket.owner );
		
		if( newTargets > 1 )
			rocket PlaySoundToPlayer( "mstrike_locked_on_multiple", rocket.owner );
	
		waitframe();
	}
}


// ----------------------------------------------------------------------------
// HUD
// ----------------------------------------------------------------------------
init_hud( rocket, MissileWeapon ) // self == player
{
	self endon( "disconnect" );
	
	self thread targeting_hud_init();
	self thread targeting_hud_think( rocket, MissileWeapon );

	self SetClientOmnvar( "ui_predator_missile", 1 );
	self SetClientOmnvar( "ui_predator_missile_count", MissileWeapon.RocketAmmo );
	
	missileType = MissileWeapon.name;
	
	if ( missileType == "remotemissile_projectile_mp" )
	{
		self SetClientOmnvar( "ui_predator_missile_type", 1 );
	}
	else if ( missileType == "remotemissile_projectile_gas_mp" )
	{
		self SetClientOmnvar( "ui_predator_missile_type", 2 );
	}
	else if ( missileType == "remotemissile_projectile_cluster_parent_mp" )
	{
		if ( MissileWeapon.clusterHellfire )
			self SetClientOmnvar( "ui_predator_missile_type", 4 );
		else
			self SetClientOmnvar( "ui_predator_missile_type", 3 );
	}
	
	waitframe(); // Give the handlers time to get in place before we set the rest
	
	self hud_update_fire_text( rocket, MissileWeapon );
	self maps\mp\killstreaks\_aerial_utility::playerEnableStreakStatic();
}

remove_hud() // self == player
{
	// Reset all of the omnvars
	self SetClientOmnvar( "ui_predator_missile", 0 );
	self SetClientOmnvar( "ui_predator_missile_text", 0 );
	self SetClientOmnvar( "ui_predator_missile_type", 0 );
	self SetClientOmnvar( "ui_predator_missile_count", 0 );
	self targeting_hud_destroy();
	
	self maps\mp\killstreaks\_aerial_utility::playerDisableStreakStatic();
}

hud_update_fire_text( rocket, MissileWeapon ) // self == player
{
	self SetClientOmnvar( "ui_predator_missile_count", MissileWeapon.RocketAmmo );
	if ( MissileWeapon.RocketAmmo > 0 )
	{
		assert( MissileWeapon.RocketAmmo <= 2 );
		
		// Missiles remaining: 2 (or 1)
		self SetClientOmnvar( "ui_predator_missile_text", MissileWeapon.RocketAmmo );
	}
	else if ( MissileWeapon.clusterMissile )
	{
		if ( !self.ClusterDeployed )
		{
			// Cluster Ready
			self SetClientOmnvar( "ui_predator_missile_text", 5 );
		}
		else
		{
			// Cluster Deployed
			self SetClientOmnvar( "ui_predator_missile_text", 6 );
		}
	}
	else
	{
		if ( !self.MissileBoostUsed )
		{
			// Boost Ready
			self SetClientOmnvar( "ui_predator_missile_text", 3 );
		}
		else
		{
			// Boost Active
			self SetClientOmnvar( "ui_predator_missile_text", 4 );
		}
	}
}

hud_watch_for_boost_active( rocket, MissileWeapon ) // self == player
{
	self endon( "disconnect" );
	MissileWeapon endon( "missile_strike_complete" );
	MissileWeapon endon( "ms_early_exit" );
	
	self waittill( "FireButtonPressed" );
	
	self PlayRumbleOnEntity( "sniper_fire" );
	
	self.MissileBoostUsed = true;
	
	self hud_update_fire_text( rocket, MissileWeapon );
}

fade_to_white() // self == player
{
	self endon( "disconnect" );
	
	if ( !IsDefined( self.StrikeWhiteFade ) )
	{
		self.StrikeWhiteFade = newClientHudElem( self );
		self.StrikeWhiteFade.x = 0;
		self.StrikeWhiteFade.y = 0;
		self.StrikeWhiteFade setshader( "white", 640, 480 );
		self.StrikeWhiteFade.alignX = "left";
		self.StrikeWhiteFade.alignY = "top";
		self.StrikeWhiteFade.horzAlign = "fullscreen";
		self.StrikeWhiteFade.vertAlign = "fullscreen";
		self.StrikeWhiteFade.alpha = 0;
	}
	
	self.StrikeWhiteFade FadeOverTime( 0.15 );
	self.StrikeWhiteFade.alpha = 1;
	
	wait( 0.15 );
	
	self notify( "full_white" );
	
	self.StrikeWhiteFade FadeOverTime( 0.2 );
	self.StrikeWhiteFade.alpha = 0;
	
	wait( 0.2);
	
	self notify("fade_white_over");
	
	self.StrikeWhiteFade Destroy();
}

playerAddNotifyCommands()
{
	self NotifyOnPlayerCommand( "FireButtonPressed", "+attack" );
	self NotifyOnPlayerCommand( "FireButtonPressed", "+attack_akimbo_accessible" );
}

playerRemoveNotifyCommands()
{
	self NotifyOnPlayerCommandRemove( "FireButtonPressed", "+attack" );
	self NotifyOnPlayerCommandRemove( "FireButtonPressed", "+attack_akimbo_accessible" );
}
