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

// Killstreak Data
CONST_DAMAGE_RADIUS = 128;
CONST_DAMAGE_MIN = 2;
CONST_DAMAGE_MAX = 8;
CONST_FIRE_TIME = 5;
CONST_FIRE_RADIUS_SQ = 70 * 70;
CONST_FIRE_DIST_RADIUS_SQ = 50 * 50;
CONST_FIRE_HEIGHT = 80;

init()
{
	level.solar_killstreak_duration = 30;
	
	//FX
	level.solar_fire_fx	= LoadFX( "vfx/fire/fire_xsglow_runner_5s" );

	//Sounds
	level.solar_reflector_sfx = "mp_solar_array_player";
	level.solar_reflector_target_sfx = "mp_solar_array_target";
	
	
	level.killstreakFuncs["mp_solar"] = ::tryUseSolarReflector;
	level.mapKillStreak = "mp_solar";
	level.mapKillstreakPickupString = &"MP_SOLAR_MAP_KILLSTREAK_PICKUP";
	level.mapKillStreakDamageFeedbackSound = ::handleDamageFeedbackSound;
	
	level.mapCustomBotKillstreakFunc = ::setupBotsForMapKillstreak;
	
	level.killstreakWieldWeapons["killstreak_solar_mp"] = "mp_solar";
}

setupBotsForMapKillstreak()
{
	level thread maps\mp\bots\_bots_ks::bot_register_killstreak_func( "mp_solar", maps\mp\bots\_bots_ks::bot_killstreak_never_use, maps\mp\bots\_bots_ks::bot_killstreak_do_not_use );
}

tryUseSolarReflector( lifeId, modules )
{
	if ( isDefined( level.solar_reflector_player ) )
	{
		self iPrintLnBold( &"MP_SOLAR_REFLECTOR_IN_USE" );
		return false;
	}

	if ( self isUsingRemote() )
	{
		return false;
	}
	
	if ( self isAirDenied() )
	{
		return false;
	}
	
	if ( self isEMPed() )
	{
		return false;
	}

	result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak();
	if ( result != "success" )
		return false;
	self setUsingRemote( "mp_solar" );
	
	result = setSolarReflectorPlayer( self );
	
	// this needs to get set after we say the player is using it because this could get set to true and then they leave the game
	// this fixes a bug where a player calls it, leaves before getting fully in it and then no one else can call it because it thinks it's being used
	if( IsDefined( result ) && result )
	{
		self maps\mp\_matchdata::logKillstreakEvent( "mp_solar", self.origin );		//TODO: set this up for the Solar Reflector.
	}
	else
	{
		if ( self isUsingRemote() )
			self clearUsingRemote();
	}

	return ( IsDefined( result ) && result );
}


setSolarReflectorPlayer( player )
{
	self endon ( "solar_reflector_player_removed" );

	level.solar_reflector_player = player;
	
	thread teamPlayerCardSplash( "used_mp_solar", player );
	
	thread onPlayerConnect();
	thread setupPlayerDeath();
	
	player thread overlay();
	
	player thread runBeam();
	
	player thread removeSolarReflectorPlayerAfterTime( level.solar_killstreak_duration );
	player thread removeSolarReflectorPlayerWatch();
	player thread removeSolarReflectorLevelWatch();
	player thread removeSolarReflectorPlayerOnCommand();
	
	return true;
}

beamMinimap(ground_ent)
{
	solar_reflector_friendShader = "compassping_orbitallaser_friendly";
	solar_reflector_foeShader = "compassping_orbitallaser_hostile";
	
	currentObj = maps\mp\gametypes\_gameobjects::getNextObjID();
	objective_add( currentObj, "invisible", (0,0,0) );
	objective_OnEntity( currentObj, ground_ent );
	objective_state( currentObj, "active" );
	if(level.teambased)
		objective_team( currentObj, self.team );
	else
		objective_player( currentObj, self GetEntityNumber() );
	objective_icon( currentObj, solar_reflector_friendShader );
	friendlyObjID = currentObj;
			
	currentObj = maps\mp\gametypes\_gameobjects::getNextObjID();
	objective_add( currentObj, "invisible", (0,0,0) );
	objective_OnEntity( currentObj, ground_ent );
	objective_state( currentObj, "active" );
	if(level.teamBased)
		objective_team( currentObj, level.otherTeam[ self.team ] );
	else
		objective_playerenemyteam( currentObj, self GetEntityNumber() );
	objective_icon( currentObj, solar_reflector_foeShader );
	enemyObjID = currentObj;
	
	level waittill( "solar_reflector_player_removed" );
	
	_objective_delete( friendlyObjID );
	_objective_delete( enemyObjID );
}

