//=============================================================================
// KFAIController_ZedHusk
//=============================================================================
//
//=============================================================================
// Killing Floor 2
// Copyright (C) 2015 Tripwire Interactive LLC
//=============================================================================

class KFAIController_ZedHusk extends KFAIController_Ranged;

var bool bBaseCommandInitialized;

var float	MinDistanceToSuicide;
var float	RequiredHealthPercentForSuicide;
var vector	LastEnemySeenPosition;

var float LastFireBallTime;
var float LastFlameThrowerTime;

/** The time until we shoot again based on difficulty without any randomization */
var float BaseTimeBetweenFireBalls;
/** The time to wait until our next shot, gets randomized with each shot */
var float TimeBetweenFireBalls;

/** The time to wait until we can use the flamethrower again*/
var float TimeBetweenFlameThrower;

var float FireballRandomizedValue;

/** The maximum distance an enemy can be from our pawn for us to be able to do a flamethrower attack */
var int MaxDistanceForFlameThrower;

/** The minimum distance an enemy must be from our pawn for us to be able to do a fireball attack */
var int MinDistanceForFireBall;

/** The maximum distance an enemy can be from our pawn for us to be able to do a fireball attack */
var int MaxDistanceForFireBall;

var bool bCanUseFlameThrower;

var float LastCheckSpecialMoveTime;
var float CheckSpecialMoveTime;

/** Fireball projectile attack */
var name							FireballSocketName;
/** Accuracy spread of fireball aim attack */
var const	float					FireballAimError;
/** Accuracy spread of leading a target for fireball aim attack */
var const	float					FireballLeadTargetAimError;
/** Speed of the fireball. Will override projectile speed. */
var const	float					FireballSpeed;

/** Chance to aim fireballs for splash damage on Normal difficulty */
var const	float					SplashAimChanceNormal;
/** Chance to aim fireballs for splash damage on Hard difficulty */
var const	float					SplashAimChanceHard;
/** Chance to aim fireballs for splash damage on Suicide difficulty */
var const	float					SplashAimChanceSuicidal;
/** Chance to aim fireballs for splash damage on Hell On Earth difficulty */
var const	float					SplashAimChanceHellOnEarth;

/** The base time to wait between firing fireballs on Normal difficulty */
var const float					FireballFireIntervalNormal;
/** The base time to wait between firing fireballs on Hard difficulty */
var const float					FireballFireIntervalHard;
/** The base time to wait between firing fireballs on Suicidal difficulty */
var const float					FireballFireIntervalSuicidal;
/** The base time to wait between firing fireballs on Hell On Earth difficulty */
var const float					FireballFireIntervalHellOnEarth;

/** How much to scale the used FireballInterval to get the low intensity attack scale */
var const float                 LowIntensityAttackScaleOfFireballInterval;

event PostBeginPlay()
{
	local KFGameInfo KFGI;

	super.PostBeginPlay();

	if( WorldInfo.Game != none )
	{
		KFGI = KFGameInfo(WorldInfo.Game);
		// If the difficulty is hard or higher, enable the flamethrower
		if ( KFGI != none && KFGI.GetModifiedGameDifficulty() >= KFGI.DifficultyInfo.GetDifficultyValue(2))
		{
			bCanUseFlameThrower = true;
		}
	}

	// Determine the interval of allowing fireballs to be fired
    if( Skill == class'KFGameDifficultyInfo'.static.GetDifficultyValue(0) ) // Normal
    {
        BaseTimeBetweenFireBalls = FireballFireIntervalNormal;
    }
    else if( Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(1) ) // Hard
    {
        BaseTimeBetweenFireBalls = FireballFireIntervalHard;
    }
    else if( Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(2) ) // Suicidal
    {
        BaseTimeBetweenFireBalls = FireballFireIntervalSuicidal;
    }
    else // Hell on Earth
    {
        BaseTimeBetweenFireBalls = FireballFireIntervalHellOnEarth;
    }

    // Set the low intensity attack cooldown based off the current fireball interval
    LowIntensityAttackCooldown = BaseTimeBetweenFireBalls * LowIntensityAttackScaleOfFireballInterval;

	TimeBetweenFireBalls = BaseTimeBetweenFireBalls + RandRange(-FireballRandomizedValue, FireballRandomizedValue);
}

