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

CONST_stinger_weaponname = "stingerm7";

CONST_lock_require_los = true;
CONST_lock_angle = 5;
CONST_lock_time = 1;
CONST_max_locks = 4;
CONST_nosight_time_limit = 500;


stingerm7_think()
{
	self thread stingerm7_targeting();
	self thread stingerm7_monitor_fire();	
}

stingerm7_targeting()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "faux_spawn" );
	self endon( "joined_team" );
	
	self.stingerm7_info = SpawnStruct();
	self.stingerm7_info.locked_targets = [];
	self.stingerm7_info.locking_time = 0;
	was_ads_stingerm7 = false;
	
	while( true )
	{
		if ( IsSubStr( self GetCurrentWeapon(), CONST_stinger_weaponname ) && self PlayerAds() > .99 )
		{
			was_ads_stingerm7 = true;
			
			if ( self.stingerm7_info.locked_targets.size > 0 )
			{
				self remove_invalid_locks();
			}
			self.stingerm7_info.locked_targets = array_remove_dead( array_removeUndefined( self.stingerm7_info.locked_targets ) );
			
			if ( IsDefined( self.stingerm7_info.locking_target ) )
			{
				// Check that the stinger's current locking target is still valid. If not, remove it.
				if ( !locking_target_still_valid( self.stingerm7_info.locking_target ) )
				{
					//Target_Remove(self.stingerm7_info.locking_target);
					self.stingerm7_info.locking_target = undefined;
					self notify( "stop_javelin_locking_feedback" );
				}
			}
			
			if ( IsDefined( self.stingerm7_info.locking_target ) )
			{
				// If the stinger locking target wasn't removed above, increment the locking_time.
				self.stingerm7_info.locking_time += .05;		
			}
			else
			{
				// If there is no locking_target, try to get a new one.
				self.stingerm7_info.locking_time = 0;
				
				if ( self.stingerm7_info.locked_targets.size < CONST_max_locks )
				{
					self.stingerm7_info.locking_target = self get_best_locking_target();
					if ( IsDefined( self.stingerm7_info.locking_target ) )
					{
						// Acquired a new locking target.
						self thread locking_feedback();
					}
				}
			}
			
			if ( self.stingerm7_info.locking_time >= CONST_lock_time && IsDefined( self.stingerm7_info.locking_target ) && self.stingerm7_info.locked_targets.size < CONST_max_locks )
			{
				// Lock on acquired.
				self notify( "stop_javelin_locking_feedback" );
				self.stingerm7_info.locked_targets[ self.stingerm7_info.locked_targets.size ] = self.stingerm7_info.locking_target;
				
				self thread locked_feedback();
				
				self.stingerm7_info.locking_target = undefined;
			}
			
			if ( self.stingerm7_info.locked_targets.size > 0 )
			{
				self WeaponLockFinalize( self.stingerm7_info.locked_targets[ 0 ] );
			}
			else
			{
				self WeaponLockFree();
				self notify( "stop_javelin_locked_feedback" );
			}
		}
		else
		{
			if ( was_ads_stingerm7 == true )
			{
				// The player is not ADS with the StingerM7.
				was_ads_stingerm7 = false;
				self WeaponLockFree();
				self notify( "stop_javelin_locking_feedback" );
				self notify( "stop_javelin_locked_feedback" );
				
				self.stingerm7_info.locked_targets = [];
				
				if ( IsDefined( self.stingerm7_info.locking_target ) )
				{
					//Target_Remove(self.stingerm7_info.locking_target);
					self.stingerm7_info.locking_target = undefined;
				}
				
				self.stingerm7_info.locking_time = 0;
			}
		}
		wait( 0.05 );
	}
}

stingerm7_monitor_fire()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "faux_spawn" );
	self endon( "joined_team" );
	
	while( true )
	{
		self waittill( "missile_fire", projectile, weaponName );
		if ( IsSubStr( weaponName, CONST_stinger_weaponname ) )
		{
			thread stinger_fire( self, projectile, weaponName );
		}
	}
}