beamSounds(cam_ent, ground_ent)
{
	waitframe();
	ground_ent PlayLoopSound( level.solar_reflector_target_sfx );
	cam_ent PlayLoopSound( level.solar_reflector_sfx );
	
	PlaySoundAtPos( cam_ent.origin, "array_beam_start");
	
	level waittill( "solar_reflector_player_removed" );
	
	PlaySoundAtPos( cam_ent.origin, "array_beam_stop");
	ground_ent StopLoopSound();
	cam_ent StopLoopSound();
}

runBeam()
{
	cam_pos = GetStruct("solar_cam_pos", "targetname");
	beam_pos = GetStruct("solar_beam_pos", "targetname");
	ground_pos = GetStruct("solar_ground_pos", "targetname");
	
	ground_ent = getGroundEnt(ground_pos);
	
	cam_ent = getCameraEnt(cam_pos, ground_pos);
	self thread playerSetCamera(cam_ent);
	
	beam_ent = getBeamEnt(beam_pos, ground_pos);
	
	self thread beamMinimap(ground_ent);
	self thread beamSounds(cam_ent, ground_ent);
	//self thread beamStartFires(ground_ent);
	//self thread beamGroundFx(ground_ent); //Moved to laser gdt
	runBeamUpdate(beam_ent, cam_ent, ground_ent);
	
	beam_ent.killCamEnt Delete();
	beam_ent Delete();
	cam_ent Delete();
	ground_ent Delete();
}

getCameraEnt(cam_pos, ground_pos)
{
	cam_ent = Spawn("script_model", cam_pos.origin );
	cam_ent.angles = VectorToAngles(ground_pos.origin - cam_pos.origin);
	cam_ent SetModel("tag_player");
	
	return cam_ent;
}

getGroundEnt(ground_pos)
{
	ground_ent = Spawn("script_model", ground_pos.origin);
	ground_ent.angles = (0,0,0);
	ground_ent SetModel("tag_origin");
	return ground_ent;
}

playerSetCamera(cam_ent)
{
	cam_ent endon("death");
	/#
	if ( GetDvarInt( "test_mp_solar_killstreak_death", 0 ) != 0 )
		return;
	#/
	
	self PlayerLinkWeaponViewToDelta( cam_ent, "tag_player", 1.0, 40, 40, 12, 10 );
	self SetPlayerAngles( cam_ent GetTagAngles( "tag_player" ) );

	self SetClientOmnvar( "fov_scale", 0.2 );
	self ThermalVisionFOFOverlayOn();
	
	while(1)
	{
		self EnableSlowAim( 0.05, 0.05 );
		
		level waittill( "host_migration_begin" );
		waitframe();
		self SetClientOmnvar( "fov_scale", 0.2 );
		self ThermalVisionFOFOverlayOn();
		maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
	}
}

getBeamEnt(beam_pos, ground_pos)
{
	beam_ent = Spawn("script_model", beam_pos.origin );
	beam_ent.angles = VectorToAngles(ground_pos.origin - beam_pos.origin);
	beam_ent SetModel("tag_laser");
	beam_ent LaserOn( "solar_laser" );
	
	offset = (AnglesToForward(beam_ent.angles) * 5000) - (0,0,100);
	killCamEnt = Spawn("script_model", beam_pos.origin + offset );
	killCamEnt.angles = beam_ent.angles;
	killCamEnt LinkTo(beam_ent);
	beam_ent.killCamEnt = killCamEnt;

	return beam_ent;
}

beamGroundFx(ground_ent)
{
	ground_ent endon ( "death" );
	
	lastFX = undefined;
	while(1)
	{
		waitframe();
		
		if ( !isDefined(ground_ent.surfacetype) )
			continue;
		
		nextFX = beamGetGroundFX(ground_ent.surfacetype);
		if(!IsDefined(lastFX) || lastFX != nextFX)
		{
			
			if(IsDefined(lastFX))
				StopFXOnTag(lastFX, ground_ent, "tag_origin");
			
			PlayFXOnTag(nextFX, ground_ent, "tag_origin");
			
			lastFX = nextFX;
		}
	}
}

beamGetGroundFX(surfacetype)
{
	switch(surfacetype)
	{
		case "water":
		case "water_waist":
			return getfx("steam_column_rising");
		default:
			return getfx("fx_flare_solar");
	}
}