simulated function Tick( FLOAT DeltaTime )
{
	local float DistToTargetSq;

	super.Tick(DeltaTime);

	if( Role == ROLE_Authority && Enemy != none && MyKFPawn != none )
	{
		// Do not check every tick
		if( `TimeSince(LastCheckSpecialMoveTime) >= CheckSpecialMoveTime && !MyKFPawn.IsDoingSpecialMove() )
		{
			if( GetActiveCommand() != none && !GetActiveCommand().IsA('AICommand_SpecialMove') )
			{
				// Trace from worldinfo, open doors ignore traces from zeds
				if( WorldInfo.FastTrace( Enemy.Location, Pawn.Location,, true ) )
				{
					DistToTargetSq = VSizeSq( Enemy.Location - Pawn.Location );

					// If you are suicidal, do not even try to use the flamethrower or fireball
					if( IsSuicidal() )
					{
						if( CanDoSuicide(DistToTargetSq) )
						{
							class'AICommand_Husk_Suicide'.static.Suicide(self);
						}
					}
					// Check if i can use my flamethrower
					else if( CanDoFlamethrower(DistToTargetSq) )
					{
                    	if( KFGameInfo(WorldInfo.Game) != none && KFGameInfo(WorldInfo.Game).GameConductor != none )
                    	{
                    	   KFGameInfo(WorldInfo.Game).GameConductor.UpdateOverallAttackCoolDowns(self);
                    	}

						class'AICommand_HuskFlameThrowerAttack'.static.FlameThrowerAttack(self);
					}
					// Check if i can use my projectile
					else if( CanDoFireball(DistToTargetSq) )
					{
                    	if( KFGameInfo(WorldInfo.Game) != none && KFGameInfo(WorldInfo.Game).GameConductor != none )
                    	{
                    	   KFGameInfo(WorldInfo.Game).GameConductor.UpdateOverallAttackCoolDowns(self);
                    	}

						class'AICommand_HuskFireBallAttack'.static.FireBallAttack(self);
						// Randomize the next fireball time
						TimeBetweenFireBalls = BaseTimeBetweenFireBalls + RandRange(-FireballRandomizedValue, FireballRandomizedValue);
					}
				}
			}
			LastCheckSpecialMoveTime = WorldInfo.TimeSeconds;
		}
	}
}

function bool IsSuicidal()
{
	if( MyKFPawn == none || MyKFPawn.bIsHeadless )
	{
		return false;
	}
	return( GetHealthPercentage() <= RequiredHealthPercentForSuicide );
}

function bool CanDoSuicide( float DistToTargetSq )
{
	 if( DistToTargetSq <= (MinDistanceToSuicide * MinDistanceToSuicide) &&
		MyKFPawn.CanDoSpecialMove(SM_Suicide) )
	 {
		 return true;
	 }
	 return false;
}

function bool CanDoFlamethrower( float DistToTargetSq )
{
    if( !CheckOverallCooldownTimer() )
    {
        return false;
    }

	 if( bCanUseFlameThrower &&
		(LastFlameThrowerTime == 0 || (`TimeSince(LastFlameThrowerTime) > TimeBetweenFlameThrower)) &&
		DistToTargetSq <= MaxDistanceForFlameThrower * MaxDistanceForFlameThrower &&
		MyKFPawn.CanDoSpecialMove(SM_HoseWeaponAttack) )
	 {
		 return true;
	 }
	 return false;
}

