#include common_scripts\utility;

CONST_microdrone_weaponname = "iw5_microdronelauncher_mp";
CONST_max_homing_angle_from_grenade = 15;
CONST_begin_homing_time = .35;
CONST_drop_accel = 800; // inches/s^2

CONST_explosion_delay_time = 3;


monitor_microdrone_launch()
{
	level._effect[ "mdl_sticky_explosion" ] = LoadFx( "vfx/explosion/frag_grenade_default" );
	level._effect[ "mdl_sticky_blinking" ] = LoadFx( "vfx/lights/light_semtex_blinking" );
	
	Assert(IsPlayer(self) || IsAgent(self));
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "faux_spawn" );
	
	while(true)
	{
		self waittill("missile_fire", projectile, weaponName);		
		if( IsSubStr( weaponName, CONST_microdrone_weaponname ) )
		{
			projectile setOtherEnt( self );
//			//projectile thread microdrone_think(self); //tracking functionality, disabling to test new delayed sticky explosives
//			projectile thread determine_projectile_position( self );
//			projectile waittill( "death" );
//			projectile thread determine_sticky_position( self );
		}			
	}
}

determine_projectile_position( firing_player )
{
	//keep track of where it was the frame before
	//when it impacts, do a trace from the frame prior to the impact location to get all your info
	
	self endon( "death" );
	
	while( true )
	{
		if( !IsDefined( self.previous_position ) )
		{
			self.previous_position = self.origin;
		}
		
		wait( 0.05 );
		
		self.previous_position = self.origin;
	}
}

determine_sticky_position( firing_player )
{
	firing_player endon( "spawned_player" );
	
	if( !IsDefined( self.previous_position ) )
		return;
	
	if( !isDefined( self ) ) //timed out
		return;
	
	previous_to_current = self.origin - self.previous_position;
	trajectory = VectorToAngles( previous_to_current );
	forward = AnglesToForward( trajectory ) * 8000;
	end = self.origin + forward;
	
	trace = BulletTrace( self.previous_position, end, true, firing_player, true, true );
	
	if( trace[ "fraction" ] < 1 && isDefined( trace[ "position" ] ) )
	{
		sticky_grenade = Spawn( "script_model", trace[ "position" ] );
		sticky_grenade SetModel( "projectile_semtex_grenade" );
		
		if( isDefined( trace[ "entity" ] ) )
		{
			if( IsPlayer( trace[ "entity" ] ) )
			{
				firing_player thread show_stuck_fanfare();
				trace[ "entity" ] thread show_stuck_fanfare();
			}
			
			sticky_grenade LinkTo( trace[ "entity" ] );
		}
		
		sticky_grenade thread sticky_timer( firing_player );
		sticky_grenade thread sticky_fx( firing_player );
		sticky_grenade thread remove_sticky( firing_player );
	}
}

microdronelauncher_cleanup()
{
	self waittill_any( "death", "disconnect", "faux_spawn" );
	
	if ( IsDefined( self.HUDItem ) )
	{
		foreach ( hudItem in self.HUDItem)
			hudItem Destroy();
		self.HUDItem = undefined;
	}
}


show_stuck_fanfare()
{
	self endon( "death" );
	self endon( "disconnect" );
	self endon( "faux_spawn" );
	
	if( !IsDefined( self.HUDItem ) )
	{
		self.HUDItem = [];
	}
	
	if( IsDefined( self.HUDItem ) && !IsDefined( self.HUDItem[ "mdlStuckText" ] ) )
	{
		self.HUDItem[ "mdlStuckText" ] = NewClientHudElem( self );
		self.HUDItem[ "mdlStuckText" ].x = 0;
		self.HUDItem[ "mdlStuckText" ].y = -170;
		self.HUDItem[ "mdlStuckText" ].alignX = "center";
		self.HUDItem[ "mdlStuckText" ].alignY = "middle";
		self.HUDItem[ "mdlStuckText" ].horzAlign = "center";
		self.HUDItem[ "mdlStuckText" ].vertAlign = "middle";
		self.HUDItem[ "mdlStuckText" ].fontScale = 3.0;
		self.HUDItem[ "mdlStuckText" ].alpha = 0.0;
		self.HUDItem[ "mdlStuckText" ] SetText( "STUCK!" );
		self thread microdronelauncher_cleanup();
	}
	
	if( IsDefined( self.HUDItem[ "mdlStuckText" ] ) )
	{
		self.HUDItem[ "mdlStuckText" ].alpha = 1.0;
		wait( 2.0 );
		self.HUDItem[ "mdlStuckText" ].alpha = 0.0;
	}
}

sticky_timer( firing_player )
{
	firing_player endon( "spawned_player" );
	
	wait( CONST_explosion_delay_time );
	
	PlayFx( getfx( "mdl_sticky_explosion" ), self.origin );
	PhysicsExplosionSphere( self.origin, 256, 32, 1.0 );
	RadiusDamage( self.origin, 256, 130, 15, firing_player, "MOD_EXPLOSIVE", "iw5_microdronelauncher_mp" );
	
	self notify( "exploded" );
}

sticky_fx( firing_player )
{
	firing_player endon( "spawned_player" );
	self endon( "exploded" );
	
	self.fx_origin = spawn_tag_origin();
	self.fx_origin.origin = self.origin;
	self.fx_origin Show();
	
	wait( 0.1 );
	
	PlayFXOnTag( getfx( "mdl_sticky_blinking" ), self.fx_origin, "tag_origin" );
}

