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

// Streak Data
CONST_UAV_BASE_TIME = 30;
CONST_UAV_MOD_INCREASED_TIME = 15;
CONST_THREAT_DRAW_TIME = 10;

init()
{
	assert( CONST_UAV_BASE_TIME > 7 );

	level._effect[ "uav_explode" ] = LoadFX( "vfx/explosion/vehicle_uav_explosion" );
	level._effect[ "uav_exit" ]	   = LoadFX( "vfx/trail/smoketrail_uav" );
	level._effect[ "uav_trail" ]   = LoadFX( "vfx/trail/smoketrail_uav" );
	
	level.killStreakFuncs["uav"] = ::tryUseUAV;
	level.killStreakFuncs["uav_support"] = ::tryUseUAV;
	level.killStreakFuncs["counter_uav"] = ::tryUseUAV;
	
	minimapOrigins = getEntArray( "minimap_corner", "targetname" );
	if ( miniMapOrigins.size )
		uavOrigin = maps\mp\gametypes\_spawnlogic::findBoxCenter( miniMapOrigins[0].origin, miniMapOrigins[1].origin );
	else
		uavOrigin = (0,0,0);
	
	level.UAVRig = spawn( "script_model", uavOrigin );
	level.UAVRig setModel( "c130_zoomrig" );
	level.UAVRig.angles = (0,115,0);
	level.UAVRig hide();

	level.UAVRig.targetname = "uavrig_script_model";	// used for debug printing

	level.UAVRig thread rotateUAVRig();
	
	if ( level.teamBased )
	{
		level.radarMode["allies"] = "normal_radar";
		level.radarMode["axis"] = "normal_radar";
		level.activeUAVs["allies"] = 0;
		level.activeUAVs["axis"] = 0;
		level.activeCounterUAVs["allies"] = 0;
		level.activeCounterUAVs["axis"] = 0;
		level.uavModels["allies"] = [];
		level.uavModels["axis"] = [];
	}
	else
	{	
		level.radarMode = [];
		level.activeUAVs = [];
		level.activeCounterUAVs = [];
		level.uavModels = [];
	}
	
	level thread onPlayerConnect();
	level thread UAVTracker();
	
/#
	SetDvarIfUninitialized( "scr_uav_timeout", "0" );
	SetDvarIfUninitialized( "scr_uav_norandom", "0" );
	SetDvarIfUninitialized( "scr_uav_alwaysshow", "0" );
#/
}


onPlayerConnect()
{
	for(;;)
	{
		level waittill( "connected", player );
		
		if ( !level.teamBased )
		{
			level.activeUAVs[ player.guid ] = 0;
			level.activeUAVs[ player.guid + "_radarStrength" ] = 0;
			level.activeCounterUAVs[ player.guid ] = 0;
			
			level.radarMode[ player.guid ] = "normal_radar";
		}
		
		player thread onPlayerSpawned();
	}
}

onPlayerSpawned()
{
	self endon("disconnect");
	
	while ( true )
	{
		self waittill( "spawned_player" );
		
		level notify ( "uav_update" );
	}
}

rotateUAVRig()
{
	for (;;)
	{
		self rotateyaw( -360, 60 );
		wait ( 60 );
	}
}

/#
debugLocation()
{
	self endon( "death" );

	while( true )
	{
		Print3d( self.origin, "UAV", ( 1, 0, 0 ) );
		Print3d( self.origin, "UAV origin: " + self.origin[0] + ", " + self.origin[1] + ", " + self.origin[2], ( 1, 0, 0 ) );
		
		Print3d( level.UAVRig.origin, "UAV Rig", ( 1, 0, 0 ) );
		Print3d( level.UAVRig.origin, "UAV Rig origin: " + level.UAVRig.origin[0] + ", " + level.UAVRig.origin[1] + ", " + level.UAVRig.origin[2], ( 1, 0, 0 ) );

		Print3d( level.UAVRig.origin - ( 0, 0, 50), "Distance: " + Distance( level.UAVRig.origin, self.origin ), ( 1, 0, 0 ) );

		Line( level.UAVRig.origin, self.origin, ( 0, 0, 1 ) );

		anglesForward = AnglesToForward( level.players[0].angles );
		scalar = (anglesForward[0] * 200, anglesForward[1] * 200, anglesForward[2] );
		Print3d( level.players[0].origin + scalar, "Distance: " + Distance( level.players[0].origin, self.origin ), ( 1, 0, 0 ) );
		wait( 0.05 );
	}
}

