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

CONST_cloak_duration 				= 10;
CONST_cloak_cooldown_min_duration 	= 5;

droneGetSpawnPoint( eyeZOffset ) // self == player
{
	if ( !IsDefined( eyeZOffset ) )
		eyeZOffset 		= 50;
	
	forwardOffset 		= 75;
	trace_radius  		= 23;				  	// Should match vehicle GDT settings
	trace_height  		= trace_radius * 2; 	// Needs to be at least twice the radius (for some reason)
	
	eye =  ( self.origin + ( 0, 0, eyeZOffset ) );
	angles =  self getplayerangles();
	forward = anglestoforward( flat_angle( angles ) );
	
	end = eye + ( forward * forwardOffset );
	
	spawnOrigin = end;
	spawnAngles = self.angles;
	
	placementOK = true;
	
	// get the drone out of water
	offset = getSpawnInWaterOffset( spawnOrigin + ( 0, 0, -1 * 30 ) );
	if ( IsDefined( offset ) && offset > 0 )
	{
		spawnOrigin += ( 0, 0, offset );
		eye += ( 0, 0, offset );
	}
	else if ( !IsDefined( offset ) )
	{
		placementOK = false;
	}
	
	// Ensure the player can see the placement position
	if ( placementOK && !SightTracePassed( eye, end, true, self ) )
	{
		placementOK = false;
	}
	
	// Small test for vehicle clip & glass
	if ( placementOK )
	{
		trace = BulletTrace( eye, end, true, self, true, false, true, true, true );
		if ( trace["fraction"] < 1 )
			placementOK = false;
	}
	
	// Ensure the point is not in solid
	if ( placementOK )
	{
		
		start = eye + ( 0, 0, trace_height * -0.5 );
		end = end + ( 0, 0, trace_height * -0.5 );
		
		trace = self AIPhysicsTrace( start, end, trace_radius, trace_height, false, true );
			if ( trace["fraction"] < 1 )
				placementOK = false;
	}
	
	results = SpawnStruct();
	results.placementOK = placementOK;
	results.origin = spawnOrigin;
	results.angles = spawnAngles;
	
	return results;
}

getSpawnInWaterOffset( spawnOrigin )
{
	triggers = getentarray( "trigger_underwater", "targetname" );
	if ( triggers.size == 0 )
		return 0;
	
	MAX_UNITS_UP = 200;
	unitsUp = 0;
	testOrigin = Spawn( "script_origin", spawnOrigin );
	touchingWater = false;
	while ( unitsUp < MAX_UNITS_UP )
	{
		if ( touchingWaterTriggers( testOrigin, triggers ) )
		{
			unitsUp += 10;
			testOrigin.origin += ( 0, 0, 10 );
		}
		else
		{
			break;
		}
	}
	
	testOrigin Delete();
	
	if ( unitsUp >= MAX_UNITS_UP )
		return undefined;
	else
		return unitsUp;
}

touchingWaterTriggers( ent, triggers )
{
	for ( i = 0; i < triggers.size; i++ )
	{
		trigger = triggers[i];
		if ( ent IsTouching( trigger ) )
			return true;
	}
	
	return false;
}

droneAddToGlobalList( entNum )
{
	level.ugvs[entNum] = self;	
}

droneRemoveFromGlobalList( entNum )
{
	level.ugvs[entNum] = undefined;
}

// Cloaking
droneInitCloakOmnvars() // self == player
{
//	self SetClientOmnvar( "ui_drone_cloak", 0 );
//	self SetClientOmnvar( "ui_drone_cloaktext", 0 );
//	self SetClientOmnvar( "ui_drone_cloaktime", 0 );
//	self SetClientOmnvar( "ui_drone_cloakdur", 0 );
}

droneSetupCloaking( vehicle, startAsCloaked ) // self == player
{
	vehicle endon( "death" );
	
	droneInitCloakOmnvars();
	vehicle.cloakState = 0;
	vehicle.CloakCooldown = 0;
	self droneCloakingTransition( vehicle, true, true ); // Wait to cloak and then show the model
	maps\mp\killstreaks\_killstreaks::playerWaittillRideKillstreakComplete();
	if ( IsDefined( startAsCloaked ) && startAsCloaked )
	{
		self thread droneMonitorDamageWhileCloaking( vehicle );	
		self SetClientOmnvar( "ui_drone_cloak", 2 );
		// Update cloak end time (for HUD meter)
		cloakMS = CONST_cloak_duration * 1000;
		cloakEndTime = GetTime() + cloakMS;
		self SetClientOmnvar( "ui_drone_cloak_time", cloakEndTime );
		
		vehicle.CloakCooldown = CONST_cloak_cooldown_min_duration;
		thread CloakCooldown( vehicle );
		self thread droneCloakWaitForExit( vehicle );
	}
	else
	{
		vehicle PlaySound( "recon_drn_cloak_deactivate" );
		self droneCloakingTransition( vehicle, false ); // Decloak immediately
	}
}

