/******************************************************************************
BasiliskEnergyWave

Creation date: 2011-08-07 10:21
Last change: $Id$
Copyright © 2011, Wormbo
Website: http://www.koehler-homepage.de/Wormbo/
Feel free to reuse this code. Send me a note if you found it helpful or want
to report bugs/provide improvements.
Please ask for permission first, if you intend to make money off reused code.
******************************************************************************/

class BasiliskEnergyWave extends Projectile;


//=============================================================================
// Imports
//=============================================================================

#exec obj load file=EnergyTankEffects2.utx package=WVBasilisk.EnergyWave


//=============================================================================
// Properties
//=============================================================================

var vector ShockwaveExtent;
var class<BasiliskEnergyWave> ReflectedShockWaveClass;


//=============================================================================
// Variables
//=============================================================================

var vector Origin;
var array<Actor> AlreadyHit;
var array<Actor> Shields;
var bool bBlueTeam;


replication
{
	reliable if (bNetInitial)
		bBlueTeam;
}


simulated function PreBeginPlay()
{
	Origin = Location;
	Super.PreBeginPlay();
	UpdateAppearance();
}

simulated function PostBeginPlay()
{
	if (Role == ROLE_Authority && Instigator != None && Instigator.Controller != None) {
		if (Instigator.Controller.ShotTarget != None && Instigator.Controller.ShotTarget.Controller != None)
			Instigator.Controller.ShotTarget.Controller.ReceiveProjectileWarning(Self);

		InstigatorController = Instigator.Controller;
	}

	Velocity = Speed * vector(Rotation);

	bReadyToSplash = True;
}

simulated function SetBlueEffects(bool bIsBlue)
{
	bBlueTeam = bIsBlue;
}

simulated function bool CanSplash()
{
	return False;
}

simulated function Tick(float DeltaTime)
{
	UpdateAppearance();

	// ensure really hitting actors after growing
	SetLocation(Location);
}

simulated function UpdateAppearance()
{
	local float Scale, NewHeight, NewRadius;
	local vector Extent;

	Scale = FClamp((default.LifeSpan - LifeSpan) / default.LifeSpan, 0.1, 1.0);

	SetDrawScale(default.DrawScale * Scale);
	LightRadius = default.LightRadius * Scale;

	Scale = Sqrt(FClamp(1.1 - 1.1 * Scale, 0.0, 1.0));
	LightBrightness = default.LightBrightness * Scale;

	// recalculate collision cylinder
	Extent = DrawScale * ((DrawScale3D * ShockwaveExtent) >> Rotation);
	NewHeight = Abs(Extent.Z);
	Extent.Z = 0;
	NewRadius = VSize(Extent);

	SetCollisionSize(NewRadius, NewHeight);
}

simulated function ClientSideTouch(Actor Other, Vector HitLocation);
simulated function Explode(vector HitLocation, vector HitNormal);
simulated function BlowUp(vector HitLocation);
simulated function HitWall(vector HitNormal, Actor Wall)
{
	log(Self@"HitWall"@HitNormal@Wall);
}

simulated singular function Touch(Actor Other)
{
	local vector HitLocation, HitNormal;

	if (Other == None) // Other just got destroyed in its touch?
		return;

	if (Other.bProjTarget || Other.bBlockActors)
	{
		LastTouched = Other;
		if (Velocity == vect(0,0,0) || Other.IsA('Mover'))
		{
			ProcessTouch(Other, Location);
			LastTouched = None;
			return;
		}

		if (Other.TraceThisActor(HitLocation, HitNormal, Other.Location, Location, vect(1,1,1)))
			HitLocation = Location;

		ProcessTouch(Other, HitLocation);
		LastTouched = None;
		if (Role < ROLE_Authority && Other.Role == ROLE_Authority && Pawn(Other) != None)
			ClientSideTouch(Other, HitLocation);
	}
}


simulated function ProcessTouch(Actor Other, Vector HitLocation)
{
	local int i;
	local float DamageScale;
	local vector HL, HN, X, RefNormal;
	local BasiliskEnergyWave P;

	if (Other == Instigator || Other.bStatic)
		return;

	for (i = AlreadyHit.Length - 1; i >= 0; i--) {
		if (AlreadyHit[i] == Other)
			return;
	}
	AlreadyHit[AlreadyHit.Length] = Other;

	DamageScale = Normal(Velocity) dot Normal(Other.Location - Origin);
	if (!FastTrace(HitLocation, Origin))
		DamageScale *= 0.6;

	if (!FastTrace(Other.Location, Origin))
		DamageScale *= 0.6;

	if (Other.bBlockProjectiles || Other.IsA('ONSPowerCoreShield')) {
		Shields[Shields.Length] = Other;
	}
	else {
		for (i = Shields.Length - 1; i >= 0; i--) {
			if (Shields[i] != None && Shields[i].bCollideActors && !Shields[i].TraceThisActor(HL, HN, Other.Location, Origin, Other.GetCollisionExtent()))
				DamageScale *= 0.3;
		}
		if (xPawn(Other) != None && xPawn(Other).CheckReflect(HitLocation, RefNormal, 0.25 * DamageScale * Damage * Sqrt(LifeSpan / Default.LifeSpan)))
		{
			if (Role == ROLE_Authority && ReflectedShockWaveClass != None)
			{
				X = Normal(Velocity);
				P = Other.Spawn(ReflectedShockWaveClass, Other,, Other.Location + Pawn(Other).EyePosition() + RefNormal * 20, Rotator(RefNormal));
				if (P != None)
				{
					P.Damage *= DamageScale * Sqrt(LifeSpan / Default.LifeSpan);
					P.UpdateAppearance();
					P.SetBlueEffects(bBlueTeam);
				}
			}

			DamageScale *= 0.3;
		}
	}
	Other.TakeDamage(DamageScale * Damage * Sqrt(LifeSpan / Default.LifeSpan), Instigator, HitLocation, DamageScale * MomentumTransfer * Sqrt(LifeSpan / Default.LifeSpan) * Normal(Other.Location - Origin), MyDamageType);

	if (Role == ROLE_Authority)
		MakeNoise(1.0);
}

function bool EncroachingOn(Actor Other)
{
	return False;
}


//=============================================================================
// Default values
//=============================================================================

defaultproperties
{
	bCollideWorld         = False
	bIgnoreOutOfWorld     = True
	bIgnoreEncroachers    = True
	bCollideWhenPlacing   = False

	ShockwaveExtent         = (X=80,Y=210,Z=110)
	ReflectedShockWaveClass = class'BasiliskReflectedEnergyWave'

	DrawType    = DT_None // can't set bHidden, otherwise would not be replicated
	DrawScale   = 4.0
	bUnlit      = True
	AmbientGlow = 254
	PrePivot    = (X=210.0)

	bDynamicLight   = True
	LightEffect     = LE_SpotLight
	LightType       = LT_Steady
	LightRadius     =  30
	LightHue        =  20
	LightSaturation =  50
	LightBrightness = 300

	Speed    = 3000.0
	MaxSpeed = 3000.0
	LifeSpan =    0.5

	Damage           = 300.0
	MyDamageType     = class'DamTypeBasiliskShockwave'
	MomentumTransfer = 30000.0
}