debugTrace()
{
	self endon( "death" );

	while( true )
	{
		result = BulletTrace( level.players[0].origin, self.origin, false, undefined );
		if( IsDefined( result ) && IsDefined( result[ "surfacetype" ] ) )
		{
			PrintLn( "UAV debugTrace: " + result[ "surfacetype" ] );
		}
		wait( 1.0 );
	}
}
#/
playTrailFX()
{
	self endon( "death" );
	level endon( "game_ended" );
	PlayFXOnTag( level._effect["uav_trail"],self,"tag_origin");
	
}
launchUAV( owner, team, uavType, modules )
{	
	UAVModel = spawn( "script_model", level.UAVRig getTagOrigin( "tag_origin" ) );
	UAVModel.modules = modules;
	
/#
	if( GetDvarInt( "scr_debuguav", 0 ) )
	{
		UAVModel thread debugLocation();
		UAVModel thread debugTrace();
	}
#/
	UAVModel.value = 1;
	
	if ( array_contains( UAVModel.modules, "uav_advanced_updates" ) ) // streak customization
		UAVModel.value = 2;
	if ( array_contains( UAVModel.modules, "uav_enemy_direction" ) ) // streak customization
		UAVModel.value = 3;
	
	if ( array_contains( UAVModel.modules, "uav_scrambler" ) ) // streak customization
		isScrambler = true;
	else
		isScrambler = false;
	
	UAVModel setModel( "uav_drone_static" );
	UAVModel thread playTrailFX();
	
	UAVModel thread maps\mp\gametypes\_damage::setEntityDamageCallback( 1000, undefined, ::uavOnDeath, undefined, true );
	
	UAVModel.team = team;
	UAVModel.owner = owner;
	UAVModel.timeToAdd = 0;
	UAVModel.orbit = array_contains( UAVModel.modules, "uav_orbit" ); // streak customization
	UAVModel.paintOutline = array_contains( UAVModel.modules, "uav_paint_outline" ); // streak customization
	UAVModel.assistPoints = array_contains( UAVModel.modules, "uav_assist_points" ); // streak customization
	UAVModel make_entity_sentient_mp( team );

	UAVModel thread handleIncomingStinger();
	
	UAVModel.StreakCustomization = owner.StreakCustomization;
	
	UAVModel addUAVModel();

	thread flyIn( UAVModel );
	UAVModel thread updateUAVModelVisibility();
	UAVModel thread maps\mp\killstreaks\_killstreaks::updateAerialKillStreakMarker();
	
	UAVModel addActiveUAV();
	
	if ( isScrambler )
		UAVModel addActiveCounterUAV();
	
	//this adds 5 seconds of time to all active UAV's of the same type.
	if ( isDefined( level.activeUAVs[team] ) )
	{
		foreach ( uav in level.UAVModels[team] )
		{
			if (uav == UAVModel)
				continue;
			
			if ( isScrambler )
				uav.timeToAdd += 5;
			else if ( !isScrambler )
				uav.timeToAdd += 5;
		}
	}
	
	waitframe();
	level notify ( "uav_update" );
	
	duration = CONST_UAV_BASE_TIME;
	if ( array_contains( UAVModel.modules, "uav_increased_time" ) ) // streak customization
		duration += CONST_UAV_MOD_INCREASED_TIME;

/#
	if ( GetDvarInt( "scr_uav_timeout", 0 ) != 0 )
		duration = GetDvarInt( "scr_uav_timeout" );
#/
	
	UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", duration );

	if ( UAVModel.damageTaken < UAVModel.maxHealth )
	{
		UAVModel unlink();
	
		destPoint = UAVModel.origin + ( AnglesToForward( UAVModel.angles ) * 20000 );
		UAVModel moveTo( destPoint, 60 );
		PlayFXOnTag( getfx( "uav_exit" ) , UAVModel, "tag_origin" );

		UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 3 );

		if ( UAVModel.damageTaken < UAVModel.maxHealth )
		{
			UAVModel notify( "leaving" );
			UAVModel.isLeaving = true;
			UAVModel moveTo( destPoint, 4, 4, 0.0 );
		}
	
		UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 4 + UAVModel.timeToAdd );
	}

	if ( isScrambler )
		UAVModel removeActiveCounterUAV();
	
	UAVModel removeActiveUAV();

	UAVModel delete();
	UAVModel removeUAVModel();
		
	level notify ( "uav_update" );
}