droneIsCloaked( vehicle ) // self == player
{
	return ( vehicle.hasCloak && vehicle.cloakState >= 0 );
}

droneCloakReady( vehicle, startAsCloaked ) // self == player
{
	vehicle endon( "death" );
		
	if ( IsDefined( startAsCloaked ) && startAsCloaked )
	{
		thread droneCloakCooldown( vehicle );
		self waittill( "CloakCharged" );
	}

	while( true )
	{
		self SetClientOmnvar( "ui_drone_cloak", 1 ); // Cloak Ready
	
		thread droneCloakActivated( vehicle );
		thread droneCloakCooldown( vehicle );
		
		if ( vehicle.CloakCooldown != 0 )
		{
			self SetClientOmnvar( "ui_drone_cloak", 3 );
			wait vehicle.CloakCooldown;
		}
	
		if ( vehicle.hasCloak )
			self SetClientOmnvar( "ui_drone_cloak", 1 );
		
		vehicle waittill( "Cloak" );
		
		vehicle notify( "ActivateCloak" );
		
		vehicle PlaySound( "recon_drn_cloak_activate" );
		
		//the cloak has been used and has fully recharged.
		self waittill( "CloakCharged" );
	}
}

droneCloakActivated( vehicle )
{
	vehicle endon ( "death" );
	
	vehicle waittill( "ActivateCloak" );
	
	self thread droneCloakingTransition( vehicle, true );
	self thread droneMonitorDamageWhileCloaking( vehicle );
	
	// Update cloak end time (for HUD meter)
	cloakMS = CONST_cloak_duration * 1000;
	cloakEndTime = GetTime() + cloakMS;
	self SetClientOmnvar( "ui_drone_cloak_time", cloakEndTime );

	// Update the hud text (decloak)	
	self SetClientOmnvar( "ui_drone_cloak", 2 );

	vehicle.CloakCooldown = CONST_cloak_cooldown_min_duration;
	thread CloakCooldown( vehicle );
	self thread droneCloakWaitForExit( vehicle );
}

droneCloakCooldown( vehicle )
{
	vehicle endon( "death" );
	
	self waittill( "UnCloak" );
	
	vehicle PlaySound( "recon_drn_cloak_deactivate" );
	self thread droneCloakingTransition( vehicle, false );

	// Update the hud text (cloak recharging)
	self SetClientOmnvar( "ui_drone_cloak", 3 );

	self thread droneCloakDeactivatedDialog( vehicle );
}

CloakCooldown( vehicle )
{
	vehicle endon( "death" );

	self waittill( "UnCloak" );
	
	while( vehicle.CloakCooldown > 0 )
	{
		vehicle.CloakCooldown -= 0.5;
		wait 0.5;
	}
	
	vehicle.CloakCooldown = 0;	
	self notify( "CloakCharged" );
}

droneCloakWaitForExit( vehicle )
{
	vehicle endon( "death" );

	start = GetTime();
	self waittill_any_timeout_no_endon_death( CONST_cloak_duration, "ForceUncloak", "Cloak" );
	end = GetTime();
	cooldownDuration = max( ( end - start ), CONST_cloak_cooldown_min_duration * 1000 );
	vehicle.CloakCooldown = cooldownDuration / 1000;
	
	cooldownEnd = GetTime() + cooldownDuration;
	self SetClientOmnvar( "ui_drone_cloak_cooldown", cooldownEnd );

	self notify( "UnCloak" );
}

droneCloakingTransition( vehicle, enable, init )
{
	vehicle notify( "cloaking_transition" );
	vehicle endon( "cloaking_transition" );
	vehicle endon( "death" );
	
	if ( enable )
	{
		if ( vehicle.cloakState == -2 )
			return;
		
		vehicle.cloakState = -1;
		vehicle CloakingEnable();
		if ( IsDefined( vehicle.mgTurret ) )
			vehicle.mgTurret CloakingEnable();
		vehicle Vehicle_SetMinimapVisible( false );
		if ( !isdefined( init ) || !init )
			wait 2.2;	// should match the code values for the transition + a small time
		else
			wait 1.5;	// during init, we want to give enough time for the cloaking to start then hide, before we unhide
		vehicle Show();
		if ( IsDefined( vehicle.mgTurret ) )
			vehicle.mgTurret Show();
		vehicle.cloakState = -2;
	}
	else
	{
		if ( vehicle.cloakState == 2 )
			return;
		
		vehicle.cloakState = 1;
		vehicle CloakingDisable();
		vehicle Vehicle_SetMinimapVisible( true );
		if ( IsDefined( vehicle.mgTurret ) )
			vehicle.mgTurret CloakingDisable();
		wait 2.2;	// should match the code values for the transition
		vehicle.cloakState = 2;
	}
}