stinger_fire( player, projectile, weapon_name )
{
	// A bit of hack, we could instead modify the engine to fire multiple projectiles.
	// instead, we just delete the one it fired and make our own.
	
	init_origin = (0,0,0);
	init_angles = (0,0,0);
	if ( isDefined( projectile ) )
	{
		init_origin = projectile.origin;
		init_angles = projectile.angles;
		projectile Delete();
	}
	else
	{
		return;
	}
	
	player.stingerm7_info.locked_targets = array_remove_dead( array_removeUndefined( self.stingerm7_info.locked_targets ) );
	
	missiles = [];
	
	for ( i = 0; i < CONST_max_locks; i++ )
	{
		fire_angles = init_angles + random_vector( 20, 20, 20 );
		fire_direction = AnglesToForward( fire_angles );
		
		new_projectile = MagicBullet( weapon_name, init_origin, init_origin + fire_direction, player );
		new_projectile.owner = player;
		
		// every locked target gets at least one shot, and remaining shots are randomly divided amongst locked targets
		if ( player.stingerm7_info.locked_targets.size > 0 )
		{
			target = undefined;
			if ( i < player.stingerm7_info.locked_targets.size )
				target = player.stingerm7_info.locked_targets[ i ];
			else
				target = player.stingerm7_info.locked_targets[ RandomInt( player.stingerm7_info.locked_targets.size ) ];
			
			new_projectile Missile_SetTargetEnt( target, stingerm7_get_target_offset( target ) );
			new_projectile.lockedStingerTarget = target;
		}
		
		missiles[ missiles.size ] = new_projectile;
	}
	
	level notify( "stinger_fired", player, missiles );
	
	// you used all your ammo
	player SetWeaponAmmoClip( weapon_name, 0 );
}

anyStingerMissileLockedOn( missiles, target )
{
	foreach ( missile in missiles )
	{
		if ( IsDefined( missile.lockedStingerTarget ) && missile.lockedStingerTarget == target )
			return true;
	}
	return false;
}

get_best_locking_target()
{
	Assert( IsPlayer( self ) );
	
	enemy_team = getOtherTeam( self.team );
	enemy_players = [];
	
	//add enemy players
	foreach( player in level.players )
	{
		if ( level.teamBased && player.team == self.team )
			continue;
		
		if( !isReallyAlive( player) )
			continue;
		
		enemy_players[ enemy_players.size ] = player;
	}
	
	//add enemy vehicles
	all_vehicles = Vehicle_GetArray();
	enemy_vehicles = [];
	
	foreach( vehicle in all_vehicles )
	{
		if ( !isDefined( vehicle.owner ) )
			continue;
		
		if ( vehicle maps\mp\killstreaks\_aerial_utility::vehicleIsCloaked() )
			continue;
		
		if ( level.teamBased && vehicle.owner.team == self.team )
			continue;
		
		enemy_vehicles[ enemy_vehicles.size ] = vehicle;
	}
		
	//add horde mode targets
	if( IsDefined( level.isHorde ) && level.isHorde )
	{
	   	foreach( agent in level.agentarray )
   			{
				if ( level.teamBased && agent.team == self.team )
					continue;
				
				if( !isReallyAlive( agent) )
					continue;
				
				enemy_players[ enemy_players.size ] = agent;
			}
   		
	   	foreach( vehicle in all_vehicles )
			{

				if ( vehicle maps\mp\killstreaks\_aerial_utility::vehicleIsCloaked() )
					continue;
				
				if ( level.teamBased && vehicle.team == self.team )
					continue;
				
				enemy_vehicles[ enemy_vehicles.size ] = vehicle;
			}
	   	
	}
	
	aerialTargets = maps\mp\killstreaks\_killstreaks::getAerialKillstreakArray( enemy_team );
	
	targets = array_combine( enemy_players, enemy_vehicles );
	targets = array_combine( targets, aerialTargets );
	if(IsDefined(level.stingerLockOnEntsFunc))
		targets = array_combine( targets, [[level.stingerLockOnEntsFunc]](self) );
	
	eye_origin = self GetEye();
	eye_dir = AnglesToForward( self GetPlayerAngles() );
	
	best_target = undefined;
	best_target_dot = Cos( CONST_lock_angle );
	foreach( target in targets )
	{
		if ( !array_contains( self.stingerm7_info.locked_targets, target ) )
		{
			target_origin = stingerm7_get_target_pos( target );
			dot = VectorDot( VectorNormalize( target_origin - eye_origin ), eye_dir );
			if ( dot > best_target_dot )
			{
				result = undefined;
				can_target = !CONST_lock_require_los;
				if ( !can_target )
				{
					tracePassed = BulletTracePassed( eye_origin, target_origin, false, target );
					if ( tracePassed )
						can_target = true;
				}
				
				if ( can_target )
				{
					best_target = target;
					best_target_dot = dot;
				}
			}
		}
	}
	
	return best_target;
}

locking_target_still_valid( target )
{
	Assert( IsPlayer( self ) );
	
	eye_origin = self GetEye();
	eye_dir = AnglesToForward( self GetPlayerAngles() );	
	target_origin = stingerm7_get_target_pos( target );
	
	if ( ( IsPlayer( target ) || IsBot( target ) || ( isDefined( level.isHorde ) && level.isHorde && IsAgent( target )) ) && !isReallyAlive( target ) )
		return false;
	
	if ( VectorDot( VectorNormalize( target_origin - eye_origin ), eye_dir ) > Cos( CONST_lock_angle ) )
	{
		if (!CONST_lock_require_los || BulletTracePassed( eye_origin, target_origin, false, target ) )
		{
			return true;
		}
	}
	return false;
}