flyIn( UAVModel )
{
	UAVModel Hide();
	
	zOffset = RandomIntRange( 3000, 5000 );

	// we need to make sure the uav doesn't go higher than 8100 units because bullets die at 8192
	if ( IsDefined( level.spawnpoints ) )
		spawns = level.spawnPoints;
	else
		spawns = level.startSpawnPoints;

	lowestSpawn = spawns[ 0 ];
	foreach ( Spawn in spawns )
	{
		if ( Spawn.origin[ 2 ] < lowestSpawn.origin[ 2 ] )
			lowestSpawn = Spawn;
	}
	lowestZ = lowestSpawn.origin [ 2 ];
	UAVRigZ = level.UAVRig.origin[ 2 ];
	if ( lowestZ < 0 )
	{
		UAVRigZ += lowestZ * -1;
		lowestZ = 0;
	}
	diffZ = UAVRigZ - lowestZ;
	AssertEx( diffZ < 8100.0, "The lowest spawn and the UAV node are more than 8100 z units apart, please notify MP Design." );
	if ( diffZ + zOffset > 8100.0 )
	{
		zOffset -= ( ( diffZ + zOffset ) - 8100.0 );
	}

	angle		 = RandomInt( 360 );
	radiusOffset = RandomInt( 2000 ) + 5000;
	
/#
	if ( GetDvarInt( "scr_uav_norandom", 0 ) != 0 )
	{
		angle = 0;
		radiusOffset = 5000;
	}
#/

	xOffset = Cos( angle ) * radiusOffset;
	yOffset = Sin( angle ) * radiusOffset;

	angleVector = VectorNormalize( ( xOffset, yOffset, zOffset ) );
	angleVector = ( angleVector * RandomIntRange( 6000, 7000 ) );
	
	UAVModel LinkTo( level.UAVRig, "tag_origin", angleVector, ( 0, angle - 90, 135 ) );
	
	waitframe();
	
	destination = UAVModel.origin;
	UAVModel Unlink();

	UAVModel.origin = destination + ( AnglesToForward( UAVModel.angles ) * -20000 );
	UAVModel MoveTo( destination, 4, 0, 2 );
	wait 4;
	
	if ( IsDefined( UAVModel ) )
		UAVModel LinkTo( level.UAVRig, "tag_origin" );
}

waittill_notify_or_timeout_hostmigration_pause( msg, timer )
{
	self endon( msg );
	
	maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timer );
}


updateUAVModelVisibility()
{
	self endon ( "death" );
	
/#
	if ( GetDvarInt( "scr_uav_alwaysshow", 0 ) != 0 )
	{
		waitframe();
		self Show();
		return;
	}
#/

	for ( ;; )
	{
		level waittill_either ( "joined_team", "uav_update" );
		
		self hide();
		foreach ( player in level.players )
		{
			if ( level.teamBased )
			{
				if ( (player.team != self.team) && !self.orbit )
					self showToPlayer( player );
			}
			else
			{
				if ( (isDefined( self.owner ) && player == self.owner) || self.orbit )
					continue;
					
				self showToPlayer( player );
			}
		}
	}	
}


uavOnDeath( attacker, weapon, meansOfDeath, damage )
{
	self Hide();
	self notify( "death" );
	
	forward = ( AnglesToRight( self.angles ) * 200 );
	playFx ( getfx( "uav_explode" ), self.origin, forward );
	playSoundAtPos( self.origin, "uav_air_death" );
	
	self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "uav_destroyed", undefined, "callout_destroyed_uav", true );
}