runBeamUpdate(beam_ent, cam_ent, ground_ent)
{
	self endon ( "solar_reflector_player_removed" );
	
	speed = 300;
	trace_dist = 20000;
	
	beam_ent_pos = SpawnStruct();
	beam_ent_pos.origin = ground_ent.origin;

	test_dvar = 0;
	/#
	test_dvar = GetDvarInt( "test_mp_solar_killstreak_death", 0 );
	if ( test_dvar )
		level.test_start_angles = cam_ent.angles;
	#/
	
	while(1)
	{
		goal_angles = self GetPlayerAngles();
		
		/#
		if( test_dvar == 2 )
		{
			offset = Sin((GetTime()/1000) * 3.14 * 10) * 10;
			goal_angles = level.test_start_angles + (0,offset,0);
		}
		else if ( test_dvar != 0 )
		{
			goal_angles = level.test_start_angles;
		}
		#/
		
		
		goal_angles = (goal_angles[0], goal_angles[1], 0); //GetPlayerAngles is is returning with a very small roll
		goal_dir = AnglesToForward(goal_angles);
		
		goal_dist = abs((cam_ent.origin[2] - beam_ent_pos.origin[2])/goal_dir[2]);
		goal_point = cam_ent.origin + goal_dir*goal_dist; //Push the point down the the same plane as the ground;
		
		move_dist = Distance2D(goal_point, beam_ent_pos.origin);
		
		if( move_dist <= speed*0.05 )
		{
			beam_ent_pos.origin = goal_point;
		}
		else
		{
			move_dir = goal_point - beam_ent_pos.origin;
			move_dir = VectorNormalize(move_dir);
			
			beam_ent_pos.origin += move_dir*speed*0.05;
		}
		
		beam_dir = VectorNormalize(beam_ent_pos.origin-beam_ent.origin);
		
		beam_ent RotateTo(VectorToAngles(beam_dir), 0.1);
		//beam_ent.angles = anglessnaptocompressedangles( beam_ent.angles );
		
		start = beam_ent.origin;
		end = start + beam_dir*trace_dist;
		results = BulletTrace(start, end, false);
		
		Assert(results["fraction"]< 1.0);
		ground_ent MoveTo(results["position"], .1);
		ground_ent.surfacetype = results["surfacetype"];
		ground_ent.killCamEnt = beam_ent.killCamEnt;
		ground_ent RadiusDamage( ground_ent.origin, CONST_DAMAGE_RADIUS, CONST_DAMAGE_MAX, CONST_DAMAGE_MIN, self, "MOD_EXPLOSIVE", "killstreak_solar_mp" );
		
		waitframe();
	}
}

handleDamageFeedbackSound()
{
	self.shouldloopdamagefeedback = true;
	self.damagefeedbacktimer = 10;
	
	self PlayLocalSound( "MP_solar_hit_alert" );
	
	self PlayRumbleLoopOnEntity("damage_light");
	
	while ( self.damagefeedbacktimer > 0 )
	{
		self.damagefeedbacktimer--;
		wait( 0.05 );
	}
	
	self StopRumble("damage_light");
	self StopLocalSound( "MP_solar_hit_alert" );
	
	self.shouldloopdamagefeedback = undefined;
}


removeSolarReflectorPlayerOnCommand()
{
	self endon ( "solar_reflector_player_removed" );
	
	while ( true )
	{
		button_hold_time = 0;
		while ( self UseButtonPressed() )
		{
			button_hold_time += 0.05;
			if ( button_hold_time > 0.75 )
			{
				level thread removeSolarReflectorPlayer( self );
				return;
			}
			waitframe();
		}
		waitframe();
	}
}


removeSolarReflectorPlayerWatch()
{
	self endon ( "solar_reflector_player_removed" );
	
	self waittill_any("disconnect", "joined_team", "joined_spectators", "spawned", "killstreak_exit");
	
	level thread removeSolarReflectorPlayer( self );
}


removeSolarReflectorLevelWatch()
{
	self endon ( "solar_reflector_player_removed" );
	
	level waittill ( "game_cleanup" );

	level thread removeSolarReflectorPlayer( self );
}


removeSolarReflectorPlayerAfterTime( removeDelay )
{
	self endon ( "solar_reflector_player_removed" );
	
	wait 1; //Don't start timer till we fade up from black
	
	if ( self _hasPerk( "specialty_blackbox" ) && IsDefined( self.specialty_blackbox_bonus ) )
	{
		removeDelay *= self.specialty_blackbox_bonus;
	}
	
	self thread solarRelectorTimer(removeDelay);
	maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( removeDelay );
	
	/#
	while ( GetDvarInt( "test_mp_solar_killstreak_death", 0 ) != 0 )
		waitframe();
	#/
	
	
	level thread removeSolarReflectorPlayer( self );
}


solarRelectorTimer( removeDelay )
{
	self endon ( "solar_reflector_player_removed" );
	
	endTime = GetTime() + removeDelay*1000;
	
	while(1)
	{
		self SetClientOmnvar("ui_solar_beam_timer", endTime);
		
		level waittill( "host_migration_begin" );
		
		timePassed = maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
		
		endTime += timePassed;
	}
}