remove_invalid_locks()
{
	for ( i = 0; i <= self.stingerm7_info.locked_targets.size; i++ )
	{
		if ( IsDefined( self.stingerm7_info.locked_targets[i] ) && IsDefined( self.stingerm7_info.locked_targets[i].origin ) )
		{
			if ( !IsDefined( self.stingerm7_info.locked_targets[i].sight_lost_time ) )
			{
				self.stingerm7_info.locked_targets[i].sight_lost_time = -1;
			}
			
			origin_mod = ( 0, 0, 0 );
			if ( IsPlayer( self.stingerm7_info.locked_targets[i] ) || IsBot( self.stingerm7_info.locked_targets[i] ) )
			{
				origin_mod = ( 0, 0, 64 );
			}
			
			if ( self WorldPointInReticle_Rect( self.stingerm7_info.locked_targets[i].origin + origin_mod, 50, 400, 200 ) )
			{
				if ( BulletTracePassed( self GetEye(), self.stingerm7_info.locked_targets[i].origin + origin_mod, false, self.stingerm7_info.locked_targets[i] ) )
				{
					// All checks pass, the target is still valid. Move onto the next one in the list.
					self.stingerm7_info.locked_targets[i].sight_lost_time = -1;
					continue;
				}
			}
			// A check failed, line of sight to target lost.
			if ( self.stingerm7_info.locked_targets[i].sight_lost_time == -1 )
			{
				self.stingerm7_info.locked_targets[i].sight_lost_time = GetTime();
			}
			else if ( GetTime() - self.stingerm7_info.locked_targets[i].sight_lost_time >= CONST_nosight_time_limit )
			{
				self.stingerm7_info.locked_targets[i].sight_lost_time = -1;
				self.stingerm7_info.locked_targets[i] = undefined;
			}
		}
	}
}

stingerm7_get_target_pos( target )
{
	if(IsDefined(target.getStingerTargetPosFunc))
		return target [[target.getStingerTargetPosFunc]]();
		
	return target GetPointInBounds( 0, 0, 0 );
}

stingerm7_get_target_offset( target )
{
	return stingerm7_get_target_pos( target ) - target.origin;
}

locking_feedback()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "faux_spawn" );
	self endon( "joined_team" );
	self endon( "stop_javelin_locking_feedback" );

	for ( ;; )
	{
		if( isDefined( level.SpawnedWarbirds ) )
		{
			foreach( warbird in level.SpawnedWarbirds )
			{
				if( isDefined( warbird.owner ) && isDefined( warbird.player ) && isDefined( self.stingerm7_info.locking_target ) && self.stingerm7_info.locking_target == warbird )
					warbird.owner playLocalSound( "wpn_stingerm7_enemy_locked" );
			}
		}
		
		if ( isDefined( level.orbitalsupport_player ) && isDefined( self.stingerm7_info.locking_target ) && self.stingerm7_info.locking_target == level.orbitalsupport_planeModel )
			level.orbitalsupport_player playLocalSound( "wpn_stingerm7_enemy_locked" );
		
		self playLocalSound( "wpn_stingerm7_locking" );
		self PlayRumbleOnEntity( "heavygun_fire" );

		wait 0.6;
	}
}

locked_feedback()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "faux_spawn" );
	self endon( "joined_team" );
	self endon( "stop_javelin_locked_feedback" );
	
	for ( ;; )
	{
		if( isDefined( level.SpawnedWarbirds ) )
		{
			foreach( warbird in level.SpawnedWarbirds )
			{
				if( isDefined( warbird.owner ) && isDefined( warbird.player ) && isDefined( self.stingerm7_info.locked_targets ) && IsInArray( self.stingerm7_info.locked_targets, warbird ) )
					warbird.owner playLocalSound( "wpn_stingerm7_enemy_locked" );
				
			}
		}
		
		if ( isDefined( level.orbitalsupport_player ) && isDefined( self.stingerm7_info.locked_targets ) && IsInArray( self.stingerm7_info.locked_targets, level.orbitalsupport_planeModel ) )
			level.orbitalsupport_player playLocalSound( "wpn_stingerm7_enemy_locked" );

		self playLocalSound( "wpn_stingerm7_locked" );
		self PlayRumbleOnEntity( "heavygun_fire" );

		wait 0.25;
	}
}

///////////////////////

array_remove_dead( array )
{
	temp = [];
	
	foreach( thing in array )
	{
		if ( !isAlive( thing ) )
			continue;
		
		temp[ temp.size ] = thing;
	}
	
	return temp;
}

random_vector( num1, num2, num3 )
{
	return( RandomFloat( num1 ) - num1 * 0.5, RandomFloat( num2 ) - num2 * 0.5, RandomFloat( num3 ) - num3 * 0.5 );
}

IsInArray( array, Ent )
{
	if ( IsDefined( array ) )
	{
		foreach ( Index in array )
		{
			if ( Index == Ent )
				return true;
		}
	}
	return false;
}