tryUseUAV( lifeId, modules )
{
	if(IsDefined(self.pers["killstreaks"][self.killstreakIndexWeapon].streakName))
	{
		StreakName = self.pers["killstreaks"][self.killstreakIndexWeapon].streakName;
	}
	else
	{
		StreakName = "uav_support";
	}
	
	if ( isdefined ( level.isHorde ) && level.isHorde && self.killstreakIndexWeapon == 1 )
		self notify ( "used_horde_uav" );
	
	return useUAV( streakname, modules );
}

useUAV( uavType, modules )
{
	self maps\mp\_matchdata::logKillstreakEvent( uavType, self.origin );

	team = self.pers["team"];		
	
	level thread launchUAV( self, team, uavType, modules );
	
	return true;
}

UAVTracker()
{
	level endon ( "game_ended" );
	
	for ( ;; )
	{
		level waittill ( "uav_update" );
		
		if ( level.teamBased )
		{
			updateTeamUAVStatus( "allies" );
			updateTeamUAVStatus( "axis" );		
		}
		else
		{
			updatePlayersUAVStatus();
		}
	}
}

_getRadarStrength( team, fastSweep, enemyDirection )
{
	activeUAVs = 0;
	activeCounterUAVs = 0;

	foreach ( uav in level.UAVModels[team] )
	{
		activeUAVs += uav.value;
	}	

	foreach ( uav in level.UAVModels[level.otherTeam[team] ] )
	{
		if ( uav.uavType != "counter" )
			continue;
	
		activeCounterUAVs += uav.value;
	}
	
	if( activeCounterUAVs > 0 )
		radarStrength = -3;
	else
		radarStrength = activeUAVs;
		
	
	strengthMin = GetUAVStrengthMin();
	strengthMax = GetUAVStrengthMax();	
	
	//clamp between min/max
	if( radarStrength <= strengthMin )
	{
		radarStrength = strengthMin;
	}
	else if( radarStrength >= strengthMax ) 
	{
		radarStrength = strengthMax;	
	}
	
	return radarStrength;
}

_getTeamPaintOutline( team )
{
	paintOutlineUavs = 0;
	paintOutline = false;
	
	foreach ( uav in level.UAVModels[team] )
	{
		if ( uav.paintOutline ) // streak customization
		   paintOutlineUavs += uav.value;
	}
	
	if( paintOutlineUavs > 0 )
		paintOutline = true;
	else
		paintOutline = false;
	
	return paintOutline;
}

_getPaintOutline( player )
{
	paintOutlineUavs = 0;
	paintOutline = false;
	
	foreach ( uav in level.UAVModels )
	{	
		if ( IsDefined( uav.owner ) && uav.owner == player && uav.paintOutline ) // streak customization
		   paintOutlineUavs += uav.value;
	}
	
	if( paintOutlineUavs > 0 )
		paintOutline = true;
	else
		paintOutline = false;
	
	return paintOutline;
}


updateTeamUAVStatus( team )
{
	radarStrength = _getRadarStrength( team );
	hasPaintOutline = _getTeamPaintOutline( team );
		
	updateTeamPaintOutline( team, hasPaintOutline );
	
	setTeamRadarStrength( team, radarStrength );

	if ( radarStrength >= GetUAVStrengthLevelNeutral() ) //note: exception for counter uavs
	{
		updateTeamRadarBlocked( team, false );
		unblockTeamRadar( team );
	}
	else
	{
		updateTeamRadarBlocked( team, true );
		blockTeamRadar( team );
	}
	
	if ( radarStrength <= GetUAVStrengthLevelNeutral() )
	{
		setTeamRadarWrapper( team, 0 );
		updateTeamUAVType( team );
		return;
	}
	
	if ( radarStrength >= GetUAVStrengthLevelShowEnemyFastSweep() )
		level.radarMode[team] = "fast_radar";
	else
		level.radarMode[team] = "normal_radar";
	
	updateTeamUAVType( team );
	setTeamRadarWrapper( team, 1 );
}

