//=============================================================================
// KFProj_Missile_Patriarch
//=============================================================================
// High explosive missile fired from Patriarch weapons platform.
// Based on flocking code from UT3
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================
class KFProj_Missile_Patriarch extends KFProjectile
	hidedropdown;

/** Used for the curling missile effect */
var KFProj_Missile_Patriarch Flock[2];

var float InitialSpeed;

var float FlockRadius;
var float FlockStiffness;
var float FlockMaxForce;
var float FlockCurlForce;

var repnotify byte bCurl;
var float VelocityScale;

var vector Dir;

/** Applies wobble to the missile, making it seem erratic. Simulates real liquid state thrusters */
var bool bWobble;
var float WobbleForce;

/** How long to wait after spawning before starting to seek */
var float SeekDelay;

/** How strong a force should be applied to the missile when seeking */
var float SeekForce;

/** Gravity application */
var float GravForce;
var float DistToApplyGravitySQ;

/** Target impact location for seeking */
var vector TargetImpactLoc;

/** Flight light */
var PointLightComponent FlightLight;

replication
{
	if ( bNetInitial && (Role == ROLE_Authority) )
		bCurl, FlockCurlForce, VelocityScale, TargetImpactLoc,
		SeekDelay, SeekForce, GravForce, DistToApplyGravitySQ, InitialSpeed;
}

simulated protected function PrepareExplosionTemplate()
{
	super.PrepareExplosionTemplate();
	
	/** Since bIgnoreInstigator is transient, its value must be defined here */
	ExplosionTemplate.bIgnoreInstigator = true;
}

function InitEx( vector AimDir, float CurlForceMultiplier, vector TargetLoc, float InitialMissileSpeed,
				float NewSeekDelay, float NewSeekForce, float NewGravForce, float NewDistToApplyGravitySQ )
{
	Init( AimDir );

	// Our base speed
	InitialSpeed = InitialMissileSpeed;
	Speed = InitialSpeed;

	// Do some randomization for an even cooler effect
	FlockCurlForce = (default.FlockCurlForce * (0.95f + (fRand()*0.1f))) * CurlForceMultiplier;
	VelocityScale = 0.95f + (fRand()*0.1f);

	// Our impact target location
	TargetImpactLoc = TargetLoc;

	// Set our seek delay
	SeekDelay = NewSeekDelay;

	// Set our seek force
	SeekForce = NewSeekForce;

	// Set our gravity force
	GravForce = NewGravForce;

	// Set our distance before we apply gravity
	DistToApplyGravitySQ = NewDistToApplyGravitySQ;
}

simulated function StartCurlTimer()
{
	SetTimer( 0.06f, true, nameOf(CurlTimer) );
}

simulated event ReplicatedEvent(name VarName)
{
	local KFProj_Missile_Patriarch R;
	local int i;

	if( VarName == 'bCurl' )
	{
		StartCurlTimer();

		// look for other missiles
		if( bCurl < 3 && Flock[1] == None )
		{
			foreach DynamicActors( class'KFProj_Missile_Patriarch', R )
			{
				if( R.Instigator == Instigator )
				{
					Flock[i] = R;
					if ( R.Flock[0] == None )
					{
						R.Flock[0] = self;
					}
					else if ( R.Flock[0] != self )
					{
						R.Flock[1] = self;
					}

					i++;
					if ( i == 2 )
					{
						break;
					}
				}
			}
		}
	}
	else
	{
		Super.ReplicatedEvent(VarName);
	}
}

simulated event Tick( float DeltaTime )
{
	super.Tick( DeltaTime );

	if( FlightLight != none && WorldInfo.NetMode != NM_DedicatedServer )
	{
		FlightLight.Radius = 120.f + FlightLight.default.Radius * Abs( Cos(WorldInfo.TimeSeconds * (DeltaTime * 800.f)) );
	}

	// Finalize rotation
	SetRotation( rotator(Velocity) );	
}