droneCloakDeactivatedDialog( vehicle )
{
	vehicle endon( "death" );
	self endon( "CloakCharged" );
	
	while ( true )
	{
		self waittill( "Cloak" );
		
		self playlocalsound( "recon_drn_cloak_notready" );
		
		wait 1;
	}
}

droneMonitorDamageWhileCloaking( vehicle )
{
	vehicle endon( "death" );
	self endon( "UnCloak" );
	
	wait 1;
	
	vehicle waittill( "damage" );
	self notify( "ForceUncloak" );
}

updateShootingLocation( vehicle, effect, ignoreTurret )
{
	vehicle endon( "death" );
	self endon( "disconnect" );
	vehicle endon( "stopShootLocationUpdate" );
	
	vehicle.targetEnt = spawn( "script_model", ( 0, 0, 0 ) );
	vehicle.targetEnt setModel( "tag_origin" );
	vehicle.targetEnt.angles = ( -90, 0, 0 );
	
	if ( IsDefined( vehicle.mgTurret ) && ( !IsDefined( ignoreTurret ) || !ignoreTurret ) )
	{
		vehicle.mgTurret SetTargetEntity( vehicle.targetEnt );
		vehicle.mgTurret TurretSetGroundAimEntity( vehicle.targetEnt );
	}
	else
	{
		vehicle SetOtherEnt( vehicle.targetEnt );
	}
	
	thread _cleanupShootingLocationOnDeath( vehicle, effect );
	
	if ( IsDefined( effect ) )
	{
		PlayFXOnTagForClients( effect, vehicle.targetEnt, "tag_origin", self );
		vehicle thread showReticleToEnemies( effect );
	}
	
	if ( IsDefined( vehicle.hasAIOption ) && vehicle.hasAIOption )
		return;
	
	while ( true )
	{
		// AI version should manually manage the position of AssaultVeh.TargetEnt
		start = self GetViewOrigin();
		angles = self GetPlayerAngles();
		forward	  = AnglesToForward( angles );
		end = start + ( forward * 8000 );
		traceResult = BulletTrace( start, end, false, vehicle );
		vehicle.targetEnt.origin = traceResult[ "position" ];
		waitframe();
	}
}

showReticleToEnemies( effect )	
{
	self endon ("death");
	self endon ( "end_remote" );
	
	if ( !level.hardcoreMode )
	{
		foreach ( player in level.players )
		{
			if ( self.owner isEnemy( player ) )
			{
				waitframe();
				PlayFXOnTagForClients( effect, self.targetEnt, "tag_origin", player );
			}
		}
	}
}

_cleanupShootingLocationOnDeath( vehicle, effect )
{
	vehicle waittill_any( "death", "stopShootLocationUpdate" );
	
	if ( IsDefined( vehicle.targetEnt ) )
	{
		targetEnt = vehicle.targetEnt;
		if ( IsDefined( effect ) )
			StopFXOnTag( effect, targetEnt, "tag_origin" );
		waitframe();
		targetEnt delete();
	}
}

playerHandleExhaustFx( vehicle, effectRef, tag, playerEndonString ) // self == player
{
	vehicle endon( "death" );
	if ( IsDefined( playerEndonString ) )
		self endon( playerEndonString );

	PlayFXOnTag( getfx( effectRef ), vehicle, tag );
	
	self thread playerDeleteExhaustFxOnVehicleDeath( vehicle, effectRef, tag );
	
	if ( !vehicle.hasCloak )
		return;
	
	while ( true )
	{
		self waittill( "Cloak" );
		StopFXOnTag( getfx( effectRef ), vehicle, tag );
		waitframe();
		PlayFXOnTagForClients( getfx( effectRef ), vehicle, tag, self );
		
		self waittill( "UnCloak" );
		StopFXOnTag( getfx( effectRef ), vehicle, tag );
		waitframe();
		PlayFXOnTag( getfx( effectRef ), vehicle, tag );
	}
}

playerDeleteExhaustFxOnVehicleDeath( vehicle, effectRef, tag ) // self == player
{
	vehicle waittill( "death" );
	
	KillFXOnTag( getfx( effectRef ), vehicle, tag );
}

setDroneVisionAndLightSetPerMap( delay, vehicle )		// self == player
{
	self endon( "disconnect" );
	vehicle endon( "death" );
	
	wait( delay );
	
	if ( IsDefined( level.droneVisionSet ) )
	{
		self SetClientTriggerVisionSet( level.droneVisionSet, 0 );
	}
	
	if ( IsDefined( level.droneLightSet ) )
	{
		self LightSetForPlayer( level.droneLightSet );
	}
}

removeDroneVisionAndLightSetPerMap( delay )		// self == player
{
	self SetClientTriggerVisionSet( "", delay );
	self LightSetForPlayer( "" );
}

playerWatchForDroneEMP( vehicle )
{
	level endon( "game_ended" );
	vehicle endon( "death" );
	self endon( "assaultDroneHunterKiller" );

	vehicle waittill( "emp_damage" );
	
	vehicle notify( "death" );
}