//for FFA
updatePlayersUAVStatus()
{
	strengthMin = GetUAVStrengthMin();
	strengthMax = GetUAVStrengthMax();
	strengthDirectional = GetUAVStrengthLevelShowEnemyDirectional();
	
	foreach ( player in level.players )
	{
		radarStrength = level.activeUAVs[ player.guid + "_radarStrength" ];
		hasPaintOutline = _getPaintOutline( player );
	
		updatePaintOutline( player, hasPaintOutline );

		// if there are any counters up that aren't this player's then they are blocked
		foreach( enemyPlayer in level.players )
		{
			if( enemyPlayer == player )
				continue;

			activeCounterUAVs = level.activeCounterUAVs[ enemyPlayer.guid ];
			if( activeCounterUAVs > 0 )
			{
				radarStrength = -3;
				break;
			}
		}

		//clamp between min/max
		if( radarStrength <= strengthMin )
		{
			radarStrength = strengthMin;
		}
		else if( radarStrength >= strengthMax ) 
		{
			radarStrength = strengthMax;	
		}
			
		player.radarstrength = radarStrength;
				
		if ( radarStrength >= GetUAVStrengthLevelNeutral() )
		{
			updatePlayerRadarBlocked( player, false );
			player.isRadarBlocked = false;
		}
		else
		{
			updatePlayerRadarBlocked( player, true );
			player.isRadarBlocked = true;
		}
				
				
		if ( radarStrength <= GetUAVStrengthLevelNeutral()  )
		{
			player.hasRadar = false;
			player.radarShowEnemyDirection = false;
			continue;
		}
		
		if ( radarStrength >= GetUAVStrengthLevelShowEnemyFastSweep())
			player.radarMode = "fast_radar";
		else
			player.radarMode = "normal_radar";
			
		//set directional status
		player.radarShowEnemyDirection = radarStrength >= strengthDirectional;
		
		player.hasRadar = true;
	}
}

updateTeamUAVType( team, ShowEnemyDirection )
{
	shouldBeDirectional = _getRadarStrength( team ) >= GetUAVStrengthLevelShowEnemyDirectional();
	
	foreach ( player in level.players )
	{
		if ( player.team == "spectator" )
			continue;
	
		enemyTeam = maps\mp\gametypes\_gameobjects::getEnemyTeam( player.team );	
		player.radarMode = level.radarMode[player.team];
		player.enemyRadarMode = level.radarMode[enemyTeam];
				
		//use direction based on uav signal strength
		if( player.team == team )
		{
			player.radarShowEnemyDirection = shouldBeDirectional;			
		}		
	}
}

updateTeamPaintOutline( team, on )
{
	level endon( "game_ended" );
	
	foreach ( player in level.players )
	{
		if ( IsDefined( player ) && player.team == team )
			player playerSetupUAVPaintOutline( on );
	}
}

updatePaintOutline( player, on )
{
	level endon( "game_ended" );
	
	player playerSetupUAVPaintOutline( on );
}

playerSetupUAVPaintOutline( on ) // self == player
{
	if ( on )
	{
		if ( !IsDefined( self.uav_paint_effect ) )
		{
			self.uav_paint_effect = maps\mp\_threatdetection::detection_highlight_hud_effect_on( self, -1 );
		}
		self SetPerk( "specialty_uav_paint", true, false );
	}
	else
	{
		if ( IsDefined( self.uav_paint_effect ) )
		{
			maps\mp\_threatdetection::detection_highlight_hud_effect_off( self.uav_paint_effect );
			self.uav_paint_effect = undefined;
		}
		self UnSetPerk( "specialty_uav_paint", true );
	}
}

updateTeamRadarBlocked( team, isBlocked )
{
	foreach ( player in level.players )
	{
		if ( IsDefined( player ) && player.team == team )
			updatePlayerRadarBlocked( player, isBlocked );
	}
}

updatePlayerRadarBlocked( player, isBlocked )
{
	if ( !isBlocked || !player _hasPerk( "specialty_class_hardwired" ) )
		player SetClientOmnvar( "ui_uav_scrambler_on", isBlocked );
}