removeSolarReflectorPlayer( player )
{
	player notify ( "solar_reflector_player_removed" );
	level notify ( "solar_reflector_player_removed" );
	
	waittillframeend;
	
	if ( IsDefined(player) )
	{
		player clearUsingRemote();

		player show();
		player unlink();

		player ThermalVisionFOFOverlayOff();
		player setBlurForPlayer( 0, 0 );
		player SetClientOmnvar("ui_solar_beam", 0);
		player DisableSlowAim();
		player SetClientOmnvar( "fov_scale", 1 );
	}

	level.solar_reflector_player = undefined;
}


overlay()
{
	self endon("disconnect");
	level endon( "solar_reflector_player_removed" );
	
	wait 1; // Don't show hud till we fade up from black
	self setBlurForPlayer( 1.2, 0 );
	
	self SetClientOmnvar("ui_solar_beam", 1);
}

onPlayerConnect()
{
	level notify( "solarOnPlayerConnect" );
	level endon( "solarOnPlayerConnect" );
	
	level endon( "solar_reflector_player_removed" );
	
	while ( true )
	{
		level waittill( "connected", player );
		
		player.preKilledFunc = ::playerPreKilled;
		player thread onPlayerSpawned();
		player thread playerImmuneToFire();
	}
}

onPlayerSpawned()
{
	level notify( "solarOnPlayerSpawned" );
	level endon( "solarOnPlayerSpawned" );
	
	level endon( "solar_reflector_player_removed" );

	while ( true )
	{
		self waittill( "player_spawned" );
		
		self.hideOnDeath = undefined;
	}
}

setupPlayerDeath()
{
	foreach ( player in level.players )
	{
		if ( !IsDefined( player ) || player == level.solar_reflector_player )
			continue;
		
		player.preKilledFunc = ::playerPreKilled;
		player thread onPlayerSpawned();
	}
}

playerPlayVaporizeFX()
{
	self.hideOnDeath = true;
	offset = ( 0, 0, 30 ); // stand;
	
	stance = self GetStance();
	if ( stance == "crouch" )
		offset = ( 0, 0, 20 );
	else if ( stance == "prone" )
		offset = ( 0, 0, 10 );
	
	PlayFX( getfx( "solar_killstreak_death" ), self.origin + offset );
}

playerPreKilled( eInflictor, attacker, victim, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration, isFauxDeath )
{
	if ( sWeapon == "killstreak_solar_mp" )
		self playerPlayVaporizeFX();
}

playerImmuneToFire()
{
	self endon( "disconnect" );
	
	self.solarImmuneFire = true;
	
	wait CONST_FIRE_TIME;
	
	self.solarImmuneFire = undefined;
}


beamStartFires(ground_ent)
{
	level endon( "solar_reflector_player_removed" );

	fireFxEnt = SpawnFx( level.solar_fire_fx, ( 0, 0, 0 ) );
	
	lastFirePos = ( 0, 0, 0 );
	lastFireTime = GetTime();
	
	while ( true )
	{
		waitframe();
		
		distSq = Distance2DSquared( ground_ent.origin, lastFirePos );
		duration = ( lastFireTime - GetTime() ) / 1000;
		if ( distSq > CONST_FIRE_DIST_RADIUS_SQ || duration > CONST_FIRE_TIME )
		{
			lastFirePos = ground_ent.origin;
			if ( !IsDefined(ground_ent.surfacetype) || !isStrStart( ground_ent.surfacetype, "water_") )
			{
				level thread fireAtPosition( lastFirePos, self );
			}

			lastFireTime = GetTime();
		}
	}
}

fireAtPosition( fireOrigin, killstreakPlayer )
{
	PlayFX( level.solar_fire_fx, fireOrigin );
	
	endTime = GetTime() + ( CONST_FIRE_TIME * 1000 );
	
	while ( GetTime() < endTime )
	{
		foreach ( player in level.players )
		{
			// just connected
			if ( IsDefined( player.solarImmuneFire ) )
				continue;
			
			// below the fire
			if ( player.origin[2] < ( fireOrigin[2] - 5 ) )
				continue;
			
			// above the fire
			if ( player.origin[2] > ( fireOrigin[2] + CONST_FIRE_HEIGHT ) )
				continue;
			
			distSq = Distance2DSquared( player.origin, fireOrigin );
			
			if ( distSq < CONST_FIRE_RADIUS_SQ )
				player DoDamage( 4, fireOrigin, killstreakPlayer, killstreakPlayer, "MOD_EXPLOSIVE", "killstreak_solar_mp" );
		}
		
		wait 0.1;
	}
}