simulated function CurlTimer()
{
	local vector TargetVec, SeekDir, X,Y,Z;

	// Initialize Dir, if necessary
	if( SeekForce > 0.f )
	{
		if( bCurl == 3 || IsZero(Dir) )
		{
			Dir = Normal(Velocity);
		}

		if( bCurl < 3 )
		{
			Velocity = (InitialSpeed * Normal(Dir * 0.5 * InitialSpeed + Velocity)) * VelocityScale;
		}

		TargetVec = TargetImpactLoc - Location;

		// Add seeking
		if( `TimeSince(CreationTime) >= SeekDelay )
		{
			SeekDir = Normal(TargetVec);
			if( GravForce > 0.f )
			{
				SeekDir.Z = 0.f;
			}

			Velocity += SeekDir * SeekForce;

			// Apply pseudo-gravity
			if( GravForce > 0.f && TargetVec.Z < Location.Z )
			{
				TargetVec.Z = 0.f;
				if( VSizeSQ(TargetVec) <= DistToApplyGravitySQ )
				{
					Velocity += vect(0,0,-1) * GravForce;
				}
				else
				{
					Velocity += vect(0,0,-1) * GravForce * 0.1f;
				}
			}
		}

		if( bCurl < 3 )
		{
			DoFlocking();
		}

		// Update our rotation before adding wobble
		SetRotation( rotator(Velocity) );
	}

	// Add some crazy wobble
	GetAxes( Rotation, X,Y,Z );
	Velocity += (bWobble ? Z * WobbleForce : -Z * WobbleForce)
				+ (bWobble ? Y * WobbleForce : -Y * WobbleForce);

	bWobble = !bWobble;
}

simulated function DoFlocking()
{
	local int i;
	local vector ForceDir, CurlDir;
	local float ForceMag;

	// Curl missiles around each other
	for( i = 0; i < 2; i++ )
	{
		if( Flock[i] == None )
			continue;

		// Attract if distance between missiles is over 2*FlockRadius, repulse if below.
		ForceDir = Flock[i].Location - Location;
		ForceMag = FlockStiffness * ( (2 * FlockRadius) - VSize(ForceDir) );
		Acceleration = Normal(ForceDir) * Min(ForceMag, FlockMaxForce*VelocityScale);

		// Vector 'curl'
		CurlDir = Flock[i].Velocity Cross ForceDir;
		if( bCurl == Flock[i].bCurl )
			Acceleration += Normal(CurlDir) * FlockCurlForce;
		else
			Acceleration -= Normal(CurlDir) * FlockCurlForce;
	}
}

/** Overloaded to apply direct damage on hit */
simulated function bool TraceProjHitZones(Pawn P, vector EndTrace, vector StartTrace, out array<ImpactInfo> out_Hits)
{
	if( P != none )
	{
		P.TakeDamage( Damage, InstigatorController, StartTrace, MomentumTransfer * Normal(Velocity), MyDamageType,, self );
		return true;
	}

	return false;
}

/** Stops projectile simulation without destroying it.  Projectile is resting, essentially. */
simulated protected function StopSimulating()
{
	super.StopSimulating();

	FlightLight.SetEnabled( false );
	DetachComponent( FlightLight );
	FlightLight = none;
}

simulated function Destroyed()
{
	ClearTimer( nameOf(CurlTimer) );

	super.Destroyed();
}

defaultproperties
{
	Physics=PHYS_Projectile
	// Speeds below are overridden by KFPawn_ZedHusk FireballSpeed property
	Speed=2000
	MaxSpeed=2000
	ProjFlightTemplate=ParticleSystem'ZED_Patriarch_EMIT.FX_Patriarch_Rocket_Projectile'
	ExplosionActorClass=class'KFExplosionActor'

	FlockRadius=10.f
	FlockStiffness=-20.f
	FlockMaxForce=700.f
	FlockCurlForce=3600.f
	WobbleForce=120.f
	SeekForce=10.f
	RotationRate=(Roll=50000)

	Damage=200
	MyDamageType=class'KFDT_Explosive_PatMissile'
	MomentumTransfer=1000.f

	// CollideActors=true allows detection via OverlappingActors or CollidingActors (for Siren scream)
	Begin Object Name=CollisionCylinder
		CollisionRadius=5.f
		CollisionHeight=5.f
		BlockNonZeroExtent=false
		CollideActors=true
	End Object

	// Flight light
	Begin Object Class=PointLightComponent Name=FlightPointLight
	    LightColor=(R=255,G=205,B=150,A=255)
		Brightness=1.5f
		Radius=120.f
		FalloffExponent=10.f
		CastShadows=false
		CastStaticShadows=false
		CastDynamicShadows=false
		bCastPerObjectShadows=false
		bEnabled=true
		LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
	End Object
	FlightLight=FlightPointLight
	Components.Add(FlightPointLight)

	// Grenade explosion light
	Begin Object Class=PointLightComponent Name=ExplosionPointLight
	    LightColor=(R=245,G=190,B=140,A=255)
		Brightness=4.f
		Radius=1400.f
		FalloffExponent=10.f
		CastShadows=False
		CastStaticShadows=FALSE
		CastDynamicShadows=False
		bCastPerObjectShadows=false
		bEnabled=FALSE
		LightingChannels=(Indoor=TRUE,Outdoor=TRUE,bInitialized=TRUE)
	End Object

	// explosion
	Begin Object Class=KFGameExplosion Name=ExploTemplate0
		Damage=70
		DamageRadius=650
		DamageFalloffExponent=2.f
		DamageDelay=0.f

		// Damage Effects
		MyDamageType=class'KFDT_Explosive_PatMissile'
		KnockDownStrength=100
		FractureMeshRadius=200.0
		FracturePartVel=500.0
		ExplosionEffects=KFImpactEffectInfo'WEP_Patriarch_ARCH.Missile_Explosion'
		ExplosionSound=AkEvent'WW_WEP_SA_RPG7.Play_WEP_SA_RPG7_Explosion'

        // Dynamic Light
        ExploLight=ExplosionPointLight
        ExploLightStartFadeOutTime=0.0
        ExploLightFadeOutTime=0.5

		// Camera Shake
		CamShake=CameraShake'FX_CameraShake_Arch.Grenades.Default_Grenade'
		CamShakeInnerRadius=200
		CamShakeOuterRadius=700
		CamShakeFalloff=1.f
		bOrientCameraShakeTowardsEpicenter=true
	End Object
	ExplosionTemplate=ExploTemplate0

	Begin Object Class=AkComponent name=AmbientAkSoundComponent
		bStopWhenOwnerDestroyed=true
        bForceOcclusionUpdateInterval=true
        OcclusionUpdateInterval=0.1f
    End Object
    AmbientComponent=AmbientAkSoundComponent
    Components.Add(AmbientAkSoundComponent)

	bAutoStartAmbientSound=true
	bStopAmbientSoundOnExplode=true
	AmbientSoundPlayEvent=AkEvent'WW_ZED_Patriarch.Play_Mini_Rocket_Trail_1'
    //AmbientSoundStopEvent=AkEvent'WW_ZED_Husk.ZED_Husk_SFX_Ranged_Shot_LP_Stop'
}