/**
 * This is our base mobile projectile class.
 *
 * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
 */

class MobileProjectile extends Projectile;

/** Acceleration magnitude. By default, acceleration is in the same direction as velocity. */
var(Projectile) float AccelRate;

/** if true, the shutdown function has been called and 'new' effects shouldn't happen */
var bool bShuttingDown;

/** 
 * If TRUE, initializes the projectile immediately when spawned using it rotation.
 * This is required if you use an Actor Factory in Kismet to spawn the projectile.
 */
var(Projectile) bool bInitOnSpawnWithRotation;

/** Effects */
/** This is the effect that is played while in flight */
var ParticleSystemComponent	ProjEffects;

/** Effects Template */
/** Effect template for the projectile while it is in flight. */
var(Projectile) ParticleSystem ProjFlightTemplate;
/** Effect template when the projectile explodes.  Projectile only explodes if Damage Radius is non-zero. */
var(Projectile) ParticleSystem ProjExplosionTemplate;

/** This value sets the cap how far away the explosion effect of this projectile can be seen */
var(Projectile) float MaxEffectDistance;

/**  The sound that is played when it explodes.  Projectile only explodes if Damage Radius is non-zero. */
var(Projectile) SoundCue	ExplosionSound;

/** Actor types to ignore if the projectile hits them */
var(Projectile) array<class<Actor> >	ActorsToIgnoreWhenHit;

/** used to prevent effects when projectiles are destroyed (see LimitationVolume) */
var bool bSuppressExplosionFX;

/**
 * Explode when the projectile comes to rest on the floor.  It's called from the native physics processing functions.  By default,
 * when we hit the floor, we just explode.
 */
simulated event Landed( vector HitNormal, actor FloorActor )
{
	HitWall(HitNormal, FloorActor, None);
}

/**
 * When this actor begins its life, play any ambient sounds attached to it
 */
simulated function PostBeginPlay()
{
	Super.PostBeginPlay();

	if ( bDeleteMe || bShuttingDown)
		return;

	// Spawn any effects needed for flight
	SpawnFlightEffects();

	if (bInitOnSpawnWithRotation)
	{
		Init( vector(Rotation) );
	}
}

simulated event SetInitialState()
{
	bScriptInitialized = true;
	if (Role < ROLE_Authority && AccelRate != 0.f)
	{
		GotoState('WaitingForVelocity');
	}
	else
	{
		GotoState((InitialState != 'None') ? InitialState : 'Auto');
	}
}

/**
 * Initialize the Projectile
 */
function Init(vector Direction)
{
	SetRotation(rotator(Direction));

	Velocity = Speed * Direction;
	Acceleration = AccelRate * Normal(Velocity);
}

/**
 *
 */
simulated function ProcessTouch(Actor Other, Vector HitLocation, Vector HitNormal)
{

	if( ActorsToIgnoreWhenHit.Find(Other.Class) != INDEX_NONE )
	{
		// The hit actor is one that should be ignored
		return;
	}


	if (DamageRadius > 0.0)
	{
		Explode(HitLocation, HitNormal);
	}
	else
	{
		PlaySound(ImpactSound);
		Other.TakeDamage(Damage,InstigatorController,HitLocation,MomentumTransfer * Normal(Velocity), MyDamageType,, self);
		Shutdown();
	}
}

/**
 * Explode this Projectile
 */
simulated function Explode(vector HitLocation, vector HitNormal)
{
	if (Damage>0 && DamageRadius>0)
	{
		if ( Role == ROLE_Authority )
			MakeNoise(1.0);
		if ( !bShuttingDown )
		{
			ProjectileHurtRadius(HitLocation, HitNormal);
		}
		SpawnExplosionEffects(HitLocation, HitNormal);
	}
	else 
	{
		PlaySound(ImpactSound);
	}

	ShutDown();
}


/**
 * Spawns any effects needed for the flight of this projectile
 */