function bool CanDoFireball( float DistToTargetSq )
{
    if( !CheckOverallCooldownTimer() )
    {
        return false;
    }

	 return ((LastFireBallTime == 0 || (`TimeSince(LastFireBallTime) > TimeBetweenFireBalls))
	 	&& DistToTargetSq >= Square(MinDistanceForFireBall)
	 	&& DistToTargetSq <= Square(MaxDistanceForFireBall)
		&& MyKFPawn.CanDoSpecialMove(SM_StandAndShootAttack));
}

/** Overridden so the husk will not change to an enemy outside his view while doing the fireball attack */
event bool SetEnemy( Pawn NewEnemy )
{
	if( MyKFPawn == none || MyKFPawn.IsDoingSpecialMove(SM_StandAndShootAttack) )
	{
		if( MyKFPawn.NeedToTurn(NewEnemy.Location) )
		{
			`AILog( GetFuncName()$"() rejecting "$NewEnemy$" because current enemy ("$Enemy$") we would need to turn to see it", 'SetEnemy' );
			return false;
		}
	}

	return super.SetEnemy( NewEnemy );
}

/** Always sprint if we are suicidal */
function bool ShouldSprint()
{
	if( IsSuicidal() )
	{
		return true;
	}
	return super.ShouldSprint();
}

function DoStrike()
{
	if( MyKFPawn.PawnAnimInfo.Attacks[PendingAnimStrikeIndex].Tag == 'Projectile' )
	{
		`DialogManager.PlaySpotRocketsDialog( MyKFPawn );
	}

	super.DoStrike();
}

function ShootFireball( class<KFProj_Husk_Fireball> FireballClass, vector FireballOffset )
{
	local vector		SocketLocation, DirToEnemy, HitLocation, HitNormal;
	local KFProj_Husk_Fireball MyFireball;
	local actor			HitActor;
	local Vector AimLocation, GroundAimLocation;
	local float SplashAimChance;
	local vector randVectorDraw;
	local float randDraw;
	local vector displacementToHitLoc;
	local float distanceToHitLoc;
	local KFPawn_ZedHusk MyHuskPawn;

	if( MyKFPawn == none )
	{
		return;
	}

   	if( bDebugAimError )
	{
		// Render debug lines and simulate several shots at once to evaluate aimerror
		DebugAimError(FireballSocketName, FireballAimError);
		return;
	}

	SocketLocation = MyKFPawn.GetPawnViewLocation() + (FireballOffset >> Pawn.GetViewRotation());
	if( MyKFPawn.Health > 0.f && Role == ROLE_Authority && MyKFPawn.IsDoingSpecialMove(SM_StandAndShootAttack) )
	{
        AimLocation = Enemy.Location;

        // Determine the random chance of aiming at the ground for splash damage
        if( Skill == class'KFGameDifficultyInfo'.static.GetDifficultyValue(0) ) // Normal
        {
            SplashAimChance = SplashAimChanceNormal;
        }
        else if( Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(1) ) // Hard
        {
            SplashAimChance = SplashAimChanceHard;
        }
        else if( Skill <= class'KFGameDifficultyInfo'.static.GetDifficultyValue(2) ) // Suicidal
        {
            SplashAimChance = SplashAimChanceSuicidal;
        }
        else // Hell on Earth
        {
            SplashAimChance = SplashAimChanceHellOnEarth;
        }

		randDraw = FRand();

		`AILog_Ext( GetFuncName() @ " SplashAimChance: " @ SplashAimChance @ " randDraw: " @ randDraw, 'FireBall', self );

        if( randDraw < SplashAimChance )
		{
			// Simple pass at making the Husk try and do splash damage when it shoots at a player rather than just shoot directly at them (and most likely miss)
            GroundAimLocation = Enemy.Location - (vect(0,0,1) * Enemy.GetCollisionHeight());

            if( GroundAimLocation.Z < SocketLocation.Z )
            {
                AimLocation = GroundAimLocation;
            }
		}

		//DrawDebugLine( SocketLocation, AimLocation, 0, 0, 255, true );

        HitActor = WorldInfo.Trace( HitLocation, HitNormal, AimLocation, SocketLocation, true );

        // Don't shoot if it's too close
		if( HitActor == Enemy )
		{
			//DrawDebugStar( HitLocation, 50, 0, 255, 0, true );

			randVectorDraw = VRand();

			if( !bCanLeadTarget )
			{
 				DirToEnemy = normal( AimLocation - SocketLocation ) + randVectorDraw * FireballAimError;

				`AILog_Ext( GetFuncName() @ " HitActor: " @ HitActor @ " Is My Enemy: " @ Enemy @ " randVectorDraw: " @ randVectorDraw @ " - not leading with an error of: " @ randVectorDraw * FireballAimError, 'FireBall', self );
			}
 			else
 			{
 				DirToEnemy = normal( CalcAimLocToHit(AimLocation, SocketLocation, FireballSpeed, FireballSpeed, Enemy.Velocity) - SocketLocation ) + randVectorDraw * FireballLeadTargetAimError;

				`AILog_Ext( GetFuncName() @ " HitActor: " @ HitActor @ " Is My Enemy: " @ Enemy @ " randVectorDraw: " @ randVectorDraw @ " - leading with an error of: " @ randVectorDraw * FireballLeadTargetAimError, 'FireBall', self );
 			}
		}
		else
		{
			// If our path to the enemy is blocked, try his last known position
			AimLocation = AICommand_HuskFireBallAttack(GetActiveCommand()).LastKnownEnemyLocation;
			HitActor = Trace( HitLocation, HitNormal, AimLocation, SocketLocation, true );

			displacementToHitLoc = HitLocation - SocketLocation;
			distanceToHitLoc = VSize( displacementToHitLoc );

			// If there is an object in front of us, cancel the attack
			if( distanceToHitLoc < 300 )
			{
				//DrawDebugStar( HitLocation, 50, 255, 0, 0, true );

				`AILog_Ext( GetFuncName() @ " HitActor: " @ HitActor @ " Is NOT My Enemy: " @ Enemy @ " and distanceToHitLoc: " @ distanceToHitLoc @ " is too close so not firing!!!", 'FireBall', self );
				MyKFPawn.SpecialMoves[ SM_StandAndShootAttack ].AbortedByAICommand();
				LastFireBallTime = WorldInfo.TimeSeconds;
				return;
			}
			else // Otherwise fire at the enemies last known position
			{
				//DrawDebugStar( HitLocation, 50, 0, 0, 255, true );

				randVectorDraw = VRand();
				DirToEnemy = normal( (AimLocation - SocketLocation) + randVectorDraw * FireballAimError );

				`AILog_Ext( GetFuncName() @" Fire at enemy last known position: " @ Enemy @ AimLocation @ " randVectorDraw: " @ randVectorDraw @ " - leading with an error of: " @ randVectorDraw * FireballLeadTargetAimError, 'FireBall', self );
			}
		}

		//DrawDebugLine( SocketLocation, SocketLocation + DirToEnemy * 5000.0, 255, 0, 0, true );
		MyHuskPawn = KFPawn_ZedHusk( MyKFPawn );

		// Shoot the fireball
		MyFireball = Spawn( FireballClass, MyKFPawn,, SocketLocation, Rotator(DirToEnemy) );
		MyFireball.Instigator			= MyKFPawn;
		MyFireball.InstigatorController	= self;
		MyFireball.Speed				= FireballSpeed;
		MyFireball.MaxSpeed				= FireballSpeed;

		// Set our difficulty setings
		MyFireball.ExplosionTemplate.MomentumTransferScale = MyHuskPawn.FireballSettings.ExplosionMomentum;
		MyFireball.bSpawnGroundFire = MyHuskPawn.FireballSettings.bSpawnGroundFire;

		// Fire
		MyFireball.Init( DirToEnemy );

		LastFireBallTime = WorldInfo.TimeSeconds;
	}
}

DefaultProperties
{
	BaseShapeOfProjectileForCalc=(X=10,Y=10,Z=10)
	FireballSocketName=FireballSocket
	bUseDesiredRotationForMelee=false
	bCanTeleportCloser=false

	// ---------------------------------------------
	// Behaviors

	EvadeGrenadeChance=0.9f

	CheckSpecialMoveTime=0.25f

	// Fireball
	MinDistanceForFireBall=300
	MaxDistanceForFireBall=4000

	FireballFireIntervalNormal=5.0
	FireballFireIntervalHard=4.5
	FireballFireIntervalSuicidal=4
	FireballFireIntervalHellOnEarth=3.5

	FireballRandomizedValue=1

	SplashAimChanceNormal=0.25
	SplashAimChanceHard=0.35
	SplashAimChanceSuicidal=0.5
	SplashAimChanceHellOnEarth=0.75

	FireballSpeed=3600.f
	FireballAimError=0.03f
	FireballLeadTargetAimError=0.03f
	bDebugAimError=false
	bCanLeadTarget=false

	// FlameThrower
	TimeBetweenFlameThrower=5
	MaxDistanceForFlameThrower=500
	LowIntensityAttackScaleOfFireballInterval=1.25

	// Suicide
	MinDistanceToSuicide=280
	RequiredHealthPercentForSuicide=0.15f
}