setTeamRadarWrapper( team, value )
{
	setTeamRadar( team, value );
	level notify( "radar_status_change", team );
}

handleIncomingStinger()
{
	level endon ( "game_ended" );
	self endon ( "death" );
	
	for ( ;; )
	{
		level waittill ( "stinger_fired", player, missiles );
		
		if ( !maps\mp\_stingerm7::anyStingerMissileLockedOn( missiles, self ) )
			continue;
		
		foreach ( missile in missiles )
		{
			if ( !IsDefined( missile ) )
				continue;
			
			missile thread stingerProximityDetonate( missile.lockedStingerTarget, player );
		}
	}
}


stingerProximityDetonate( targetEnt, player )
{
	self endon ( "death" );

	minDist = distance( self.origin, targetEnt GetPointInBounds( 0, 0, 0 ) );
	lastCenter = targetEnt GetPointInBounds( 0, 0, 0 );

	for ( ;; )
	{
		// UAV already destroyed
		if ( !isDefined( targetEnt ) )
			center = lastCenter;
		else
			center = targetEnt GetPointInBounds( 0, 0, 0 );
			
		lastCenter = center;		
		
		curDist = distance( self.origin, center );
		
		if ( curDist < minDist )
			minDist = curDist;
		
		if ( curDist > minDist )
		{
			if ( curDist > 1536 )
				return;
				
			RadiusDamage( self.origin, 1536, 600, 600, player, "MOD_EXPLOSIVE", "stinger_mp" );
			playFx( level.stingerFXid, self.origin );

			//self playSound( "remotemissile_explode" );
			self hide();
			
			self notify("deleted");
			wait ( 0.05 );
			self delete();
			player notify( "killstreak_destroyed" );
		}
		
		wait ( 0.05 );
	}	
}


addUAVModel() // self == uavmodel
{
	if ( level.teamBased )
		level.UAVModels[ self.team ][level.UAVModels[ self.team ].size] = self;
	else
		level.UAVModels[ self.owner.guid + "_" + getTime() ] = self;	
}	


removeUAVModel() // self == uavmodel
{
	UAVModels = [];

	if ( level.teamBased )
	{
		team = self.team;
		
		foreach ( uavModel in level.UAVModels[team] )
		{
			if ( !isDefined( uavModel ) )
				continue;
				
			UAVModels[UAVModels.size] = uavModel;
		}

		level.UAVModels[team] = UAVModels;
	}
	else
	{
		foreach ( uavModel in level.UAVModels )
		{
			if ( !isDefined( uavModel ) )
				continue;
				
			UAVModels[UAVModels.size] = uavModel;
		}

		level.UAVModels = UAVModels;
	}	
}


addActiveUAV() // self == uav model
{
	self.uavType = "standard";
	
	if ( level.teamBased )
		level.activeUAVs[self.team]++;
	else
	{
		level.activeUAVs[ self.owner.guid ]++;
		level.activeUAVs[ self.owner.guid + "_radarStrength" ] += self.value;
	}
}


addActiveCounterUAV()
{
	self.uavType = "counter";
	
	if ( level.teamBased )
		level.activeCounterUAVs[self.team]++;	
	else
		level.activeCounterUAVs[self.owner.guid]++;	
}


removeActiveUAV() // self == uav model
{
	if ( level.teamBased )
	{
		level.activeUAVs[self.team]--;
		
		if ( !level.activeUAVs[self.team] )
		{
			//printOnTeam( &"MP_WAR_RADAR_EXPIRED", self.team );
			//printOnTeam( &"MP_WAR_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] );
		}
	}
	else if ( isDefined( self.owner ) )
	{
		level.activeUAVs[ self.owner.guid ]--;
		level.activeUAVs[ self.owner.guid + "_radarStrength" ] -= self.value;
	}
}


removeActiveCounterUAV()
{
	if ( level.teamBased )
	{
		level.activeCounterUAVs[self.team]--;

		if ( !level.activeCounterUAVs[self.team] )
		{
			//printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED", self.team );
			//printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] );
		}
	}
	else if ( isDefined( self.owner ) )
	{
		level.activeCounterUAVs[self.owner.guid]--;
	}
}