simulated function SpawnFlightEffects()
{
	if (WorldInfo.NetMode != NM_DedicatedServer && ProjFlightTemplate != None)
	{
		ProjEffects = WorldInfo.MyEmitterPool.SpawnEmitterCustomLifetime(ProjFlightTemplate, true);
		ProjEffects.SetAbsolute(false, false, false);
		ProjEffects.SetLODLevel(WorldInfo.bDropDetail ? 1 : 0);
		ProjEffects.OnSystemFinished = MyOnParticleSystemFinished;
		ProjEffects.bUpdateComponentInTick = true;
		ProjEffects.SetTickGroup(TG_EffectsUpdateWork);
		AttachComponent(ProjEffects);
		ProjEffects.ActivateSystem(true);
	}

	if (SpawnSound != None)
	{
		PlaySound(SpawnSound);
	}
}

/** sets any additional particle parameters on the explosion effect required by subclasses */
simulated function SetExplosionEffectParameters(ParticleSystemComponent ProjExplosion);

/**
 * Spawn Explosion Effects
 */
simulated function SpawnExplosionEffects(vector HitLocation, vector HitNormal)
{
	local ParticleSystemComponent ProjExplosion;
	local Actor EffectAttachActor;

	if (WorldInfo.NetMode != NM_DedicatedServer)
	{
		if (EffectIsRelevant(Location, false, MaxEffectDistance))
		{
			if (ProjExplosionTemplate != None)
			{
				EffectAttachActor = ImpactedActor;
				ProjExplosion = WorldInfo.MyEmitterPool.SpawnEmitter(ProjExplosionTemplate, HitLocation, rotator(HitNormal), EffectAttachActor);
				SetExplosionEffectParameters(ProjExplosion);
			}

			if (ExplosionSound != None)
			{
				PlaySound(ExplosionSound, true);
			}
		}

		bSuppressExplosionFX = true; // so we don't get called again
	}
}

/**
 * Clean up
 */
simulated function Shutdown()
{
	local vector HitLocation, HitNormal;

	bShuttingDown=true;
	HitNormal = normal(Velocity * -1);
	Trace(HitLocation,HitNormal,(Location + (HitNormal*-32)), Location + (HitNormal*32),true,vect(0,0,0));

	SetPhysics(PHYS_None);

	if (ProjEffects!=None)
	{
		ProjEffects.DeactivateSystem();
	}

	HideProjectile();
	SetCollision(false,false);

	Destroy();
}

// If this actor

event TornOff()
{
	ShutDown();
	Super.TornOff();
}

/**
 * Hide any meshes/etc.
 */
simulated function HideProjectile()
{
	local MeshComponent ComponentIt;
	foreach ComponentList(class'MeshComponent',ComponentIt)
	{
		ComponentIt.SetHidden(true);
	}
}

simulated function Destroyed()
{
	if (ProjEffects != None)
	{
		DetachComponent(ProjEffects);
		WorldInfo.MyEmitterPool.OnParticleSystemFinished(ProjEffects);
		ProjEffects = None;
	}

	super.Destroyed();
}

simulated function MyOnParticleSystemFinished(ParticleSystemComponent PSC)
{
	if (PSC == ProjEffects)
	{
		// clear component and return to pool
		DetachComponent(ProjEffects);
		WorldInfo.MyEmitterPool.OnParticleSystemFinished(ProjEffects);
		ProjEffects = None;
	}
}

/** state used only on the client for projectiles with AccelRate > 0 to wait for Velocity to be replicated so we can use it to set Acceleration
 *	the alternative would be to make Velocity repnotify in Actor.uc, but since many Actors (such as Pawns) change their
 *	velocity very frequently, that would have a greater performance hit
 */
state WaitingForVelocity
{
	simulated function Tick(float DeltaTime)
	{
		if (!IsZero(Velocity))
		{
			Acceleration = AccelRate * Normal(Velocity);
			GotoState((InitialState != 'None') ? InitialState : 'Auto');
		}
	}
}

simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out float out_FOV )
{
	out_CamLoc = Location + (CylinderComponent.CollisionHeight * Vect(0,0,1));
	return true;
}

/** called when this Projectile is the ViewTarget of a local player
 * @return the Pawn to use for rendering HUD displays
 */
simulated function Pawn GetPawnOwner();


defaultproperties
{
	Speed=2000
	MaxSpeed=5000
	AccelRate=15000

	Damage=10
	DamageRadius=0
	MomentumTransfer=500
	LifeSpan=2.0

	bCollideWorld=true
	DrawScale=2.0

	bInitOnSpawnWithRotation=true
	bShuttingDown=false
}