remove_sticky( firing_player )
{
	self thread remove_sticky_on_explosion( firing_player );
	self thread remove_sticky_on_respawn( firing_player );
}

remove_sticky_on_explosion( firing_player )
{
	firing_player endon( "spawned_player" );
	
	self waittill( "exploded" );
	
	if( isDefined( self ) )
	{
		self cleanup_sticky();
	}
}

remove_sticky_on_respawn( firing_player )
{
	self endon( "exploded" );
	
	firing_player waittill( "spawned_player" );
	
	if( isDefined( self ) )
	{
		self cleanup_sticky();
	}
}

cleanup_sticky()
{
	StopFXOnTag( getfx( "mdl_sticky_blinking" ), self.fx_origin, "tag_origin" );
	self Delete();
}

microdrone_think(firing_player)
{
	self endon( "death" );
	
	firing_player endon( "death" );
	firing_player endon( "disconnect" );
	firing_player endon( "faux_spawn" );

	start_origin = self.origin;
	
	// seems missiles don't move on the first frame, so need to wait 2 frames to get the velocity
	self get_differentiated_velocity();
	wait .05;
	self get_differentiated_velocity();
	wait .05;
	elapsed_time = .1;
	
	init_vel = self get_differentiated_velocity();
	while(true)
	{		
		// must call every frame to get accurate results
		cur_vel = self get_differentiated_velocity();
		
		set_target = false;
		if(elapsed_time >= CONST_begin_homing_time)
		{
			best_target = microdrone_get_best_target(start_origin, VectorNormalize(init_vel), cur_vel, firing_player);
			if(IsDefined(best_target))
			{
				self Missile_SetTargetEnt(best_target, microdrone_get_target_offset(best_target));
				set_target = true;
				
				// so that if we lose the lock, we will start dropping from our current velocity
				init_vel = cur_vel;
				
				/#
				//Line(self.origin, best_target.origin + microdrone_get_target_offset(best_target), (0, 1, 0));
				#/
			}
		}
		else
		{
			/#
			//Line(self.origin, self.origin + (0, 0, 100), (0, 1, 0));
			#/
		}
		
		if(!set_target)
		{
			// simulate drop.  We could do this in projectile steering code (and multiplayer would want to do that), but this is simpler.
			desired_dir = VectorNormalize(init_vel + (0, 0, -.5 * CONST_drop_accel * Squared(elapsed_time)));
			self Missile_SetTargetPos(self.origin + desired_dir * 10000);		
		}
		
		wait .05;
		elapsed_time += .05;		
	}	
}

microdrone_get_best_target(start_origin, dir, cur_vel, firing_player)
{
	min_dot_from_grenade = Cos(CONST_max_homing_angle_from_grenade);
	
	best_target = undefined;
	best_target_dot = Cos(CONST_max_homing_angle_from_grenade);
	foreach( target in level.players )
	{
		if( target == firing_player )
			continue;
		
		if( target.team == firing_player.team )
			continue;
		
		target_pos = microdrone_get_target_pos(target);
		dot = VectorDot(VectorNormalize(cur_vel), VectorNormalize(target_pos - self.origin));			
		if(dot > best_target_dot)
		{
	
			if(BulletTracePassed(self.origin, target_pos, false, target))
			{
				best_target = target;
				best_target_dot = dot;
			}
			else
			{
				/#
				//result = BulletTrace(self.origin, target_pos, false, target);
				//Assert(!IsDefined(result["entity"]) || result["entity"] != target);
				//line(self.origin, result["position"], (1, 0, 0));
				#/
			}			
		}
	}
	
	return best_target;
}

is_enemy_target(target, firing_player)
{
	team = undefined;
	
	if(IsAi(target))
		team = target.team;
	else if(IsDefined(target.script_team))
		team = target.script_team;
	
	return IsEnemyTeam(team, firing_player.team);
}

microdrone_get_target_pos(target)
{
	return target GetPointInBounds(0, 0, 0);
}

microdrone_get_target_offset(target)
{
	return microdrone_get_target_pos(target) - target.origin;
}

get_differentiated_velocity()
{
	self differentiate_motion();
	return self.differentiated_velocity;	
}

differentiate_motion()
{
	time = GetTime() * .001;
	
	if(!IsDefined(self.differentiated_last_update))
	{
		self.differentiated_last_update = time;
		self.differentiated_last_origin = self.origin;
		self.differentiated_last_velocity = (0, 0, 0);
		self.differentiated_last_acceleration = (0, 0, 0);
		self.differentiated_jerk = (0, 0, 0);
		self.differentiated_acceleration = (0, 0, 0);
		self.differentiated_velocity = (0, 0, 0);
		self.differentiated_speed = 0;		
	}
	else if(self.differentiated_last_update != time)
	{	
		dt = time - self.differentiated_last_update;
		self.differentiated_last_update = time;
		self.differentiated_jerk = (self.differentiated_acceleration - self.differentiated_last_acceleration) / dt;
		self.differentiated_last_acceleration = self.differentiated_acceleration;		
		self.differentiated_acceleration = (self.differentiated_velocity - self.differentiated_last_velocity) / dt;
		self.differentiated_last_velocity = self.differentiated_velocity;
		self.differentiated_velocity = (self.origin - self.differentiated_last_origin) / dt;
		self.differentiated_last_origin = self.origin;
		self.differentiated_speed = Length(self.differentiated_velocity);
	}
}